<?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>Forem: Alex</title>
    <description>The latest articles on Forem by Alex (@alexottr).</description>
    <link>https://forem.com/alexottr</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F911133%2F372c0e48-f5d4-4a89-9b45-2e591500b027.png</url>
      <title>Forem: Alex</title>
      <link>https://forem.com/alexottr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/alexottr"/>
    <language>en</language>
    <item>
      <title>kustomize: alternative to Helm for lightweight deployments with secrets</title>
      <dc:creator>Alex</dc:creator>
      <pubDate>Fri, 02 Sep 2022 20:36:44 +0000</pubDate>
      <link>https://forem.com/alexottr/kustomize-alternative-to-helm-for-lightweight-deployments-with-secrets-57fj</link>
      <guid>https://forem.com/alexottr/kustomize-alternative-to-helm-for-lightweight-deployments-with-secrets-57fj</guid>
      <description>&lt;p&gt;My journey started with the simple wish to move some of my applications from a docker environment to a kubernetes cluster I configured for some of my microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why I looked for an alternative to Helm&lt;/li&gt;
&lt;li&gt;kustomize as an alternative&lt;/li&gt;
&lt;li&gt;The proposed deployment&lt;/li&gt;
&lt;li&gt;Using system environment variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Target applications for my thoughts were &lt;strong&gt;small applications&lt;/strong&gt;, especially &lt;strong&gt;Discord bots&lt;/strong&gt;. Those applications also need a couple of &lt;strong&gt;secrets&lt;/strong&gt; like tokens and other credentials which might change depending on the deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I looked for an alternative to Helm &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Since I was already used to utilising &lt;a href="https://helm.sh/"&gt;Helm&lt;/a&gt; for quick and easy deployments, it seemed reasonable to also use it for a microservice like a Discord bot. For those who don't know Helm, it's a fully grown package manager for kubernetes with dozens of articles about it.&lt;/p&gt;

