<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Joaquin Menchaca</title>
    <description>The latest articles on DEV Community by Joaquin Menchaca (@joachim8675309).</description>
    <link>https://dev.to/joachim8675309</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F8758%2F1U3OJ6pH.jpg</url>
      <title>DEV Community: Joaquin Menchaca</title>
      <link>https://dev.to/joachim8675309</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joachim8675309"/>
    <language>en</language>
    <item>
      <title>Architecting Kubernetes Deployments with Python</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Sun, 12 Jul 2026 21:56:20 +0000</pubDate>
      <link>https://dev.to/joachim8675309/architecting-kubernetes-deployments-with-python-4jhe</link>
      <guid>https://dev.to/joachim8675309/architecting-kubernetes-deployments-with-python-4jhe</guid>
      <description>&lt;p&gt;Python is an excellent language for automating cloud infrastructure, but the official Kubernetes Python client leaves developers with an important architectural decision:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Where should Kubernetes manifests live?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Should they be constructed directly with Python objects? Embedded as multiline strings? Or stored as external files and rendered at runtime?&lt;/p&gt;

&lt;p&gt;Each approach works, but they have very different implications for readability, maintainability, and long-term operational cost.&lt;/p&gt;

&lt;p&gt;The key is recognizing that deployment logic and platform configuration evolve on different lifecycles. Your deployment code, the part that authenticates to Kubernetes, renders templates, and applies resources, may remain unchanged for months. Your manifests, however, often change weekly as applications evolve, resource limits are tuned, cloud-provider annotations are added, or networking requirements change.&lt;/p&gt;

&lt;p&gt;When those two concerns are tightly coupled, even a configuration, only change forces you to modify, test, and redeploy the delivery or application code itself. Over time, this increases maintenance costs, slows platform changes, and makes configuration drift and production mistakes more likely.&lt;/p&gt;

&lt;p&gt;This is a familiar software engineering principle: &lt;strong&gt;separate concerns&lt;/strong&gt; that evolve independently. The same thinking that keeps application configuration separate from executable code also applies to Kubernetes manifests. Treating manifests as first-class &lt;strong&gt;configuration artifacts&lt;/strong&gt; allows them to evolve independently from the Python code that delivers them.&lt;/p&gt;

&lt;p&gt;In this article we'll compare three ways of deploying &lt;strong&gt;Kubernetes&lt;/strong&gt; resources with the official &lt;code&gt;kubernetes-python-client&lt;/code&gt;, ranging from tightly coupled implementations to a design that cleanly separates deployment logic from platform configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Landscape at a Glance
&lt;/h2&gt;

&lt;p&gt;The comparison below assumes a common application deployment scenario, where the desired state is largely known ahead of time. Controllers and Operators have fundamentally different requirements where the desiredstate is determined dynamically at runtime. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method¹&lt;/th&gt;
&lt;th&gt;Maintainability²&lt;/th&gt;
&lt;th&gt;Readability³&lt;/th&gt;
&lt;th&gt;Flexibility⁴&lt;/th&gt;
&lt;th&gt;Best Used For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Direct API&lt;br&gt;Object Calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ Medium&lt;/td&gt;
&lt;td&gt;⭐ Low&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐ High&lt;/td&gt;
&lt;td&gt;Dynamic,&lt;br&gt;complex operators,&lt;br&gt;custom controllers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Embedded Strings&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⭐⭐ Low&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ Medium&lt;/td&gt;
&lt;td&gt;⭐⭐ Low&lt;/td&gt;
&lt;td&gt;Small utilities, remediation scripts, one-off automation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External YAML&lt;br&gt;Templates (Jinja2)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐ High&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐ High&lt;/td&gt;
&lt;td&gt;⭐⭐⭐ Medium&lt;/td&gt;
&lt;td&gt;Declarative,&lt;br&gt;Standardized application deployments,&lt;br&gt;CI/CD pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Method&lt;/strong&gt; refers to the overall approach used to define and deploy Kubernetes resources, not just features of the official &lt;code&gt;kubernetes-python-client&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt; measures the cost to safely modify the solution over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readability&lt;/strong&gt; measures ability of the engineer quickly determine the desired state and confidently modify it?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt; measures the ability to determine or modify the desired state dynamically at runtime.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. The Most Verbose: Direct API Object Calls
&lt;/h2&gt;

&lt;p&gt;The official client libary (&lt;code&gt;kubernetes-python-client&lt;/code&gt;) exposes the &lt;strong&gt;Kubernetes API&lt;/strong&gt; as native &lt;strong&gt;Python&lt;/strong&gt; objects. This provides complete programmatic control over every resource, but that flexibility comes at the cost of verbose, deeply nested object construction.&lt;/p&gt;

&lt;p&gt;Instead of expressing the desired state directly as a &lt;strong&gt;Kubernetes&lt;/strong&gt; manifest, it is represented through a hierarchy of &lt;strong&gt;Python&lt;/strong&gt; objects that are ultimately translated back into the &lt;strong&gt;Kubernetes API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This additional abstraction makes the desired state less obvious during code reviews and collaboration, since engineers must mentally reconstruct the resulting &lt;strong&gt;Kubernetes&lt;/strong&gt; resource instead of reading it directly as a manifest.&lt;/p&gt;

&lt;p&gt;The example below shows simple web application deployment and a &lt;strong&gt;service&lt;/strong&gt; (&lt;code&gt;type: LoadBalancer&lt;/code&gt;) with some annotations for AWS NLB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;kubernetes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_kube_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;apps_v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AppsV1Api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;core_v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CoreV1Api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Define the Deployment Object
&lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1Deployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1ObjectMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-nlb-app-deployment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1DeploymentSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1LabelSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match_labels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-nlb-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1PodTemplateSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1ObjectMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-nlb-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1PodSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;containers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx:alpine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define the Service Object with AWS NLB Annotations
&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1ObjectMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-nlb-app-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.beta.kubernetes.io/aws-load-balancer-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;external&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.beta.kubernetes.io/aws-load-balancer-scheme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;internet-facing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.beta.kubernetes.io/aws-load-balancer-nlb-target-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1ServiceSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LoadBalancer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-nlb-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;V1ServicePort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Apply to Cluster
&lt;/span&gt;&lt;span class="n"&gt;apps_v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_namespaced_deployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;core_v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_namespaced_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the &lt;strong&gt;desired state&lt;/strong&gt; is no longer represented as a &lt;strong&gt;Kubernetes&lt;/strong&gt; manifest. Instead, it is distributed across a hierarchy of &lt;strong&gt;Python&lt;/strong&gt; object constructors. Understanding the resulting &lt;strong&gt;Deployment&lt;/strong&gt; requires understanding both the &lt;strong&gt;Kubernetes&lt;/strong&gt; resource model and the &lt;strong&gt;Python API&lt;/strong&gt; simultaneously. This makes the deployed infrastructure less transparent during reviews, since the desired state must first be reconstructed from the implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Verdict&lt;/strong&gt;: This approach excels when the desired state cannot be known until runtime, such as Kubernetes Controllers, Operators, or other applications that continuously reconcile resources based on external events. For conventional application deployments, however, the additional flexibility rarely outweighs the increased verbosity and maintenance cost compared to declarative manifests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Segue: Community Client Libraries
&lt;/h3&gt;

&lt;p&gt;As the official client is generated directly from the Kubernetes OpenAPI specification, its object model closely mirrors the &lt;strong&gt;Kubernetes API&lt;/strong&gt; rather than idiomatic &lt;strong&gt;Python&lt;/strong&gt;. This has led the community to develop alternative client libraries that provide a more &lt;strong&gt;Pythonic&lt;/strong&gt; developer experience while still interacting with the same &lt;strong&gt;Kubernetes APIs&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;pykube-ng&lt;/strong&gt; - A lightweight client that models Kubernetes resources much like a Python ORM such as SQLAlchemy or Django's ORM. Resources are manipulated through familiar &lt;a href="https://grokipedia.com/page/Create%2C_read%2C_update_and_delete" rel="noopener noreferrer"&gt;CRUD&lt;/a&gt;-style operations rather than constructing deeply nested API objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;kr8s&lt;/strong&gt; - Lightweight client whose API closely mirrors the &lt;code&gt;kubectl&lt;/code&gt; experience while abstracting away much of the connection setup and boilerplate required by the official client. It also includes native &lt;code&gt;asyncio&lt;/code&gt; support for asynchronous applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. The Hardcoded Copy-Paste: Embedded Strings
&lt;/h2&gt;