&lt;p&gt;Being a - as mentioned "fully grown package manager" - also comes with the downside of shipping a lot of unnecessary bloat for very simple applications. To bring this into perspective, the &lt;code&gt;helm create&lt;/code&gt; command allows us to generate a basic template - or - Helm chart. This very basic bootstrapped chart already comes with 10 files, spread over 3 directories (as shown below).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|-- Chart.yaml
|-- charts
|-- templates
|   |-- NOTES.txt
|   |-- _helpers.tpl
|   |-- deployment.yaml
|   |-- hpa.yaml
|   |-- ingress.yaml
|   |-- service.yaml
|   |-- serviceaccount.yaml
|   `-- tests
|       `-- test-connection.yaml
`-- values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I might be a bit of a purist, but &lt;strong&gt;10 files&lt;/strong&gt; for the deployment of an application that doesn't even need a service sounds like a lot of bloat. Sure you can start cleaning the chart of unnecessary templates and keep the Helm benefits like versioning. This tho takes a considerable amount of time and doesn't really remove the clutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  kustomize as an alternative &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kustomize.io/"&gt;kustomize&lt;/a&gt; is a built-in configuration management system that allows to &lt;em&gt;patch&lt;/em&gt; deployments if needed. It also ships a handy tool called &lt;code&gt;secretGenerator&lt;/code&gt; that allows to &lt;strong&gt;dynamically generate secrets&lt;/strong&gt; from a given source, something I'll heavily rely on in this example.&lt;/p&gt;

&lt;p&gt;One sad thing about kustomize that I realised quite early: the documentation is still very poor and it lacks a lot of functionality that is already implemented. &lt;/p&gt;

&lt;h2&gt;
  
  
  The proposed deployment &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It's recommended to structure a kustomize deployment into two folders &lt;code&gt;base&lt;/code&gt; for the default deployment and &lt;code&gt;overlays&lt;/code&gt; for the patching deployments.&lt;/p&gt;

&lt;p&gt;The simplest deployment that infuses our secrets can look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;base
├── deployment.yaml
├── kustomization.yaml
└── secrets.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;deployment.yaml&lt;/code&gt; contains the kubernetes manifest that we'll use for the deployment.&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;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;my-app&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;my-app&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;my-app&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;my-app&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;my-app&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/staubrein/myapp&lt;/span&gt;
        &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&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="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TOKEN&lt;/span&gt;
          &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretKeyRef&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;my-app-secrets&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TOKEN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This manifest expects a secret &lt;code&gt;my-app-secret&lt;/code&gt; with a key &lt;code&gt;TOKEN&lt;/code&gt; and adds it to the container environment.&lt;/p&gt;

&lt;p&gt;The content of this secret can be read from a casual &lt;code&gt;.env&lt;/code&gt; file or in our case &lt;code&gt;secrets.env&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TOKEN=XXXXXXXXXYXYYXYXYXYXYXYXYXYXXYXXXXXXXXYYY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To generate the secret from the file (and also system environment variables!) we first need to write the &lt;code&gt;kustomize.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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kustomize.config.k8s.io/v1beta1&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;Kustomization&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="s"&gt;deployment.yaml&lt;/span&gt;

&lt;span class="na"&gt;secretGenerator&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;my-app-secrets&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secrets.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kustomize file takes the &lt;code&gt;deployment.yaml&lt;/code&gt; as resource and combines it with generated secret from the &lt;code&gt;secretGenerator&lt;/code&gt;.&lt;br&gt;
It takes the values from the given &lt;code&gt;secrets.env&lt;/code&gt; file into context and creates a secret with the given name &lt;code&gt;my-app-secrets&lt;/code&gt;. It's important to import the file via the &lt;code&gt;env&lt;/code&gt; key and &lt;strong&gt;not&lt;/strong&gt; the &lt;code&gt;file&lt;/code&gt; key, since &lt;code&gt;file&lt;/code&gt; will turn the whole file into a secret while &lt;code&gt;env&lt;/code&gt; makes all containing keys of the &lt;code&gt;secrets.env&lt;/code&gt; accessible.&lt;/p&gt;

&lt;p&gt;We can take a look at the finished manifests with &lt;code&gt;kubectl kustomize base&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;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;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;T888as8d8dja8sdjjajd8ahjjdknkasndanddnasd8a8sda8sd&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;Secret&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;my-app-secrets-fmk84tgkh5&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;Opaque&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;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;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;my-app&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;my-app&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;my-app&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;my-app&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;env&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;TOKEN&lt;/span&gt;
          &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TOKEN&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;my-app-secrets-fmk84tgkh5&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/staubrein/myapp&lt;/span&gt;
        &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&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;my-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;secretGenerator&lt;/code&gt; of kustomize dynamically generated a secret from the &lt;code&gt;secrets.env&lt;/code&gt; file with base64 encoded values and those are accessible within the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using system environment variables &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The best part on this solution, what makes it also very interesting for &lt;strong&gt;usage in pipelines&lt;/strong&gt; or environments that don't use variables from files, is that it also &lt;strong&gt;works with system environment variables&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By not assigning a value to the key in a environment file, it will automatically try to read out the value for this key from the system environment. Let's demonstrate this in a simple example and add another key to the &lt;code&gt;secrets.env&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TOKEN=XXXXXXXXXYXYYXYXYXYXYXYXYXYXXYXXXXXXXXYYY
FOO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important, that there is &lt;strong&gt;no equal sign&lt;/strong&gt; after the key, otherwise the value will be considered empty and not overwritten with a system environment variable.&lt;br&gt;
Let's fill this variable locally with &lt;code&gt;export FOO=bar&lt;/code&gt; and generate the secret with kustomize by running &lt;code&gt;kubectl kustomize base&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;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;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;FOO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;YmFy&lt;/span&gt;
  &lt;span class="na"&gt;TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WFhYWFhYWFhYWVhZWVhZWFlYWVhZWFlYWVhZWFhZWFhYWFhYWFhZWVk=&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;Secret&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;dailyottr-secrets-h7b26g7c74&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;my-app&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;Opaque&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As seen in the output above, it set the variable &lt;code&gt;FOO&lt;/code&gt; with the value we set in the system environment variable :)&lt;/p&gt;

&lt;p&gt;The deployment can be triggered via &lt;code&gt;kubectl kustomize base | kubectl apply -f -&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To fully show the potential of kustomize, especially the functionality of merging and overwriting different environments, I'll write another article soon that patches the &lt;code&gt;base&lt;/code&gt; deployment.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>microservices</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