&lt;p&gt;The easiest shortcut is copying raw &lt;strong&gt;YAML&lt;/strong&gt; directly into a Python file using triple-quoted &lt;code&gt;"""&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As the manifest remains recognizable &lt;strong&gt;YAML&lt;/strong&gt;, this approach initially feels like the best of both worlds: the readability of &lt;strong&gt;Kubernetes&lt;/strong&gt; manifests combined with the convenience of keeping everything in one &lt;strong&gt;Python&lt;/strong&gt; file. However, that convenience comes from embedding platform configuration directly inside the application or delivery logic, creating a coupling that becomes increasingly expensive as the application evolves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;kubernetes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_kube_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Hardcoded variables injected via Python f-strings
&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-nlb-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;image_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx:alpine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;replicas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="n"&gt;manifest_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
apiVersion: apps/v1
kind: Deployment
metadata:
  name: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-deployment
spec:
  replicas: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;replicas&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
  selector:
    matchLabels:
      app: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
  template:
    metadata:
      labels:
        app: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    spec:
      containers:
      - name: web
        image: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;image_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
---
apiVersion: v1
kind: Service
metadata:
  name: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;external&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
    service.beta.kubernetes.io/aws-load-balancer-scheme: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;internet-facing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
spec:
  type: LoadBalancer
  selector:
    app: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
  ports:
  - port: 80
    targetPort: 80
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# Split the string and apply each document sequentially
&lt;/span&gt;&lt;span class="n"&gt;apps_v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AppsV1Api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;core_v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CoreV1Api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manifest_string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Deployment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;apps_v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_namespaced_deployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;core_v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_namespaced_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Verdict&lt;/strong&gt;: Embedded manifests are a pragmatic choice for small utilities, remediation scripts, or one-off automation where the manifest is simply an implementation detail. For long-lived deployment tooling, however, embedding configuration inside the application or delivery logic tightly couples two concerns that often evolve independently, increasing maintenance costs over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Segue: The Cost of Tethering Configuration to Delivery Logic
&lt;/h3&gt;

&lt;p&gt;Embedding manifests directly into &lt;strong&gt;Python&lt;/strong&gt; couples two concerns that frequently evolve at different rates: the deployment logic and the platform configuration. The deployment engine may remain unchanged for months, while manifests evolve regularly as applications grow, cloud-provider annotations change, or operational requirements shift.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Asymmetric Velocity&lt;/strong&gt;: Deployment logic and platform configuration rarely evolve together. A deployment script may remain unchanged for months while Kubernetes manifests are updated frequently to adjust resource limits, networking policies, or cloud-provider annotations. Coupling them forces both to follow the same release lifecycle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Testing Tax&lt;/strong&gt;: If your configuration is baked inside the application code, modifying a simple JSON block or YAML string often requires rebuilding, retesting, and redeploying the full application, which can slow your release down and impact time-to-market and revenue.  If testing is bypass to run a fix out, your blast radius explodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Silent Outage&lt;/strong&gt;: Finding configuration buried inside hundreds of lines of code logic increases cognitive load. In practice, this exact pattern frequently leads to real-world outages because a developer updates an application specification but forgets to dig through the delivery codebase to update the embedded string matching it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a simple atomic web application deployment, the desired state should be immediately visible during a code review. Reviewers should not have to mentally search through delivery logic to understand what &lt;strong&gt;Kubernetes&lt;/strong&gt; will ultimately receive. Keeping manifests as first-class artifacts preserves that transparency.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Gold Standard: External YAML Templates (Jinja2)
&lt;/h2&gt;

&lt;p&gt;When the desired state is largely known ahead of time, the cleanest approach is to keep Kubernetes manifests as first-class artifacts and render only the values that must be determined dynamically. Standard &lt;code&gt;.yaml&lt;/code&gt; files remain in your repository—preserving IDE support, schema validation, and the familiar Kubernetes representation, while &lt;a href="https://jinja.palletsprojects.com/" rel="noopener noreferrer"&gt;Jinja2&lt;/a&gt; provides lightweight parameter substitution at runtime.&lt;/p&gt;

&lt;p&gt;This allows you to link your service and deployment fluidly using shared variables like &lt;code&gt;{{ app_name }}&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;manifest.yaml.j2&lt;/code&gt; (The Clean Template):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;app_name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-deployment&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;replicas&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;app_name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;app_name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;image_name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;app_name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-service&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;service.beta.kubernetes.io/aws-load-balancer-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;external"&lt;/span&gt;
    &lt;span class="na"&gt;service.beta.kubernetes.io/aws-load-balancer-scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;internet-facing"&lt;/span&gt;
    &lt;span class="na"&gt;service.beta.kubernetes.io/aws-load-balancer-nlb-target-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instance"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;app_name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;deploy.py&lt;/code&gt; (The Clean Script)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;kubernetes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_kube_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Read the external Jinja2 template file
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;manifest.yaml.j2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;raw_template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Render variables into the template seamlessly
&lt;/span&gt;&lt;span class="n"&gt;rendered_yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_template&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;app_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-nlb-app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;image_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx:alpine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Parse and safely push to Kubernetes
&lt;/span&gt;&lt;span class="n"&gt;apps_v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AppsV1Api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;core_v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CoreV1Api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rendered_yaml&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Deployment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;apps_v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_namespaced_deployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;core_v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_namespaced_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the deployment logic and the desired state are now represented independently. The &lt;strong&gt;Python&lt;/strong&gt; code is responsible only for supplying runtime values and applying resources, while the manifest remains a transparent description of what &lt;strong&gt;Kubernetes&lt;/strong&gt; will receive. Each artifact can evolve independently without forcing changes to the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Verdict&lt;/strong&gt;: For conventional application deployments, this approach provides the best balance of maintainability, readability, and flexibility. The deployment implementation remains focused on supplying runtime values, while the manifests continue to express the desired state as first-class configuration artifacts. This separation reduces the cost of change, improves collaboration across engineering teams, and leaves the door open to adopting tools such as Helm or GitOps workflows in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond the Official Client
&lt;/h2&gt;

&lt;p&gt;The approaches discussed in this article intentionally stay close to the official &lt;strong&gt;Kubernetes Python client&lt;/strong&gt; (&lt;code&gt;kubernetes-python-client&lt;/code&gt;). As your applications grow, higher-level frameworks such as &lt;strong&gt;Kopf&lt;/strong&gt;, &lt;strong&gt;cdk8s&lt;/strong&gt;, and &lt;strong&gt;Pulumi&lt;/strong&gt; provide different programming models that may better fit specific classes of problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kopf&lt;/strong&gt; — A Python framework for building Kubernetes Operators and Controllers. Rather than manually watching resources and reconciling state, Kopf provides an event-driven framework designed specifically for applications whose desired state is determined at runtime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;cdk8s&lt;/strong&gt; — Generates Kubernetes manifests from object-oriented Python code. This allows infrastructure to be modeled using software abstractions while still producing standard Kubernetes YAML as the final artifact.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pulumi&lt;/strong&gt; — Treats infrastructure as software with state management, dependency tracking, and cloud resource provisioning. Unlike cdk8s, Pulumi manages infrastructure lifecycle rather than simply generating Kubernetes manifests.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When automating &lt;strong&gt;Kubernetes&lt;/strong&gt; deployments with &lt;strong&gt;Python&lt;/strong&gt;, the goal isn't to eliminate &lt;strong&gt;YAML&lt;/strong&gt; or force every deployment through an object-oriented API. The goal is to choose a representation that matches the nature of the problem.&lt;/p&gt;

&lt;p&gt;If the desired state is largely known ahead of time, keep it &lt;strong&gt;declarative&lt;/strong&gt;. External manifests preserve &lt;strong&gt;Kubernetes&lt;/strong&gt; as the source of truth while allowing the Python implementation to focus on supplying runtime values and interacting with the Kubernetes API. Because the deployment logic and platform configuration evolve independently, treating manifests as first-class artifacts reduces the cost of change, improves transparency during code reviews, and encourages collaboration across engineering disciplines.&lt;/p&gt;

&lt;p&gt;When the desired state cannot be known until runtime, however, the trade-offs change. Controllers, Operators, and other event-driven applications continuously reconcile resources based on external events. In these cases, the flexibility of the Kubernetes Python API justifies the additional complexity because the &lt;strong&gt;desired state&lt;/strong&gt; must be synthesized rather than declared.&lt;/p&gt;

&lt;p&gt;Ultimately, there isn't a single "best" approach. Each representation optimizes for a different class of problems. Understanding those trade-offs, and choosing the abstraction that matches the problem—is far more important than the particular library or templating engine you use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical Guidance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;external manifest templates&lt;/strong&gt; when the &lt;strong&gt;desired state&lt;/strong&gt; is largely known ahead of time and only a subset of values must be determined dynamically.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;embedded manifests&lt;/strong&gt; for small utilities, remediation scripts, or one-off operational automation where the manifest is simply an implementation detail.&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Kubernetes Python API&lt;/strong&gt; directly when resources must be created or modified dynamically in response to runtime events, such as Controllers or Operators.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;Here are some articles and resources I came across in research for this article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client Libraries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://codeberg.org/hjacobs/pykube-ng" rel="noopener noreferrer"&gt;pykube-ng&lt;/a&gt; - A highly lightweight client tailored for simple scripts and basic cluster interactions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kr8s-org/kr8s" rel="noopener noreferrer"&gt;kr8s&lt;/a&gt; - A simple, extensible Python client library for Kubernetes that feels familiar for folks who already know how to use kubectl&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kubernetes-client/python" rel="noopener noreferrer"&gt;kubernetes-python-client&lt;/a&gt; - Official, comprehensive library that provides full access to all Kubernetes API endpoints, authentication mechanisms, and resource types.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Higher Level Frameworks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/nolar/kopf" rel="noopener noreferrer"&gt;kopf&lt;/a&gt; - A Python framework to write Kubernetes operators in just a few lines of code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cdk8s-team/cdk8s" rel="noopener noreferrer"&gt;cdk8s&lt;/a&gt; - Define Kubernetes native apps and abstractions using object-oriented programming &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pulumi/pulumi" rel="noopener noreferrer"&gt;Pulumi&lt;/a&gt; -  Infrastructure as Code in any programming language

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/pulumi/pulumi-kubernetes" rel="noopener noreferrer"&gt;pulumi-kubernetes&lt;/a&gt; - A Pulumi resource provider for Kubernetes to manage API resources and workloads in running clusters &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Articles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://johndanielraines.medium.com/workload-parallelization-in-kubernetes-with-python-diy-style-1dfd32cf4366" rel="noopener noreferrer"&gt;Workload Parallelization in Kubernetes with Python, DIY-style: Because sometimes you just need to do it yourself, with Python&lt;/a&gt; by John Raines on May 12, 2021&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@kr8sdevelopers/kr8s-kubernetes-cluster-data-visualization-at-your-fingertips-ab4f8b4a2f0" rel="noopener noreferrer"&gt;Kr8s — Seamless Kubernetes Cluster Data Visualization&lt;/a&gt; by Kr8s Dev Team on Nov 10, 2021&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.spectrocloud.com/blog/writing-kubernetes-operators-with-python" rel="noopener noreferrer"&gt;Writing Kubernetes Operators with Python&lt;/a&gt; by Oz Tiram on Sep 22, 2022&lt;/li&gt;
&lt;li&gt; &lt;a href="https://jacobtomlinson.dev/posts/2023/introducing-kr8s-a-new-kubernetes-client-library-for-python-inspired-by-kubectl/" rel="noopener noreferrer"&gt;Introducing kr8s, a new Kubernetes client library for Python inspired by kubectl &lt;/a&gt; by Jacaob Tomlinson on Jan 19, 2023&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://proxys.io/en/blog/life-hacking/python-requests-vs-curl-which-is-best-for-web-requests" rel="noopener noreferrer"&gt;Python Requests vs cURL&lt;/a&gt; by unknown author on Oct 18, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.plural.sh/blog/python-kubernetes-guide/" rel="noopener noreferrer"&gt;Practical Guide to Kubernetes with Python&lt;/a&gt; by Michael Gaurino on Jul 14, 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ashokan/build-a-kubernetes-operator-with-python-27e1"&gt;Building a Kubernetes operator with Python&lt;/a&gt; by Ashok Nagaraj on Jan 12, 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@flexion_infotech/how-python-is-used-in-kubernetes-for-automation-e0795812fdc0" rel="noopener noreferrer"&gt;How Python Is Used in Kubernetes for Automation&lt;/a&gt; by Flexion Infotech Pvt Ltd. on Jan 30, 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://python.plainenglish.io/why-python-is-still-dominating-in-2025-and-why-it-matters-be58e210f431" rel="noopener noreferrer"&gt;Why Python Is Still Dominating in 2025 (And Why It Matters)&lt;/a&gt; by Lalit Sharma on May 17, 2025&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How AI Bridged a Tooling Gap: Extending InSpec for Kubernetes CRDs</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Mon, 06 Jul 2026 21:31:31 +0000</pubDate>
      <link>https://dev.to/joachim8675309/how-ai-bridged-a-tooling-gap-extending-inspec-for-kubernetes-crds-1m75</link>
      <guid>https://dev.to/joachim8675309/how-ai-bridged-a-tooling-gap-extending-inspec-for-kubernetes-crds-1m75</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: Trailing Ecosystems in Infrastructure-as-Code
&lt;/h2&gt;

&lt;p&gt;As infrastructure and platform engineers, we spend a lot of time dealing with libraries that are &lt;em&gt;just&lt;/em&gt; feature-complete enough to be useful, but still have rough edges. &lt;/p&gt;

&lt;p&gt;Recently, I was writing compliance tests using &lt;strong&gt;InSpec&lt;/strong&gt; to validate some Kubernetes infrastructure. While InSpec provides solid &lt;strong&gt;compliance-as-code&lt;/strong&gt; capabilities, its Kubernetes resource pack hasn't evolved much in recent years. If you are working with modern &lt;strong&gt;cloud-native architectures&lt;/strong&gt;, like testing the newer &lt;strong&gt;Gateway API&lt;/strong&gt; standards, you quickly realize it's missing foundational building blocks.&lt;/p&gt;

&lt;p&gt;In my case, I needed to validate &lt;strong&gt;Custom Resource Definitions (CRDs)&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Mirage: A Confident Hallucination
&lt;/h2&gt;

&lt;p&gt;When I first paired with an AI model to spin up the compliance tests, it spit out exactly what I &lt;em&gt;wanted&lt;/em&gt; to see. The loop looked clean, intuitive, and natural:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;expected_standard_crds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;crd_name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="n"&gt;k8s_custom_resource_definition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;crd_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;exist&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;its&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'spec.group'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="s1"&gt;'gateway.networking.k8s.io'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There was just one issue: &lt;code&gt;k8s_custom_resource_definition&lt;/code&gt; does not exist in the InSpec Kubernetes resource pack. The model had completely hallucinated the API wrapper.&lt;/p&gt;

&lt;p&gt;Normally, this is the point where you sigh and prepare to write a messy workaround. In InSpec, that usually means dropping into raw Ruby, shelling out to kubectl via command resources, or parsing raw JSON strings, completely destroying the clean, expressive nature of compliance-as-code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Twist: Engineering the Abstraction with Claude
&lt;/h2&gt;

&lt;p&gt;When I brought this problem to Claude, it didn't just apologize and rewrite the test using a messy workaround. Instead, it spotted the framework's gap and pivoted to an engineering mindset.&lt;/p&gt;

&lt;p&gt;It realized that to make that ideal test code work, it just needed to build the missing abstraction.&lt;/p&gt;

&lt;p&gt;By dropping into InSpec's underlying K8s client backend, Claude generated a custom resource implementation and placed it right into the profile's &lt;code&gt;libraries/&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# libraries/k8s_custom_resource_definition.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;K8sCustomResourceDefinition&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Inspec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="s2"&gt;"k8s_custom_resource_definition"&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Verifies a Kubernetes CustomResourceDefinition exists."&lt;/span&gt;

  &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="s2"&gt;"
    describe k8s_custom_resource_definition(name: 'gateways.gateway.networking.k8s.io') do
      it { should exist }
      its('group') { should cmp 'gateway.networking.k8s.io' }
    end
  "&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="vi"&gt;@crd_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="vi"&gt;@crd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inspec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"apiextensions.k8s.io/v1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"customresourcedefinitions"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@crd_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;K8s&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotFound&lt;/span&gt;
      &lt;span class="vi"&gt;@crd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exists?&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="vi"&gt;@crd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;group&lt;/span&gt;
    &lt;span class="vi"&gt;@crd&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resource_id&lt;/span&gt;
    &lt;span class="vi"&gt;@crd_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_s&lt;/span&gt;
    &lt;span class="s2"&gt;"Kubernetes CustomResourceDefinition &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@crd_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Claude correctly mapped the &lt;code&gt;apiextensions.k8s.io/v1&lt;/code&gt; resource endpoint, the original, elegant test loop now worked flawlessly.&lt;/p&gt;

&lt;p&gt;The output of a successful run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔄 Fetching ephemeral session details from active 'aws login' context...
📍 Target context resolved: Region=us-east-2, Cluster=mycluster
🚀 Launching stage 2 (bindings-ready) compliance sweep via cinc-auditor...

Profile:   Stage 2: AWS LBC Preparation Succeeded (bindings-ready-profile)
Version:   0.1.0
Target:    kubernetes://0123456789ABCDEF0123456789ABCDEF.gr7.us-east-2.eks.amazonaws.com:443
Target ID: 188e8b62-f664-5221-82ad-9eec791facd6

  ✔  gateway-api-standard-crds: Verify Kubernetes Gateway API v1.5.0 Standard CRDs
     ✔  backendtlspolicies.gateway.networking.k8s.io
     ✔  gatewayclasses.gateway.networking.k8s.io
     ✔  gateways.gateway.networking.k8s.io
     ✔  grpcroutes.gateway.networking.k8s.io
     ✔  httproutes.gateway.networking.k8s.io
     ✔  listenersets.gateway.networking.k8s.io
     ✔  referencegrants.gateway.networking.k8s.io
     ✔  tlsroutes.gateway.networking.k8s.io
  ✔  gateway-api-experimental-crds: Verify Kubernetes Gateway API v1.5.0 Experimental CRDs
     ✔  backendtlspolicies.gateway.networking.k8s.io
     ✔  gatewayclasses.gateway.networking.k8s.io
     ✔  gateways.gateway.networking.k8s.io
     ✔  grpcroutes.gateway.networking.k8s.io
     ✔  httproutes.gateway.networking.k8s.io
     ✔  listenersets.gateway.networking.k8s.io
     ✔  referencegrants.gateway.networking.k8s.io
     ✔  tcproutes.gateway.networking.k8s.io
     ✔  tlsroutes.gateway.networking.k8s.io
     ✔  udproutes.gateway.networking.k8s.io
     ✔  xbackendtrafficpolicies.gateway.networking.x-k8s.io
     ✔  xmeshes.gateway.networking.x-k8s.io
  ✔  aws-lbc-gateway-extensions-crds: Verify AWS Load Balancer Controller Gateway API CRDs
     ✔  listenerruleconfigurations.gateway.k8s.aws
     ✔  loadbalancerconfigurations.gateway.k8s.aws
     ✔  targetgroupconfigurations.gateway.k8s.aws
  ✔  lbc-iam-binding-ready: ServiceAccount is bound to an IAM role via IRSA or Pod Identity
     ✔  kube-system/aws-load-balancer-controller is expected to exist
     ✔  IRSA role-arn annotation is expected not to be empty
  ✔  lbc-iam-policy-attached: The controller's IAM role actually grants the permissions AWS LBC needs
     ✔  IAM role discovered from the ServiceAccount binding is expected not to be empty
     ✔  AWS IAM Role eksctl-mycluster-addon-iamserviceaccount-kub-Role1-iI9ixQ19jzJ8 is expected to exist
     ✔  IAM policies attached to role eksctl-mycluster-addon-iamserviceaccount-kub-Role1-iI9ixQ19jzJ8 is expected not to be empty
     ✔  iam:CreateServiceLinkedRole  →  *
     ✔  ec2:DescribeAccountAttributes  (+28 more)  →  *
     ✔  cognito-idp:DescribeUserPoolClient  (+16 more)  →  *
     ✔  ec2:AuthorizeSecurityGroupIngress  (+1 more)  →  *
     ✔  ec2:CreateSecurityGroup  →  *
     ✔  ec2:CreateTags  →  security-group/*
     ✔  ec2:CreateTags  (+1 more)  →  security-group/*
     ✔  ec2:AuthorizeSecurityGroupIngress  (+2 more)  →  *
     ✔  elasticloadbalancing:CreateLoadBalancer  (+1 more)  →  *
     ✔  elasticloadbalancing:CreateListener  (+3 more)  →  *
     ✔  elasticloadbalancing:AddTags  (+1 more)  →  [3 resources]
     ✔  elasticloadbalancing:AddTags  (+1 more)  →  [4 resources]
     ✔  elasticloadbalancing:ModifyLoadBalancerAttributes  (+10 more)  →  *
     ✔  elasticloadbalancing:AddTags  →  [3 resources]
     ✔  elasticloadbalancing:RegisterTargets  (+1 more)  →  targetgroup/*/*
     ✔  elasticloadbalancing:SetWebAcl  (+5 more)  →  *
     ✔  elasticloadbalancing:DescribeListenerAttributes  (+1 more)  →  *


Profile:   Amazon Web Services Resource Pack (inspec-aws)
Version:   1.83.63
Target:    kubernetes://0123456789ABCDEF0123456789ABCDEF.gr7.us-east-2.eks.amazonaws.com:443
Target ID: 188e8b62-f664-5221-82ad-9eec791facd6

     No tests executed.

Profile:   InSpec Profile (inspec-k8s)
Version:   0.1.3
Target:    kubernetes://0123456789ABCDEF0123456789ABCDEF.gr7.us-east-2.eks.amazonaws.com:443
Target ID: 188e8b62-f664-5221-82ad-9eec791facd6

     No tests executed.

Profile Summary: 5 successful controls, 0 control failures, 0 controls skipped
Test Summary: 45 successful, 0 failures, 0 skipped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Real Leverage of AI in Platform Engineering
&lt;/h2&gt;

&lt;p&gt;We often talk about AI in the context of writing boilerplate application code or scaffolding quick utility scripts. But for infrastructure engineers wrestling with fragmenting or trailing open-source ecosystems, the real leverage might be ecosystem expansion.&lt;/p&gt;

&lt;p&gt;When open-source tools are 80% complete, we usually inherit the technical debt of bridging the final 20% ourselves. If LLMs can shift from "writing code within the strict constraints of a tool" to "dynamically rewriting and extending the tool itself," it completely changes how we maintain our platform tooling stack.&lt;/p&gt;

&lt;p&gt;Have you experienced a similar workflow where one model's hallucination became the blueprint for another model to extend a framework? &lt;/p&gt;

&lt;p&gt;Let's discuss in the comments!&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading &amp;amp; Resources
&lt;/h2&gt;

&lt;p&gt;If you are looking to implement similar compliance-as-code patterns or want to explore the tools mentioned in this post, check out these projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cinc.sh/" rel="noopener noreferrer"&gt;CINC Auditor&lt;/a&gt;&lt;/strong&gt; – The community-built, completely free-as-in-freedom distribution of Chef InSpec. It offers 100% profile compatibility under the Apache 2.0 license, making it a popular drop-in replacement for enterprise CI/CD pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://community.chef.io/tools/chef-inspec" rel="noopener noreferrer"&gt;Chef InSpec&lt;/a&gt;&lt;/strong&gt; – The industry-standard open-source testing framework that turns your security, compliance, and infrastructure policy requirements into human-readable code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.chef.io/inspec/resources/" rel="noopener noreferrer"&gt;InSpec Custom Resources Documentation&lt;/a&gt;&lt;/strong&gt; – The official guide on how to extend the framework by writing your own resource DSL mappings.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>platformengineering</category>
      <category>ai</category>
    </item>
    <item>
      <title>Minimalist EKS: The Easy Way</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Fri, 26 Jun 2026 19:56:54 +0000</pubDate>
      <link>https://dev.to/joachim8675309/minimalist-eks-the-easy-way-hph</link>
      <guid>https://dev.to/joachim8675309/minimalist-eks-the-easy-way-hph</guid>
      <description>&lt;p&gt;Amazon EKS manages the Kubernetes control plane, but you remain responsible for provisioning the underlying AWS infrastructure, including networking, security groups, IAM roles, and worker nodes.&lt;/p&gt;

&lt;p&gt;The eksctl command-line tool automates this entire process. Using a clean, declarative syntax, it handles the complex multi-step orchestration across different AWS services that would normally require extensive manual configuration.&lt;/p&gt;

&lt;p&gt;In this guide, we will use &lt;code&gt;eksctl&lt;/code&gt; to deploy both the VPC and EKS layers simultaneously, establishing a complete testing sandbox with a single command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fflemi22km8oi8b79tc70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fflemi22km8oi8b79tc70.png" alt="Figure 1: eksctl automatically provisions both the VPC networking and EKS compute layers" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  About eksctl
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“&lt;em&gt;&lt;strong&gt;eksctl&lt;/strong&gt; is a command-line utility tool that automates and simplifies the process of creating, managing, and operating Amazon &lt;strong&gt;Elastic Kubernetes Service&lt;/strong&gt; (&lt;strong&gt;Amazon EKS&lt;/strong&gt;) clusters. Written in Go, eksctl provides a declarative syntax through YAML configurations and CLI commands to handle complex EKS cluster operations that would otherwise require multiple manual steps across different AWS services.&lt;/em&gt;” (source &lt;a href="https://docs.aws.amazon.com/eks/latest/eksctl/what-is-eksctl.html" rel="noopener noreferrer"&gt;What is Eksctl?&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ep9sqgych87grjw1x7p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ep9sqgych87grjw1x7p.png" alt="Tools Utilized" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the tools used in this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; [&lt;code&gt;aws&lt;/code&gt;]: Interact with AWS services.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/reference/kubectl/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt; [&lt;code&gt;kubectl&lt;/code&gt;]: Manage Kubernetes clusters and workloads.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/eksctl/installation.html" rel="noopener noreferrer"&gt;eksctl&lt;/a&gt; [&lt;code&gt;eksctl&lt;/code&gt;]: Provision Amazon EKS clusters and supporting infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Optional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;helm&lt;/a&gt; [&lt;code&gt;helm&lt;/code&gt;]: Install and manage Kubernetes applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Verifying Tool Versions
&lt;/h3&gt;

&lt;p&gt;This guide has been tested with these tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;aws&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.35.7&lt;/span&gt;
&lt;span class="na"&gt;kubectl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.36.2&lt;/span&gt;
&lt;span class="na"&gt;helm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v4.2.0&lt;/span&gt;
&lt;span class="na"&gt;eksctl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.227.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify your versions using a &lt;a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html" rel="noopener noreferrer"&gt;POSIX Shell&lt;/a&gt;, such as &lt;a href="https://www.gnu.org/software/bash/" rel="noopener noreferrer"&gt;bash&lt;/a&gt; or &lt;a href="https://www.zsh.org/" rel="noopener noreferrer"&gt;zsh&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"aws: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;kubectl: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;helm: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;eksctl: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[/ ]'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/Client Version:/ {print $3}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;helm version &lt;span class="nt"&gt;--short&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;+ &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;eksctl version | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d-&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prerequisite: AWS Profile
&lt;/h2&gt;

&lt;p&gt;Before running the deployment script, you need an AWS CLI profile configured for an IAM identity with sufficient administrative permissions to provision EKS, IAM roles, and VPC infrastructure. &lt;strong&gt;NEVER use your AWS root account for local development&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📓 &lt;strong&gt;NOTE&lt;/strong&gt;: Recent versions of the AWS CLI (v2.32.0+) support &lt;code&gt;aws login&lt;/code&gt;, which uses your AWS Management Console credentials to obtain temporary credentials. This eliminates the need to store long-lived static access keys on your machine.. (see &lt;a href="https://docs.aws.amazon.com/signin/latest/userguide/command-line-sign-in.html" rel="noopener noreferrer"&gt;Sign in through the AWS Command Line Interface&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1. Configure the Profile Setting
&lt;/h3&gt;

&lt;p&gt;Run the following below to create a config profile block dynamically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Define your local variables&lt;/span&gt;
&lt;span class="nv"&gt;MY_ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"123456789012"&lt;/span&gt; &lt;span class="c"&gt;# Replace with your real AWS Account ID&lt;/span&gt;
&lt;span class="nv"&gt;MY_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-2"&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.aws

&lt;span class="c"&gt;# Append the login profile structure to your AWS config file&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; ~/.aws/config
[profile myuser]
login_session = arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;&lt;span class="sh"&gt;:user/myuser
region = &lt;/span&gt;&lt;span class="nv"&gt;$MY_REGION&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Authenticate and Activate
&lt;/h3&gt;

&lt;p&gt;Trigger the browser-based authentication handshake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws login &lt;span class="nt"&gt;--profile&lt;/span&gt; myuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A browser window will open automatically, prompting you to complete your standard console sign-in. Once authorized, activate the profile in your active shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Verify Your Active Session
&lt;/h3&gt;

&lt;p&gt;Confirm that your terminal is actively communicating with AWS using your temporary token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon successful connection, the AWS CLI will output your active identity details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AIDA0123456789EXAMPLE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789012"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:user/myuser"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the command returns your metadata, your authentication profile is active and ready to spin up your testing sandbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Minimalist One-Command EKS Cluster
&lt;/h2&gt;

&lt;p&gt;If you want the absolute fastest path to an operational Kubernetes sandbox, you can rely on the native CLI defaults of &lt;strong&gt;&lt;code&gt;eksctl&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The following snippet isolates your configuration file, provisions a default public/private network topology, and configures the control plane using a single baseline execution:&lt;/p&gt;

&lt;p&gt;This is the least amount of steps to create a Kubernetes cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set your active profile and environment targets&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myuser"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mycluster"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws configure get region&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Direct your Kubernetes context to an isolated configuration file&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/aws/
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.kube/aws/&lt;/span&gt;&lt;span class="nv"&gt;$MY_REGION&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt;&lt;span class="s2"&gt;.yaml"&lt;/span&gt;

&lt;span class="c"&gt;# Spin up the infrastructure stack&lt;/span&gt;
eksctl create cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$MY_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; 1.36
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚨 &lt;strong&gt;IMPORTANT SECURITY NOTE&lt;/strong&gt;: While this simple deployment is perfect for rapid testing, it relies on legacy permissions mapping. By default, it attaches the broad AWS VPC CNI network policy directly to the underlying worker node’s shared IAM Instance Profile role. This means any standard application pod running on the cluster technically inherits those raw EC2 network management capabilities at the hardware layer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the creation process finishes, logging into your AWS Management Console will reveal your newly active cluster topology, complete with healthy control plane paths and managed compute node resources:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftnjj979rbqun71j17gzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftnjj979rbqun71j17gzx.png" alt="Figure 2: Viewing the newly deployed infrastructure sandbox directly inside the EKS AWS Console" width="799" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the core system components running on your cluster, query all namespaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command displays the baseline networking, metrics, and DNS workloads initialized on the cluster:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmvtos61vm4wzuicjaej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmvtos61vm4wzuicjaej.png" alt="Figure 3: kubectl get all output" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying a Demo Nginx Application
&lt;/h2&gt;

&lt;p&gt;With your cluster up and running, you can verify external traffic routing by deploying a simple Nginx application and exposing it via an AWS Classic Load Balancer (ELBv1):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the Nginx deployment&lt;/span&gt;
kubectl create deployment demo-lb-app &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx:alpine

&lt;span class="c"&gt;# Expose the deployment as a Kubernetes LoadBalancer Service&lt;/span&gt;
kubectl expose deployment demo-lb-app &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80 &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To find the public AWS DNS name assigned to your load balancer, check the status of your services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will display the external hostname provisioning in your AWS account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78rq05vccod66arqycye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78rq05vccod66arqycye.png" alt="Figure 4: kubectl get services output" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the address from the &lt;code&gt;EXTERNAL-IP&lt;/code&gt; field and paste it into a web browser to verify connectivity:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftyza88m5pxbx8m6jlvv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftyza88m5pxbx8m6jlvv5.png" alt="Figure 5: Browser verification" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can query the endpoint directly from your terminal using curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Capture the hostname dynamically&lt;/span&gt;
&lt;span class="nv"&gt;DEMO_ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get service demo-lb-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.status.loadBalancer.ingress[0].hostname}'&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Send an HTTP request to the load balancer&lt;/span&gt;
curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$DEMO_ADDR&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command returns the raw HTTP response headers and HTML body from the container:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp4093di34swln5quq5dg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp4093di34swln5quq5dg.png" alt="Figure 6: curl output verification" width="800" height="791"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum: Free-Tier EKS Cluster
&lt;/h2&gt;

&lt;p&gt;If you are strictly working within the AWS Free Tier, you can provision a minimal single-node cluster using t3.micro instances.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📓 &lt;strong&gt;NOTE&lt;/strong&gt;: The t3.micro instances have severe memory and IP assignment limitations. To make a single micro node usable without running out of pod capacity instantly, you should pass the --zones flag to restrict network placement to two availability zones instead of three (saving NAT gateway overhead costs), and explicitly drop the node count to one:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set your environment variables&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myuser"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mycluster"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws configure get region&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Isolate your Kubernetes configuration file&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/aws/
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.kube/aws/&lt;/span&gt;&lt;span class="nv"&gt;$MY_REGION&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt;&lt;span class="s2"&gt;.yaml"&lt;/span&gt;

&lt;span class="c"&gt;# Create a minimal, single-node free tier cluster&lt;/span&gt;
eksctl create cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$MY_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zones&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;a,&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MY_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;b"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--nodes&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--node-type&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;WARNING&lt;/strong&gt;: This configuration is strictly for basic CLI testing and experimentation. Because a t3.micro only supports a maximum of 4 pods per network interface, default system components (like coredns, kube-proxy, and vpc-cni) will consume almost the entire available capacity, leaving minimal room to run custom applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Addendum: Robust Minimalist Cluster
&lt;/h2&gt;

&lt;p&gt;For development or short-lived playground clusters, running standard &lt;code&gt;eksctl create cluster&lt;/code&gt; command line defaults is perfectly adequate. However, for formal test and staging environments, you will want an infrastructure profile that is more robust, secure, and production-like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strict Isolation via Pod Identity&lt;/strong&gt;: Only the explicit vpc-cni driver is permitted to modify network configurations, locking out malicious or unprivileged application pods from tampering with AWS routing infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Availability Architecture&lt;/strong&gt;: Three managed worker nodes are automatically calculated and balanced across three distinct Availability Zones (AZs) to prevent single-datacenter outages from dropping your services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Persistent Storage&lt;/strong&gt;: The AWS EBS CSI driver is packaged directly into the cluster lifecycle to easily back stateful application requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Cost Optimization&lt;/strong&gt;: Upgrades compute selections to the modern m6i.large general-purpose instance family (saving on price-per-performance relative to the older m5.large defaults).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of managing an endless string of CLI arguments, you can capture these configurations cleanly inside a version-controlled file. Below is a one-touch command that dynamically interpolates your local environment variables into a &lt;code&gt;ClusterConfig&lt;/code&gt; layout and pipes it directly into the &lt;code&gt;eksctl&lt;/code&gt; builder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myuser"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mycluster"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws configure get region&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MY_EKS_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.36"&lt;/span&gt;

eksctl create cluster &lt;span class="nt"&gt;--config-file&lt;/span&gt; /dev/stdin &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: &lt;/span&gt;&lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt;&lt;span class="sh"&gt;
  region: &lt;/span&gt;&lt;span class="nv"&gt;$MY_REGION&lt;/span&gt;&lt;span class="sh"&gt;
  version: &lt;/span&gt;&lt;span class="nv"&gt;$MY_EKS_VERSION&lt;/span&gt;&lt;span class="sh"&gt;

managedNodeGroups:
  - name: ng-1
    instanceType: m6i.large
    desiredCapacity: 3
    minSize: 3
    maxSize: 3
    labels:
      alpha.eksctl.io/cluster-name: &lt;/span&gt;&lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt;&lt;span class="sh"&gt;
      alpha.eksctl.io/nodegroup-name: ng-1
    tags:
      alpha.eksctl.io/nodegroup-name: ng-1
      alpha.eksctl.io/nodegroup-type: managed

iam:
  withOIDC: true

addonsConfig:
  autoApplyPodIdentityAssociations: true

addons:
  - name: vpc-cni
    useDefaultPodIdentityAssociations: true
  - name: aws-ebs-csi-driver
    useDefaultPodIdentityAssociations: true
  - name: eks-pod-identity-agent
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Addendum: Optional Enhancements
&lt;/h2&gt;

&lt;p&gt;The default minimalist cluster is sufficient for basic experimentation. If you choose not to use the automated &lt;code&gt;ClusterConfig&lt;/code&gt; heredoc template from the earlier addendum, you can manually install these individual components via the CLI to support real-world application workloads:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. IAM OIDC Provider
&lt;/h3&gt;

&lt;p&gt;If your specific testing tools require legacy IAM Roles for Service Accounts (IRSA) rather than the modern EKS Pod Identity agent, you must manually associate an OpenID Connect (OIDC) identity provider with your cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl utils associate-iam-oidc-provider &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$MY_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Pod Identity Agent Addon
&lt;/h3&gt;

&lt;p&gt;The Pod Identity Agent enables Kubernetes service accounts to access AWS resources using EKS Pod Identity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create addon &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$MY_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; eks-pod-identity-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Amazon EBS CSI Driver Addon
&lt;/h3&gt;

&lt;p&gt;The EBS CSI Driver allows your cluster to dynamically provision and manage lifecycle storage volumes backed by Amazon EBS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create addon &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$MY_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; aws-ebs-csi-driver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;To avoid recurring AWS charges after you finish experimenting with your sandbox, delete all provisioned resources using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl delete cluster &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$MY_CLUSTER_NAME&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$MY_REGION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single command automatically tears down both the EKS compute resources and the underlying VPC network stack.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Essential DevOps Tools for Ubuntu</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Wed, 14 Jan 2026 20:25:54 +0000</pubDate>
      <link>https://dev.to/joachim8675309/essential-devops-tools-for-ubuntu-5421</link>
      <guid>https://dev.to/joachim8675309/essential-devops-tools-for-ubuntu-5421</guid>
      <description>&lt;p&gt;Popular tools for provisioning the cloud are &lt;a href="https://developer.hashicorp.com/terraform" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; and &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; tools &lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;helm&lt;/code&gt;. For Linux, online instructions for how to install these tools can be inconsistent and potentially error prone. This article shows how to install these tools on &lt;a href="https://www.debian.org/" rel="noopener noreferrer"&gt;Debian&lt;/a&gt; and &lt;a href="https://ubuntu.com/download/server" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt; based distros, which by extension would include &lt;a href="https://linuxmint.com/" rel="noopener noreferrer"&gt;Mint&lt;/a&gt;, &lt;a href="https://system76.com/pop/" rel="noopener noreferrer"&gt;Pop! OS&lt;/a&gt;, &lt;a href="https://zorin.com/os/" rel="noopener noreferrer"&gt;Zorin OS&lt;/a&gt; and others.&lt;/p&gt;

&lt;p&gt;This guide walk you through how to install the latest versions of popular DevOps tool on Ubuntu Linux. This was tested on &lt;a href="https://releases.ubuntu.com/noble/" rel="noopener noreferrer"&gt;Ubuntu 24.04 Noble Nambat&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Segue: Why&amp;nbsp;Linux?
&lt;/h2&gt;

&lt;p&gt;For developers accustomed to &lt;strong&gt;macOS&lt;/strong&gt; or &lt;strong&gt;Windows&lt;/strong&gt;, choosing &lt;strong&gt;Linux&lt;/strong&gt; as a primary development platform may seem unusual. For others, &lt;strong&gt;Linux&lt;/strong&gt; is the default choice. With &lt;strong&gt;Windows&lt;/strong&gt; now supporting &lt;strong&gt;Linux&lt;/strong&gt; through &lt;strong&gt;WSL&lt;/strong&gt;, Linux-based development environments have become increasingly common.&lt;/p&gt;

&lt;p&gt;Beyond personal preference, the strongest case for &lt;strong&gt;Linux&lt;/strong&gt; is environment consistency. Production systems, and especially CI/CD pipelines, almost always run on &lt;strong&gt;Linux&lt;/strong&gt;. Developing and testing directly in a &lt;strong&gt;Linux&lt;/strong&gt; environment allows you to validate build, test, and deployment scripts under the same conditions they'll run in production.&lt;/p&gt;

&lt;p&gt;This consistency shortens development cycles by reducing surprises late in the pipeline and eliminates platform-specific "gotchas" that exist on &lt;strong&gt;macOS&lt;/strong&gt; or &lt;strong&gt;Windows&lt;/strong&gt; but never appear in Linux-based CI/CD or production systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools
&lt;/h2&gt;

&lt;p&gt;This is an overview of the tools.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cloud Provisioning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/terraform" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; [&lt;code&gt;terraform&lt;/code&gt;]: tool for provisioning cloud infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; [&lt;code&gt;aws&lt;/code&gt;]: tool to interact with Amazon Web Services&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/okta/okta-aws-cli" rel="noopener noreferrer"&gt;okta-aws-cli&lt;/a&gt; [&lt;code&gt;okta-aws-cli&lt;/code&gt;]: allows you to get temporary IAM credentials using Okta as the Identity provider.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/cli/azure/" rel="noopener noreferrer"&gt;Azure CLI Tools&lt;/a&gt; [&lt;code&gt;az&lt;/code&gt;]: tool to interact with Azure&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.cloud.google.com/sdk/docs/install-sdk" rel="noopener noreferrer"&gt;Google Cloud CLI Tools&lt;/a&gt; [&lt;code&gt;gcloud&lt;/code&gt;]: tool to interact with GCP&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Containers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;docker&lt;/a&gt; [&lt;code&gt;docker&lt;/code&gt;]: tools to build container images and run containers &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/reference/kubectl/" rel="noopener noreferrer"&gt;Kubernetes CLI&lt;/a&gt; [&lt;code&gt;kubectl&lt;/code&gt;]: command line tool for interacting with Kubernetes control plan&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; [&lt;code&gt;helm&lt;/code&gt;]: Tool for configuring, installing, and distributing Kubernetes applications.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kustomize.io/" rel="noopener noreferrer"&gt;Kustomize&lt;/a&gt; [&lt;code&gt;kustomize&lt;/code&gt;]: tool to configure Kubernetes application through patching.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://helmfile.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Helmfile&lt;/a&gt; [&lt;code&gt;helmfile&lt;/code&gt;]: is a declarative spec for deploying helm charts that allows you to template helm chart values as well integrate patching support&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://krew.sigs.k8s.io/" rel="noopener noreferrer"&gt;Krew&lt;/a&gt; [&lt;code&gt;kubectl krew&lt;/code&gt;]: is a plugin system for Kubernetes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/corretto/" rel="noopener noreferrer"&gt;Correto OpenJDK&lt;/a&gt; [&lt;code&gt;java&lt;/code&gt;, &lt;code&gt;javac&lt;/code&gt;, &lt;code&gt;jar&lt;/code&gt;, &lt;code&gt;keytool&lt;/code&gt;]: Corretto is an OpenJDK distribution that is supported on many platforms.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Others&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/vault" rel="noopener noreferrer"&gt;Vault&lt;/a&gt; [vault]: Allow securing, storing, and controlling access to secrets artifacts: tokens, passwords, certificates, and encryption keys.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Installation
&lt;/h2&gt;

&lt;p&gt;Below are script steps that you can use to install the packages and tools. These scripts follow the &lt;a href="https://google.github.io/styleguide/shellguide.html" rel="noopener noreferrer"&gt;Google Shell Style Guide&lt;/a&gt; and I took some measures to reduce the width so that it would be nicely in this guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repositories
&lt;/h3&gt;

&lt;p&gt;Many of these tools are available from private repositories. You can add them with the following script below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

  &lt;span class="c"&gt;# Hashicorp&lt;/span&gt;
  &lt;span class="c"&gt;# Uses Ubuntu codename dynamically&lt;/span&gt;
  &lt;span class="nv"&gt;UBUNTU_CODENAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'(?&amp;lt;=UBUNTU_CODENAME=).*'&lt;/span&gt; /etc/os-release&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;# terraform, vault&lt;/span&gt;
  &lt;span class="c"&gt;# https://developer.hashicorp.com/vault/install#linux&lt;/span&gt;
  &lt;span class="c"&gt;# https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli&lt;/span&gt;
  add_apt_repo &lt;span class="s2"&gt;"hashicorp"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://apt.releases.hashicorp.com/gpg"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://apt.releases.hashicorp.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UBUNTU_CODENAME&lt;/span&gt;&lt;span class="s2"&gt; main"&lt;/span&gt;

  &lt;span class="c"&gt;# kubectl&lt;/span&gt;
  &lt;span class="c"&gt;# https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-using-native-package-management&lt;/span&gt;
  add_apt_repo &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://pkgs.k8s.io/core:/stable:/v1.35/deb/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"/"&lt;/span&gt;

  &lt;span class="c"&gt;# helm&lt;/span&gt;
  &lt;span class="c"&gt;# https://helm.sh/docs/intro/install/#from-apt-debianubuntu&lt;/span&gt;
  add_apt_repo &lt;span class="s2"&gt;"helm"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://packages.buildkite.com/helm-linux/helm-debian/gpgkey"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://packages.buildkite.com/helm-linux/helm-debian/any/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"any main"&lt;/span&gt;

  &lt;span class="c"&gt;# Corretto&lt;/span&gt;
  &lt;span class="c"&gt;# https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/generic-linux-install.html&lt;/span&gt;
  add_apt_repo &lt;span class="s2"&gt;"corretto"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://apt.corretto.aws/corretto.key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://apt.corretto.aws"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"stable main"&lt;/span&gt;

  &lt;span class="c"&gt;# google-cloud-cli&lt;/span&gt;
  &lt;span class="c"&gt;# https://docs.cloud.google.com/sdk/docs/install-sdk#deb&lt;/span&gt;
  add_apt_repo &lt;span class="s2"&gt;"googlecloud"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://packages.cloud.google.com/apt/doc/apt-key.gpg"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://packages.cloud.google.com/apt"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"cloud-sdk main"&lt;/span&gt;

  &lt;span class="c"&gt;# azure-cli&lt;/span&gt;
  &lt;span class="c"&gt;# https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-linux?view=azure-cli-latest&amp;amp;pivots=apt&lt;/span&gt;
  add_apt_repo &lt;span class="s2"&gt;"azure"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://packages.microsoft.com/keys/microsoft.asc"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://packages.microsoft.com/repos/azure-cli/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UBUNTU_CODENAME&lt;/span&gt;&lt;span class="s2"&gt; main"&lt;/span&gt;

  &lt;span class="c"&gt;# docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin&lt;/span&gt;
  &lt;span class="c"&gt;# Docs: https://docs.docker.com/engine/install/ubuntu/&lt;/span&gt;
  add_apt_repo &lt;span class="s2"&gt;"docker"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://download.docker.com/linux/ubuntu/gpg"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"https://download.docker.com/linux/ubuntu"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
               &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$UBUNTU_CODENAME&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

add_apt_repo&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;KEY_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="c"&gt;# Signed-By&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;REPO_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;DISTRO_STRING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt; &lt;span class="c"&gt;# "Suites Components" or "/"&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;KEYRING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/share/keyrings/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-archive-keyring.gpg"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;LIST_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/apt/sources.list.d/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.list"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Configuring repository: &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Download and dearmor the GPG key&lt;/span&gt;
  wget &lt;span class="nt"&gt;-qO&lt;/span&gt; - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEYRING&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Construct and save the list file&lt;/span&gt;
  &lt;span class="c"&gt;# Note: arch=$ARCH is included by default &lt;/span&gt;
  &lt;span class="c"&gt;#       as it is standard for most 3rd party repos&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="nv"&gt;$ARCH&lt;/span&gt;&lt;span class="s2"&gt; signed-by=&lt;/span&gt;&lt;span class="nv"&gt;$KEYRING&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="nv"&gt;$REPO_URI&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$DISTRO_STRING&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LIST_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; Dir::Etc::sourcelist&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LIST_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; Dir::Etc::sourceparts&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; APT::Get::List-Cleanup&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tools Installed from Repositories
&lt;/h3&gt;

&lt;p&gt;For the tools that can be installed from these repositories, we can run through the following script below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 apt-transport-https &lt;span class="se"&gt;\&lt;/span&gt;
 ca-certificates &lt;span class="se"&gt;\&lt;/span&gt;
 curl &lt;span class="se"&gt;\&lt;/span&gt;
 gnupg &lt;span class="se"&gt;\&lt;/span&gt;
 lsb-release &lt;span class="se"&gt;\&lt;/span&gt;
 jq &lt;span class="se"&gt;\&lt;/span&gt;
 software-properties-common &lt;span class="se"&gt;\&lt;/span&gt;
 unzip

&lt;span class="c"&gt;# Install Tools from Private Repos&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;terraform
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;vault
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;kubectl
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;helm
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;java-17-amazon-corretto-jdk
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;google-cloud-cli
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;azure-cli
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce &lt;span class="se"&gt;\&lt;/span&gt;
                 docker-ce-cli &lt;span class="se"&gt;\&lt;/span&gt;
                 containerd.io &lt;span class="se"&gt;\&lt;/span&gt;
                 docker-buildx-plugin &lt;span class="se"&gt;\&lt;/span&gt;
                 docker-compose-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tools Installed as Binaries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# install AWS CLI&lt;/span&gt;
  install_binary_aws_cli
  install_binary_okta_aws_cli

  &lt;span class="c"&gt;# install other binaries&lt;/span&gt;
  install_binary_kustomize
  install_binary_helmfile
&lt;span class="o"&gt;}&lt;/span&gt;

install_binary_kustomize&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;URL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"&lt;/span&gt;
  &lt;span class="nv"&gt;URL_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/&lt;/span&gt;&lt;span class="nv"&gt;$URL_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bash
  &lt;span class="nb"&gt;sudo mv&lt;/span&gt; ~/kustomize /usr/local/bin
&lt;span class="o"&gt;}&lt;/span&gt;

install_binary_helmfile&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;
  &lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.2.3"&lt;/span&gt;
  &lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"helmfile_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_linux_&lt;/span&gt;&lt;span class="nv"&gt;$ARCH&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
  &lt;span class="nv"&gt;URL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"helmfile/helmfile/releases/download/v&lt;/span&gt;&lt;span class="nv"&gt;$VER&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/&lt;/span&gt;&lt;span class="nv"&gt;$URL_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;TEMP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sudo mv&lt;/span&gt; &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;/helmfile /usr/local/bin
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

install_binary_okta_aws_cli&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;
  &lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2.5.1"&lt;/span&gt;
  &lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"okta-aws-cli_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_linux_&lt;/span&gt;&lt;span class="nv"&gt;$ARCH&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
  &lt;span class="nv"&gt;URL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"okta/okta-aws-cli/releases/download/v&lt;/span&gt;&lt;span class="nv"&gt;$VER&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/&lt;/span&gt;&lt;span class="nv"&gt;$URL_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;TEMP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sudo mv&lt;/span&gt; &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;/okta-aws-cli /usr/local/bin
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

install_binary_aws_cli&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;TEMP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"awscli-exe-linux-x86_64.zip"&lt;/span&gt;
  wget &lt;span class="s2"&gt;"https://awscli.amazonaws.com/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;
  unzip &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;/&lt;span class="nv"&gt;$PKG&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;
  &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;/aws/install
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; TEMP_DIR
&lt;span class="o"&gt;}&lt;/span&gt;

main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Installations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# verify installations&lt;/span&gt;
terraform &lt;span class="nt"&gt;--version&lt;/span&gt;
vault &lt;span class="nt"&gt;--version&lt;/span&gt;
kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
helm version
kustomize version
helmfile &lt;span class="nt"&gt;--version&lt;/span&gt;
java &lt;span class="nt"&gt;--version&lt;/span&gt;
aws &lt;span class="nt"&gt;--version&lt;/span&gt;
okta-aws-cli &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Plugins
&lt;/h2&gt;

&lt;p&gt;Both helm and Kubernetes can extend their functionality through a plugin. Here’s how you can get some popular plugins for these tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Helm Plugins
&lt;/h3&gt;

&lt;p&gt;Helm has a few plugins that you can install, as shown below. I highly recommend the helm-diff, as this allows you to see what resources you will modify before installing or upgrading the the application.&lt;/p&gt;

&lt;p&gt;Here are some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/databus23/helm-diff
helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/aslafy-z/helm-git
helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/hypnoglow/helm-s3.git
helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/jkroepke/helm-secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kubernetes Plugin Engine
&lt;/h3&gt;

&lt;p&gt;Kubernetes have a plugin system with Kubernetes Krew, which you can install using the script below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  install_krew

  &lt;span class="c"&gt;# Add this in your startup scripts &lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.krew&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

install_krew&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;TEMP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;

  &lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;
    &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/x86_64/amd64/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\(arm\)\(64\)\?.*/\1\2/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/aarch64$/arm64/'&lt;/span&gt;
  &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"krew-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
  &lt;span class="nv"&gt;URL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"kubernetes-sigs/krew/releases/latest/download/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/&lt;/span&gt;&lt;span class="nv"&gt;$URL_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;tar &lt;/span&gt;zxvf &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
  ./&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;krew

  &lt;span class="nb"&gt;popd
  rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; TEMP_DIR
&lt;span class="o"&gt;}&lt;/span&gt;

main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kubernetes Plugins
&lt;/h3&gt;

&lt;p&gt;Once &lt;a href="https://krew.sigs.k8s.io/" rel="noopener noreferrer"&gt;krew&lt;/a&gt; is installed, you can install some popular plugins.&lt;/p&gt;

&lt;p&gt;Here are some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;neat
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;sort-manifests
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;ktop
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;tree
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;graph
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;cert-manager 
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;get-all
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;ice
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;deprecations
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;ns
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;access-matrix
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;pod-lens
kubectl krew &lt;span class="nb"&gt;install &lt;/span&gt;resource-capacity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Ubuntu is a powerful platform for development, but it can be surprisingly complex for new developers, especially when it comes to installing and maintaining modern DevOps tooling. Most online instructions are fragmented, outdated, or inconsistent in how they handle repositories, keys, and binary installs. This can lead inconsistencies across developer workstations as well as production environments.&lt;/p&gt;

&lt;p&gt;So my goal here is to reduce the complexity by providing consistent set of installation steps that follow best practices and rely on official sources wherever possible.&lt;/p&gt;

&lt;p&gt;This can help avoid environment drift, especially when install scripts like this pin the tool versions and are routinely tested. This will help avoid those situations where you’ll hear: “works on my laptop”…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxamuugxbwotaeulb73x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxamuugxbwotaeulb73x.png" alt="Ops Problem Now" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>terraform</category>
      <category>kubernetes</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Essential DevOps Tools for macOS</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Sat, 10 Jan 2026 00:43:09 +0000</pubDate>
      <link>https://dev.to/joachim8675309/essential-devops-tools-for-macos-53m2</link>
      <guid>https://dev.to/joachim8675309/essential-devops-tools-for-macos-53m2</guid>
      <description>&lt;p&gt;This guide walks through installing popular tools for cloud provisioning and container management on &lt;a href="https://grokipedia.com/page/MacOS" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;. While many may not realize it, &lt;a href="https://grokipedia.com/page/MacOS" rel="noopener noreferrer"&gt;macOS&lt;/a&gt; traces its roots back to &lt;strong&gt;&lt;a href="https://grokipedia.com/page/NeXTSTEP" rel="noopener noreferrer"&gt;NeXTSTEP&lt;/a&gt;&lt;/strong&gt; and is built on the &lt;a href="https://grokipedia.com/page/Mach_(kernel)" rel="noopener noreferrer"&gt;Mach&lt;/a&gt;-derived &lt;a href="https://grokipedia.com/page/XNU" rel="noopener noreferrer"&gt;XNU&lt;/a&gt; kernel. The &lt;a href="https://grokipedia.com/page/Darwin_(operating_system)" rel="noopener noreferrer"&gt;Darwin&lt;/a&gt; userland provides a &lt;a href="https://grokipedia.com/page/Comparison_of_BSD_operating_systems" rel="noopener noreferrer"&gt;BSD&lt;/a&gt;-based &lt;a href="https://grokipedia.com/page/Unix" rel="noopener noreferrer"&gt;Unix&lt;/a&gt; environment with familiar &lt;a href="https://grokipedia.com/page/POSIX" rel="noopener noreferrer"&gt;POSIX&lt;/a&gt; tools, along with macOS-specific additions such as the launchd init and service management system. This &lt;a href="https://grokipedia.com/page/Unix" rel="noopener noreferrer"&gt;Unix&lt;/a&gt; heritage is what allows many &lt;a href="https://grokipedia.com/page/Linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;-focused cloud and container tools to run reliably on &lt;a href="https://grokipedia.com/page/MacOS" rel="noopener noreferrer"&gt;macOS&lt;/a&gt; with minimal differences.&lt;/p&gt;

&lt;p&gt;The challenge arises when scripts written for &lt;a href="https://grokipedia.com/page/Linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;-based cloud environments are run on &lt;a href="https://grokipedia.com/page/MacOS" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;. Because &lt;a href="https://grokipedia.com/page/MacOS" rel="noopener noreferrer"&gt;macOS&lt;/a&gt; ships with &lt;a href="https://grokipedia.com/page/Comparison_of_BSD_operating_systems" rel="noopener noreferrer"&gt;BSD&lt;/a&gt;-flavored utilities, such as &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, and &lt;code&gt;awk&lt;/code&gt;, scripts that work correctly on &lt;a href="https://grokipedia.com/page/Linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt; may behave differently or fail entirely. To address this, this guide not only covers installing cloud provisioning tools like &lt;a href="https://developer.hashicorp.com/terraform" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; and &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; tooling, but also walks through installing a common set of command-line tools so that scripts behave consistently across both &lt;a href="https://grokipedia.com/page/Linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt; and &lt;a href="https://grokipedia.com/page/MacOS" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools
&lt;/h2&gt;

&lt;p&gt;This is an overview of the tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloud Provisioning
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/terraform" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; [&lt;code&gt;terraform&lt;/code&gt;]: tool for provisioning cloud infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; [&lt;code&gt;aws&lt;/code&gt;]: tool to interact with Amazon Web Services&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/okta/okta-aws-cli" rel="noopener noreferrer"&gt;okta-aws-cli&lt;/a&gt; [&lt;code&gt;okta-aws-cli&lt;/code&gt;]: allows you to get temporary IAM credentials using Okta as the Identity provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Kubernetes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/reference/kubectl/" rel="noopener noreferrer"&gt;Kubernetes CLI &lt;/a&gt;[&lt;code&gt;kubectl&lt;/code&gt;]: command line tool for interacting with Kubernetes control plan&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; [&lt;code&gt;helm&lt;/code&gt;]: Tool for configuring, installing, and distributing Kubernetes applications.&lt;/li&gt;
&lt;li&gt;[Kustomize] [&lt;code&gt;kustomize&lt;/code&gt;]: tool to configure Kubernetes application through patching.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kustomize.io/" rel="noopener noreferrer"&gt;Helmfile&lt;/a&gt; [&lt;code&gt;helmfile&lt;/code&gt;]: is a declarative spec for deploying helm charts that allows you to template helm chart values as well integrate patching support&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://krew.sigs.k8s.io/" rel="noopener noreferrer"&gt;Krew&lt;/a&gt; [&lt;code&gt;kubectl krew&lt;/code&gt;]: is a plugin system for Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java
&lt;/h3&gt;

&lt;p&gt;Beyond compiling languages and running java programs, there are tools to manage certificate, keytool, and a useful tool for managing tar archives: jar.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/corretto/" rel="noopener noreferrer"&gt;Correto OpenJDK&lt;/a&gt; [&lt;code&gt;java&lt;/code&gt;, &lt;code&gt;javac&lt;/code&gt;, &lt;code&gt;jar&lt;/code&gt;, &lt;code&gt;keytool&lt;/code&gt;]: Corretto is an OpenJDK distribution that is supported on many platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Others
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/vault" rel="noopener noreferrer"&gt;Vault&lt;/a&gt; [&lt;code&gt;vault&lt;/code&gt;]: Allow securing, storing, and controlling access to secrets artifacts: tokens, passwords, certificates, and encryption keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Package System
&lt;/h2&gt;

&lt;p&gt;You can install &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt; with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install XCode command line tools&lt;/span&gt;
xcode-select &lt;span class="nt"&gt;--install&lt;/span&gt;

&lt;span class="c"&gt;# Install Homebrew&lt;/span&gt;
&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"&lt;/span&gt;
/bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Common Core Commands
&lt;/h2&gt;

&lt;p&gt;These commands represent a common set of tools I use across Linux and Windows MSYS2 environments. They include GNU utilities such as &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;gawk&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, and &lt;code&gt;sed&lt;/code&gt;, along with commonly used tools like &lt;code&gt;bc&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;, and &lt;code&gt;zsh&lt;/code&gt;. While macOS includes some of these commands by default, they are often BSD variants with different behavior or significantly older versions that lack features commonly relied upon in scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install common core commands (GNU + bc, curl, jq, zsh) &lt;/span&gt;
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  autoconf &lt;span class="se"&gt;\&lt;/span&gt;
  bash &lt;span class="se"&gt;\&lt;/span&gt;
  bc &lt;span class="se"&gt;\&lt;/span&gt;
  binutils &lt;span class="se"&gt;\&lt;/span&gt;
  coreutils &lt;span class="se"&gt;\&lt;/span&gt;
  curl &lt;span class="se"&gt;\&lt;/span&gt;
  diffutils &lt;span class="se"&gt;\&lt;/span&gt;
  ed &lt;span class="se"&gt;\&lt;/span&gt;
  findutils &lt;span class="se"&gt;\&lt;/span&gt;
  flex &lt;span class="se"&gt;\&lt;/span&gt;
  gawk &lt;span class="se"&gt;\&lt;/span&gt;
  git &lt;span class="se"&gt;\&lt;/span&gt;
  gnu-indent &lt;span class="se"&gt;\&lt;/span&gt;
  gnu-sed &lt;span class="se"&gt;\&lt;/span&gt;
  gnu-tar &lt;span class="se"&gt;\&lt;/span&gt;
  gnu-which &lt;span class="se"&gt;\&lt;/span&gt;
  gpatch &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="se"&gt;\&lt;/span&gt;
  less &lt;span class="se"&gt;\&lt;/span&gt;
  m4 &lt;span class="se"&gt;\&lt;/span&gt;
  make &lt;span class="se"&gt;\&lt;/span&gt;
  nano &lt;span class="se"&gt;\&lt;/span&gt;
  screen &lt;span class="se"&gt;\&lt;/span&gt;
  watch &lt;span class="se"&gt;\&lt;/span&gt;
  wdiff &lt;span class="se"&gt;\&lt;/span&gt;
  wget &lt;span class="se"&gt;\&lt;/span&gt;
  zip &lt;span class="se"&gt;\&lt;/span&gt;
  zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Available Shells
&lt;/h3&gt;

&lt;p&gt;You can run this to make these updated shells available to your environment by appending them to &lt;code&gt;/etc/shells&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;brew &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/bin/bash"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/shells
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;brew &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/bin/zsh"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/shells
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Default Shell
&lt;/h3&gt;

&lt;p&gt;While I prefer &lt;a href="https://www.zsh.org/" rel="noopener noreferrer"&gt;Zsh&lt;/a&gt; as my default interactive shell due to its rich prompt and plugin ecosystem provided by &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;Oh My Zsh!&lt;/a&gt;, all of my scripts are written and tested using &lt;a href="https://www.gnu.org/software/bash/" rel="noopener noreferrer"&gt;GNU Bash&lt;/a&gt; for portability and consistency.&lt;/p&gt;

&lt;p&gt;You can change your default shell using one of the commands below to the shell of your choice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chsh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;brew &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/bin/bash
chsh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;brew &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/bin/zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When running shell scripts, use &lt;code&gt;env&lt;/code&gt; command for your shebang to select the shell from your path, rather than override with a dinosaur version of the shell provided by the system.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bash shebang line&lt;/strong&gt;: &lt;code&gt;#!/usr/bin/env bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zsh shebang line&lt;/strong&gt;: &lt;code&gt;#!/usr/bin/env zsh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Paths
&lt;/h3&gt;

&lt;p&gt;These new GNU commands and other Keg-only commands will not be available for scripts, so they need to be added to the &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You will want to add this below to your startup scripts, like &lt;code&gt;.bash_profile&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; for macOS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;update_homebrew_path&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;BREW_PREFIX
  &lt;span class="nb"&gt;local &lt;/span&gt;GNU_PATHS
  &lt;span class="nb"&gt;local &lt;/span&gt;KEG_PATHS
  &lt;span class="nb"&gt;local &lt;/span&gt;COMBINED_PATHS
  &lt;span class="nb"&gt;local &lt;/span&gt;DEDUPE_SCRIPT

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; brew &amp;amp;&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then return fi

  &lt;/span&gt;&lt;span class="nv"&gt;BREW_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;brew &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;GNU_PATHS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s:"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BREW_PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/opt/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"/libexec/gnubin"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;KEG_PATHS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;
    brew info &lt;span class="nt"&gt;--installed&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1 &lt;span class="se"&gt;\&lt;/span&gt;
      | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'map(select(.keg_only == true)) | .[].name'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; PKG&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
          if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BREW_PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/opt/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s:"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BREW_PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/opt/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin"&lt;/span&gt;
          &lt;span class="k"&gt;fi
        done&lt;/span&gt;
  &lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="nv"&gt;COMBINED_PATHS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GNU_PATHS&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;KEG_PATHS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;DEDUPE_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'print join ":", grep { ! $seen{$_}++ } split /:/, shift'&lt;/span&gt;

  perl &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEDUPE_SCRIPT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMBINED_PATHS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^://; s/:$//'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;NEW_PATHS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;update_homebrew_path&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NEW_PATHS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Installation
&lt;/h2&gt;

&lt;p&gt;Now that we have the basic consistent core commands available, we can install &lt;strong&gt;cloud provisioning&lt;/strong&gt; (&lt;code&gt;terraform&lt;/code&gt;), &lt;strong&gt;Kubernetes&lt;/strong&gt; tools (&lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;helm&lt;/code&gt;, &lt;code&gt;helmfile&lt;/code&gt;, &lt;code&gt;kustomize&lt;/code&gt;), &lt;strong&gt;Corretto&lt;/strong&gt; (&lt;code&gt;java&lt;/code&gt;, &lt;code&gt;jar&lt;/code&gt;, &lt;code&gt;keytool&lt;/code&gt;), and other tools (&lt;code&gt;vault&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install AWS CLI&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;awscli
brew &lt;span class="nb"&gt;install &lt;/span&gt;okta-aws-cli

&lt;span class="c"&gt;# install packages&lt;/span&gt;
brew tap hashicorp/tap
brew &lt;span class="nb"&gt;install &lt;/span&gt;hashicorp/tap/terraform
brew &lt;span class="nb"&gt;install &lt;/span&gt;hashicorp/tap/vault
brew &lt;span class="nb"&gt;install &lt;/span&gt;kubernetes-cli  &lt;span class="c"&gt;# kubectl&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;helm &lt;span class="c"&gt;# helm&lt;/span&gt;

&lt;span class="c"&gt;# Java&lt;/span&gt;
brew tap homebrew/cask-versions
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; corretto17

&lt;span class="c"&gt;# Helmfile and Kustomize&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;kustomize
brew &lt;span class="nb"&gt;install &lt;/span&gt;helmfile

&lt;span class="c"&gt;# Other&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verification
&lt;/h3&gt;

&lt;p&gt;You can verify the tool installations with the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# verify installations&lt;/span&gt;
terraform &lt;span class="nt"&gt;--version&lt;/span&gt;
vault &lt;span class="nt"&gt;--version&lt;/span&gt;
kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
helm version
kustomize version
helmfile &lt;span class="nt"&gt;--version&lt;/span&gt;
java &lt;span class="nt"&gt;--version&lt;/span&gt;
aws &lt;span class="nt"&gt;--version&lt;/span&gt;
okta-aws-cli &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Plugins
&lt;/h2&gt;

&lt;p&gt;Both helm and Kubernetes can extend their functionality through a plugin. Here’s how you can get some popular plugins for these tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Helm Plugins
&lt;/h3&gt;

&lt;p&gt;Helm has a few plugins that you can install, as shown below. I highly recommend the helm-diff, as this allows you to see what resources you will modify before installing or upgrading the the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/databus23/helm-diff
helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/aslafy-z/helm-git
helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/hypnoglow/helm-s3.git
helm plugin &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/jkroepke/helm-secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kubernetes Plugin Engine
&lt;/h3&gt;

&lt;p&gt;Kubernetes has a plugin system with Kubernetes Krew.  You can use this script below to install Krew.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  install_krew
  &lt;span class="c"&gt;# Add this in your startup scripts &lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.krew&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

install_krew&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;TEMP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="nv"&gt;$TEMP_DIR&lt;/span&gt;
  &lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;
    &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/x86_64/amd64/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\(arm\)\(64\)\?.*/\1\2/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/aarch64$/arm64/'&lt;/span&gt;
  &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"krew-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
  &lt;span class="nv"&gt;URL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"kubernetes-sigs/krew/releases/latest/download/&lt;/span&gt;&lt;span class="nv"&gt;$PKG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/&lt;/span&gt;&lt;span class="nv"&gt;$URL_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;tar &lt;/span&gt;zxvf &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
  ./&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KREW&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;krew
  &lt;span class="nb"&gt;popd
  rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; TEMP_DIR
&lt;span class="o"&gt;}&lt;/span&gt;

main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extra: The Package Manifest
&lt;/h2&gt;

&lt;p&gt;Homebrew supports using a package manifest that list all the packages in a single file called &lt;code&gt;Brewfile&lt;/code&gt;.  Create a file named &lt;code&gt;Brewfile&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Common Core Commands&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'autoconf'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'bash'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'bc'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'binutils'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'coreutils'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'curl'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'diffutils'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'ed'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'findutils'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'flex'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'gawk'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'git'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'gnu-indent'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'gnu-sed'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'gnu-tar'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'gnu-which'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'gpatch'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'grep'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'gzip'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'jq'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'less'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'m4'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'make'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'nano'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'screen'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'watch'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'wdiff'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'wget'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'zip'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'zsh'&lt;/span&gt;

&lt;span class="c1"&gt;# AWS Tools&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'awscli'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'okta-aws-cli'&lt;/span&gt;
&lt;span class="c1"&gt;# Hashicorp&lt;/span&gt;
&lt;span class="n"&gt;tap&lt;/span&gt; &lt;span class="s1"&gt;'hashicorp/tap'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'hashicorp/tap/terraform'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'hashicorp/tap/vault'&lt;/span&gt;
&lt;span class="c1"&gt;# Kubernetes&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'kubernetes-cli'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'helm'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'kustomize'&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'helmfile'&lt;/span&gt;
&lt;span class="c1"&gt;# Java&lt;/span&gt;
&lt;span class="n"&gt;tap&lt;/span&gt; &lt;span class="s1"&gt;'homebrew/cask-versions'&lt;/span&gt;
&lt;span class="n"&gt;cask&lt;/span&gt; &lt;span class="s1"&gt;'corretto17'&lt;/span&gt;
&lt;span class="c1"&gt;# Other&lt;/span&gt;
&lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="s1"&gt;'jq'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When ready, you can install packages listed in the &lt;code&gt;Brewfile&lt;/code&gt; with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew bundle &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to set up the &lt;code&gt;PATH&lt;/code&gt; correctly to include the paths to GNU and Keg-only commands. Also, update the available shells in &lt;code&gt;/etc/shells&lt;/code&gt; to that your new version of &lt;code&gt;zsh&lt;/code&gt; and &lt;code&gt;bash&lt;/code&gt; are available. &lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>terraform</category>
      <category>macos</category>
    </item>
    <item>
      <title>Loki: Getting Started</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Sun, 06 Aug 2023 23:22:44 +0000</pubDate>
      <link>https://dev.to/joachim8675309/loki-getting-started-n8o</link>
      <guid>https://dev.to/joachim8675309/loki-getting-started-n8o</guid>
      <description>&lt;p&gt;The realm of open source log aggregators has seen significant growth over the years, with key players like &lt;a href="https://www.elastic.co/elastic-stack" rel="noopener noreferrer"&gt;ElasticSearch&lt;/a&gt; (2010) and &lt;a href="https://graylog.org/" rel="noopener noreferrer"&gt;Graylog&lt;/a&gt; (2016) dominating the field. More recently, &lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Grafana Loki&lt;/a&gt; (2019) has emerged as a compelling addition to this ecosystem. &lt;/p&gt;

&lt;p&gt;This article aims to facilitate a rapid onboarding process for log shipping using &lt;a href="https://grafana.com/docs/loki/latest/clients/promtail/" rel="noopener noreferrer"&gt;Promtail&lt;/a&gt;, log aggregation with &lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Loki&lt;/a&gt;, and data visualization through &lt;a href="https://grafana.com/oss/grafana/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;. Collectively, this powerful trio of tools is commonly referred to as the PLG (Promtail-Loki-Grafana) stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Server System
&lt;/h2&gt;

&lt;p&gt;These instructions will work on Debian or Ubuntu system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Vagrant (Virtualbox)
&lt;/h3&gt;

&lt;p&gt;If you have Intel system, you can use &lt;a href="https://www.vagrantup.com/" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt; with &lt;a href="https://www.virtualbox.org/" rel="noopener noreferrer"&gt;Virtualbox&lt;/a&gt; to quickly bring up virtual servers on your workstation.  Follow the instructions from respect sites to install these tools.&lt;/p&gt;

&lt;p&gt;When ready, create a file called &lt;code&gt;Vagrantfile&lt;/code&gt; with the following contents:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;

&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"generic/ubuntu2204"&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"forwarded_port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest: &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host_ip: &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;"forwarded_port"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest: &lt;/span&gt;&lt;span class="mi"&gt;3100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="mi"&gt;3100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host_ip: &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: For this setup, the configuration will utilize &lt;code&gt;3000&lt;/code&gt; for &lt;a href="https://grafana.com/oss/grafana/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; and &lt;code&gt;3100&lt;/code&gt; for &lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Loki&lt;/a&gt; on the localhost. If any services are currently running on these ports, you'll have to either halt those services or modify the host: key in the provided above configuration to use an alternative port.&lt;/p&gt;

&lt;p&gt;When ready, you can bring up the system and log into the system with the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

vagrant up
vagrant ssh


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Installing Loki
&lt;/h3&gt;

&lt;p&gt;On your desired system, such as the &lt;a href="https://www.vagrantup.com/" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt; managed virtual machine above, you can run the following to install PLG:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; software-properties-common wget
&lt;span class="nb"&gt;sudo &lt;/span&gt;wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; /usr/share/keyrings/grafana.key &lt;span class="se"&gt;\&lt;/span&gt;
  https://apt.grafana.com/gpg.key

&lt;span class="c"&gt;# add a repository for stable releases&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/apt/sources.list.d/grafana.list

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; promtail loki grafana


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Verify Services are running
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;service loki status
&lt;span class="nb"&gt;sudo &lt;/span&gt;service grafana-server status


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If any of these services are stopped, you can run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;service loki start
&lt;span class="nb"&gt;sudo &lt;/span&gt;service grafana-server start


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  The Client Workstation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Visualization
&lt;/h3&gt;

&lt;p&gt;You can access the login page by opening a web browser on your system and navigating to &lt;a href="http://127.0.0.1:3000" rel="noopener noreferrer"&gt;http://127.0.0.1:3000&lt;/a&gt;. Simply use the default credentials, admin for both the username and password. Upon login, you will be prompted to set a new password for the admin account.&lt;/p&gt;

&lt;p&gt;After logging in, you can click on Explore, Loki, and then select on of the logs by clicking on Label.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fujyociv0oapsro55x8uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fujyociv0oapsro55x8uy.png" alt="Screen Shot of Grafana UI"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Ship More Logs with Promtail
&lt;/h3&gt;

&lt;p&gt;When setting up a new system, there may not be many interesting logs initially. However, your host workstation system is likely to have accumulated a wealth of interesting logs over time.&lt;/p&gt;

&lt;p&gt;If you are running the server as a virtual machine using the previously mentioned &lt;code&gt;Vagrantfile&lt;/code&gt; configuration, you can easily ship logs from the laptop to &lt;code&gt;localhost:3100&lt;/code&gt;, where &lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Loki&lt;/a&gt; is up and running. This allows you to leverage the abundant logs from your host workstation for analysis and visualization.&lt;/p&gt;

&lt;p&gt;You can install &lt;a href="https://grafana.com/docs/loki/latest/clients/promtail/" rel="noopener noreferrer"&gt;Promtail&lt;/a&gt; locally as well. If the host system is running Debian or Ubuntu, you can run this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# add a repository for stable releases&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; /usr/share/keyrings/grafana.key &lt;span class="se"&gt;\&lt;/span&gt;
  https://apt.grafana.com/gpg.key
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/apt/sources.list.d/grafana.list

&lt;span class="c"&gt;# install promtail&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; promtail


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By default, the configuration file &lt;code&gt;/etc/promtail/config.yml&lt;/code&gt; will set up Promtail to ship logs to the URL: &lt;code&gt;http://localhost:3100/loki/api/v1/push&lt;/code&gt;. This configuration will function correctly if you are using &lt;a href="https://www.vagrantup.com/" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt; with &lt;a href="https://www.virtualbox.org/" rel="noopener noreferrer"&gt;Virtualbox&lt;/a&gt; to run &lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Loki&lt;/a&gt;. However, if you are not running the &lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Loki&lt;/a&gt; server locally, you will need to modify the address (currently set to &lt;code&gt;localhost&lt;/code&gt;) in the configuration accordingly.&lt;/p&gt;

&lt;p&gt;As an option you can modify the configuration with the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;http_listen_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9080&lt;/span&gt;
  &lt;span class="na"&gt;grpc_listen_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

&lt;span class="na"&gt;positions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/positions.yaml&lt;/span&gt;

&lt;span class="na"&gt;clients&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3100/loki/api/v1/push&lt;/span&gt;

&lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;system&lt;/span&gt;
  &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;varlogs&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_workstation&lt;/span&gt;
      &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;promtail&lt;/span&gt;
      &lt;span class="na"&gt;__path__&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/log/*log&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And later restart the service:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart promtail


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>logaggregator</category>
      <category>observability</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Ultimate EKS Baseline Cluster: Part 2 - Storage</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Mon, 24 Jul 2023 00:56:03 +0000</pubDate>
      <link>https://dev.to/joachim8675309/ultimate-eks-baseline-cluster-part-2-storage-3kpi</link>
      <guid>https://dev.to/joachim8675309/ultimate-eks-baseline-cluster-part-2-storage-3kpi</guid>
      <description>&lt;p&gt;This may come as a surprise to some, but the AWS managed Kubernetes service, &lt;a href="https://aws.amazon.com/eks/"&gt;EKS&lt;/a&gt; (&lt;a href="https://aws.amazon.com/eks/"&gt;Elastic Kubernetes Service&lt;/a&gt;), no longer comes with support for storage through Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;persistent volumes&lt;/a&gt;, which is required for many applications, such as databases.&lt;/p&gt;

&lt;p&gt;You will need to add a (&lt;a href="https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/"&gt;CSI&lt;/a&gt;) driver to enable &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;persistent volumes&lt;/a&gt; support.&lt;/p&gt;

&lt;p&gt;This guide, Part 2 of &lt;em&gt;Ultimate EKS Baseline Cluster&lt;/em&gt; series,  covers doing just that by installing &lt;a href="https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/"&gt;CSI&lt;/a&gt;) called &lt;a href="https://github.com/kubernetes-sigs/aws-ebs-csi-driver"&gt;AWS EBS CSI Driver&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  0. Prerequisites
&lt;/h2&gt;

&lt;p&gt;These are are some prerequisites and initial steps needed to get started before provisioning a Kubernetes cluster and installing add-ons.&lt;/p&gt;

&lt;h3&gt;
  
  
  0.1 Knowledge: Systems
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q1OrSwOU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqzshuffp4kni7i9t487.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q1OrSwOU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqzshuffp4kni7i9t487.png" alt="image of system concept icons" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basic concepts of systems, such as Linux and the shell (redirection, pipes, process substitution, command substitution, environment variables), as well as virtualization and containers are useful. The concept of a service (daemon) is important.&lt;/p&gt;

&lt;h3&gt;
  
  
  0.2 Knowledge: Kubernetes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oOvemFw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5i7xyp8oty9jp44r0dv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oOvemFw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5i7xyp8oty9jp44r0dv.png" alt="image of Kubernetes concept icons" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Kubernetes, familiarity with service types: ClusterIP, NodePort, LoadBalancer, ExternalName and the ingress resource are important.&lt;/p&gt;

&lt;p&gt;Exposure to other types of Kubernetes resource objects used in this guide are helpful: &lt;code&gt;persistentvolumeclaims&lt;/code&gt; (&lt;code&gt;pvc&lt;/code&gt;), &lt;code&gt;storageclass&lt;/code&gt; (&lt;code&gt;sc&lt;/code&gt;), &lt;code&gt;pods&lt;/code&gt;, &lt;code&gt;deployments&lt;/code&gt;, &lt;code&gt;statefulsets&lt;/code&gt; (&lt;code&gt;sts&lt;/code&gt;), &lt;code&gt;configmaps&lt;/code&gt;, &lt;code&gt;serviceaccount&lt;/code&gt; (&lt;code&gt;sa&lt;/code&gt;) and &lt;code&gt;networkpolicies&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  0.3 Tools
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6ZkkVDOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7u87et62nyhbwrksb29d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6ZkkVDOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7u87et62nyhbwrksb29d.png" alt="image of tool icons" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the tools used in this article series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cli/"&gt;AWS CLI&lt;/a&gt; [&lt;code&gt;aws&lt;/code&gt;] is a tool that interacts with AWS.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/reference/kubectl/"&gt;kubectl client&lt;/a&gt; [&lt;code&gt;kubectl&lt;/code&gt;] a the tool that can interact with the Kubernetes cluster. This can be installed using adsf tool.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://eksctl.io/"&gt;eksctl&lt;/a&gt; [eksctl] is the tool that can provision EKS cluster as well as supporting VPC network infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html"&gt;POSIX Shell&lt;/a&gt; [&lt;code&gt;sh&lt;/code&gt;] such as &lt;a href="https://www.gnu.org/software/bash/"&gt;bash&lt;/a&gt;[&lt;code&gt;bash&lt;/code&gt;] or &lt;a href="https://www.zsh.org/"&gt;zsh&lt;/a&gt; [&lt;code&gt;zsh&lt;/code&gt;] are used to run the commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools are highly recommended:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://asdf-vm.com/"&gt;adsf&lt;/a&gt; [&lt;code&gt;adsf&lt;/code&gt;] is a tool that installs versions of popular tools like kubectl.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jqlang.github.io/jq/"&gt;jq&lt;/a&gt; [&lt;code&gt;jq&lt;/code&gt;] is a tool to query and print JSON data&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/grep/"&gt;GNU Grep&lt;/a&gt; [&lt;code&gt;grep&lt;/code&gt;] supports extracting string patterns using extended &lt;a href="https://en.wikipedia.org/wiki/Regular_expression"&gt;Regex&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions"&gt;PCRE&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  0.3 Setup Environment Variables
&lt;/h3&gt;

&lt;p&gt;These environment variables will be used throughout this guide. If opening up a new browser tab, make sure to set the environment variables accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# variables used to create EKS&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-aws-profile"&lt;/span&gt; &lt;span class="c"&gt;# CHANGEME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EKS_CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-unique-cluster-name"&lt;/span&gt; &lt;span class="c"&gt;# CHANGEME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EKS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EKS_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.26"&lt;/span&gt;

&lt;span class="c"&gt;# KUBECONFIG variable&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/&lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt;.&lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt;.yaml

&lt;span class="c"&gt;# account id&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Account"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ebs-csi-driver&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ROLE_NAME_ECSI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EKS_CLUSTER_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_EBS_CSI_DriverRole"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_ROLE_ARN_ECSI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_ID&lt;/span&gt;&lt;span class="s2"&gt;:role/&lt;/span&gt;&lt;span class="nv"&gt;$ROLE_NAME_ECSI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;POLICY_NAME_ECSI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"AmazonEBSCSIDriverPolicy"&lt;/span&gt; &lt;span class="c"&gt;# preinstalled by AWS&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;POLICY_ARN_ECSI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/&lt;/span&gt;&lt;span class="nv"&gt;$POLICY_NAME_ECSI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  0.4 AWS Setup
&lt;/h3&gt;

&lt;p&gt;There's an assumption that AWS CLI have been setup and configured with a profile. This is required before creating or interacting with an EKS cluster.&lt;/p&gt;

&lt;p&gt;You can test access to a configured profile with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-profile-goes-here&amp;gt;"&lt;/span&gt;
aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like the following with values appropriate to your environment, e.g. example output to IAM user named &lt;code&gt;kwisatzhaderach&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AIDAXXXXXXXXXXXXXXXXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XXXXXXXXXXXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::XXXXXXXXXXXX:user/kwisatzhaderach"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  0.5 EKS Cluster
&lt;/h3&gt;

&lt;p&gt;This article requires that an EKS cluster has been previously provisioned using &lt;code&gt;eksctl&lt;/code&gt; tool.&lt;/p&gt;

&lt;p&gt;I wrote a previous article that covered this how to provision EKS with two commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/joachim8675309/ultimate-eks-baseline-cluster-part-1-provision-eks-17f"&gt;Ultimate EKS Baseline Cluster: Part 1 - Provision EKS &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  0.5.1 Existing EKS Cluster
&lt;/h4&gt;

&lt;p&gt;If you have an existing EKS cluster, but need to configure &lt;code&gt;KUBECONFIG&lt;/code&gt; for access, you can run this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube &lt;span class="c"&gt;# conditionally create ~/.kube&lt;/span&gt;
&lt;span class="c"&gt;# use consistent $KUBECONFIG&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/&lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt;.&lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt;.yaml

&lt;span class="c"&gt;# update config pointed to by $KUBECONFIG &lt;/span&gt;
aws eks update-kubeconfig &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$CLUSTER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  0.5.2 Create a new EKS Cluster
&lt;/h4&gt;

&lt;p&gt;If you have not setup an EKS cluster, you can set it up with the following commands (~20 minutes process):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube &lt;span class="c"&gt;# conditionally create ~/.kube&lt;/span&gt;
&lt;span class="c"&gt;# use consistent $KUBECONFIG&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/&lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt;.&lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt;.yaml

&lt;span class="c"&gt;# provision EKS + add config to $KUBECONFIG &lt;/span&gt;
eksctl create cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="nv"&gt;$EKS_VERSION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--nodes&lt;/span&gt; 3

&lt;span class="c"&gt;# setup OIDC provider for least privilege&lt;/span&gt;
eksctl utils associate-iam-oidc-provider &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Details of this were covered in the &lt;a href="https://dev.to/joachim8675309/ultimate-eks-baseline-cluster-part-1-provision-eks-17f"&gt;previous article&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  0.6 Kubernetes Client Setup
&lt;/h3&gt;

&lt;p&gt;If you use &lt;code&gt;asdf&lt;/code&gt; to install &lt;code&gt;kubectl&lt;/code&gt;, you can get the latest version with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install kubectl plugin for asdf&lt;/span&gt;
asdf plugin-add kubectl &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/asdf-community/asdf-kubectl.git
asdf &lt;span class="nb"&gt;install &lt;/span&gt;kubectl latest

&lt;span class="c"&gt;# fetch latest kubectl &lt;/span&gt;
asdf &lt;span class="nb"&gt;install &lt;/span&gt;kubectl latest
asdf global kubectl latest

&lt;span class="c"&gt;# test results of latest kubectl &lt;/span&gt;
kubectl version &lt;span class="nt"&gt;--short&lt;/span&gt; &lt;span class="nt"&gt;--client&lt;/span&gt; 2&amp;gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Client Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.27.4&lt;/span&gt;
&lt;span class="na"&gt;Kustomize Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v5.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. AWS EBS CSI driver
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bBkzklPh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bug7a5d2vpxzirfr6hs7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bBkzklPh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bug7a5d2vpxzirfr6hs7.png" alt="Title bar with icons for EBS and CSI" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current versions of &lt;a href="https://aws.amazon.com/eks/"&gt;EKS&lt;/a&gt; starting with 1.23 no longer come with a persistent volume support, so you have to install it on your own. The best method or at least the easiest way to install this, is using &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-add-ons.html"&gt;EKS add-ons&lt;/a&gt; facility. This will install the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html"&gt;EBS CSI driver&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Installation of this component will require the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html"&gt;IAM Role&lt;/a&gt; (e.g. &lt;code&gt;EBS_CSI_DriverRole&lt;/code&gt;) and associate it to &lt;a href="https://kubernetes.io/docs/concepts/security/service-accounts/"&gt;Kubernetes service account&lt;/a&gt; (i.e. &lt;code&gt;ebs-csi-controller-sa&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Deploy AWS EBS CSI driver using EKS add-ons facility, which also sets up the &lt;a href="https://kubernetes.io/docs/concepts/security/service-accounts/"&gt;Kubernetes service account&lt;/a&gt; (i.e. &lt;code&gt;ebs-csi-controller-sa&lt;/code&gt;) with an association back to the above &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html"&gt;IAM Role&lt;/a&gt; (e.g. &lt;code&gt;EBS_CSI_DriverRole&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Create storage class that uses new the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html"&gt;EBS CSI driver&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.1 Setup IAM Role and K8S SA association
&lt;/h3&gt;

&lt;p&gt;The following process will create an &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html"&gt;IAM Role&lt;/a&gt; with permissions to access AWS EBS API. The service account &lt;code&gt;ebs-csi-controller-sa&lt;/code&gt; will be created later when installing the driver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# AWS IAM role bound to a Kubernetes service account&lt;/span&gt;
eksctl create iamserviceaccount &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"ebs-csi-controller-sa"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"kube-system"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach-policy-arn&lt;/span&gt; &lt;span class="nv"&gt;$POLICY_ARN_ECSI&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-only&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="nv"&gt;$ROLE_NAME_ECSI&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html"&gt;IAM Role&lt;/a&gt;, which you can verify with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam get-role &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="nv"&gt;$ROLE_NAME_ECSI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"RoleName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mycluster_EBS_CSI_DriverRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"RoleId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AROAZYKZFDW7YZGW3Q5S7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::XXXXXXXXXXXX:role/mycluster_EBS_CSI_DriverRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"CreateDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-07-07T21:19:32+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AssumeRolePolicyDocument"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::XXXXXXXXXXXX:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/6311CF96A267242FD6587B1C29D57F1D"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="nl"&gt;"oidc.eks.us-west-2.amazonaws.com/id/6311CF96A267242FD6587B1C29D57F1D:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="nl"&gt;"oidc.eks.us-west-2.amazonaws.com/id/6311CF96A267242FD6587B1C29D57F1D:sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system:serviceaccount:kube-system:ebs-csi-controller-sa"&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"MaxSessionDuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alpha.eksctl.io/cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mycluster"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eksctl.cluster.k8s.io/v1alpha1/cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mycluster"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alpha.eksctl.io/iamserviceaccount-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kube-system/ebs-csi-controller-sa"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alpha.eksctl.io/eksctl-version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.141.0-dev+5c8318ed5.2023-05-12T11:33:48Z"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"RoleLastUsed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Install AWS EBS CSI Drvier
&lt;/h3&gt;

&lt;p&gt;This installation uses the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-add-ons.html"&gt;EKS Addons&lt;/a&gt; feature to install the component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Addon&lt;/span&gt;
eksctl create addon &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"aws-ebs-csi-driver"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-account-role-arn&lt;/span&gt; &lt;span class="nv"&gt;$ACCOUNT_ROLE_ARN_ECSI&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="c"&gt;# Pause here until driver is active&lt;/span&gt;
&lt;span class="nv"&gt;ACTIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ACTIVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if &lt;/span&gt;eksctl get addon &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"aws-ebs-csi-driver"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $3}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;
  &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;ACTIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to wait until status changes to &lt;code&gt;ACTIVE&lt;/code&gt; before proceeding.&lt;/p&gt;

&lt;p&gt;You can inspect the pods created by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"kube-system"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--selector&lt;/span&gt; &lt;span class="s2"&gt;"app.kubernetes.io/name=aws-ebs-csi-driver"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                  READY   STATUS    RESTARTS   AGE
ebs-csi-controller-6d5b7bfd56-bwr5x   6/6     Running   0          2m1s
ebs-csi-controller-6d5b7bfd56-wtxf6   6/6     Running   0          2m2s
ebs-csi-node-hjmf5                    3/3     Running   0          2m2s
ebs-csi-node-tzpgs                    3/3     Running   0          2m2s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify the service account annotations references the IAM Role for the EBS CSI driver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get serviceaccount &lt;span class="s2"&gt;"ebs-csi-controller-sa"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"kube-system"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;automountServiceAccountToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;eks.amazonaws.com/role-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::XXXXXXXXXXXX:role/my-unique-cluster-name_EBS_CSI_DriverRole&lt;/span&gt;
  &lt;span class="na"&gt;creationTimestamp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2023-07-24T21:12:02Z"&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;csi-driver&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/managed-by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EKS&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-ebs-csi-driver&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.21.0&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ebs-csi-controller-sa&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
  &lt;span class="na"&gt;resourceVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1922"&lt;/span&gt;
  &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1b8ecfdb-ce64-491c-8ced-e08b5519755c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1.2.1 Sidebar: eks-addons vs helm chart?
&lt;/h4&gt;

&lt;p&gt;The CSI driver can be installed using either &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-add-ons.html"&gt;EKS Addons&lt;/a&gt; facility from the previous step, or using the &lt;a href="https://artifacthub.io/packages/helm/aws-ebs-csi-driver/aws-ebs-csi-driver"&gt;aws-ebs-csi-driver&lt;/a&gt; Helm chart. I prefer the EKS addons because of simplicity, but also because it installs an extra snapshot container that doesn't come by default with the helm chart. &lt;/p&gt;

&lt;p&gt;I documented the full process using the helm chart in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@joachim8675309/eks-ebs-storage-with-eksctl-3e526f534215"&gt;EKS + EBS Storage with eksctl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.3 Create storage class that uses the EBS CSI driver
&lt;/h3&gt;

&lt;p&gt;In order to use the driver, we will need to create a storage class. You can do so by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create ebs-sc storage class&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | kubectl apply --filename -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When completed you can verify the storage class was created with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get storageclass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
ebs-sc          ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   17s
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  30m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.4 Set new storage class to the default (optional)
&lt;/h3&gt;

&lt;p&gt;This is an optional step. As there’s no functional default storage class, we can set the newly created storage class to be the default with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch storageclass gp2 &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="s1"&gt;'{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'&lt;/span&gt;
kubectl patch storageclass ebs-sc &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="s1"&gt;'{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, you can verify the change with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get storageclass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME               PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
ebs-sc (default)   ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   31s
gp2                kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  30m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.5 Testing Persistent Volume
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OH3fjqZL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k7uochrbvzgnd47q5zts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OH3fjqZL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k7uochrbvzgnd47q5zts.png" alt="Image for title bar with icon for pvc" width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this small test, we deploy a pod that continually writes to the external volume, and a volume claim to allocate storage using the storage class we created earlier.&lt;/p&gt;

&lt;p&gt;If this works, the storage will be provisioned in the cloud to create the volume, and then it will be attached to the node and mounted in the pod. If this fails, you will see that the pod will be stuck in &lt;code&gt;pending&lt;/code&gt; mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create pod with persistent volume&lt;/span&gt;
kubectl create namespace &lt;span class="s2"&gt;"ebs-test"&lt;/span&gt;

&lt;span class="c"&gt;# deploy application with mounted volume&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' | kubectl apply --namespace "ebs-test" --filename -
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: ubuntu
      command: ["/bin/sh"]
      args: ["-c", "while true; do echo &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; /data/out.txt; sleep 5; done"]
      volumeMounts:
      - name: persistent-storage
        mountPath: /data
  volumes:
    - name: persistent-storage
      persistentVolumeClaim:
        claimName: ebs-claim
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-claim
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ebs-sc
  resources:
    requests:
      storage: 4Gi
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test the results of the volume creation with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all,pvc &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"ebs-test"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also look at the events that took place in this namespace with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl events &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"ebs-test"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will show something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LAST SEEN           TYPE      REASON                   OBJECT                            MESSAGE
56s                 Warning   FailedScheduling         Pod/app                           0/3 nodes are available: persistentvolumeclaim "ebs-claim" not found. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod..
55s                 Normal    WaitForPodScheduled      PersistentVolumeClaim/ebs-claim   waiting for pod app to be scheduled
54s                 Normal    Provisioning             PersistentVolumeClaim/ebs-claim   External provisioner is provisioning volume for claim "ebs-test/ebs-claim"
54s (x2 over 54s)   Normal    ExternalProvisioning     PersistentVolumeClaim/ebs-claim   waiting for a volume to be created, either by external provisioner "ebs.csi.aws.com" or manually created by system administrator
50s                 Normal    Scheduled                Pod/app                           Successfully assigned ebs-test/app to ip-192-168-22-207.us-west-2.compute.internal
50s                 Normal    ProvisioningSucceeded    PersistentVolumeClaim/ebs-claim   Successfully provisioned volume pvc-39b9cf94-8b35-436c-b56d-e2fa587245ee
48s                 Normal    SuccessfulAttachVolume   Pod/app                           AttachVolume.Attach succeeded for volume "pvc-39b9cf94-8b35-436c-b56d-e2fa587245ee"
44s                 Normal    Pulling                  Pod/app                           Pulling image "ubuntu"
41s                 Normal    Pulled                   Pod/app                           Successfully pulled image "ubuntu" in 2.952872938s (2.952907339s including waiting)
41s                 Normal    Created                  Pod/app                           Created container app
41s                 Normal    Started                  Pod/app                           Started container app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.6 Delete test application
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete pod app &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"ebs-test"&lt;/span&gt;
kubectl delete pvc ebs-claim &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"ebs-test"&lt;/span&gt;
kubectl delete ns &lt;span class="s2"&gt;"ebs-test"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2.0 Deleting EKS
&lt;/h2&gt;

&lt;p&gt;When deleting up the EKS cluster, you may want to run through these steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Deleting persistent volume claims
&lt;/h3&gt;

&lt;p&gt;When deleting EKS cluster, if you did not delete persistent volumes, you will have left over unused EBS volumes eating costs.&lt;/p&gt;

&lt;p&gt;You should delete all the persistent volume claims, which will delete associate persistent volumes.  You can list all of the &lt;code&gt;persistentvolumeclaim&lt;/code&gt; resources with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get persistentvolumeclaim &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; none
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not that some of these will not be deleted if there is an application running that has a lock to the storage.  So you will need to delete the associated application as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Reset Default to original Storage Class
&lt;/h3&gt;

&lt;p&gt;As a precaution, we don’t want to have any resources locked that may prevent deletion of the Kubernetes cluster. Run this command if we changed the defaults earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch storageclass ebs-sc &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'&lt;/span&gt;
kubectl patch storageclass gp2 &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 IAM Roles
&lt;/h3&gt;

&lt;p&gt;These should be removed when removing Kubernetes with &lt;code&gt;eksctl&lt;/code&gt;, but it is a good practice to remove them just in case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl delete iamserviceaccount &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"ebs-csi-controller-sa"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"kube-system"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.4 Kubernetes cluster
&lt;/h3&gt;

&lt;p&gt;Finally, the Kubernetes cluster itself can be deleted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl delete cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This second article shows add support for storage, called persistent volumes on Kubernetes.  &lt;/p&gt;

&lt;p&gt;The automation using the &lt;code&gt;eksctl&lt;/code&gt; will do the following additional things besides provisioning EKS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setup restricted access to AWS cloud resources using &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html"&gt;IRSA&lt;/a&gt;, which associates &lt;a href="https://kubernetes.io/docs/concepts/security/service-accounts/"&gt;KSA&lt;/a&gt; with &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html"&gt;IAM Role&lt;/a&gt; using an &lt;a href="https://openid.net/developers/how-connect-works/"&gt;OIDC&lt;/a&gt; provider.&lt;/li&gt;
&lt;li&gt;install applications using &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-add-ons.html"&gt;EKS addons&lt;/a&gt; facility, specifically the EBS CSI driver. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In future articles, I will cover how to add support for load balancers and network policies, as well as a demo application that demonstrates all of these features.&lt;/p&gt;

</description>
      <category>awscloud</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Ultimate EKS Baseline Cluster: Part 1 - Provision EKS</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Fri, 21 Jul 2023 19:14:52 +0000</pubDate>
      <link>https://dev.to/joachim8675309/ultimate-eks-baseline-cluster-part-1-provision-eks-17f</link>
      <guid>https://dev.to/joachim8675309/ultimate-eks-baseline-cluster-part-1-provision-eks-17f</guid>
      <description>&lt;p&gt;The AWS managed Kubernetes service, &lt;a href="https://aws.amazon.com/eks/" rel="noopener noreferrer"&gt;EKS&lt;/a&gt; (&lt;a href="https://aws.amazon.com/eks/" rel="noopener noreferrer"&gt;Elastic Kubernetes Service&lt;/a&gt;), has the highest level of complexity amongst cloud offerings. &lt;/p&gt;

&lt;p&gt;Besides having to build out the networking, routing, security, and worker nodes separately from the managed master nodes, there’s no longer any bundled support for storage starting from 1.23, and the existing support for external load balancers is limited.&lt;/p&gt;

&lt;p&gt;This tutorial, presented in separate parts, will demonstrate how to build out a robust Kubernetes cluster with just a few commands. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;EKS Cluster with support for OIDC&lt;/li&gt;
&lt;li&gt;Add Storage support: &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/" rel="noopener noreferrer"&gt;persistent volumes&lt;/a&gt; using &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html" rel="noopener noreferrer"&gt;EBS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add load balancer support: layer 4 (&lt;a href="https://aws.amazon.com/elasticloadbalancing/network-load-balancer/" rel="noopener noreferrer"&gt;NLB&lt;/a&gt;) and layer 7 (&lt;a href="https://aws.amazon.com/elasticloadbalancing/application-load-balancer/" rel="noopener noreferrer"&gt;ALB&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add traffic security support: network policies using &lt;a href="https://www.tigera.io/project-calico/" rel="noopener noreferrer"&gt;Calico&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo application with &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; putting it all together&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to secure services that need access to cloud resources, an &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OIDC provider&lt;/a&gt; through &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html" rel="noopener noreferrer"&gt;IAM Roles for Service Account&lt;/a&gt; facility will be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  0. Prerequisites
&lt;/h2&gt;

&lt;p&gt;These are are some prerequisites and initial steps needed to get started before provisioning a Kubernetes cluster and installing add-ons.&lt;/p&gt;

&lt;h3&gt;
  
  
  0.1 Knowledge: Systems
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqzshuffp4kni7i9t487.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqzshuffp4kni7i9t487.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basic concepts of systems, such as Linux and the shell (redirection, pipes, process substitution, command substitution, environment variables), as well as virtualization and containers are useful. The concept of a service (daemon) is important.&lt;/p&gt;

&lt;h3&gt;
  
  
  0.2 Knowledge: Networking
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hd97akfkxxg8dzg26mh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hd97akfkxxg8dzg26mh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article requires some basic understanding of networking with TCP/IP and the OSI model, specifically the Transport Layer 4 and Application Layer 7 for HTTP. This article covers using load balancing and reverse proxy.&lt;/p&gt;

&lt;p&gt;In Kubernetes, familiarity with service types: ClusterIP, NodePort, LoadBalancer, ExternalName and the ingress resource are important.&lt;/p&gt;

&lt;p&gt;Exposure to other types of Kubernetes resource objects used in this guide are helpful: &lt;code&gt;persistentvolumeclaims&lt;/code&gt; (&lt;code&gt;pvc&lt;/code&gt;), &lt;code&gt;storageclass&lt;/code&gt; (&lt;code&gt;sc&lt;/code&gt;), &lt;code&gt;pods&lt;/code&gt;, &lt;code&gt;deployments&lt;/code&gt;, &lt;code&gt;statefulsets&lt;/code&gt; (&lt;code&gt;sts&lt;/code&gt;), &lt;code&gt;configmaps&lt;/code&gt;, &lt;code&gt;serviceaccount&lt;/code&gt; (&lt;code&gt;sa&lt;/code&gt;) and &lt;code&gt;networkpolicies&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  0.4 Tools
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u87et62nyhbwrksb29d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u87et62nyhbwrksb29d.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the tools used in this article series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; [&lt;code&gt;aws&lt;/code&gt;] is a tool that interacts with AWS.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/reference/kubectl/" rel="noopener noreferrer"&gt;kubectl client&lt;/a&gt; [&lt;code&gt;kubectl&lt;/code&gt;] a the tool that can interact with the Kubernetes cluster. This can be installed using adsf tool.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;helm&lt;/a&gt; [&lt;code&gt;helm&lt;/code&gt;] is a tool that can install Kubernetes applications that are packaged as helm charts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://eksctl.io/" rel="noopener noreferrer"&gt;eksctl&lt;/a&gt; [&lt;code&gt;eksctl&lt;/code&gt;] is the tool that can provision EKS cluster as well as supporting VPC network infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html" rel="noopener noreferrer"&gt;POSIX Shell&lt;/a&gt; [&lt;code&gt;sh&lt;/code&gt;] such as &lt;a href="https://www.gnu.org/software/bash/" rel="noopener noreferrer"&gt;bash&lt;/a&gt;[&lt;code&gt;bash&lt;/code&gt;] or &lt;a href="https://www.zsh.org/" rel="noopener noreferrer"&gt;zsh&lt;/a&gt; [&lt;code&gt;zsh&lt;/code&gt;] are used to run the commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools are highly recommended:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://asdf-vm.com/" rel="noopener noreferrer"&gt;adsf&lt;/a&gt; [&lt;code&gt;adsf&lt;/code&gt;] is a tool that installs versions of popular tools like kubectl.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jqlang.github.io/jq/" rel="noopener noreferrer"&gt;jq&lt;/a&gt; [&lt;code&gt;jq&lt;/code&gt;] is a tool to query and print JSON data&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/grep/" rel="noopener noreferrer"&gt;GNU Grep&lt;/a&gt; [&lt;code&gt;grep&lt;/code&gt;] supports extracting string patterns using extended &lt;a href="https://en.wikipedia.org/wiki/Regular_expression" rel="noopener noreferrer"&gt;Regex&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions" rel="noopener noreferrer"&gt;PCRE&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  0.5 AWS Setup
&lt;/h3&gt;

&lt;p&gt;Before getting started on EKS, you will need to set up billing to an AWS account (there’s a free tier), and then configure a profile that has provides access to an IAM User identity. See Setting up the AWS CLI for more information on configuring a profile.&lt;/p&gt;

&lt;p&gt;After setup, you can test access with the following below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-profile-goes-here&amp;gt;"&lt;/span&gt;
aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like the following with values appropriate to your environment, e.g. example output to IAM user named &lt;code&gt;kwisatzhaderach&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqlvd4rpb6fj0763mwrj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqlvd4rpb6fj0763mwrj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  0.6 Kubernetes Client Setup
&lt;/h3&gt;

&lt;p&gt;If you use &lt;code&gt;asdf&lt;/code&gt; to install &lt;code&gt;kubectl&lt;/code&gt;, you can get the latest version with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install kubectl plugin for asdf&lt;/span&gt;
asdf plugin-add kubectl &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/asdf-community/asdf-kubectl.git
asdf &lt;span class="nb"&gt;install &lt;/span&gt;kubectl latest

&lt;span class="c"&gt;# fetch latest kubectl &lt;/span&gt;
asdf &lt;span class="nb"&gt;install &lt;/span&gt;kubectl latest
asdf global kubectl latest

&lt;span class="c"&gt;# test results of latest kubectl &lt;/span&gt;
kubectl version &lt;span class="nt"&gt;--short&lt;/span&gt; &lt;span class="nt"&gt;--client&lt;/span&gt; 2&amp;gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Client Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.27.1&lt;/span&gt;
&lt;span class="na"&gt;Kustomize Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v5.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, create directory to store Kubernetes configurations that will be used by the &lt;code&gt;KUBECONFIG&lt;/code&gt; env variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  0.7 Setup Environment Variables
&lt;/h3&gt;

&lt;p&gt;These environment variables will be used throughout this guide. If opening up a new browser tab, make sure to set the environment variables accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# variables used to create EKS&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-aws-profile"&lt;/span&gt; &lt;span class="c"&gt;# CHANGEME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EKS_CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-unique-cluster-name"&lt;/span&gt; &lt;span class="c"&gt;# CHANGEME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EKS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EKS_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.26"&lt;/span&gt;
&lt;span class="c"&gt;# KUBECONFIG variable&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/&lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt;.&lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt;.yaml

&lt;span class="c"&gt;# account id&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Account"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. Provision an EKS cluster
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffu5ewlzhqd5qdgjdvfg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffu5ewlzhqd5qdgjdvfg9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After prerequisite tools are installed and setup, we can start provisioning cloud resources and deploy components to Kubernetes. &lt;/p&gt;

&lt;h3&gt;
  
  
  1.1. Provision the EKS Cluster
&lt;/h3&gt;

&lt;p&gt;The cluster can be brought up with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="nv"&gt;$EKS_VERSION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--nodes&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, check the status of the worker nodes and applications running on Kubernetes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes
kubectl get all &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like the following.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdlwkg7iospru75jpyki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdlwkg7iospru75jpyki.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2. Install the Kubernetes client
&lt;/h3&gt;

&lt;p&gt;Once this finished in about 20 minutes to provision both VPC and EKS, install a kubectl version that matches the Kubernetes server version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# fetch exact version of Kubernetes server (Requires GNU Grep)&lt;/span&gt;
&lt;span class="nv"&gt;VER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl version &lt;span class="nt"&gt;--short&lt;/span&gt; 2&amp;gt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep &lt;/span&gt;Server &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'(\d{1,2}\.){2}\d{1,2}'&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# setup kubectl tool&lt;/span&gt;
asdf &lt;span class="nb"&gt;install &lt;/span&gt;kubectl &lt;span class="nv"&gt;$VER&lt;/span&gt;
asdf global kubectl &lt;span class="nv"&gt;$VER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;NOTE&lt;/strong&gt;: The above command requires &lt;a href="https://www.gnu.org/software/grep/" rel="noopener noreferrer"&gt;GNU grep&lt;/a&gt;. If you have &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt;, you can run &lt;code&gt;brew install grep&lt;/code&gt;. Windows can get this with &lt;a href="https://www.msys2.org/" rel="noopener noreferrer"&gt;MSYS2&lt;/a&gt; or &lt;a href="https://git-scm.com/download/win" rel="noopener noreferrer"&gt;git-bash&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1.3 Add OIDC Provider Support
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgesyugb0faz6d5qxz8bf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgesyugb0faz6d5qxz8bf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The EKS cluster has an &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt; (&lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OIDC&lt;/a&gt;) issuer URL associated with it. To use &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html" rel="noopener noreferrer"&gt;IRSA&lt;/a&gt; (&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html" rel="noopener noreferrer"&gt;IAM roles for service accounts&lt;/a&gt;), an IAM OIDC provider must exist for the cluster’s OIDC issuer URL.&lt;/p&gt;

&lt;p&gt;You can set this up with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl utils associate-iam-oidc-provider &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify the &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html" rel="noopener noreferrer"&gt;OIDC provider&lt;/a&gt; is added with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OIDC_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws eks describe-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$EKS_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$EKS_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"cluster.identity.oidc.issuer"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 5
&lt;span class="si"&gt;)&lt;/span&gt;

aws iam list-open-id-connect-providers &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nv"&gt;$OIDC_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Cloud Resources
&lt;/h3&gt;

&lt;p&gt;These are some of the AWS resource objects used in this guide.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/eks/" rel="noopener noreferrer"&gt;Elastic Kubernetes Service&lt;/a&gt; (&lt;a href="https://aws.amazon.com/eks/" rel="noopener noreferrer"&gt;EKS&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/vpc/" rel="noopener noreferrer"&gt;Virtual Compute Cloud&lt;/a&gt; (&lt;a href="https://aws.amazon.com/vpc/" rel="noopener noreferrer"&gt;VPC&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;Elastic Computer Cloud&lt;/a&gt; (&lt;a href="https://aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;EC2&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/auto-scaling-groups.html" rel="noopener noreferrer"&gt;AutoScale Group&lt;/a&gt; (&lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/auto-scaling-groups.html" rel="noopener noreferrer"&gt;AGS&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html" rel="noopener noreferrer"&gt;Managed Node Groups&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This first article shows have to get a modern Kubernetes cluster working with support for OIDC that will allow restricted access to cloud resources from Kubernetes applications using concept of &lt;a href="https://wikipedia.org/wiki/Principle_of_least_privilege" rel="noopener noreferrer"&gt;least privilege&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article uses &lt;code&gt;eksctl&lt;/code&gt; tool to provision the necessary cloud resources to bring up EKS.  The &lt;code&gt;eksctl&lt;/code&gt; tool will use &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;Cloud Formation&lt;/a&gt; stacks to deploy the necessary cloud resources (VPC, EC2, SGs, etc) that make up the full solution.&lt;/p&gt;

&lt;p&gt;I am sure there are many wondering, why not use something like &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; for provisioning? &lt;/p&gt;

&lt;p&gt;That is actually a good idea except that this will be more involved because we'll have to deploy all of the components required to make a functional EKS cluster: VPC, internet gateways, nat groups, subnet, routing tables, auto-scale groups, security groups, etc. in addition to the master control plane (EKS).&lt;/p&gt;

&lt;p&gt;In the future, I would like to make a similar series with Terraform to setup the base EKS cluster, and then use &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; with &lt;a href="https://developer.hashicorp.com/terraform/language/functions/templatefile" rel="noopener noreferrer"&gt;templatefile&lt;/a&gt; or &lt;a href="https://tanzu.vmware.com/developer/guides/helmfile-what-is/" rel="noopener noreferrer"&gt;Helmfile&lt;/a&gt; to manage installing helm charts and kubernetes manifests.&lt;/p&gt;

&lt;p&gt;The ultimate goal of this series is to quickly get a robust and secure Kubernetes cluster with necessary features like storage and networking with preferably low complexity and friction.&lt;/p&gt;

&lt;p&gt;From here, we can explore other developments and tutorials on Kubernetes, such as o11y or observability (&lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;PLG&lt;/a&gt;, &lt;a href="https://www.elastic.co/what-is/elk-stack" rel="noopener noreferrer"&gt;ELK&lt;/a&gt;, &lt;a href="https://fluentbit.io/" rel="noopener noreferrer"&gt;ELF&lt;/a&gt;, &lt;a href="https://www.influxdata.com/blog/introduction-to-influxdatas-influxdb-and-tick-stack/" rel="noopener noreferrer"&gt;TICK&lt;/a&gt;, &lt;a href="https://www.jaegertracing.io/" rel="noopener noreferrer"&gt;Jaeger&lt;/a&gt;, &lt;a href="https://pyroscope.io/" rel="noopener noreferrer"&gt;Pyroscope&lt;/a&gt;), service mesh (&lt;a href="https://linkerd.io/" rel="noopener noreferrer"&gt;Linkerd&lt;/a&gt;, &lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt;, &lt;a href="https://www.nginx.com/blog/introducing-nginx-service-mesh/" rel="noopener noreferrer"&gt;NSM&lt;/a&gt;, &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt;, &lt;a href="https://isovalent.com/blog/post/cilium-service-mesh/" rel="noopener noreferrer"&gt;Cillium&lt;/a&gt;), and progressive delivery (&lt;a href="https://argo-cd.readthedocs.io/" rel="noopener noreferrer"&gt;ArgoCD&lt;/a&gt;, &lt;a href="https://fluxcd.io/" rel="noopener noreferrer"&gt;FluxCD&lt;/a&gt;, &lt;a href="https://spinnaker.io/" rel="noopener noreferrer"&gt;Spinnaker&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>awscloud</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Install Terraform with tfenv</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Sun, 15 Jan 2023 18:36:35 +0000</pubDate>
      <link>https://dev.to/joachim8675309/install-terraform-with-tfenv-1cg2</link>
      <guid>https://dev.to/joachim8675309/install-terraform-with-tfenv-1cg2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsplvh7lxc41upuigwrky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsplvh7lxc41upuigwrky.png" alt="Image description" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There will come a time when you will need to change to different versions of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;.  Similar to tools like &lt;code&gt;rbenv&lt;/code&gt; for &lt;a href="https://www.ruby-lang.org" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt; or &lt;code&gt;pyenv&lt;/code&gt; for &lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;, there's a popular tool used to install different versions of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; called &lt;code&gt;tfenv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Most tooling for infrastructure operations runs on Intel processors, called the &lt;code&gt;amd64&lt;/code&gt; architecture with in &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, but given the recent addition of Macbook M1 laptops, the architecture is &lt;code&gt;arm64&lt;/code&gt;.  Older versions of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; that may be needed will require installing &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; that is only available on Intel processors (&lt;code&gt;amd64&lt;/code&gt; binaries).  This can be installed using &lt;code&gt;tfenv&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;This article will have cover the following sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing &lt;code&gt;tfenv&lt;/code&gt; on Ubuntu (&lt;code&gt;linux&lt;/code&gt;) and mac OS (&lt;code&gt;darwin&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Installing a latest or recent version of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Installing legacy versions of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; on macOS with Arm processor (Macbook M1/M2)&lt;/li&gt;
&lt;li&gt;Installing Legacy providers plugins (Ubuntu and Macbook M1/M2)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Installing tfenv
&lt;/h1&gt;

&lt;p&gt;Here's how you can install &lt;code&gt;tfenv&lt;/code&gt; on Ubuntu (&lt;code&gt;linux&lt;/code&gt;) or mac OS (&lt;code&gt;darwin&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing tfenv on Ubuntu
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tfutils/tfenv.git &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.tfenv
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.tfenv/bin:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# install to appropriate shell startup file, e.g. $HOME/.bashrc&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="$HOME/.tfenv/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing tfenv on mac OS
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;tfenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Installing Terraform
&lt;/h1&gt;

&lt;p&gt;Once &lt;code&gt;tfenv&lt;/code&gt; is installed and available in the path, you can simply run something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tfenv &lt;span class="nb"&gt;install &lt;/span&gt;1.3.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;tfenv list-remote&lt;/code&gt; you can fetch different versions of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; that are available for installation.  From there, you can use &lt;a href="https://www.gnu.org/software/grep/" rel="noopener noreferrer"&gt;GNU grep&lt;/a&gt; to select the desired version.  For example, here's how to install the latest version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LATEST_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;tfenv list-remote &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-vP&lt;/span&gt; &lt;span class="s1"&gt;'\d.\d{0,2}.\d{0,2}-.*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

tfenv &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LATEST_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: On macOS, which has BSD flavor &lt;code&gt;grep&lt;/code&gt;, it doesn't support &lt;a href="https://www.pcre.org/" rel="noopener noreferrer"&gt;PCRE&lt;/a&gt;.  To get &lt;a href="https://www.gnu.org/software/grep/" rel="noopener noreferrer"&gt;GNU Grep&lt;/a&gt; with support for &lt;a href="https://www.pcre.org/" rel="noopener noreferrer"&gt;PCRE&lt;/a&gt;, use &lt;code&gt;brew install grep&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You can extract the last minor version by passing in an explicit major version to &lt;a href="https://www.gnu.org/software/grep/" rel="noopener noreferrer"&gt;GNU Grep&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# find latest 0.12&lt;/span&gt;
&lt;span class="nv"&gt;LATEST_0_12&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;tfenv list-remote  &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oP&lt;/span&gt; &lt;span class="s1"&gt;'^0.12.\d{1,2}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;--version-sort&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

tfenv &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LATEST_0_12&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the latest version, whatever that is, you can also just run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tfenv &lt;span class="nb"&gt;install &lt;/span&gt;latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Using the desired Terraform version
&lt;/h1&gt;

&lt;p&gt;When you want to use the a particular version of an installed version &lt;code&gt;terraform&lt;/code&gt;, you can select it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tfenv use 1.3.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, to have the &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; switch automatically when navigating to the project directory, you can place a &lt;code&gt;.terraform-version&lt;/code&gt; to auto-select the version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;  &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/my_project/.terraform-version
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'1.3.7'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/my_project/.terraform-version

&lt;span class="c"&gt;# change version of Terraform automatically&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/my_project/.terraform-version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Installing Legacy x64 versions on Mac
&lt;/h1&gt;

&lt;p&gt;If you Macbook sports a arm64 processor, you need to get &lt;a href="https://support.apple.com/en-us/HT211861" rel="noopener noreferrer"&gt;Rosetta&lt;/a&gt; to run &lt;code&gt;amd64&lt;/code&gt; binaries on macOS.  Afterward, you can follow these steps below to install legacy versions of &lt;code&gt;terraform&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TFENV_ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;

&lt;span class="c"&gt;# install amd64 terrafrom version since there are no arm64 binaries&lt;/span&gt;
tfenv &lt;span class="nb"&gt;install &lt;/span&gt;0.12.31
tfenv use 0.12.31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Installing Legacy providers
&lt;/h1&gt;

&lt;p&gt;In recent versions of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, this installation will happen automatically when you run &lt;code&gt;terraform init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First, you need to find the appropriate plugin version that matches the supported version of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;.  In the case of &lt;code&gt;terraform-provider-kubectl&lt;/code&gt;, you need to install &lt;code&gt;1.11.2&lt;/code&gt; of that version of that plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing legacy provider on Ubuntu Linux
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"linux_amd64"&lt;/span&gt;
&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.11.2"&lt;/span&gt;
&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"terraform-provider-kubectl_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.zip"&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gavinbunney/terraform-provider-kubectl"&lt;/span&gt;
&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/releases/download/v&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# setup plugins directory if this does not exist already&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.terraform.d/plugins/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/

&lt;span class="c"&gt;# download archive and install plugin&lt;/span&gt;
&lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/Downloads
curl &lt;span class="nt"&gt;-sOL&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
unzip &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;README.md LICENSE
&lt;span class="nb"&gt;mv &lt;/span&gt;terraform-provider-kubectl_v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.terraform.d/plugins/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/
&lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing legacy provider on Macbook M1
&lt;/h2&gt;

&lt;p&gt;For current Macbooks running the arm64 processor, you will need to install the legacy provider for amd64 (not arm64).  This is how you can do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"darwin_amd64"&lt;/span&gt; &lt;span class="c"&gt;# install amd64 plugin for legacy terraform &lt;/span&gt;
&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.11.2"&lt;/span&gt;
&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"terraform-provider-kubectl_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.zip"&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gavinbunney/terraform-provider-kubectl"&lt;/span&gt;
&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/releases/download/v&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# setup plugins directory if this does not exist already&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.terraform.d/plugins/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/

&lt;span class="c"&gt;# download archive&lt;/span&gt;
&lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/Downloads
curl &lt;span class="nt"&gt;-sOL&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
unzip &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PKG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;README.md LICENSE
&lt;span class="nb"&gt;mv &lt;/span&gt;terraform-provider-kubectl_v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.terraform.d/plugins/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/
&lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I hope this is useful to your &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; adventures.  The reason why locking down versions of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; is important, is that there are significant language changes that make the code incompatible.  The installer dependency chain with &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; modules and providers will need to match the target version of the &lt;code&gt;terraform&lt;/code&gt; command.  &lt;/p&gt;

&lt;p&gt;In addition to this, the state that is generated from interacting with the provider will likely need to match, so it is important that the infrastructure that created with a particular version of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, also be removed when ultimately &lt;code&gt;terraform destroy&lt;/code&gt; is used.&lt;/p&gt;

&lt;p&gt;As the infrastructure progresses forward, there are techniques you can use to smoothly transition.  One method it to have modular modules that depend on looking up the state in the cloud through data sources, and important existing infrastructure into the state using &lt;code&gt;terraform import&lt;/code&gt;.  This is definitely material for a future article.&lt;/p&gt;

&lt;p&gt;In the meantime, you can use the supported version with tools like &lt;code&gt;tfenv&lt;/code&gt;.  Another tool, as an alternative to &lt;code&gt;tfenv&lt;/code&gt; is &lt;a href="https://asdf-vm.com/" rel="noopener noreferrer"&gt;asdf&lt;/a&gt;.  I have not explored supported legacy amd64 legacy binaries on Macbook M1 yet, but assume this can work.  The &lt;a href="https://asdf-vm.com/" rel="noopener noreferrer"&gt;asdf&lt;/a&gt; is nice in that it has a plugin architecture to support a variety of languages.&lt;/p&gt;

&lt;p&gt;Lastly, about this article, which I am sure might unnerve shell purists, where I use &lt;code&gt;$HOME&lt;/code&gt; instead of &lt;code&gt;~&lt;/code&gt; and I use of curly braces for &lt;code&gt;${VARIABLES}&lt;/code&gt;, when they are not needed, when simply &lt;code&gt;$VARIABLE&lt;/code&gt; is preferred: this was done intentionally to make the variables light up in color syntax highlighters on different platforms, and &lt;code&gt;$HOME&lt;/code&gt; was used for novice uses that may not be familiar with &lt;code&gt;~&lt;/code&gt; notation.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>GKE with Consul Service Mesh</title>
      <dc:creator>Joaquin Menchaca</dc:creator>
      <pubDate>Sun, 04 Dec 2022 01:05:02 +0000</pubDate>
      <link>https://dev.to/joachim8675309/gke-with-consul-service-mesh-52hm</link>
      <guid>https://dev.to/joachim8675309/gke-with-consul-service-mesh-52hm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdw9bhj4gs5yw3ykqg6iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdw9bhj4gs5yw3ykqg6iw.png" alt="Article Header" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article shows how to set up and get started with &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;CCSM&lt;/a&gt; (&lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect service mesh&lt;/a&gt;) or more recently called just Consul Service Mesh.&lt;/p&gt;

&lt;p&gt;This article will cover how to install and configure services to use &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;CCSM&lt;/a&gt;. An example application &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt;, a distributed graph database, will be used as this demonstrates a real world application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📔 NOTE: This was tested on following below 
and may not work if versions are significantly 
different.

* Kubernetes API v1.22
* gcloud 402.0.0
* gsutil 5.13
* kubectl v1.22
* kustomize v4.5.4
* helm v3.8.2
* helmfile v0.144.0
* Docker 20.10.17
* Dgraph v21.03.2
* Consul 1.13.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  About Consul
&lt;/h2&gt;

&lt;p&gt;Consul is a popular tool for &lt;a href="https://en.wikipedia.org/wiki/Service_discovery" rel="noopener noreferrer"&gt;service discovery&lt;/a&gt; and a &lt;a href="https://en.wikipedia.org/wiki/Key%E2%80%93value_database" rel="noopener noreferrer"&gt;key-value store&lt;/a&gt; that was released in April-2014.&lt;/p&gt;

&lt;p&gt;Service discovery is important for clusters members or microservices as it provides “&lt;em&gt;automatic detection of devices and services offered by these devices on a computer network&lt;/em&gt; (&lt;a href="https://en.wikipedia.org/wiki/Service_discovery" rel="noopener noreferrer"&gt;ref&lt;/a&gt;)”. This allows “&lt;em&gt;applications and microservices locate different components on a network&lt;/em&gt; (&lt;a href="https://middleware.io/blog/service-discovery/" rel="noopener noreferrer"&gt;ref&lt;/a&gt;)”.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Key%E2%80%93value_database" rel="noopener noreferrer"&gt;key-value store&lt;/a&gt; is a network database to store &lt;a href="https://en.wikipedia.org/wiki/Hash_table" rel="noopener noreferrer"&gt;hash maps&lt;/a&gt; (also called &lt;a href="https://en.wikipedia.org/wiki/Associative_array" rel="noopener noreferrer"&gt;associative arrays&lt;/a&gt; or &lt;a href="https://realpython.com/python-dicts/" rel="noopener noreferrer"&gt;dictionaries&lt;/a&gt;). This allows services create and retrieve configuration.&lt;/p&gt;

&lt;p&gt;Building upon this, &lt;a href="https://www.hashicorp.com/" rel="noopener noreferrer"&gt;Hashicorp&lt;/a&gt; developed &lt;a href="https://github.com/hashicorp/consul-template" rel="noopener noreferrer"&gt;consul-template&lt;/a&gt;, which is essentially cloud native change configuration, and &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt;, now called &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Service Mesh&lt;/a&gt;, which can automatically inject side-car proxy containers into your network, so that services can communicate securely.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Service Mesh
&lt;/h2&gt;

&lt;p&gt;A service mesh uses automation to secure internal network traffic between member nodes. It does this by inserting reverse-proxy sidecar containers to every pod that is apart of the service mesh.&lt;/p&gt;

&lt;p&gt;With a network of side-car proxies installed, the traffic can be further secured using strict &lt;a href="https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/" rel="noopener noreferrer"&gt;mTLS&lt;/a&gt;, where not only the client must authenticate the validity of the server, but the server must authenticate the validity of the client. This is on top of the encryption of traffic between all the members of the mesh.&lt;/p&gt;

&lt;p&gt;A service mesh is divided into three planes (illustration below): &lt;em&gt;control plane&lt;/em&gt; to manage the overall service mesh, a data plane that consists of the members within the mesh that are secured with a proxy, and &lt;em&gt;observability plane&lt;/em&gt; to monitor traffic from within the mesh.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsg99m2mgkl6y8gnfipqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsg99m2mgkl6y8gnfipqw.png" alt="Consul Service Mesh: Control Plane, Data Plane, Observability Plane" width="800" height="777"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📔 NOTE: Observability is not supported for Consul Service 
Mesh with services that use multiple-ports.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; leverages off of &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt; to manage the connectivity through use of service discovery, health checks, and a service catalog. &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; is the default proxy that is injected into each of the pods to create the service mesh. This proxy can be swapped for another proxy component, such as &lt;a href="http://www.haproxy.org/" rel="noopener noreferrer"&gt;HAProxy&lt;/a&gt; or &lt;a href="http://nginx.org/" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;These are the requirements to use this solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accounts
&lt;/h3&gt;

&lt;p&gt;No commercial licenses are needed for either &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt; and &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt;. All of the tools are accessible from the public Internet. For creating resources on &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;, you will need to create an account.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt; account with ownership of a project where you can deploy resources (where billing account was linked to the project)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Knowledge
&lt;/h3&gt;

&lt;p&gt;You should be familiar or have exposure to the following concepts to get more thorough understanding of this tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Virtual_hosting" rel="noopener noreferrer"&gt;virtual hosting&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Reverse_proxy" rel="noopener noreferrer"&gt;reverse-proxy&lt;/a&gt;, and &lt;a href="https://www.f5.com/services/resources/glossary/load-balancer" rel="noopener noreferrer"&gt;load balancer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.techtarget.com/searchnetworking/definition/OSI" rel="noopener noreferrer"&gt;Layer 4 vs Layer 7 (OSI)&lt;/a&gt;, &lt;a href="https://www.cloudflare.com/learning/ddos/glossary/tcp-ip/" rel="noopener noreferrer"&gt;TCP/IP&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/IP_routing" rel="noopener noreferrer"&gt;routing&lt;/a&gt;, &lt;a href="https://www.gartner.com/en/information-technology/glossary/gateway" rel="noopener noreferrer"&gt;gateway&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.digitalocean.com/community/tutorials/http-1-1-vs-http-2-what-s-the-difference" rel="noopener noreferrer"&gt;HTTP/1.1 vs HTTP/2&lt;/a&gt; and &lt;a href="https://grpc.io/docs/what-is-grpc/core-concepts/" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aquasec.com/cloud-native-academy/container-security/container-images/" rel="noopener noreferrer"&gt;images&lt;/a&gt; vs &lt;a href="https://www.docker.com/resources/what-container/" rel="noopener noreferrer"&gt;containers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.vmware.com/topics/glossary/content/virtual-machine.html" rel="noopener noreferrer"&gt;virtual machines instances&lt;/a&gt; (&lt;a href="https://kubernetes.io/docs/concepts/architecture/nodes/" rel="noopener noreferrer"&gt;nodes&lt;/a&gt;) vs &lt;a href="https://kubernetes.io/docs/concepts/containers/" rel="noopener noreferrer"&gt;containers&lt;/a&gt; vs &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/" rel="noopener noreferrer"&gt;pods&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt;, experience with deploying applications with &lt;code&gt;service&lt;/code&gt; resources is useful, but even if you don’t have this, this guide will walk you through it. Configuring &lt;code&gt;KUBECONFIG&lt;/code&gt; to access the &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; cluster with Kubernetes client (&lt;code&gt;kubectl&lt;/code&gt;) and using &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; (&lt;code&gt;helm&lt;/code&gt;), so familiarity to this is useful.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;, you should be familiar &lt;a href="https://cloud.google.com/sdk" rel="noopener noreferrer"&gt;Google Cloud SDK&lt;/a&gt; (&lt;code&gt;gcloud&lt;/code&gt; tool) with setting up an account, project, and provisioning resources. This is important as there are cost factors involved in setting these things up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools (Required)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68talvtkwlpbfvs35qit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68talvtkwlpbfvs35qit.png" alt="icons of required tools" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/sdk" rel="noopener noreferrer"&gt;Google Cloud SDK&lt;/a&gt; (&lt;code&gt;gcloud&lt;/code&gt; command) to interact with Google Cloud&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/#kubectl" rel="noopener noreferrer"&gt;Kubernetes client&lt;/a&gt; (&lt;code&gt;kubectl&lt;/code&gt; command) to interact with Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; (&lt;code&gt;helm&lt;/code&gt; command) to install Kubernetes packages&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/databus23/helm-diff" rel="noopener noreferrer"&gt;helm-diff&lt;/a&gt; plugin to see differences about what will be deployed.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/helmfile/helmfile" rel="noopener noreferrer"&gt;helmfile&lt;/a&gt; (&lt;code&gt;helmfile&lt;/code&gt; command) to automate installing many helm charts&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kustomize.io/" rel="noopener noreferrer"&gt;Kustomize&lt;/a&gt; (&lt;code&gt;kustomize&lt;/code&gt; command) to apply patches to existing Helm charts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tools (Recommended)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem2jzma59t1nifjd00de.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem2jzma59t1nifjd00de.png" alt="icons of optional tools" width="800" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These tools are useful in using the automation used form within this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html" rel="noopener noreferrer"&gt;POSIX shell&lt;/a&gt; (sh) such as &lt;a href="https://www.gnu.org/software/bash/" rel="noopener noreferrer"&gt;GNU Bash&lt;/a&gt; (bash) or &lt;a href="https://www.zsh.org/" rel="noopener noreferrer"&gt;Zsh&lt;/a&gt; (zsh): these scripts in this guide were tested using either of these shells on macOS and Ubuntu Linux.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/sed/" rel="noopener noreferrer"&gt;GNU stream-editor&lt;/a&gt; (sed) and &lt;a href="https://www.gnu.org/software/grep/" rel="noopener noreferrer"&gt;GNU grep&lt;/a&gt; (grep): scripts were tested with these tools. Note that BSD versions of these tools may NOT WORK, such as tools bundled with macOS or BSD.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;Docker Engine&lt;/a&gt; (docker command) to automate building and pushing running pydgraph client to Google container registry.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;git&lt;/a&gt; (git command) to download source code from git code repositories.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;This will setup all the content for this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directory Structure
&lt;/h3&gt;

&lt;p&gt;The directory structure should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/projects/consul_connect
├── consul
│   └── helmfile.yaml
└── examples
    └── dgraph
        ├── helmfile.yaml
        └── pydgraph_client.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;a href="https://www.gnu.org/software/bash/" rel="noopener noreferrer"&gt;GNU Bash&lt;/a&gt;, you can create the above structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PROJECT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/projects/consul_connect
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT_DIR&lt;/span&gt;/&lt;span class="o"&gt;{&lt;/span&gt;examples/dgraph,consul&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT_DIR&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;consul,examples/dgraph&lt;span class="o"&gt;}&lt;/span&gt;/helmfile.yaml &lt;span class="se"&gt;\&lt;/span&gt;
 examples/dgraph/pydgraph_client.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;p&gt;These environment variables will be used in this project. Create a file called &lt;code&gt;env.sh&lt;/code&gt; with the contents below, changing values as appropriate, and then run &lt;code&gt;source env.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# gke&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GKE_PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-gke-project"&lt;/span&gt; &lt;span class="c"&gt;# CHANGE ME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GKE_CLUSTER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"csm-demo"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GKE_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-central1"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GKE_SA_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gke-worker-nodes-sa"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GKE_SA_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GKE_SA_NAME&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GKE_PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.iam.gserviceaccount.com"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.kube/&lt;span class="nv"&gt;$GKE_REGION&lt;/span&gt;-&lt;span class="nv"&gt;$GKE_CLUSTER_NAME&lt;/span&gt;.yaml

&lt;span class="c"&gt;# other&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;USE_GKE_GCLOUD_AUTH_PLUGIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;True
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ClOUD_BILLING_ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;my-billing-account&amp;gt;"&lt;/span&gt; &lt;span class="c"&gt;# CHANGEME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Google Project Setup
&lt;/h3&gt;

&lt;p&gt;For this tutorial, we’ll need to setup a Google cloud &lt;a href="https://cloud.google.com/storage/docs/projects" rel="noopener noreferrer"&gt;project&lt;/a&gt; and provide access to allow use to create the necessary cloud resources. Here is an example of how you can set this up with &lt;code&gt;gcloud&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# enable billing and APIs for GKE project if not done already&lt;/span&gt;
gcloud projects create &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt;
gcloud beta billing projects &lt;span class="nb"&gt;link&lt;/span&gt; &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--billing-account&lt;/span&gt; &lt;span class="nv"&gt;$ClOUD_BILLING_ACCOUNT&lt;/span&gt;
gcloud services &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="s2"&gt;"container.googleapis.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provision Cloud Resources
&lt;/h2&gt;

&lt;p&gt;These instructions will create the necessary cloud resources for this project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provision Google Kubernetes Engine cluster
&lt;/h3&gt;

&lt;p&gt;The steps below will allow you to bring up a &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; cluster with 3 worker nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📔 NOTE: This will deploy a robust 3 worker node Kubernetes 
cloud that is suitable for Consul.  This will create a 
principal identity (Google Service Account) with the minimal 
necessary privileges required to manage the Kubernetes nodes 
(GCE).

📔 NOTE: For production environments, you will want to 
explore further security measures, such as private cluster, 
to block access from the public Internet.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;env.sh

&lt;span class="c"&gt;#######################&lt;/span&gt;
&lt;span class="c"&gt;# GSA with least priv for GKE&lt;/span&gt;
&lt;span class="c"&gt;##########################################&lt;/span&gt;
&lt;span class="nv"&gt;ROLES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  roles/logging.logWriter
  roles/monitoring.metricWriter
  roles/monitoring.viewer
  roles/stackdriver.resourceMetadata.writer
&lt;span class="o"&gt;)&lt;/span&gt;

gcloud iam service-accounts create &lt;span class="nv"&gt;$GKE_SA_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="nv"&gt;$GKE_SA_NAME&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt;

&lt;span class="c"&gt;# assign google service account to roles in GKE project&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;ROLE &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ROLES&lt;/span&gt;&lt;span class="p"&gt;[*]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
 &lt;/span&gt;gcloud projects add-iam-policy-binding &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt; &lt;span class="s2"&gt;"serviceAccount:&lt;/span&gt;&lt;span class="nv"&gt;$GKE_SA_EMAIL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt; &lt;span class="nv"&gt;$ROLE&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;#######################&lt;/span&gt;
&lt;span class="c"&gt;# GKE with least priv. GSA + Workload Identity&lt;/span&gt;
&lt;span class="c"&gt;##########################################&lt;/span&gt;
gcloud container clusters create &lt;span class="nv"&gt;$GKE_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$GKE_REGION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--num-nodes&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-account&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GKE_SA_EMAIL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--machine-type&lt;/span&gt; &lt;span class="s2"&gt;"e2-standard-2"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-ip-alias&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--workload-pool&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;.svc.id.goog"&lt;/span&gt;

&lt;span class="c"&gt;#######################&lt;/span&gt;
&lt;span class="c"&gt;# KUBECONFIG&lt;/span&gt;
&lt;span class="c"&gt;##########################################&lt;/span&gt;
gcloud container clusters get-credentials &lt;span class="nv"&gt;$GKE_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$GKE_REGION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test access to the cluster as well as the components installed with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes
kubectl get all &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another useful command to test a new cluster is to see how many resources are available and what is consumed in the new cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl top nodes
kubectl top pods &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy Kubernetes Resources
&lt;/h2&gt;

&lt;p&gt;This section covers deploying Kubernetes resources such as &lt;code&gt;Deployment&lt;/code&gt;, &lt;code&gt;StatefulSet&lt;/code&gt;, &lt;code&gt;ServiceAccount&lt;/code&gt;, &lt;code&gt;Service&lt;/code&gt;, and so on. This will cover installing the &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh, &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt;, and &lt;a href="https://github.com/darkn3rd/pydgraph-client/tree/consul" rel="noopener noreferrer"&gt;pydgraph-client&lt;/a&gt; to access &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; through the service mesh.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Consul Connect service mesh
&lt;/h3&gt;

&lt;p&gt;This will deploy the &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; Service mesh. Save the following code below as &lt;code&gt;consul/helmfile.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# https://artifacthub.io/packages/helm/hashicorp/consul&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hashicorp&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://helm.releases.hashicorp.com&lt;/span&gt;

&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hashicorp/consul&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.49.0&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consul&lt;/span&gt;
          &lt;span class="na"&gt;datacenter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dc1&lt;/span&gt;
          &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if eq (env "CCSM_SECURITY_ENABLED") "true"&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;enableAutoEncrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;gossipEncryption&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;autoGenerate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;acls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;manageSystemACLs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
        &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;runAsNonRoot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="na"&gt;connectInject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;controller&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Helm chart configuration values will install &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh with automatic injection enabled. When you deploy a pod with annotation of &lt;code&gt;consul.hashicorp.com/connect-inject: "true"&lt;/code&gt;, side-car containers will be installed to copy the consul binary into the container and setup and configure &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxy. The &lt;code&gt;service&lt;/code&gt; proxy resources will be used as a blueprint to register the service with Consul's service catalog and configure the &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxy.&lt;/p&gt;

&lt;p&gt;Run the following to deploy the service mesh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;env.sh
helmfile &lt;span class="nt"&gt;--file&lt;/span&gt; ./consul/helmfile.yaml apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check that everything is deployed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all &lt;span class="nt"&gt;--namespace&lt;/span&gt; consul
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxq16v1h27h4n19q2u0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxq16v1h27h4n19q2u0e.png" alt="Consul Service Mesh services" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Observability
&lt;/h3&gt;

&lt;p&gt;Currently observability is not supported with multi-port services like &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt;. Hopefully this will get fixed in the future.&lt;/p&gt;

&lt;p&gt;For further information, see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/k8s/connect#caveats-for-multi-port-pods" rel="noopener noreferrer"&gt;Caveats for Multi-port Pods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hashicorp/consul-k8s/issues/1594" rel="noopener noreferrer"&gt;injection stack traces when deploying multi-port application&lt;/a&gt; (issue &lt;a href="https://github.com/hashicorp/consul-k8s/issues/1594" rel="noopener noreferrer"&gt;1594&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deploy Dgraph
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; is a distributed graph database communicates through both HTTP on port &lt;code&gt;8080&lt;/code&gt; and gRPC on port &lt;code&gt;9080&lt;/code&gt;. &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; uses the DQL (Dgraph Query Language) through either gRPC or HTTP, and can also use GraphQL with HTTP. &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; supports administrative operations using GraphQL or REST.&lt;/p&gt;

&lt;p&gt;For this reason, to fully use &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; on a service mesh, you have to use the recently added multi-port configuration with &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt;. This requires separating the single multi-port &lt;code&gt;service&lt;/code&gt; proxy into two separate &lt;code&gt;service&lt;/code&gt; proxies: one for gRPC (&lt;code&gt;9080&lt;/code&gt;) and one for HTTP (&lt;code&gt;8080&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Save the following helmfile config below as &lt;code&gt;examples/dgraph/helmfile.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# https://artifacthub.io/packages/helm/dgraph/dgraph/0.0.19&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://charts.dgraph.io&lt;/span&gt;
  &lt;span class="c1"&gt;# https://artifacthub.io/packages/helm/main/raw&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bedag&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://bedag.github.io/helm-charts/&lt;/span&gt;

&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Dgraph additional resources required to support Consul&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-extra&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bedag/raw&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;1.1.0&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
            &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-zero&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
            &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-alpha&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
            &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-alpha-grpc&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
            &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-alpha-grpc&lt;/span&gt;
            &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grpc-alpha&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9080&lt;/span&gt;
              &lt;span class="na"&gt;publishNotReadyAddresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
              &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph&lt;/span&gt;
                &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-0.0.19&lt;/span&gt;
                &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpha&lt;/span&gt;
                &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;

  &lt;span class="c1"&gt;# Dgraph cluster with 2 x StatefulSet (3 Zero pods, 3 Alpha pods)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph/dgraph&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.19&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dgraph/dgraph-extra&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v21.03.2&lt;/span&gt;
        &lt;span class="na"&gt;zero&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;extraAnnotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/connect-inject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
            &lt;span class="c1"&gt;# disable transparent-proxy for multi-port services&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/transparent-proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;false'&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/transparent-proxy-exclude-inbound-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5080,7080"&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/transparent-proxy-exclude-outbound-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5080,7080"&lt;/span&gt;
        &lt;span class="na"&gt;alpha&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;extraAnnotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/connect-inject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
            &lt;span class="c1"&gt;# disable transparent-proxy for multi-port services&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/transparent-proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;false'&lt;/span&gt;
            &lt;span class="c1"&gt;# use these registered consul services for different ports&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/connect-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dgraph-dgraph-alpha,dgraph-dgraph-alpha-grpc'&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/connect-service-port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8080,9080'&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/transparent-proxy-exclude-inbound-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5080,7080"&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/transparent-proxy-exclude-outbound-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5080,7080"&lt;/span&gt;
          &lt;span class="na"&gt;configFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;config.yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;security:&lt;/span&gt;
                &lt;span class="s"&gt;whitelist: {{ env "DG_ACCEPT_LIST" | default "0.0.0.0/0" | quote }}&lt;/span&gt;
    &lt;span class="c1"&gt;# patch existing resources using merge patches&lt;/span&gt;
    &lt;span class="na"&gt;strategicMergePatches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# add serviceAccountName to Alpha StatefulSet&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StatefulSet&lt;/span&gt;
        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-alpha&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-alpha&lt;/span&gt;

      &lt;span class="c1"&gt;# add serviceAccountName to Zero StatefulSet&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StatefulSet&lt;/span&gt;
        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-zero&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-zero&lt;/span&gt;

      &lt;span class="c1"&gt;# add label to Alpha headless service&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-alpha-headless&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/service-ignore&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;

      &lt;span class="c1"&gt;# add label to Zero headless service&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-zero-headless&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;consul.hashicorp.com/service-ignore&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;

    &lt;span class="c1"&gt;# patch existing resource using jsonPatches&lt;/span&gt;
    &lt;span class="na"&gt;jsonPatches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# remove existing grpc port from serivce&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
          &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dgraph-dgraph-alpha&lt;/span&gt;
        &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;op&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;remove&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/spec/ports/1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helmfile config uses some advance features to make some necessary changes required by &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pre-install service accounts and new gRPC service all packaged up as &lt;code&gt;dgraph-extras&lt;/code&gt; chart&lt;/li&gt;
&lt;li&gt;render Dgraph resources with required annotations for consul&lt;/li&gt;
&lt;li&gt;apply patches to add Dgraph headless service labels that instructs Consul to ignore these services when is configures the proxies.&lt;/li&gt;
&lt;li&gt;remove gRPC port (&lt;code&gt;9080&lt;/code&gt;) from the Dgraph Alpha &lt;code&gt;service&lt;/code&gt;, as this was defined earlier as a separate gRPC &lt;code&gt;service&lt;/code&gt; with the &lt;code&gt;dgraph-extras&lt;/code&gt; chart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; will inject &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; sidecar proxy containers. Dgraph Zero will get a sidecar for port &lt;code&gt;6080&lt;/code&gt;, while Dgraph Alpha will have two sidecar proxy containers per pod: one for gRPC at port &lt;code&gt;9080&lt;/code&gt; and another one for HTTP at port &lt;code&gt;8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When ready to deploy all of this, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;env.sh
helmfile &lt;span class="nt"&gt;--file&lt;/span&gt; ./examples/dgraph/helmfile.yaml apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check on the status using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all &lt;span class="nt"&gt;--namespace&lt;/span&gt; dgraph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cd63h9qqy5gz3bqylty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cd63h9qqy5gz3bqylty.png" alt="Dgraph output" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You notice the extra containers per pod in the ready state, which are the &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxy sidecar containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Pydgraph client
&lt;/h3&gt;

&lt;p&gt;The client is a small python script that can load data into &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; using gRPC, and the container also has some useful tools like &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;grpcurl&lt;/code&gt;, and &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Save the following below as &lt;code&gt;examples/dgraph/pydgraph_client.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# https://artifacthub.io/packages/helm/main/raw&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bedag&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://bedag.github.io/helm-charts/&lt;/span&gt;

&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bedag/raw&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;1.1.0&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
            &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
            &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
            &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
              &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
              &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
                &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
                  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
                    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;requiredEnv "DOCKER_REGISTRY"&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;/pydgraph-client:{{ env "BUILD_VERSION" | default "latest" }}&lt;/span&gt;
                    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
                    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                      &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if eq (env "CCSM_ENABLED") "true"&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
                      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DGRAPH_ALPHA_SERVER&lt;/span&gt;
                        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
                      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DGRAPH_GRPC_SERVER&lt;/span&gt;
                        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
                      &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- else&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
                      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DGRAPH_ALPHA_SERVER&lt;/span&gt;
                        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;env "DGRAPH_RELEASE" | default "dgraph"&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-dgraph-alpha.{{ env "DGRAPH_NS" | default "dgraph" }}.svc.cluster.local&lt;/span&gt;
                      &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
                    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                      &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;64Mi"&lt;/span&gt;
                        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8m"&lt;/span&gt;
                      &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128Mi"&lt;/span&gt;
                        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;25m"&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
            &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
            &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
              &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
                &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
              &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if eq (env "CCSM_ENABLED") "true"&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;strategicMergePatches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pydgraph-client&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;consul.hashicorp.com/connect-inject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
                &lt;span class="na"&gt;consul.hashicorp.com/transparent-proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
                &lt;span class="na"&gt;consul.hashicorp.com/connect-service-upstreams&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
                  &lt;span class="s"&gt;{{ env "DGRAPH_RELEASE" | default "dgraph" }}-dgraph-alpha:8080,{{ env "DGRAPH_RELEASE" | default "dgraph" }}-dgraph-alpha-grpc:9080&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When ready to deploy this, you can run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;env.sh
&lt;span class="c"&gt;# https://hub.docker.com/r/darknerd/pydgraph-client&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;darknerd
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CCSM_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;helmfile &lt;span class="nt"&gt;--file&lt;/span&gt; ./examples/dgraph/pydgraph_client.yaml apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check the deployment with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all &lt;span class="nt"&gt;--namespace&lt;/span&gt; pydgraph-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should result in something similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lkkt5mx6xhcf2kyhs54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lkkt5mx6xhcf2kyhs54.png" alt="pydgraph client output" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Upstream Traffic
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; will set up a tunnel between the upstream ports specified in the annotation to the ports that are serviced by &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First remote into the client container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CLIENT_NS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"pydgraph-client"&lt;/span&gt;
&lt;span class="nv"&gt;PYDGRAPH_POD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$CLIENT_NS&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; name&lt;span class="si"&gt;)&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"pydgraph-client"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$CLIENT_NS&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYDGRAPH_POD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One in the container, test that HTTP traffic is working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--silent&lt;/span&gt; localhost:8080/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For gRPC traffic, you can run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grpcurl &lt;span class="nt"&gt;-plaintext&lt;/span&gt; &lt;span class="nt"&gt;-proto&lt;/span&gt; api.proto &lt;span class="se"&gt;\&lt;/span&gt;
  localhost:9080 api.Dgraph/CheckVersion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, you can try loading data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 load_data.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--plaintext&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--alpha&lt;/span&gt; localhost:9080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--files&lt;/span&gt; ./sw.nquads.rdf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--schema&lt;/span&gt; ./sw.schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These should work through the tunnel that is configured by &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; using the &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxy side-cars.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dgraph Graphical Viewer: Ratel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; hosts an online graphical viewer at &lt;a href="https://play.dgraph.io/" rel="noopener noreferrer"&gt;https://play.dgraph.io/&lt;/a&gt;. If you would like to access the data we deployed with &lt;code&gt;load_data.py&lt;/code&gt;, you can run this in a new terminal tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/dgraph-dgraph-alpha &lt;span class="nt"&gt;-n&lt;/span&gt; dgraph 8080:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can you can point the connection configuration in &lt;a href="https://github.com/dgraph-io/ratel/" rel="noopener noreferrer"&gt;Ratel&lt;/a&gt; to &lt;a href="http://localhost:8080:" rel="noopener noreferrer"&gt;http://localhost:8080:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2Asvl_LfGWZLVCcCn7aWNxug.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2Asvl_LfGWZLVCcCn7aWNxug.webp" alt="ratel url" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Console&lt;/strong&gt; and select &lt;strong&gt;Query&lt;/strong&gt; and enter the following DQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;allofterms&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;Star&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Wars&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;release_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"1980"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;release_date&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;revenue&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;running_time&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;director&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="n"&gt;starring&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Run&lt;/strong&gt; to see the results of the query:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AcfY85xGePSYe3lEddyuw4A.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AcfY85xGePSYe3lEddyuw4A.webp" alt="ratel query" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Consul User Interface
&lt;/h2&gt;

&lt;p&gt;The Consul UI can be accessed by running this command in a new terminal tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;env.sh
kubectl port-forward service/consul-ui &lt;span class="nt"&gt;--namespace&lt;/span&gt; consul 8500:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can access the Consul UI through &lt;a href="http://localhost:8500" rel="noopener noreferrer"&gt;http://localhost:8500&lt;/a&gt;. The Consul UI should look like this below with other services appearing after &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; and pydgraph-client were deployed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A-kVtR1DymSyeGJOLQkhsGw.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2A-kVtR1DymSyeGJOLQkhsGw.webp" alt="consul ui" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on &lt;a href="https://github.com/darkn3rd/pydgraph-client/tree/consul" rel="noopener noreferrer"&gt;pydgraph-client&lt;/a&gt;, you can see the connections:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AZMLlrLGG50uCJnYyOwKrOA.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F1%2AZMLlrLGG50uCJnYyOwKrOA.webp" alt="consul ui connections" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Kubernetes Resources
&lt;/h3&gt;

&lt;p&gt;You can cleanup Kubernetes resources with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;env.sh

&lt;span class="c"&gt;# delete pydgraph-client&lt;/span&gt;
helmfile &lt;span class="nt"&gt;--file&lt;/span&gt; ./examples/dgraph/helmfile.yaml delete
kubectl delete namespace pydgraph-client

&lt;span class="c"&gt;# delete dgraph&lt;/span&gt;
helmfile &lt;span class="nt"&gt;--file&lt;/span&gt; ./examples/dgraph/helmfile.yaml delete
kubectl delete pvc &lt;span class="nt"&gt;--selector&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dgraph &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"dgraph"&lt;/span&gt;
kubectl delete namespace dgraph

&lt;span class="c"&gt;# delete consul&lt;/span&gt;
helmfile &lt;span class="nt"&gt;--file&lt;/span&gt; ./consul/helmfile.yaml delete
kubectl delete pvc &lt;span class="nt"&gt;--selector&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;consul &lt;span class="nt"&gt;--namespace&lt;/span&gt; &lt;span class="s2"&gt;"consul"&lt;/span&gt;
kubectl delete namespace consul
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to delete the consul namespace if you intend to deploy new version of &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh in the future. This is because there are secrets left behind that will break future installations, so deleting the namespace will avoid this scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloud Resources
&lt;/h3&gt;

&lt;p&gt;The Kubernetes cluster and the associated Google service account can be deleted with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;env.sh

gcloud container clusters delete &lt;span class="nv"&gt;$GKE_CLUSTER_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$GKE_REGION&lt;/span&gt;

gcloud iam service-accounts delete &lt;span class="nv"&gt;$GKE_SA_EMAIL&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Addendum: Publishing Pygraph-Client Images
&lt;/h2&gt;

&lt;p&gt;If you would like to publish the &lt;a href="https://github.com/darkn3rd/pydgraph-client/tree/consul" rel="noopener noreferrer"&gt;pydgraph-client&lt;/a&gt; images to an alternative registry, you can run the following steps below.&lt;br&gt;
Download the source code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pushd &lt;/span&gt;examples
git clone &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt; &lt;span class="s2"&gt;"consul"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  git@github.com:darkn3rd/pydgraph-client.git
&lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Publishing to GCR
&lt;/h3&gt;

&lt;p&gt;If you wish to use &lt;a href="https://cloud.google.com/container-registry" rel="noopener noreferrer"&gt;Google Container Registry&lt;/a&gt;, you can run the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; ./examples/pydgraph-client

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 1: Environment variables&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
&lt;span class="nb"&gt;source &lt;/span&gt;env.sh
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GCR_PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-gcr-project"&lt;/span&gt;  &lt;span class="c"&gt;# CHANGE ME&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gcr.io/&lt;/span&gt;&lt;span class="nv"&gt;$GCR_PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ClOUD_BILLING_ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;my-cloud-billing-account&amp;gt;"&lt;/span&gt; &lt;span class="c"&gt;# CHANGEME&lt;/span&gt;

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 2: Create GCR project and enable GCR&lt;/span&gt;
&lt;span class="c"&gt;# NOTE: Best practices is to use a single project for store/retreive images&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
gcloud projects create &lt;span class="nv"&gt;$GCR_PROJECT_ID&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project &lt;span class="nv"&gt;$GCR_PROJECT_ID&lt;/span&gt;
gcloud beta billing projects &lt;span class="nb"&gt;link&lt;/span&gt; &lt;span class="nv"&gt;$GCR_PROJECT_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--billing-account&lt;/span&gt; &lt;span class="nv"&gt;$ClOUD_BILLING_ACCOUNT&lt;/span&gt;
gcloud services &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="s2"&gt;"containerregistry.googleapis.com"&lt;/span&gt; &lt;span class="c"&gt;# Enable GCR API&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project &lt;span class="nv"&gt;$GKE_PROJECT_ID&lt;/span&gt;

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 3: Build local image&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; pydgraph-client:latest &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 4: Publish Image to GCR&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
gcloud auth configure-docker
docker tag pydgraph-client:latest &lt;span class="nv"&gt;$DOCKER_REGISTRY&lt;/span&gt;/pydgraph-client:latest
docker push &lt;span class="nv"&gt;$DOCKER_REGISTRY&lt;/span&gt;/pydgraph-client:latest

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 5: Grant GCR read access to GKE containers&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
gsutil iam ch &lt;span class="se"&gt;\&lt;/span&gt;
  serviceAccount:&lt;span class="nv"&gt;$GKE_SA_EMAIL&lt;/span&gt;:objectViewer &lt;span class="se"&gt;\&lt;/span&gt;
  gs://artifacts.&lt;span class="nv"&gt;$GCR_PROJECT_ID&lt;/span&gt;.appspot.com

&lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Publising to DockerHub
&lt;/h3&gt;

&lt;p&gt;If you have an account on &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;DockerHub&lt;/a&gt;, you can publish it there with these steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; ./examples/pydgraph-client

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 1: Environment variables&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
&lt;span class="nb"&gt;source &lt;/span&gt;env.sh
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-docker-hub-account-goes-here&amp;gt;"&lt;/span&gt;

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 2: Build local image&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; pydgraph-client:latest &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;###################&lt;/span&gt;
&lt;span class="c"&gt;# STEP 3: Publish Image to DcokerHub&lt;/span&gt;
&lt;span class="c"&gt;#######################################&lt;/span&gt;
docker login &lt;span class="c"&gt;# IMPORTANT: use api-token not the actual password&lt;/span&gt;
docker tag pydgraph-client:latest &lt;span class="nv"&gt;$DOCKER_REGISTRY&lt;/span&gt;/pydgraph-client:latest
docker push &lt;span class="nv"&gt;$DOCKER_REGISTRY&lt;/span&gt;/pydgraph-client:latest

&lt;span class="nb"&gt;popd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;These are some resources and references that may be useful in using this solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consul Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/k8s/connect#kubernetes-pods-with-multiple-ports" rel="noopener noreferrer"&gt;Kubernetes Pods with Multiple Ports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.hashicorp.com/tutorials/consul/kubernetes-secure-agents" rel="noopener noreferrer"&gt;Secure Consul and Registered Service on Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/k8s/annotations-and-labels" rel="noopener noreferrer"&gt;Annotations and Labels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/consul/docs/connect/transparent-proxy" rel="noopener noreferrer"&gt;Enable Transparent Proxy Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.hashicorp.com/blog/transparent-proxy-on-consul-service-mesh" rel="noopener noreferrer"&gt;Transparent Proxy on Consul Service Mesh&lt;/a&gt;, 2020-JUL-01&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/consul/docs/k8s/connect" rel="noopener noreferrer"&gt;How does Consul Service Mesh Work on Kubernetes?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Gateways and Ingress
&lt;/h3&gt;

&lt;p&gt;These are links for north-south traffic into mesh.&lt;br&gt;
I have not tested these solutions yet&lt;/p&gt;

&lt;p&gt;These are links that cover integration of either ingress controllers or API gateways with Consul. This may be using Consul as a backend database or the &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📔 NOTE: I have not tested the content of this material, just 
documenting any material I find on the topic for later 
exploration.  If you find any useful material out there, 
please send me a note.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.getambassador.io/docs/edge-stack/latest/howtos/consul/" rel="noopener noreferrer"&gt;Amabassador Edge Stack integration with Consul&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/api-gateway" rel="noopener noreferrer"&gt;Consul API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hashicorp.com/blog/using-hashicorp-consul-with-kong-ingress-controller-for-kubernetes" rel="noopener noreferrer"&gt;Using HashiCorp Consul with Kong Ingress Controller for Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://traefik.io/blog/getting-started-traefik-proxy-and-hashicorp-consul/" rel="noopener noreferrer"&gt;Getting Started With Traefik Proxy and HashiCorp Consul&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://doc.traefik.io/traefik/providers/consul/" rel="noopener noreferrer"&gt;Taefik Consul Provider configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kschoche/traefik-consul" rel="noopener noreferrer"&gt;traefik-consul walk-through&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/joatmon08/consul-k8s-ingress-controllers" rel="noopener noreferrer"&gt;consul ingress controllers&lt;/a&gt; integration with Traefik or Kong using terrafom&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dhiaayachi/eks-consul-ingressnginx" rel="noopener noreferrer"&gt;Consul on EKS using nginx as ingress (transparent mode)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tracing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://discuss.hashicorp.com/t/consul-connect-integration-of-jaeger-in-kubernetes/4394" rel="noopener noreferrer"&gt;Consul connect integration of jaeger in Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hashicorp/consul-demo-tracing" rel="noopener noreferrer"&gt;Consul Service Mesh Tracing Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/envoyproxy/envoy/tree/main/examples/jaeger-tracing" rel="noopener noreferrer"&gt;https://github.com/envoyproxy/envoy/tree/main/examples/jaeger-tracing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dgraph Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dgraph.io/docs/query-language/graphql-fundamentals/" rel="noopener noreferrer"&gt;DQL Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Helmfile
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/helmfile/helmfile" rel="noopener noreferrer"&gt;https://github.com/helmfile/helmfile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tanzu.vmware.com/developer/guides/helmfile-what-is/" rel="noopener noreferrer"&gt;What is Helmfile?&lt;/a&gt; by Paul Czarkowski (VMWare Tanzu Developer Center)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Blog Source Code
&lt;/h3&gt;

&lt;p&gt;This is some code that I developed when testing &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blog Source Code&lt;/strong&gt;: &lt;a href="https://github.com/darkn3rd/blog_tutorials/tree/master/kubernetes/gke/service-mesh/consul-connect" rel="noopener noreferrer"&gt;https://github.com/darkn3rd/blog_tutorials/tree/master/kubernetes/gke/service-mesh/consul-connect&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP/gRPC Greeter Application&lt;/strong&gt;: &lt;a href="https://github.com/darkn3rd/greeter" rel="noopener noreferrer"&gt;https://github.com/darkn3rd/greeter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pydgraph-client w consul support&lt;/strong&gt;: &lt;a href="https://github.com/darkn3rd/pydgraph-client/tree/consul" rel="noopener noreferrer"&gt;https://github.com/darkn3rd/pydgraph-client/tree/consul&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There you have it, a small (cough) overview how to get started with &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; Service Mesh. In particular, here some of the takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provisioning &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubenertes&lt;/a&gt; (&lt;a href="https://cloud.google.com/kubernetes-engine" rel="noopener noreferrer"&gt;GKE&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(addendum) Provisioning &lt;a href="https://cloud.google.com/container-registry" rel="noopener noreferrer"&gt;GCR&lt;/a&gt; and publishing images to &lt;a href="https://cloud.google.com/container-registry" rel="noopener noreferrer"&gt;GCR&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deploying &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; Service Mesh on &lt;a href="https://cloud.google.com/kubernetes-engine" rel="noopener noreferrer"&gt;GKE&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deploying a server and a client with multiport support: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP" rel="noopener noreferrer"&gt;HTTP&lt;/a&gt; and &lt;a href="https://grpc.io/" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Testing &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP" rel="noopener noreferrer"&gt;HTTP&lt;/a&gt; traffic with &lt;code&gt;curl&lt;/code&gt; and &lt;a href="https://grpc.io/" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; traffic with &lt;code&gt;gprcurl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Limitations and Challenges with current multi-port scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, here’s some extra takeaways beyond just using &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;a href="https://github.com/helmfile/helmfile" rel="noopener noreferrer"&gt;Helmfile&lt;/a&gt; to deploy &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; charts with templated chart config values, where values and branch logic is set by env vars.&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://github.com/helmfile/helmfile" rel="noopener noreferrer"&gt;Helmfile&lt;/a&gt; to patch using &lt;a href="https://elatov.github.io/2021/08/using-kustomize/" rel="noopener noreferrer"&gt;Kustomize merge&lt;/a&gt; and &lt;a href="https://skryvets.com/blog/2019/05/15/kubernetes-kustomize-json-patches-6902/" rel="noopener noreferrer"&gt;JSON Patch&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; raw chart to package Kubernetes manifests as templated values&lt;/li&gt;
&lt;li&gt;Introduction to &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; distributed graph database&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Challenges with Consul
&lt;/h3&gt;

&lt;p&gt;You may have noticed that &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt; is, dare I say, complex, beyond complex. The documentation is good, but perhaps maybe not all that well organized, with many missing things.&lt;/p&gt;

&lt;p&gt;The underlying tool &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt; is very powerful, and &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh on top of this tool is quite robust and extremely flexible where you can swap out the default CA for other solutions, like &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;Vault&lt;/a&gt; CA, and swap out the &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxy for another solution, like &lt;a href="http://nginx.org/" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt; or &lt;a href="http://www.haproxy.org/" rel="noopener noreferrer"&gt;HAProxy&lt;/a&gt;. For ingress into the cluster, you can use &lt;a href="https://developer.hashicorp.com/consul/docs/api-gateway" rel="noopener noreferrer"&gt;Consul API Gateway&lt;/a&gt;, or another API Gateway or an ingress controller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh has some challenges or limitations (see below) when you have a service that supports multiple ports.&lt;/p&gt;

&lt;h4&gt;
  
  
  Complexity
&lt;/h4&gt;

&lt;p&gt;I have experimented with other service meshes and I was able to get up to speed quickly: &lt;a href="https://linkerd.io/" rel="noopener noreferrer"&gt;Linkerd&lt;/a&gt; = 1 day, &lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt; = 3 days, &lt;a href="https://www.nginx.com/products/nginx-service-mesh/" rel="noopener noreferrer"&gt;NGINX Service Mesh&lt;/a&gt; = 5 days, but &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh took at least 11 days to get off the ground. This is by far the most complex solution available.&lt;/p&gt;

&lt;h4&gt;
  
  
  Unable to Update
&lt;/h4&gt;

&lt;p&gt;If you need to update &lt;a href="https://www.consul.io/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; with a configuration change and use helm to update consul, the consul-server pods may not reach a healthy state. You may have to delete everything and recreate it from scratch.&lt;/p&gt;

&lt;p&gt;Apparently there’s some way to ameliorate this by adding &lt;code&gt;leave_on_terminate: true&lt;/code&gt; setting in the &lt;code&gt;server.extraConfig&lt;/code&gt; (&lt;a href="https://discuss.hashicorp.com/t/unstable-deployment-on-k8s-with-helm-chart/44411" rel="noopener noreferrer"&gt;ref&lt;/a&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Higher Memory Footprint
&lt;/h4&gt;

&lt;p&gt;Consul Connect service mesh has a higher memory footprint, so on a small cluster with &lt;code&gt;e5-medium&lt;/code&gt; nodes (2 vCPUs, 4 GB memory), you will only be able to support a maximum of 6 side-car proxies. In order to get an application like &lt;a href="https://dgraph.io/" rel="noopener noreferrer"&gt;Dgraph&lt;/a&gt; working, which will have 6 nodes (3 Dgraph Alpha pods and 3 Dgraph Zero pods) for high availability along with at least one client, a larger footprint with more robust &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; worker nodes were required.&lt;/p&gt;

&lt;h4&gt;
  
  
  Requirement for Service Resource
&lt;/h4&gt;

&lt;p&gt;One challenge to &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh is that it configures the &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; side-car proxy based on what you specify for a &lt;code&gt;service&lt;/code&gt;. This added some challenges.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A pure client that is not listening on a port, still requires you to specify a &lt;code&gt;service&lt;/code&gt; resource so that it can be added to the service mesh.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;StatefulSet&lt;/code&gt; that requires specifying a headless &lt;code&gt;service&lt;/code&gt; in addition to &lt;code&gt;service&lt;/code&gt; endpoint into the cluster will fail spectacularly if both service and headless service use the same port.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The docs explicitly note this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: As of consul-k8s &lt;code&gt;v0.26.0&lt;/code&gt; and Consul Helm &lt;code&gt;v0.32.0&lt;/code&gt;, having a Kubernetes service is &lt;strong&gt;required&lt;/strong&gt; to run services on the Consul Service Mesh.&lt;/em&gt; (&lt;a href="https://developer.hashicorp.com/consul/docs/k8s/connect#accepting-inbound-connections" rel="noopener noreferrer"&gt;ref&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  More Complexity with Multiport
&lt;/h4&gt;

&lt;p&gt;The Kubernetes &lt;code&gt;service&lt;/code&gt; API supports an array of &lt;code&gt;ports&lt;/code&gt; that you can specify, but &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; only supports a single port for &lt;a href="https://www.hashicorp.com/blog/transparent-proxy-on-consul-service-mesh" rel="noopener noreferrer"&gt;transparent-proxy&lt;/a&gt; mode. This is very bizarre, because a service with multiple ports is quite common, such as an admin port vs API port, or scenarios where a service has both HTTP and gRPC interfaces.&lt;/p&gt;

&lt;p&gt;This is also part of the Kubernetes &lt;code&gt;service&lt;/code&gt; API specification, which &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; reads to configure the &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxy. So, in this sense, &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; is not fully supported as far a parity with the &lt;code&gt;service&lt;/code&gt; API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55qrokru98zv74dg0b3s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55qrokru98zv74dg0b3s.png" alt="Consul SM vs Other SM with multiport" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;a href="https://developer.hashicorp.com/consul/docs/k8s/connect#kubernetes-pods-with-multiple-ports" rel="noopener noreferrer"&gt;multi-port&lt;/a&gt; scenario, the following will need to be done on the server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all &lt;code&gt;services&lt;/code&gt; with multiple ports will need to be broken up into separate services with only one port&lt;/li&gt;
&lt;li&gt;need to specify &lt;code&gt;consul.hashicorp.com/connect-service&lt;/code&gt; annotation listing each of the services supported that will be mapped into consul.&lt;/li&gt;
&lt;li&gt;need to specify &lt;code&gt;consul.hashicorp.com/connect-service-port annotation&lt;/code&gt; listing ports that correspond to the previous above annotation&lt;/li&gt;
&lt;li&gt;if ACLs are enabled, a &lt;code&gt;serviceaccount&lt;/code&gt; needs to be specified corresponding to each &lt;code&gt;service&lt;/code&gt; specified.&lt;/li&gt;
&lt;li&gt;if ACLs are enabled and Kubernetes 1.24+ is used, a corresponding &lt;code&gt;secret&lt;/code&gt; for the service token needs to be created as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The client will need the following in order to connect to the server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;specify &lt;code&gt;consul.hashicorp.com/connect-service-upstreams&lt;/code&gt; annotation listing the consul service and outbound port to use from &lt;code&gt;localhost&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;if ACLs are enabled, a &lt;code&gt;serviceaccount&lt;/code&gt; that corresponds to the &lt;code&gt;service&lt;/code&gt; specified for the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The client is now required to connect to &lt;code&gt;localhost&lt;/code&gt; at the target outbound port, not to the service endpont DNS name, such as &lt;code&gt;mysvc.myns.svc.cluster.local&lt;/code&gt;. This will be the only way to use the service mesh. Directly connecting to the service endpoint, e.g. &lt;code&gt;mysvc.myns.svc.cluster.local&lt;/code&gt;, will bypass the service mesh and thus will not be protected with encryption.&lt;/p&gt;

&lt;h4&gt;
  
  
  Insecurity with Multiport
&lt;/h4&gt;

&lt;p&gt;When &lt;a href="https://developer.hashicorp.com/consul/docs/connect/transparent-proxy" rel="noopener noreferrer"&gt;transparent-proxy&lt;/a&gt; is enabled, members can communicate using the DNS of the service endpoint, for example: &lt;code&gt;mysvc.myns.svc.cluster.local&lt;/code&gt;. And when you use &lt;a href="https://developer.hashicorp.com/consul/docs/k8s/connect#kubernetes-pods-with-multiple-ports" rel="noopener noreferrer"&gt;multi-port&lt;/a&gt; scenario, &lt;a href="https://www.hashicorp.com/blog/transparent-proxy-on-consul-service-mesh" rel="noopener noreferrer"&gt;transparent-proxy&lt;/a&gt; is unfortunately disabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd1540dpckrby8kgeite.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd1540dpckrby8kgeite.png" alt="Consul SM vs other SM for security" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because of this situation, security through &lt;a href="https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/" rel="noopener noreferrer"&gt;mTLS&lt;/a&gt; or &lt;a href="https://developer.hashicorp.com/consul/docs/security/acl" rel="noopener noreferrer"&gt;ACLs&lt;/a&gt; (tokens) can be bypassed completely when multi-port services are configured. Any non-mesh member or mesh member that does not have access granted (through configuring an intention) can connect to the service endpoint, such as &lt;code&gt;mysvc.myns.svc.cluster.local&lt;/code&gt;. The only thing ACLs offer at this point is blocking encrypted traffic through the mesh, and thus the ACL feature is pointless.&lt;/p&gt;

&lt;p&gt;This issue can be ameliorated by configuring the service itself to only communicate through &lt;code&gt;localhost&lt;/code&gt;, which forces it to use the service mesh, but then this poses problems, such as trying to use an &lt;code&gt;ingress&lt;/code&gt;. Alternatively, you could use a firewall, such as a network policy. Ultimately, another non-Consul solution is needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ingress Challenge with Multiport
&lt;/h4&gt;

&lt;p&gt;An ingress controller is an interesting challenge to integrate to the service mesh, as annotations will be needed to put the ingress controller pods onto the service mesh. The ingress controller will route traffic to the backend &lt;code&gt;service&lt;/code&gt; using the local DNS, such as &lt;code&gt;mysvc.myns.svc.cluster.local&lt;/code&gt;, where the &lt;code&gt;service&lt;/code&gt; named &lt;code&gt;mysvc&lt;/code&gt; running in the &lt;code&gt;myns&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://developer.hashicorp.com/consul/docs/k8s/connect#kubernetes-pods-with-multiple-ports" rel="noopener noreferrer"&gt;multi-port&lt;/a&gt; scenario however, this will not work, because the ingress controller is now required to route to &lt;code&gt;localhost&lt;/code&gt; for a specific outbound ports that are specified in the &lt;code&gt;consul.hashicorp.com/connect-service-upstreams&lt;/code&gt; annotation. The normal &lt;code&gt;ingress&lt;/code&gt; resource API does not support this setup, as it routes to Kubernetes &lt;code&gt;service&lt;/code&gt; DNS name, not to &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There may be some ingress controllers that may provide extra non-standard configurations that could support this requirement to route to &lt;code&gt;localhost&lt;/code&gt;, but unfortunately no one at &lt;a href="https://www.hashicorp.com/" rel="noopener noreferrer"&gt;Hashicorp&lt;/a&gt; has even tested this common use case (&lt;a href="https://github.com/hashicorp/consul-k8s/issues/1606" rel="noopener noreferrer"&gt;ref&lt;/a&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  No Observability with Multport
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3448i209nk0fbri4il8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3448i209nk0fbri4il8u.png" alt="Observability Tool Icons" width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are using &lt;a href="https://developer.hashicorp.com/consul/docs/k8s/connect#kubernetes-pods-with-multiple-ports" rel="noopener noreferrer"&gt;multi-port&lt;/a&gt; scenario, observability is not an option. Just forget you even heard of the word observability, one of the three planes that make up the service mesh solution. The &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; injection process will actually cause stack traces.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hashicorp/consul-k8s/issues/1594" rel="noopener noreferrer"&gt;https://github.com/hashicorp/consul-k8s/issues/1594&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;I hope this is useful in exposure to &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt; service mesh and can help you get started should you want to try this out. If you have services that only listen on a single port, then this certainly an interesting solution to explore.&lt;/p&gt;

&lt;p&gt;If however, you have an application service that needs support for 2+ ports, because you know, &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; supports this, I would recommend avoiding &lt;a href="https://developer.hashicorp.com/consul/docs/connect" rel="noopener noreferrer"&gt;Consul Connect&lt;/a&gt;, as it is not functional to meet minimum requirements for a service mesh. Perhaps someday, when &lt;a href="https://www.hashicorp.com/" rel="noopener noreferrer"&gt;Hashicorp&lt;/a&gt; prioritizes basic functionality and usability in future version, this product can be considered.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>googlecloud</category>
      <category>consul</category>
      <category>dgraph</category>
    </item>
  </channel>
</rss>
