<?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: Kevin Davin</title>
    <description>The latest articles on DEV Community by Kevin Davin (@davinkevin).</description>
    <link>https://dev.to/davinkevin</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%2F275726%2Ffa0ac2a2-0823-4ff0-8953-cca5be891222.png</url>
      <title>DEV Community: Kevin Davin</title>
      <link>https://dev.to/davinkevin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davinkevin"/>
    <language>en</language>
    <item>
      <title>Taming FluxCD HelmReleases: The Kustomize Way approach</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Tue, 04 Jun 2024 16:00:00 +0000</pubDate>
      <link>https://dev.to/davinkevin/taming-fluxcd-helmreleases-the-kustomize-way-approach-48l8</link>
      <guid>https://dev.to/davinkevin/taming-fluxcd-helmreleases-the-kustomize-way-approach-48l8</guid>
      <description>&lt;p&gt;&lt;a href="https://fluxcd.io/" rel="noopener noreferrer"&gt;FluxCD&lt;/a&gt; is a powerful tool for managing deployments in Kubernetes using GitOps principles. While it offers a wide range of features, this post will explore scenarios where a simpler approach might be preferable, aligning with the #SimplerIsBetter philosophy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: This article shares insights and perspectives gained through experience in various contexts and companies. Feel free to disagree, but with respect! 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;HelmRelease&lt;/code&gt;, what is this?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://fluxcd.io/flux/use-cases/helm/" rel="noopener noreferrer"&gt;&lt;code&gt;HelmRelease&lt;/code&gt; is a custom resource provided by FluxCD&lt;/a&gt;, which provides users&lt;br&gt;
a way to automatically install &lt;code&gt;helm&lt;/code&gt; charts using FluxCD and its declarative system. &lt;/p&gt;

&lt;p&gt;For example, if you want to install the &lt;a href="https://github.com/stefanprodan/podinfo" rel="noopener noreferrer"&gt;PodInfo&lt;/a&gt; app, you have to declare&lt;br&gt;
the following manifest:&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;helm.toolkit.fluxcd.io/v2&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;HelmRelease&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;podinfo&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;chart&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;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;podinfo&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;6.5.*'&lt;/span&gt;
      &lt;span class="na"&gt;sourceRef&lt;/span&gt;&lt;span class="pi"&gt;:&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;HelmRepository&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;podinfo&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
  &lt;span class="na"&gt;releaseName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;podinfo&lt;/span&gt;
  &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# part dedicated to the all `values` the chart accept&lt;/span&gt;
    &lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: For the sake of simplicity, I kept only relevant attributes, but I can say FluxCD offers a rich API to cover most of use cases.&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%2Flwizd0gvqtzctacvfjs5.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%2Flwizd0gvqtzctacvfjs5.png" alt="Workflow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simplified workflow can be described like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;HelmOperator&lt;/code&gt; verifies and retrieves the Helm chart from a remote source like GitHub, GitLab, Artifactory or an OCI registry (recommended)&lt;/li&gt;
&lt;li&gt;It then renders the chart using configuration from the &lt;code&gt;HelmRelease&lt;/code&gt; object, which can include values provided in different ways (inlined, using ConfigMap or Secret)&lt;/li&gt;
&lt;li&gt;Finally, the &lt;code&gt;HelmOperator&lt;/code&gt; applies the generated &lt;code&gt;YAML&lt;/code&gt; manifests to the Kubernetes API to install or upgrade the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach can work for simple deployments, but as an operator, I've encountered several design drawbacks that I'd like to discuss. Let's see these issues!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; Like every GitOps solution, FluxCD requires a connection to a &lt;code&gt;GitRepository&lt;/code&gt; too. To keep the diagram simple, I've not materialized this item. &lt;/p&gt;
&lt;h2&gt;
  
  
  GitOps, aka "only source of truth"
&lt;/h2&gt;

&lt;p&gt;The GitOps philosophy is built on top of 3 key principles: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative System&lt;/strong&gt;: You define the desired state of your system (what you want) using declarative language (&lt;a href="https://en.wikipedia.org/wiki/Fourth-generation_programming_language" rel="noopener noreferrer"&gt;4GL&lt;/a&gt;). This approach focuses on the "what" instead of the "how," making your configuration easier to understand and maintain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System State Captured in a Git Repository&lt;/strong&gt;: The desired state of your system is stored in a Git repository. This provides a central location for managing your infrastructure configurations, enabling version control, collaboration, and easy rollbacks if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Deployment system&lt;/strong&gt;: Any changes pushed to the Git repository trigger an automated deployment process. This automates the process of translating your desired state into actual changes within your system, reducing manual intervention and the risk of errors.&lt;/li&gt;
&lt;/ul&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%2F0ilcr9vf1y9jwia24au1.jpg" 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%2F0ilcr9vf1y9jwia24au1.jpg" alt="3 pillars"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;HelmRelease&lt;/code&gt; is a &lt;strong&gt;Declarative&lt;/strong&gt; approach, where &lt;strong&gt;automation&lt;/strong&gt; is managed by the controller. However, "&lt;strong&gt;desired state of your system is stored in a Git repository&lt;/strong&gt;" and all it implies are not respected.&lt;/p&gt;
&lt;h3&gt;
  
  
  External Chart Dependencies: A Potential Weak Point
&lt;/h3&gt;

&lt;p&gt;A core principle of building resilient systems is ensuring the availability of all their components. When using &lt;code&gt;HelmRelease&lt;/code&gt;, we introduce an external dependency, the location where the Helm chart resides.&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%2Frr35t0rbcs9ilcb0mdr4.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%2Frr35t0rbcs9ilcb0mdr4.png" alt="HelmRelease not able to fetch chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If communication with this external location fails (due to server downtime, network issues, etc.), you might not be able to install or update your application. This introduces a potential single point of failure (SPOF) in your deployment process. Additionally, the desired state of your cluster is not solely defined by your &lt;code&gt;git&lt;/code&gt; repository; it also depends on all the charts your system downloads.   &lt;/p&gt;
&lt;h3&gt;
  
  
  Limited Visibility into Helm Chart Content
&lt;/h3&gt;

&lt;p&gt;Another purpose of this &lt;strong&gt;System State Captured in a Git Repository&lt;/strong&gt; is its auditability. If you capture the complete state of a system in &lt;code&gt;git&lt;/code&gt;, you can review it before an installation or upgrade. However, using &lt;code&gt;HelmRelease&lt;/code&gt; introduces a layer of opacity.&lt;/p&gt;

&lt;p&gt;While you declare the specific chart you want to use, you might not have a complete picture of what resources the chart will actually install in your cluster. It could potentially create various resources like &lt;code&gt;ClusterRoles&lt;/code&gt;, &lt;code&gt;NetworkPolicies&lt;/code&gt;, or &lt;code&gt;DaemonSets&lt;/code&gt;. You can't say without… deploying it, running it locally or worth, reading the chart's source. 😞&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1648619974934642689-925" src="https://platform.twitter.com/embed/Tweet.html?id=1648619974934642689"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1648619974934642689-925');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1648619974934642689&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of Immutability in Helm Charts!
&lt;/h3&gt;

&lt;p&gt;The problem we already have with container images applied the same way to &lt;code&gt;helm&lt;/code&gt; charts. A chart produced and published at a specific date might be un-published or re-published with a different content. In those case, you expose your system to the two previous points again… &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%2Fwnafjo7x5jahkfhlycwb.jpg" 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%2Fwnafjo7x5jahkfhlycwb.jpg" alt="representation of immutability with zebras…"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; We can now store charts in OCI registries, with immutability feature. To leverage it, you have to complexify your system with &lt;code&gt;digest&lt;/code&gt; pinning, because in security, we can't blindly trust a 3rd party 😇. &lt;/p&gt;

&lt;h3&gt;
  
  
  Chart customisation, another nightmare 👻
&lt;/h3&gt;

&lt;p&gt;While Helm charts offer a convenient way to package deployments, maintaining them can be challenging due to their complexity. Kubernetes itself provides a wide range of configuration options, which can further complicate matters.&lt;/p&gt;

&lt;p&gt;This complexity can lead to situations where the desired configuration isn't readily available within a chart.  For example, you might want to add an annotation to a workload or modify taints and tolerations, but the chart may not offer built-in ways to do so.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1622925402845970433-191" src="https://platform.twitter.com/embed/Tweet.html?id=1622925402845970433"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1622925402845970433-191');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1622925402845970433&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;To address this challenge, FluxCD introduced the concept of &lt;code&gt;postRenderers&lt;/code&gt; (see &lt;a href="https://fluxcd.io/flux/components/helm/helmreleases/#post-renderers" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; within &lt;code&gt;HelmRelease&lt;/code&gt; resources. This feature leverages the Kustomize API to customize deployments after the initial Helm chart rendering.&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;helm.toolkit.fluxcd.io/v2&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;HelmRelease&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;podinfo&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;releaseName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;podinfo&lt;/span&gt;
  &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;…&lt;/span&gt; &lt;span class="pi"&gt;}&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="nv"&gt;…&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;postRenderers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kustomize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;patches&lt;/span&gt;&lt;span class="pi"&gt;:&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;Deployment&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;metrics-server&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="s"&gt;- op: add&lt;/span&gt;
                &lt;span class="s"&gt;path: /metadata/labels/environment&lt;/span&gt;
                &lt;span class="s"&gt;value: production              &lt;/span&gt;
        &lt;span class="na"&gt;images&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;docker.io/bitnami/metrics-server&lt;/span&gt;
            &lt;span class="na"&gt;newName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.io/bitnami/metrics-server&lt;/span&gt;
            &lt;span class="na"&gt;newTag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.4.1-debian-10-r54&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The configuration for &lt;code&gt;postRenderers&lt;/code&gt; is separate from the Helm chart result. This can make it difficult to understand the complete picture of how the final deployment will be configured, potentially leading to hidden errors. 🤯&lt;/p&gt;

&lt;p&gt;FluxCD doesn't provide robust mechanisms to analyze the resources created after the combined rendering and post-rendering steps. This can make troubleshooting issues arising from these modifications cumbersome. 😞&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution is simplicity 🚀!
&lt;/h2&gt;

&lt;p&gt;We've discussed the challenges associated with relying solely on Helm charts within FluxCD deployments. These challenges can compromise the visibility, maintainability, and overall health of your GitOps workflow.&lt;/p&gt;

&lt;p&gt;So, how can we achieve the ideal balance: a declarative system, automatic deployments, and a clear picture of your system state captured entirely within your Git repository?&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%2F7zogjy9jj9snirmvk8nb.jpg" 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%2F7zogjy9jj9snirmvk8nb.jpg" alt="human printing document with an old machine made of wood"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Render the chart and store it into &lt;code&gt;git&lt;/code&gt; for better auditability
&lt;/h3&gt;

&lt;p&gt;The answer lies in a simple yet powerful sub-command – &lt;code&gt;helm template&lt;/code&gt; (or &lt;code&gt;helmfile template&lt;/code&gt; if you use &lt;a href="https://helmfile.readthedocs.io/" rel="noopener noreferrer"&gt;Helmfile&lt;/a&gt;). This command allows you to locally render helm charts along with your desired values, generating the final deployment manifest files.&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;$ &lt;/span&gt;helm repo add podinfo https://stefanprodan.github.io/podinfo
&lt;span class="s2"&gt;"podinfo"&lt;/span&gt; has been added to your repositories
&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo update podinfo
Hang tight &lt;span class="k"&gt;while &lt;/span&gt;we grab the latest from your chart repositories...
...Successfully got an update from the &lt;span class="s2"&gt;"podinfo"&lt;/span&gt; chart repository
Update Complete. ⎈Happy Helming!⎈
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"replicaCount: 2"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; values.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt;helm template podinfo/podinfo &lt;span class="nt"&gt;-f&lt;/span&gt; values.yaml &lt;span class="nt"&gt;--version&lt;/span&gt; 6.6.3 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; podinfo.yaml


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

&lt;/div&gt;

&lt;p&gt;And that's it! It was not so complicated 😇. We have a file called &lt;code&gt;podinfo.yaml&lt;/code&gt;, located in &lt;code&gt;/k8s/podinfo&lt;/code&gt; of our &lt;code&gt;git&lt;/code&gt; repository. &lt;/p&gt;

&lt;p&gt;This file is now yours, it can be read, analyzed and pushed to &lt;code&gt;kubernetes&lt;/code&gt;. After this generation, your system is free from the external chart registry where the chart is located.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; I recommend to &lt;strong&gt;never&lt;/strong&gt; modify a file "generated" by another tool, because you will loose this modification during the next rendering. tldr; treat them as "read-only".&lt;/p&gt;

&lt;h3&gt;
  
  
  How to deploy generated files with FluxCD?
&lt;/h3&gt;

&lt;p&gt;Instead of using &lt;code&gt;HelmRelease&lt;/code&gt;, we're going to use &lt;code&gt;Kustomization&lt;/code&gt; resources provided by FluxCD. It is way simpler than &lt;code&gt;HelmRelease&lt;/code&gt;, because it just deploys manifests located in a specific location.&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.toolkit.fluxcd.io/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;Kustomization&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;podinfo&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;sourceRef&lt;/span&gt;&lt;span class="pi"&gt;:&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;GitRepository&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;our-gitops-repository&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/k8s/podinfo"&lt;/span&gt; &lt;span class="c1"&gt;# our location in our GitOps repo&lt;/span&gt;
  &lt;span class="na"&gt;prune&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;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1m&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Because we use the &lt;code&gt;GitRepository&lt;/code&gt;, called &lt;code&gt;our-gitops-repository&lt;/code&gt; and eventually used by &lt;code&gt;FluxCD&lt;/code&gt; itself, there is no extra dependency in our system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct Customization with Kustomize
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;HelmRelease&lt;/code&gt; offers &lt;code&gt;postRenderers&lt;/code&gt; for some customizations, the &lt;code&gt;Kustomization&lt;/code&gt; resource provides full access to the powerful Kustomize capabilities. &lt;br&gt;
This allows for more granular and flexible control over your manifests. Here's how to achieve a similar customization as the previous &lt;code&gt;postRenderer&lt;/code&gt; example using a &lt;code&gt;kustomization.yaml&lt;/code&gt; file:&lt;/p&gt;

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

&lt;span class="c1"&gt;# kustomization.yaml&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;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;podinfo.yaml&lt;/span&gt; &lt;span class="c1"&gt;# your generated file from the previous step&lt;/span&gt;

&lt;span class="na"&gt;patches&lt;/span&gt;&lt;span class="pi"&gt;:&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;Deployment&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;metrics-server&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="s"&gt;- op: add&lt;/span&gt;
      &lt;span class="s"&gt;path: /metadata/labels/environment&lt;/span&gt;
      &lt;span class="s"&gt;value: production              &lt;/span&gt;

&lt;span class="na"&gt;images&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;docker.io/bitnami/metrics-server&lt;/span&gt;
  &lt;span class="na"&gt;newName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.io/bitnami/metrics-server&lt;/span&gt;
  &lt;span class="na"&gt;newTag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.4.1-debian-10-r54&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/" rel="noopener noreferrer"&gt;kustomize&lt;/a&gt; offers a wider range of functionalities compared to &lt;code&gt;postRenderers&lt;/code&gt;. You can manipulate resources using features like &lt;code&gt;namePrefix&lt;/code&gt;, &lt;code&gt;labels&lt;/code&gt;, &lt;code&gt;replacements&lt;/code&gt;, &lt;code&gt;components&lt;/code&gt;… The list is too long to be detailed here 😇.&lt;/p&gt;

&lt;p&gt;As a bonus point, you can run &lt;code&gt;kustomize build /k8s/podinfo/&lt;/code&gt; and see the complete result of the generation before any interaction with FluxCD. &lt;/p&gt;

&lt;h3&gt;
  
  
  Enjoy reviews and audit with rich diff!
&lt;/h3&gt;

&lt;p&gt;One of the significant advantages of managing your full Kubernetes state with GitOps is the ability to leverage Git's powerful version control capabilities for reviewing and auditing deployments.&lt;/p&gt;

&lt;p&gt;From an operator perspective, there is nothing better than a clear and detailed diff views during tool upgrade:&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%2Ftl6j9n7c5uusbhp8k7r9.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%2Ftl6j9n7c5uusbhp8k7r9.png" alt="rancher/local-path-provisioner to v0.0.27 diff view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Obviously, your IDE will be your best friend to understand what happened, with clear context and details of changes:&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%2Fvlxqzs8koih0coeacccs.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%2Fvlxqzs8koih0coeacccs.png" alt="cert-manager modification history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; Upgrade can be automated using tools like &lt;code&gt;renovate&lt;/code&gt; or &lt;code&gt;dependabot&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;While Helm charts offer a convenient way to package deployments, maintaining them in a FluxCD workflow can introduce challenges related to &lt;strong&gt;transparency&lt;/strong&gt;, &lt;strong&gt;maintainability&lt;/strong&gt;, and &lt;strong&gt;control&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article explored the limitations of &lt;code&gt;HelmReleases&lt;/code&gt; and presented &lt;code&gt;helm template …&lt;/code&gt; as a more powerful and flexible alternative, leveraging FluxCD &lt;code&gt;Kustomize&lt;/code&gt; resource. Using Kustomize directly within your &lt;code&gt;git&lt;/code&gt; repository, you gain greater control, visibility, and the benefits of Git version control for reviewing and auditing changes.&lt;/p&gt;

&lt;p&gt;Ultimately, by adopting a Kustomize-based approach within your FluxCD workflows, you can achieve a more &lt;strong&gt;declarative&lt;/strong&gt;, &lt;strong&gt;transparent&lt;/strong&gt;, and &lt;strong&gt;auditable&lt;/strong&gt; approach to managing your Kubernetes deployments.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>fluxcd</category>
      <category>yaml</category>
      <category>helm</category>
    </item>
    <item>
      <title>Deploy with Kustomize, FluxCD and Remote Resources</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Fri, 12 Aug 2022 14:41:32 +0000</pubDate>
      <link>https://dev.to/davinkevin/deploy-with-kustomize-fluxcd-and-remote-resources-2id7</link>
      <guid>https://dev.to/davinkevin/deploy-with-kustomize-fluxcd-and-remote-resources-2id7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As a Kubernetes user, I chose to use the default tooling available, named &lt;code&gt;Kustomize&lt;/code&gt;, to manage my manifests and all modifications I need to do over them for each environment I want to deploy. For that, &lt;a href="https://kustomize.io/" rel="noopener noreferrer"&gt;&lt;code&gt;Kustomize&lt;/code&gt;&lt;/a&gt; is a powerful solution based on concepts of &lt;a href="https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/resource/" rel="noopener noreferrer"&gt;inheritance&lt;/a&gt; (&lt;code&gt;resources&lt;/code&gt;) and &lt;a href="https://kubectl.docs.kubernetes.io/guides/config_management/components/" rel="noopener noreferrer"&gt;composition&lt;/a&gt; (&lt;code&gt;components&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;But one thing I miss the most is the ability to distribute manifests to other people, letting them inherit or compose them as they want. This is one subject where &lt;code&gt;helm&lt;/code&gt; is ahead of &lt;code&gt;Kustomize&lt;/code&gt; now.&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%2Fgh3ffmhnziqp6509y2rp.jpg" 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%2Fgh3ffmhnziqp6509y2rp.jpg" alt="beautiful landscape"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;Kustomize&lt;/code&gt; provides a mechanism to use &lt;code&gt;remote&lt;/code&gt; elements, for both &lt;code&gt;resources&lt;/code&gt; and &lt;code&gt;components&lt;/code&gt; using &lt;code&gt;git&lt;/code&gt; under the hood. This solution is perfect, because we can use any &lt;code&gt;git&lt;/code&gt; repository as a &lt;code&gt;yaml&lt;/code&gt; registry for our &lt;code&gt;reousrces&lt;/code&gt; and &lt;code&gt;components&lt;/code&gt; 🔥.&lt;/p&gt;

&lt;p&gt;But, the downside is &lt;code&gt;FluxCD&lt;/code&gt; does not supports so well, for &lt;a href="https://github.com/fluxcd/flux2/issues/1749#issuecomment-905410469" rel="noopener noreferrer"&gt;a lot of good reasons&lt;/a&gt; (especially when your repositories are private)… In this article, we will see how to bypass this limitation and use remote &lt;code&gt;resources&lt;/code&gt; and &lt;code&gt;components&lt;/code&gt;, the &lt;code&gt;FluxCD&lt;/code&gt; way!&lt;/p&gt;

&lt;h2&gt;
  
  
  Cluster &amp;amp; FluxCD configuration
&lt;/h2&gt;

&lt;p&gt;In this article, I won't detail so much the cluster &amp;amp; flux configuration… because there is already plenty of documentation and blogposts about that!&lt;/p&gt;

&lt;p&gt;For this article, I have set up a cluster using &lt;a href="https://cloud.google.com/kubernetes-engine" rel="noopener noreferrer"&gt;Google Kubernetes Engine&lt;/a&gt; (aka GKE) using this &lt;a href="https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-zonal-cluster" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and installed &lt;code&gt;FluxCD&lt;/code&gt; connected to a GitLab repository with the following command (extracted from the &lt;a href="https://fluxcd.io/legacy/flux/tutorials/get-started/" rel="noopener noreferrer"&gt;official documentation&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="nv"&gt;$ &lt;/span&gt;flux bootstrap gitlab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;davinkevin.fr/articles/flux-and-kustomize-remote-base &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clusters &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gke &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fluxcd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: I don't like to use &lt;code&gt;default&lt;/code&gt; branch, so I chose to create a branch for this cluster, so if I have another cluster, I just need another branch 😉. This is some kind of &lt;a href="https://docs.gitlab.com/ee/topics/gitlab_flow.html" rel="noopener noreferrer"&gt;GitLab Flow&lt;/a&gt; for environment.&lt;/p&gt;

&lt;p&gt;With this, I have a complete Kubernetes Cluster ready for GitOps, using &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters" rel="noopener noreferrer"&gt;&lt;code&gt;clusters&lt;/code&gt; repository&lt;/a&gt; as source-of-truth. Every modification made in this repository will be automatically deployed by &lt;code&gt;FluxCD&lt;/code&gt; 🤩.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;bases&lt;/code&gt; repository
&lt;/h2&gt;

&lt;p&gt;I have set up another &lt;code&gt;git&lt;/code&gt; repository dedicated to &lt;code&gt;resources&lt;/code&gt; and &lt;code&gt;components&lt;/code&gt;. It hosts what we usually call &lt;code&gt;base&lt;/code&gt;, because it contains the common core of an application and also many &lt;code&gt;components&lt;/code&gt; to enable some extra features (at infrastructure or application level).&lt;/p&gt;

&lt;p&gt;This repository &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases" rel="noopener noreferrer"&gt;is available in GitLab&lt;/a&gt; and is agnostic of my deployment environment. Again, I only host here the bare definition of my applications, we can compare this to an &lt;code&gt;helm&lt;/code&gt; registry. Because it is managed in &lt;code&gt;git&lt;/code&gt;, I can use branch &amp;amp; tags for management 🚀.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Usually, this kind of &lt;code&gt;yaml&lt;/code&gt; repository is only populated by CI from other projects automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  PodInfo base publication
&lt;/h2&gt;

&lt;p&gt;I have chosen the well known &lt;a href="https://github.com/stefanprodan/podinfo" rel="noopener noreferrer"&gt;&lt;code&gt;podinfo&lt;/code&gt; application&lt;/a&gt; as an example, and I have developed the &lt;code&gt;base&lt;/code&gt; and some &lt;code&gt;components&lt;/code&gt; associated to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
└── podinfo
    └── base
        ├── components
        │   ├── hpa
        │   │   ├── hpa.yaml
        │   │   └── kustomization.yaml
        │   ├── ingress
        │   │   ├── ingress.yaml
        │   │   └── kustomization.yaml
        │   └── redis
        │       ├── kustomization.yaml
        │       ├── redis.conf
        │       └── redis.yaml
        ├── kustomization.yaml
        └── podinfo.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Core PodInfo
&lt;/h3&gt;

&lt;p&gt;This part is the &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases/-/blob/podinfo/podinfo/base/podinfo.yaml" rel="noopener noreferrer"&gt;common part of all potential deployment&lt;/a&gt;. It contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;podinfo&lt;/code&gt; Kubernetes Service&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;podinfo&lt;/code&gt; Kubernetes Deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And nothing else… if you need to deploy any other part of the app, you have to use &lt;code&gt;components&lt;/code&gt; 👇.&lt;/p&gt;

&lt;h3&gt;
  
  
  Components
&lt;/h3&gt;

&lt;p&gt;In this &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases/-/tree/podinfo/podinfo/base/components" rel="noopener noreferrer"&gt;&lt;code&gt;components&lt;/code&gt;&lt;/a&gt;, we can find all &lt;strong&gt;optional&lt;/strong&gt; part of our application. As an end-user, I need to declare those I want to use, because by default, they are not applied at all in the common core.&lt;/p&gt;

&lt;p&gt;Here, we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases/-/tree/podinfo/podinfo/base/components/hpa" rel="noopener noreferrer"&gt;&lt;code&gt;hpa&lt;/code&gt;&lt;/a&gt; with a default preconfigured &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/" rel="noopener noreferrer"&gt;&lt;code&gt;Horizontal-Pod-Autoscaler&lt;/code&gt;&lt;/a&gt; if you want the application to scale up and down automatically&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases/-/tree/podinfo/podinfo/base/components/ingress" rel="noopener noreferrer"&gt;&lt;code&gt;ingress&lt;/code&gt;&lt;/a&gt; definition to expose the application to the outside world (&lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" rel="noopener noreferrer"&gt;see official documentation&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases/-/tree/podinfo/podinfo/base/components/redis" rel="noopener noreferrer"&gt;&lt;code&gt;redis&lt;/code&gt;&lt;/a&gt; component to enable cache on the PodInfo application with default configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's interesting to note components can be of any sort, &lt;code&gt;hpa&lt;/code&gt; and &lt;code&gt;ingress&lt;/code&gt; are focused on infrastructure when &lt;code&gt;redis&lt;/code&gt; is a feature of the application. Without this &lt;code&gt;redis&lt;/code&gt; component, the application is able to work, just without any caching system. This &lt;code&gt;redis&lt;/code&gt; component is here to deploy a &lt;code&gt;redis&lt;/code&gt; server (of course 😅) but also to enable caching at &lt;code&gt;podinfo&lt;/code&gt; level too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If you want a better description of this &lt;code&gt;component&lt;/code&gt; feature, I advise you to discover &lt;a href="https://kubectl.docs.kubernetes.io/guides/config_management/components/" rel="noopener noreferrer"&gt;this article from the official documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, we have a complete, environment agnostic &lt;code&gt;resource&lt;/code&gt; and &lt;code&gt;components&lt;/code&gt; committed to our &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases/-/tree/podinfo/" rel="noopener noreferrer"&gt;&lt;code&gt;base&lt;/code&gt; repository&lt;/a&gt;. Now, It is time to use it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish PodInfo to &lt;code&gt;dev&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Back to our cluster, I want to deploy this &lt;code&gt;podinfo&lt;/code&gt; application, but of course I want to apply some extra customizations on it, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define &lt;code&gt;components&lt;/code&gt; I want to enable&lt;/li&gt;
&lt;li&gt;Define a domain name for the &lt;code&gt;ingress&lt;/code&gt; definition&lt;/li&gt;
&lt;li&gt;Define the required &lt;code&gt;tls&lt;/code&gt; configuration to the &lt;code&gt;ingress&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Define an alternative registry for the image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, to do that, I need to have &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters/-/blob/gke/podinfo-dev/kustomization.yaml" rel="noopener noreferrer"&gt;one (or multiple) &lt;code&gt;kustomization.yaml&lt;/code&gt;&lt;/a&gt; to define this:&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="c1"&gt;# &amp;lt;clusters-repo&amp;gt;/podinfo-dev/kustomization.yaml&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;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;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;podinfo-dev&lt;/span&gt;

&lt;span class="na"&gt;images&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;podinfo&lt;/span&gt;
    &lt;span class="na"&gt;newName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/stefanprodan/podinfo&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;../base&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ingress&lt;/span&gt;

&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../base/components/redis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;code&gt;ingress&lt;/code&gt; modification is in &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters/-/blob/gke/podinfo-dev/ingress/kustomization.yaml" rel="noopener noreferrer"&gt;its own file&lt;/a&gt; to keep this one simple enough.&lt;/p&gt;

&lt;p&gt;But, what is this &lt;code&gt;../base&lt;/code&gt; folder? There is no &lt;code&gt;base&lt;/code&gt; at all &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters" rel="noopener noreferrer"&gt;in &lt;code&gt;cluster&lt;/code&gt; repository&lt;/a&gt;. And you are right… we will use &lt;code&gt;FluxCD&lt;/code&gt; feature available in flux to &lt;code&gt;include&lt;/code&gt; the &lt;code&gt;base&lt;/code&gt; repository at the right location during &lt;code&gt;reconciliation&lt;/code&gt;. To do that, we need to define multiple flux resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;podinfo-base&lt;/code&gt; flux repository
&lt;/h3&gt;

&lt;p&gt;First, we need a representation of our &lt;code&gt;flux.GitRepository&lt;/code&gt; for &lt;code&gt;bases&lt;/code&gt;, which is materialized in flux with:&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;source.toolkit.fluxcd.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;GitRepository&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;podinfo-base-dev&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;flux-system&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;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
  &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;podinfo&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;ssh://git@gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases&lt;/span&gt;
  &lt;span class="na"&gt;secretRef&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;flux-system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Here, I have chosen to define &lt;code&gt;spec.ref.branch: podinfo&lt;/code&gt; to follow every commit made to this branch. It is, for development, a continuous deployment system 🚀.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;podinfo&lt;/code&gt; flux repository
&lt;/h3&gt;

&lt;p&gt;Now, we need to create a &lt;code&gt;flux.GitRepository&lt;/code&gt; for manifests in &lt;code&gt;clusters&lt;/code&gt; repository &lt;strong&gt;and&lt;/strong&gt; manifests from the &lt;code&gt;bases&lt;/code&gt; repository created in the step before. To do that, we are going to use the &lt;a href="https://fluxcd.io/docs/components/source/gitrepositories/#include" rel="noopener noreferrer"&gt;&lt;code&gt;include&lt;/code&gt; feature&lt;/a&gt; from FluxCD:&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;source.toolkit.fluxcd.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;GitRepository&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;podinfo-dev&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;flux-system&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;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repository&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;podinfo-base-dev&lt;/span&gt;
      &lt;span class="na"&gt;fromPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;podinfo/base&lt;/span&gt;
      &lt;span class="na"&gt;toPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;base&lt;/span&gt;
  &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gke&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;ssh://git@gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters&lt;/span&gt;
  &lt;span class="na"&gt;secretRef&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;flux-system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is 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;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repository&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;podinfo-base-dev&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;1&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;fromPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;podinfo/base&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;2&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;toPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;base&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;3&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the folder &lt;code&gt;podinfo/base&lt;/code&gt; (&lt;strong&gt;2&lt;/strong&gt;) previously defined in the &lt;code&gt;podinfo-base-dev&lt;/code&gt; &lt;code&gt;flux.GitRepository&lt;/code&gt; (&lt;strong&gt;1&lt;/strong&gt;) will be injected in the path &lt;code&gt;base&lt;/code&gt; (&lt;strong&gt;3&lt;/strong&gt;) of the current &lt;code&gt;podinfo-dev&lt;/code&gt; &lt;code&gt;flux.GitRepository&lt;/code&gt; during FluxCD reconciliation… making all &lt;code&gt;resources&lt;/code&gt; and &lt;code&gt;components&lt;/code&gt; available.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;podinfo-dev&lt;/code&gt; flux Kustomization
&lt;/h3&gt;

&lt;p&gt;This one is the standard &lt;code&gt;flux.Kustomization&lt;/code&gt; you would use in any case. This one is just using the composite &lt;code&gt;flux.GitRepository&lt;/code&gt; from the previous step as source (&lt;code&gt;spec.sourceRef&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.toolkit.fluxcd.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;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;podinfo-dev&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;flux-system&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;suspend&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;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10m0s&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;podinfo-dev&lt;/span&gt; &lt;span class="c1"&gt;# file path in the &amp;lt;clusters&amp;gt; repository  &lt;/span&gt;
  &lt;span class="na"&gt;prune&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;sourceRef&lt;/span&gt;&lt;span class="pi"&gt;:&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;GitRepository&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;flux-system&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;podinfo-dev&lt;/span&gt;
  &lt;span class="na"&gt;validation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we deploy all those files (you can put them in &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters/-/blob/gke/fluxcd/podinfo-dev.yaml" rel="noopener noreferrer"&gt;the same one&lt;/a&gt;), you will see your application deployed by FluxCD with your &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters/-/tree/gke/podinfo-dev" rel="noopener noreferrer"&gt;provided custom configuration&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;❯ kubectl get all &lt;span class="nt"&gt;-n&lt;/span&gt; podinfo-dev
NAME                           READY   STATUS    RESTARTS   AGE
pod/redis-869ff7c78b-jjbbk     1/1     Running   0          103m
pod/podinfo-6dbf56d6d7-ctxxr   1/1     Running   0          103m

NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;             AGE
service/podinfo   ClusterIP   10.43.115.207   &amp;lt;none&amp;gt;        9898/TCP,9999/TCP   103m
service/redis     ClusterIP   10.43.192.84    &amp;lt;none&amp;gt;        6379/TCP            103m

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/redis     1/1     1            1           103m
deployment.apps/podinfo   1/1     1            1           103m

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/redis-869ff7c78b     1         1         1       103m
replicaset.apps/podinfo-6dbf56d6d7   1         1         1       103m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Publish PodInfo to &lt;code&gt;prod&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now, we want to use again the same &lt;code&gt;base&lt;/code&gt; this time for a production environment.&lt;/p&gt;

&lt;p&gt;Because we want something relatively stable, we chose to define &lt;code&gt;spec.ref.tag&lt;/code&gt; to a specific tag in the &lt;code&gt;bases&lt;/code&gt; repository (instead of the &lt;code&gt;podinfo&lt;/code&gt; branch).&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;source.toolkit.fluxcd.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;GitRepository&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;podinfo-base-prod&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;flux-system&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;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
  &lt;span class="na"&gt;ref&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;podinfo-1.0&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;ssh://git@gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/bases&lt;/span&gt;
  &lt;span class="na"&gt;secretRef&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;flux-system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All others &lt;code&gt;flux.Kustomization&lt;/code&gt; and &lt;code&gt;flux.GitRepository&lt;/code&gt; are &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters/-/blob/gke/fluxcd/podinfo-prod.yaml" rel="noopener noreferrer"&gt;the same&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because we are in production, we also want to enable different modules, here the &lt;code&gt;HorizontalPodAutoscaler&lt;/code&gt;. So we have a different &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base/clusters/-/blob/gke/podinfo-prod/kustomization.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;kustomization.yaml&lt;/code&gt;&lt;/a&gt;. Thanks to the full control we have over the &lt;code&gt;kustomization.yaml&lt;/code&gt; per environment, we can modify this only in production 😍.&lt;/p&gt;

&lt;p&gt;Once everything is published and FluxCD reconciliation is OK, we have:&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;-n&lt;/span&gt; podinfo-prod
NAME                           READY   STATUS    RESTARTS   AGE
pod/redis-869ff7c78b-lnll5     1/1     Running   0          113m
pod/podinfo-6dbf56d6d7-k8rjl   1/1     Running   0          113m
pod/podinfo-6dbf56d6d7-qldr8   1/1     Running   0          113m

NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;             AGE
service/podinfo   ClusterIP   10.43.109.213   &amp;lt;none&amp;gt;        9898/TCP,9999/TCP   113m
service/redis     ClusterIP   10.43.252.54    &amp;lt;none&amp;gt;        6379/TCP            113m

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/redis     1/1     1            1           113m
deployment.apps/podinfo   2/2     2            2           113m

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/redis-869ff7c78b     1         1         1       113m
replicaset.apps/podinfo-6dbf56d6d7   2         2         2       113m

NAME                                          REFERENCE            TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/podinfo   Deployment/podinfo   4%/99%    2         4         2          113m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;It was tough, with many &lt;code&gt;yaml&lt;/code&gt; but we have achieved our original goal. Being able to publish bare manifests in a central place and allow other team / people to consume them instantaneously with all features available in Kustomize.&lt;br&gt;
We are then able to enjoy all the feature of a GitOps model with a dedicated (&lt;code&gt;yaml&lt;/code&gt;) registry, like &lt;code&gt;helm&lt;/code&gt; already have 😇.&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%2F8jpxcjoo57lqh0odlfbj.jpg" 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%2F8jpxcjoo57lqh0odlfbj.jpg" alt="light after the article…"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is some follow-up improvements to come, like OCI registries (announced in &lt;a href="https://fluxcd.io/blog/2022/08/july-2022-update/" rel="noopener noreferrer"&gt;FluxCD 0.32&lt;/a&gt;), to replace &lt;code&gt;flux.GitRepository&lt;/code&gt;. We still need &lt;a href="https://github.com/fluxcd/source-controller/issues/854" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; to be solved to be able to use it with all features provided by Kustomize.&lt;/p&gt;

&lt;p&gt;In a more distant future, some other solution like &lt;a href="https://kpt.dev/guides/porch-user-guide" rel="noopener noreferrer"&gt;porch&lt;/a&gt; could be a good candidate to simplify again our deployment strategy… but it is currently in &lt;code&gt;alpha&lt;/code&gt; stage.&lt;/p&gt;

&lt;p&gt;I hope you liked this article, you can find &lt;a href="https://gitlab.com/davinkevin.fr/articles/flux-and-kustomize-remote-base" rel="noopener noreferrer"&gt;all resources used in GitLab&lt;/a&gt;, and you can reproduce it in your own cluster! If you have any questions, don't hesitate to comment or ping me on twitter &lt;a href="https://twitter.com/davinkevin" rel="noopener noreferrer"&gt;@davinkevin&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>kustomize</category>
      <category>fluxcd</category>
    </item>
    <item>
      <title>Scaling Keycloak on Distroless into Kubernetes</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Fri, 04 Jun 2021 11:40:28 +0000</pubDate>
      <link>https://dev.to/stack-labs/scaling-keycloak-on-distroless-into-kubernetes-fj3</link>
      <guid>https://dev.to/stack-labs/scaling-keycloak-on-distroless-into-kubernetes-fj3</guid>
      <description>&lt;p&gt;In the two previous articles, we discovered how to &lt;strong&gt;build&lt;/strong&gt; and &lt;strong&gt;run&lt;/strong&gt; &lt;strong&gt;Keycloak&lt;/strong&gt; with a &lt;strong&gt;Distroless&lt;/strong&gt; base image in a &lt;strong&gt;Kubernetes&lt;/strong&gt; cluster. The previously seen configuration was Ok for one instance, but the &lt;strong&gt;clustering&lt;/strong&gt; capabilities of &lt;strong&gt;Keycloak&lt;/strong&gt; was not used, which can cause some &lt;strong&gt;problems&lt;/strong&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv5ulw03zn6qnihvfive.jpeg" 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%2Fkv5ulw03zn6qnihvfive.jpeg" alt="clustering" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keycloak&lt;/strong&gt; has a built-in clustering mode, based on &lt;strong&gt;Wildfly&lt;/strong&gt; &amp;amp; &lt;strong&gt;Infinispan&lt;/strong&gt;. To activate it, some &lt;strong&gt;start-up&lt;/strong&gt; scripts are using &lt;strong&gt;environment values&lt;/strong&gt; to set up everything for you… and of course, those scripts are &lt;code&gt;bash&lt;/code&gt; based, not compatible with our &lt;em&gt;version&lt;/em&gt; of &lt;strong&gt;Keycloak&lt;/strong&gt;. Here, we will see how to configure this and deploy it to &lt;strong&gt;Kubernetes&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;code&gt;standalone-ha.xml&lt;/code&gt; extraction
&lt;/h1&gt;

&lt;p&gt;We will use the same strategy seen before to generate the &lt;code&gt;standalone-ha.xml&lt;/code&gt;, by running the official image with parameters we want to use and extract the file with &lt;code&gt;docker cp&lt;/code&gt; command line. Let's see:&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;# In the first shell&lt;/span&gt;
&lt;span class="c"&gt;# Creation of a docker network&lt;/span&gt;
first-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create keycloak-network
4da77163731b584bef2c6d0b00386b9d62e31fa216204c6c6795f66e109ba1a6
&lt;span class="c"&gt;# Launching PostgreSQL linked to the network previously created&lt;/span&gt;
first-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="nt"&gt;--net&lt;/span&gt; keycloak-network &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password postgres
229816da42707e772542f1b089c616a2333a6fbe1aea2be7efe658d6f2c934a1
first-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bar &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;JGROUPS_DISCOVERY_PROTOCOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dns.DNS_PING"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;JGROUPS_TRANSPORT_STACK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tcp &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;JGROUPS_DISCOVERY_PROPERTIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dns_query=keycloak-headless"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--net&lt;/span&gt; keycloak-network jboss/keycloak:13.0.1

&lt;span class="o"&gt;=========================================================================&lt;/span&gt;

  Using PostgreSQL database

&lt;span class="o"&gt;=========================================================================&lt;/span&gt;

19:15:45,322 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.modules] &lt;span class="o"&gt;(&lt;/span&gt;CLI &lt;span class="nb"&gt;command &lt;/span&gt;executor&lt;span class="o"&gt;)&lt;/span&gt; JBoss Modules version 1.11.0.Final
19:15:45,389 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.msc] &lt;span class="o"&gt;(&lt;/span&gt;CLI &lt;span class="nb"&gt;command &lt;/span&gt;executor&lt;span class="o"&gt;)&lt;/span&gt; JBoss MSC version 1.4.12.Final
19:15:45,399 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.threads] &lt;span class="o"&gt;(&lt;/span&gt;CLI &lt;span class="nb"&gt;command &lt;/span&gt;executor&lt;span class="o"&gt;)&lt;/span&gt; JBoss Threads version 2.4.0.Final
19:15:45,542 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-2&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0049: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; starting
...
19:16:23,596 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as.server] &lt;span class="o"&gt;(&lt;/span&gt;ServerService Thread Pool &lt;span class="nt"&gt;--&lt;/span&gt; 46&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0010: Deployed &lt;span class="s2"&gt;"keycloak-server.war"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;runtime-name : &lt;span class="s2"&gt;"keycloak-server.war"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
19:16:23,671 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as.server] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0212: Resuming server
19:16:23,679 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0025: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; started &lt;span class="k"&gt;in &lt;/span&gt;25820ms - Started 692 of 978 services &lt;span class="o"&gt;(&lt;/span&gt;686 services are lazy, passive or on-demand&lt;span class="o"&gt;)&lt;/span&gt;
19:16:23,685 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
19:16:23,686 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0051: Admin console listening on http://127.0.0.1:9990 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see we add some extra parameters for the &lt;strong&gt;clustering mode&lt;/strong&gt;, based on &lt;code&gt;JGROUPS&lt;/code&gt;. Some details are in the &lt;a href="https://hub.docker.com/r/jboss/keycloak/" rel="noopener noreferrer"&gt;docker official documentation&lt;/a&gt; but you will find more in the &lt;a href="https://www.keycloak.org/docs/latest/server_installation/#_clustering" rel="noopener noreferrer"&gt;keycloak server installation documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The simplest solution to set up cluster mode in a &lt;strong&gt;Kubernetes&lt;/strong&gt; environment is to use &lt;code&gt;DNS_PING&lt;/code&gt; over &lt;code&gt;TCP&lt;/code&gt;. This is why we defined the following environment values in the previous &lt;code&gt;shell&lt;/code&gt; example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;JGROUPS_DISCOVERY_PROTOCOL="dns.DNS_PING"&lt;/code&gt; to activate &lt;code&gt;DNS_PING&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JGROUPS_TRANSPORT_STACK=tcp&lt;/code&gt; to activate clustering over &lt;code&gt;TCP&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JGROUPS_DISCOVERY_PROPERTIES="dns_query=keycloak-headless"&lt;/code&gt; to provide a way to find other instance (we will describe it in the next paragraph).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, in another shell, we will &lt;strong&gt;steal&lt;/strong&gt; again the &lt;code&gt;standalone-ha.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: In the previous article, we were targeting the &lt;code&gt;standalone.xml&lt;/code&gt;, the &lt;code&gt;HA&lt;/code&gt; version contains a more robust configuration for our use case in a cluster mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;second-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;cp &lt;/span&gt;keycloak:/opt/jboss/keycloak/standalone/configuration/standalone-ha.xml &lt;span class="nb"&gt;.&lt;/span&gt;
second-shell&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;standalone-ha.xml
&lt;span class="c"&gt;# We can now stop the keycloak container&lt;/span&gt;
second-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop keycloak
keycloak
second-shell&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If you want to set up other parameters, you can use this method for almost everything 🤩.&lt;/p&gt;

&lt;p&gt;If we look into the &lt;code&gt;standalone-ha.xml&lt;/code&gt; file, we can see an important configuration for our clustering mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- standalone-ha.xml --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;subsystem&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"urn:jboss:domain:jgroups:8.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;channels&lt;/span&gt; &lt;span class="na"&gt;default=&lt;/span&gt;&lt;span class="s"&gt;"ee"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;channel&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ee"&lt;/span&gt; &lt;span class="na"&gt;stack=&lt;/span&gt;&lt;span class="s"&gt;"tcp"&lt;/span&gt; &lt;span class="na"&gt;cluster=&lt;/span&gt;&lt;span class="s"&gt;"ejb"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/channels&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;stacks&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;stack&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"tcp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;transport&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"TCP"&lt;/span&gt; &lt;span class="na"&gt;socket-binding=&lt;/span&gt;&lt;span class="s"&gt;"jgroups-tcp"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"dns.DNS_PING"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"dns_query"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;keycloak-headless&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/protocol&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"MERGE3"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;socket-protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"FD_SOCK"&lt;/span&gt; &lt;span class="na"&gt;socket-binding=&lt;/span&gt;&lt;span class="s"&gt;"jgroups-tcp-fd"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"FD_ALL"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"VERIFY_SUSPECT"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"pbcast.NAKACK2"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"UNICAST3"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"pbcast.STABLE"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"pbcast.GMS"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"MFC"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;protocol&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"FRAG3"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/stack&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/stacks&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/subsystem&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file will configure &lt;strong&gt;Keycloak&lt;/strong&gt; to find other instances through the &lt;code&gt;DNS_PING&lt;/code&gt; protocol. In fact, &lt;strong&gt;Keycloak&lt;/strong&gt; will forge a DNS request to find IPs behind the domain name &lt;code&gt;keycloak-headless&lt;/code&gt;… easy as pie!&lt;/p&gt;

&lt;h1&gt;
  
  
  Kubernetes deployment
&lt;/h1&gt;

&lt;p&gt;Keycloak is ready for clustering mode, but we have to adapt our &lt;strong&gt;deployment&lt;/strong&gt; to allow this specific configuration where each instance can communicate to each other. &lt;/p&gt;

&lt;p&gt;The first modification is at &lt;strong&gt;deployment&lt;/strong&gt; level, to expose some extra ports dedicated to instance-to-instance communication:&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;keycloak&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;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;keycloak&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Standard HTTP port used by keycloak&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;8080&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
            &lt;span class="c1"&gt;# Port used by Jgroups to communicate&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;7600&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To work well, &lt;strong&gt;Jgroups&lt;/strong&gt; has to be bound to the Pod IP. In &lt;strong&gt;Kubernetes&lt;/strong&gt; world, we usually don't know the Pod IP in advance, so we will have to inject the Pod IP in the &lt;strong&gt;deployment&lt;/strong&gt; and use it in the &lt;code&gt;args&lt;/code&gt; part, like below:&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;keycloak&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;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;keycloak&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-D[Standalone]"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-server"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Xms64m"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Xmx512m"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:MetaspaceSize=96M"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:MaxMetaspaceSize=256m"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djava.net.preferIPv4Stack=true"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djboss.modules.system.pkgs=org.jboss.byteman"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djava.awt.headless=true"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--add-exports=java.base/sun.nio.ch=ALL-UNNAMED"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Dorg.jboss.boot.log.file=/opt/jboss/keycloak/standalone/log/server.log"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Dlogging.configuration=file:/opt/jboss/keycloak/standalone/configuration/logging.properties"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/jboss/keycloak/jboss-modules.jar"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-mp"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/jboss/keycloak/modules"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;org.jboss.as.standalone"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djboss.home.dir=/opt/jboss/keycloak"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djboss.server.base.dir=/opt/jboss/keycloak/standalone"&lt;/span&gt;
            &lt;span class="c1"&gt;# Note we have changed the command here to use the standalone-ha.xml file&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c=standalone-ha.xml"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-b=0.0.0.0"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-bprivate=0.0.0.0"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-bmanagement=0.0.0.0"&lt;/span&gt;
           &lt;span class="c1"&gt;# Thanks to the Kubernetes interpolation, we are able to launch the app&lt;/span&gt;
           &lt;span class="c1"&gt;# with a custom parameter for each pods. &lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-Djgroups.bind_addr=$(HOST_IP)'&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# the HOST_IP environment value is populated by Kubernetes with &lt;/span&gt;
            &lt;span class="c1"&gt;# the current Pod IP coming from `status.podIP`.&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;HOST_IP&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;fieldRef&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;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;status.podIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those modifications, &lt;strong&gt;Keycloak&lt;/strong&gt; will be able to work in cluster mode… but it won't be able to find any other instances 😔. We have to add a way to discover other instances 💇‍♀️! &lt;/p&gt;

&lt;h1&gt;
  
  
  Headless Service to the rescue!
&lt;/h1&gt;

&lt;p&gt;In &lt;strong&gt;Kubernetes&lt;/strong&gt;, usually we are using &lt;strong&gt;Service&lt;/strong&gt; to expose one domain name with multiple instances of an application behind it. In our case, we want to be able to fetch every IPs behind a domain name, and this is what &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#headless-services" rel="noopener noreferrer"&gt;&lt;strong&gt;Headless Service&lt;/strong&gt;&lt;/a&gt; is for!&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;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;keycloak-headless&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Important parameter to discover every instance even before its complete startup&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;clusterIP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;None&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;ping&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;7600&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;7600&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;keycloak&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to this, every &lt;strong&gt;DNS&lt;/strong&gt; query made by &lt;strong&gt;Jgroups&lt;/strong&gt; on the domaine &lt;code&gt;keycloak-headless&lt;/code&gt; will result to the complete list of &lt;strong&gt;Keycloak&lt;/strong&gt; pod IPs in namespace!&lt;/p&gt;

&lt;h1&gt;
  
  
  Demo time!
&lt;/h1&gt;

&lt;p&gt;We will deploy and scale our keycloak application and see clustering mode in action. The &lt;code&gt;kustomization.yaml&lt;/code&gt; is similar to version in the second part of this series:&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;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&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;keycloak.yaml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database.yaml&lt;/span&gt;

&lt;span class="na"&gt;configMapGenerator&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;keycloak&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;standalone-ha.xml&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;database&lt;/span&gt;
    &lt;span class="na"&gt;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;user=keycloak&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;name=keycloak&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;database&lt;/span&gt;
    &lt;span class="na"&gt;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;password=sPCwZjuq8CMvrBn7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we deploy it, we will have the following result:&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;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
configmap/database-56h9f7gfdh created
configmap/keycloak-k97c6gkct6 created
secret/database-8g8gk22d26 created
service/database created
service/keycloak-headless created
service/keycloak created
deployment.apps/database created
deployment.apps/keycloak created
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
database-5dcc69b7b6-m48h9   1/1     Running   0          7s
keycloak-7f5f7bd8c6-7s2br   0/1     Running   0          7s
&lt;span class="c"&gt;# After few seconds…&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
database-5dcc69b7b6-m48h9   1/1     Running   0          67s
keycloak-7f5f7bd8c6-7s2br   1/1     Running   0          67s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we look at the &lt;strong&gt;Keycloak&lt;/strong&gt; logs, everything looks good. We can scale it up and see if &lt;strong&gt;clustering&lt;/strong&gt; mode do its job:&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;$ &lt;/span&gt;kubectl scale deploy/keycloak &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
deployment.apps/keycloak scaled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in the log of the previously running instance, we can see the following messages:&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;$ &lt;/span&gt;kubectl logs keycloak-7f5f7bd8c6-7s2br
20:05:51,480 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-19,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;actionTokens] ISPN100009: Advancing to rebalance phase READ_NEW_WRITE_ALL, topology &lt;span class="nb"&gt;id &lt;/span&gt;10
20:05:51,480 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-27,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;offlineSessions] ISPN100009: Advancing to rebalance phase READ_NEW_WRITE_ALL, topology &lt;span class="nb"&gt;id &lt;/span&gt;10
20:05:51,480 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-28,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;authenticationSessions] ISPN100010: Finished rebalance with members &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br, keycloak-7f5f7bd8c6-dbfxh], topology &lt;span class="nb"&gt;id &lt;/span&gt;11
20:05:51,482 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-12,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;offlineClientSessions] ISPN100009: Advancing to rebalance phase READ_NEW_WRITE_ALL, topology &lt;span class="nb"&gt;id &lt;/span&gt;10
20:05:51,471 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-25,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clientSessions] ISPN100009: Advancing to rebalance phase READ_ALL_WRITE_ALL, topology &lt;span class="nb"&gt;id &lt;/span&gt;9
20:05:51,486 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;non-blocking-thread--p6-t2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sessions] ISPN100010: Finished rebalance with members &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br, keycloak-7f5f7bd8c6-dbfxh], topology &lt;span class="nb"&gt;id &lt;/span&gt;11
20:05:51,493 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;non-blocking-thread--p6-t2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;offlineSessions] ISPN100010: Finished rebalance with members &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br, keycloak-7f5f7bd8c6-dbfxh], topology &lt;span class="nb"&gt;id &lt;/span&gt;11
20:05:51,493 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;non-blocking-thread--p6-t1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;loginFailures] ISPN100009: Advancing to rebalance phase READ_NEW_WRITE_ALL, topology &lt;span class="nb"&gt;id &lt;/span&gt;10
20:05:51,499 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;non-blocking-thread--p6-t1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;actionTokens] ISPN100010: Finished rebalance with members &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br, keycloak-7f5f7bd8c6-dbfxh], topology &lt;span class="nb"&gt;id &lt;/span&gt;11
20:05:51,503 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-28,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;offlineClientSessions] ISPN100010: Finished rebalance with members &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br, keycloak-7f5f7bd8c6-dbfxh], topology &lt;span class="nb"&gt;id &lt;/span&gt;11
20:05:51,506 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;non-blocking-thread--p6-t2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clientSessions] ISPN100009: Advancing to rebalance phase READ_NEW_WRITE_ALL, topology &lt;span class="nb"&gt;id &lt;/span&gt;10
20:05:51,512 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;non-blocking-thread--p6-t2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;loginFailures] ISPN100010: Finished rebalance with members &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br, keycloak-7f5f7bd8c6-dbfxh], topology &lt;span class="nb"&gt;id &lt;/span&gt;11
20:05:51,522 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-28,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clientSessions] ISPN100010: Finished rebalance with members &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br, keycloak-7f5f7bd8c6-dbfxh], topology &lt;span class="nb"&gt;id &lt;/span&gt;11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see the successful operation made by &lt;strong&gt;Infinispan&lt;/strong&gt; to communicate between instances. In the log we found the name of our current pod &lt;code&gt;keycloak-7f5f7bd8c6-7s2br&lt;/code&gt; and the name of the new one created through the &lt;code&gt;scale&lt;/code&gt; command &lt;code&gt;keycloak-7f5f7bd8c6-dbfxh&lt;/code&gt;. If we scale it back to &lt;code&gt;1&lt;/code&gt; instance, new logs will be available:&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;$ &lt;/span&gt;kubectl logs keycloak-7f5f7bd8c6-7s2br
20:10:28,787 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-34,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; ISPN100001: Node keycloak-7f5f7bd8c6-dbfxh left the cluster
20:10:28,790 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-34,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; ISPN000094: Received new cluster view &lt;span class="k"&gt;for &lt;/span&gt;channel ejb: &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br|4] &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;keycloak-7f5f7bd8c6-7s2br]
20:10:28,791 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.infinispan.CLUSTER] &lt;span class="o"&gt;(&lt;/span&gt;thread-34,ejb,keycloak-7f5f7bd8c6-7s2br&lt;span class="o"&gt;)&lt;/span&gt; ISPN100001: Node keycloak-7f5f7bd8c6-dbfxh left the cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;strong&gt;Voila&lt;/strong&gt;!&lt;/p&gt;

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

&lt;p&gt;This ends this 3-part article on &lt;strong&gt;Keycloak&lt;/strong&gt;, &lt;strong&gt;Distroless&lt;/strong&gt; and &lt;strong&gt;Kubernetes&lt;/strong&gt;. You are now able to deploy a rock-solid, less vulnerable and scalable instance of &lt;strong&gt;Keycloak&lt;/strong&gt; in your own cluster 🚀.  &lt;/p&gt;

&lt;p&gt;I hope you enjoyed it as mush as I enjoyed writing this article and share this experience about &lt;strong&gt;Keycloak&lt;/strong&gt; configuration. You can find all the sample files from this article in this &lt;strong&gt;GitLab&lt;/strong&gt; repository: &lt;a href="https://gitlab.com/davinkevin/keycloak-distroless" rel="noopener noreferrer"&gt;davinkevin/keycloak-distroless&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>kubernetes</category>
      <category>security</category>
      <category>docker</category>
    </item>
    <item>
      <title>Keycloak on Distroless into Kubernetes</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Tue, 01 Jun 2021 09:14:27 +0000</pubDate>
      <link>https://dev.to/stack-labs/keycloak-on-distroless-into-kubernetes-f5g</link>
      <guid>https://dev.to/stack-labs/keycloak-on-distroless-into-kubernetes-f5g</guid>
      <description>&lt;p&gt;In the previous article, we've seen how to &lt;strong&gt;build&lt;/strong&gt; and &lt;strong&gt;run&lt;/strong&gt; a &lt;strong&gt;Keycloak&lt;/strong&gt; application based on &lt;strong&gt;Distroless&lt;/strong&gt; base image. However, this article only used &lt;strong&gt;Docker&lt;/strong&gt; to launch containers, and you mostly use an orchestrator to do that. In this article, we will see how to run our previously created image in a &lt;strong&gt;Kubernetes&lt;/strong&gt; cluster. &lt;/p&gt;

&lt;h1&gt;
  
  
  Challenges
&lt;/h1&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%2Folt1rwsnvjbs440k3b80.jpeg" 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%2Folt1rwsnvjbs440k3b80.jpeg" alt="lots of challenges" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using a &lt;strong&gt;Keycloak&lt;/strong&gt; image based on &lt;strong&gt;Distroless&lt;/strong&gt; requires some adaptation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launching the application require a specific command&lt;/li&gt;
&lt;li&gt;Stopping the application require a specific command&lt;/li&gt;
&lt;li&gt;Probes should be defined &lt;/li&gt;
&lt;li&gt;Database connection should be provided via env variables&lt;/li&gt;
&lt;li&gt;Configuration file should be mounted by the orchestrator&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Database
&lt;/h2&gt;

&lt;p&gt;Before doing some configuration, we will "install" a PostgreSQL instance inside our cluster. This will be done with this &lt;em&gt;manifest&lt;/em&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="c1"&gt;# database.yaml&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;database&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;database&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;pg-port&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;5432&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;5432&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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;database&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;database&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;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;database&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;database&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:13.3-alpine&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;IfNotPresent&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;database&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;POSTGRES_USER&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;configMapKeyRef&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;database&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;user&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;POSTGRES_PASSWORD&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;database&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;password&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;POSTGRES_DB&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;configMapKeyRef&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;database&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;name&lt;/span&gt;
          &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;preStop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/bin/sh&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-c&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;su - postgres -c "pg_ctl stop -m fast"&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/bin/sh&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-c&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;exec pg_isready -U $POSTGRES_USER -d $POSTGRES_DB -h 127.1 -p &lt;/span&gt;&lt;span class="m"&gt;5432&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/bin/sh&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-c&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-e&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;exec pg_isready -U $POSTGRES_USER -d $POSTGRES_DB -h 127.1 -p &lt;/span&gt;&lt;span class="m"&gt;5432&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;pg-port&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;5432&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: This manifest is some kind of &lt;code&gt;hello-world&lt;/code&gt; at PostgreSQL level and &lt;strong&gt;SHOULD NOT&lt;/strong&gt; be used for production… because it doesn't manage any state. Every data will be lost if the pod restart. This is just for this example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launching Keycloak
&lt;/h2&gt;

&lt;p&gt;In the previous step, we fetched the &lt;code&gt;java&lt;/code&gt; command used to launched &lt;strong&gt;Keycloak&lt;/strong&gt;. We will reuse it, in the &lt;strong&gt;Kubernetes&lt;/strong&gt; manifest. For information, the command was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java '-D[Standalone]' -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED -Dorg.jboss.boot.log.file=/opt/jboss/keycloak/standalone/log/server.log -Dlogging.configuration=file:/opt/jboss/keycloak/standalone/configuration/logging.properties -jar /opt/jboss/keycloak/jboss-modules.jar -mp /opt/jboss/keycloak/modules org.jboss.as.standalone -Djboss.home.dir=/opt/jboss/keycloak -Djboss.server.base.dir=/opt/jboss/keycloak/standalone -Djboss.bind.address=172.17.0.2 -Djboss.bind.address.private=172.17.0.2 -c=standalone-ha.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will set &lt;code&gt;$.spec.template.spec.containers[0].command&lt;/code&gt; and &lt;code&gt;$.spec.template.spec.containers[0].args&lt;/code&gt; to the previously used &lt;code&gt;ENTRYPOINT&lt;/code&gt; in the &lt;code&gt;Dockerfile&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="c1"&gt;# keycloak.yaml&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;keycloak&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;keycloak&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;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;keycloak&lt;/span&gt;
          &lt;span class="c1"&gt;# We define Java as the main command of the image.&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;java"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="c1"&gt;# We put back all the parameters from the previous step.&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-D[Standalone]"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-server"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Xms64m"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Xmx512m"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:MetaspaceSize=96M"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:MaxMetaspaceSize=256m"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djava.net.preferIPv4Stack=true"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djboss.modules.system.pkgs=org.jboss.byteman"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djava.awt.headless=true"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--add-exports=java.base/sun.nio.ch=ALL-UNNAMED"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Dorg.jboss.boot.log.file=/opt/jboss/keycloak/standalone/log/server.log"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Dlogging.configuration=file:/opt/jboss/keycloak/standalone/configuration/logging.properties"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/jboss/keycloak/jboss-modules.jar"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-mp"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/jboss/keycloak/modules"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;org.jboss.as.standalone"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djboss.home.dir=/opt/jboss/keycloak"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Djboss.server.base.dir=/opt/jboss/keycloak/standalone"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c=standalone.xml"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-b=0.0.0.0"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-bprivate=0.0.0.0"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-bmanagement=0.0.0.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Like previously, the parameter &lt;strong&gt;can&lt;/strong&gt; and &lt;strong&gt;should&lt;/strong&gt; be adapted to your needs, for performance and security reasons. &lt;/p&gt;

&lt;h2&gt;
  
  
  Providing configuration to Keycloak
&lt;/h2&gt;

&lt;p&gt;Another part of the Keycloak configuration is the &lt;code&gt;standalone.xml&lt;/code&gt;. Our goal here will be to mount the file inside the running container, at the right place, to be used as source of configuration at boot time.&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="c1"&gt;# keycloak.yaml&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;keycloak&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;keycloak&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="c1"&gt;# We declare a volume containing the configuration of keycloak&lt;/span&gt;
      &lt;span class="na"&gt;volumes&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;keycloak-config&lt;/span&gt;
          &lt;span class="c1"&gt;# The configMap targeted here will be declared in the next step of the article&lt;/span&gt;
          &lt;span class="na"&gt;configMap&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;keycloak&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;keycloak&lt;/span&gt;
          &lt;span class="c1"&gt;# And then, we mount the file from the previously seen volume at the &lt;/span&gt;
          &lt;span class="c1"&gt;# location where keycloak want to find it…  &lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&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;keycloak-config&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/opt/jboss/keycloak/standalone/configuration/standalone.xml&lt;/span&gt;
              &lt;span class="na"&gt;subPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;standalone.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Probes
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Kubernetes&lt;/strong&gt; world, the orchestrator needs to know when the system is &lt;strong&gt;live&lt;/strong&gt; and &lt;strong&gt;ready&lt;/strong&gt;. For this, we have to define some &lt;strong&gt;liveness&lt;/strong&gt; and &lt;strong&gt;readyness&lt;/strong&gt; probes allowing &lt;strong&gt;Kubernetes&lt;/strong&gt; to act and react to problem with &lt;strong&gt;Keycloak&lt;/strong&gt; if required. Again, this is just some values in our manifest:&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="c1"&gt;# keycloak.yaml&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;keycloak&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;keycloak&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;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;keycloak&lt;/span&gt;
          &lt;span class="c1"&gt;# Liveness, if in error, keycloak will be restarted&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&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;/auth/&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
            &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
          &lt;span class="c1"&gt;# Readiness, if in success, traffic will be routed to this pod&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&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;/auth/realms/master&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
            &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: RedHat team will introduce more advanced and precise &lt;code&gt;liveness&lt;/code&gt; and &lt;code&gt;readyness&lt;/code&gt; probes in future version of Keycloak 😇.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lifecycle Pre-Stop Command
&lt;/h2&gt;

&lt;p&gt;To gracefully shutdown &lt;strong&gt;Keycloak&lt;/strong&gt;, we usually have to execute a specific shell script. In our case, without any shell, this will be problematic… like for &lt;code&gt;ENTRYPOINT&lt;/code&gt;, we will extract the &lt;code&gt;java&lt;/code&gt; command from the shell script and use it in our manifest.&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;# We launch the keycloak original image and launch a bash inside it&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bash jboss/keycloak:13.0.1
&lt;span class="c"&gt;# We use our magic trick to see commands executed by the jboss-cli.sh we will use&lt;/span&gt;
bash-4.4&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; inplace &lt;span class="s1"&gt;'NR==2 {print "set -x"} 1'&lt;/span&gt; /opt/jboss/keycloak/bin/jboss-cli.sh
&lt;span class="c"&gt;# Finally, we launch the script with parameters we want to execute in our pre-hook&lt;/span&gt;
bash-4.4&lt;span class="nv"&gt;$ &lt;/span&gt;/opt/jboss/keycloak/bin/jboss-cli.sh &lt;span class="nt"&gt;--connect&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:shutdown &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20
++ &lt;span class="nb"&gt;dirname&lt;/span&gt; /opt/jboss/keycloak/bin/jboss-cli.sh
+ &lt;span class="nv"&gt;DIRNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/bin
+ &lt;span class="nv"&gt;GREP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt;
+ &lt;span class="nb"&gt;.&lt;/span&gt; /opt/jboss/keycloak/bin/common.sh
++ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nv"&gt;COMMON_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/bin/common.conf
++ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /opt/jboss/keycloak/bin/common.conf &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;cygwin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;darwin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
++ &lt;span class="nb"&gt;uname&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
++ &lt;span class="nb"&gt;cd&lt;/span&gt; /opt/jboss/keycloak/bin/..
++ &lt;span class="nb"&gt;pwd&lt;/span&gt;
+ &lt;span class="nv"&gt;RESOLVED_JBOSS_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x/opt/jboss/keycloak &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nb"&gt;cd&lt;/span&gt; /opt/jboss/keycloak
++ &lt;span class="nb"&gt;pwd&lt;/span&gt;
+ &lt;span class="nv"&gt;SANITIZED_JBOSS_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak
+ &lt;span class="s1"&gt;'['&lt;/span&gt; /opt/jboss/keycloak &lt;span class="s1"&gt;'!='&lt;/span&gt; /opt/jboss/keycloak &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;export &lt;/span&gt;JBOSS_HOME
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;JBOSS_MODULEPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/modules
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="s1"&gt;'!='&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;JAVA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java
+ setDefaultModularJvmOptions
+ setModularJdk
+ java &lt;span class="nt"&gt;--add-modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.se &lt;span class="nt"&gt;-version&lt;/span&gt;
+ &lt;span class="nv"&gt;MODULAR_JDK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt;
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\-\-add\-modules'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' --add-exports=java.base/sun.nio.ch=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'  --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'  --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED -Djboss.modules.system.pkgs=com.sun.java.swing'&lt;/span&gt;
+ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'  --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED -Djboss.modules.system.pkgs=com.sun.java.swing -Dcom.ibm.jsse2.overrideDefaultTLS=true'&lt;/span&gt;
++ &lt;span class="nb"&gt;eval echo&lt;/span&gt; &lt;span class="s1"&gt;'"/opt/jboss/keycloak/modules"'&lt;/span&gt;
+++ &lt;span class="nb"&gt;echo&lt;/span&gt; /opt/jboss/keycloak/modules
+ &lt;span class="nv"&gt;JBOSS_MODULEPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/modules
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.base/sun.nio.ch&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.misc&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.reflect&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;com.sun.java.swing &lt;span class="nt"&gt;-Dcom&lt;/span&gt;.ibm.jsse2.overrideDefaultTLS&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;grep &lt;/span&gt;logging.configuration
+ &lt;span class="nv"&gt;LOG_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;exec &lt;/span&gt;java &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.base/sun.nio.ch&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.misc&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.reflect&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;com.sun.java.swing &lt;span class="nt"&gt;-Dcom&lt;/span&gt;.ibm.jsse2.overrideDefaultTLS&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Dlogging&lt;/span&gt;.configuration&lt;span class="o"&gt;=&lt;/span&gt;file:/opt/jboss/keycloak/bin/jboss-cli-logging.properties &lt;span class="nt"&gt;-jar&lt;/span&gt; /opt/jboss/keycloak/jboss-modules.jar &lt;span class="nt"&gt;-mp&lt;/span&gt; /opt/jboss/keycloak/modules org.jboss.as.cli &lt;span class="nt"&gt;--connect&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:shutdown &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20
Failed to connect to the controller: The controller is not available at localhost:9990: java.net.ConnectException: WFLYPRT0053: Could not connect to remote+http://localhost:9990. The connection failed: WFLYPRT0053: Could not connect to remote+http://localhost:9990. The connection failed: Connection refused
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution ends in error, which is normal because, in this case, no Keycloak instance are running. The &lt;code&gt;java&lt;/code&gt; command is displayed, starting with &lt;code&gt;+ exec java&lt;/code&gt;. All of this can be moved into our &lt;strong&gt;yaml&lt;/strong&gt; manifest:&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;keycloak&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;keycloak&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;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;keycloak&lt;/span&gt;
          &lt;span class="na"&gt;lifecycle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;preStop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;java"&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--add-exports=java.base/sun.nio.ch=ALL-UNNAMED'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-Djboss.modules.system.pkgs=com.sun.java.swing'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-Dcom.ibm.jsse2.overrideDefaultTLS=true'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-Dlogging.configuration=file:/opt/jboss/keycloak/bin/jboss-cli-logging.properties'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/jboss/keycloak/jboss-modules.jar"&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-mp"&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/jboss/keycloak/modules"&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;org.jboss.as.cli"&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--connect"&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--commands=shutdown&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--timeout=20'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment Configuration
&lt;/h2&gt;

&lt;p&gt;Finally, we have to provide all the configuration to &lt;strong&gt;Keycloak&lt;/strong&gt;. Until now, we only define &lt;strong&gt;how&lt;/strong&gt; the value will be linked but not &lt;strong&gt;which&lt;/strong&gt; value at all. To do that, we will first include some environment values into the Keycloak manifest:&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;keycloak&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;keycloak&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;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;keycloak&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;DB_ADDR&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;database&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;DB_DATABASE&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;configMapKeyRef&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;database&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;name&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;DB_USER&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;configMapKeyRef&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;database&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;user&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;DB_PASSWORD&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;database&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;password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are mainly targeting values required for database connection. To provide it to the deployment, we will use a &lt;code&gt;kustomization.yaml&lt;/code&gt; file. It will gather every manifests presented until now and add concret values:&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="c1"&gt;# kustomization.yaml&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;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="c1"&gt;# Install everything in the kubernetes namespace named Keycloak&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;keycloak&lt;/span&gt;

&lt;span class="c1"&gt;# Load both keycloak.yaml and database.yaml&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;keycloak.yaml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database.yaml&lt;/span&gt;

&lt;span class="c1"&gt;# Generate two ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;configMapGenerator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# One for Keycloak, containing the standalone.xml&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;keycloak&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;standalone.xml&lt;/span&gt;
  &lt;span class="c1"&gt;# One for database, containing user and name&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;database&lt;/span&gt;
    &lt;span class="na"&gt;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;user=keycloak&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;name=keycloak&lt;/span&gt;

&lt;span class="c1"&gt;# Generate a Secret&lt;/span&gt;
&lt;span class="na"&gt;secretGenerator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# For the database, containing just the password of the database&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;database&lt;/span&gt;
    &lt;span class="na"&gt;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;password=sPCwZjuq8CMvrBn7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The password is here stored in clear, you can choose to use some tooling to encrypt the secret at git level (with &lt;a href="https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-g0a"&gt;&lt;code&gt;SOPS&lt;/code&gt;&lt;/a&gt;) or cluster level (with &lt;a href="https://dev.to/stack-labs/store-your-kubernetes-secrets-in-git-thanks-to-kubeseal-hello-sealedsecret-2i6h"&gt;&lt;code&gt;SealedSecret&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying Keycloak
&lt;/h2&gt;

&lt;p&gt;In this step, we will deploy our version of &lt;strong&gt;Keycloak&lt;/strong&gt; in a &lt;strong&gt;Kubernetes&lt;/strong&gt; Cluster. For this example, I choose to use &lt;code&gt;docker-for-mac&lt;/code&gt; and its &lt;strong&gt;Kubernetes&lt;/strong&gt; integration. You can, of course, use any &lt;strong&gt;Kubernetes&lt;/strong&gt; distribution (Google Kubernetes Engine, Azure Kubernetes Service, Amazon Elastic Kubernetes Service…).&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;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;database.yaml  keycloak.yaml  kustomization.yaml  standalone.xml
&lt;span class="c"&gt;# We deploy all the manifests&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
configmap/database-56h9f7gfdh created
configmap/keycloak-96k2tfg747 created
secret/database-8g8gk22d26 created
service/database created
service/keycloak created
deployment.apps/database created
deployment.apps/keycloak created
&lt;span class="c"&gt;# We create a port-forward to the keycloak pod&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; keycloak  port-forward pod/keycloak-567797b6bb-6vqvz 8080:8080
Forwarding from 127.0.0.1:8080 -&amp;gt; 8080
Forwarding from &lt;span class="o"&gt;[&lt;/span&gt;::1]:8080 -&amp;gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access it, we create a &lt;code&gt;port-forward&lt;/code&gt; between my computer and the cluster. Thanks to that, I'm able to access the UI:&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%2F2y5wuyg2708ea054zczy.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%2F2y5wuyg2708ea054zczy.png" alt="creation-user" width="800" height="550"&gt;&lt;/a&gt;&lt;br&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%2F4ildfow8j87bsmrqplpz.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%2F4ildfow8j87bsmrqplpz.png" alt="creation-user-in-progress" width="800" height="550"&gt;&lt;/a&gt;&lt;br&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%2F92fj38bjluyd2ghirtrj.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%2F92fj38bjluyd2ghirtrj.png" alt="login" width="800" height="550"&gt;&lt;/a&gt;&lt;br&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%2F2vomubefzab1darab7ny.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%2F2vomubefzab1darab7ny.png" alt="keycloak ui" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;Voila&lt;/strong&gt;!&lt;/p&gt;

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

&lt;p&gt;In this article, we've seen how to deploy our custom &lt;strong&gt;Keycloak&lt;/strong&gt; based on &lt;strong&gt;Distroless Java&lt;/strong&gt;. This is a challenging setup, but at the end we can use a more secure version of &lt;strong&gt;Keycloak&lt;/strong&gt; without any &lt;code&gt;shell&lt;/code&gt;, which prevent any attack using &lt;code&gt;shell&lt;/code&gt; as a vector of code execution for example!&lt;/p&gt;

&lt;p&gt;I hope you liked it, you can find all the sample files from this article in this &lt;strong&gt;GitLab&lt;/strong&gt; repository: &lt;a href="https://gitlab.com/davinkevin/keycloak-distroless" rel="noopener noreferrer"&gt;davinkevin/keycloak-distroless&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>kubernetes</category>
      <category>security</category>
      <category>docker</category>
    </item>
    <item>
      <title>Keycloak on Distroless</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Fri, 28 May 2021 06:04:52 +0000</pubDate>
      <link>https://dev.to/stack-labs/keycloak-on-distroless-12ng</link>
      <guid>https://dev.to/stack-labs/keycloak-on-distroless-12ng</guid>
      <description>&lt;p&gt;&lt;strong&gt;⚠️&lt;/strong&gt; This article has been written and tested on Keycloak v13 and is working with version until 16. For later version, using Quarkus based distribution (v17+), another article will be redacted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keycloak&lt;/strong&gt; is a wonderful piece of software, managed with success by RedHat, to be used as an Identity and Access Management software. RedHat distribute it as a zip package to be run on a machine with a JVM installed or as a container. Nowadays, container is a simpler solution, especially if you are using an orchestrator like &lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Keycloak&lt;/strong&gt; image is available on the &lt;a href="https://hub.docker.com/r/jboss/keycloak/" rel="noopener noreferrer"&gt;DockerHub&lt;/a&gt; or &lt;a href="https://quay.io/repository/keycloak/keycloak" rel="noopener noreferrer"&gt;Quay&lt;/a&gt;. It  provides an important level of configuration through environment variables, which is useful if you are not familiar with &lt;strong&gt;WildFly&lt;/strong&gt; configuration. But, this solution has an important downside, especially for a tool dedicated to security… tags are not maintained at OS level over time and has many vulnerabilities.&lt;/p&gt;

&lt;p&gt;You can see below, a lot of vulnerabilities in the latest &lt;strong&gt;Keycloak&lt;/strong&gt; image, especially at the OS level. In some case, you can't choose to rely on so many vulnerabilities and need to fix that, or at least reduce them.&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;$ &lt;/span&gt;trivy image jboss/keycloak:13.0.1
2021-05-26T19:23:14.416+0200    INFO    Detected OS: redhat
2021-05-26T19:23:14.416+0200    INFO    Detecting RHEL/CentOS vulnerabilities...
2021-05-26T19:23:14.432+0200    INFO    Number of PL dependency files: 621

jboss/keycloak:13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;redhat 8.4&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;==================================&lt;/span&gt;
Total: 118 &lt;span class="o"&gt;(&lt;/span&gt;UNKNOWN: 0, LOW: 49, MEDIUM: 67, HIGH: 2, CRITICAL: 0&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Number of CVEs in an image evolves over time, so reports in this article can be way different if you run it by yourself.&lt;/p&gt;

&lt;p&gt;On one side, you can choose to upgrade every packages in the image manually, hoping a fix is available in the official CentOS registry. Another solution is to change the &lt;em&gt;base image&lt;/em&gt; to something with less vulnerability like &lt;a href="https://github.com/GoogleContainerTools/distroless" rel="noopener noreferrer"&gt;Google Distroless&lt;/a&gt;. Those images only contain the runtime for your application and nothing less… no shell, no package manager, nothing… just your runtime. For &lt;strong&gt;Keycloak&lt;/strong&gt;, we will use the &lt;a href="https://github.com/GoogleContainerTools/distroless/tree/main/java" rel="noopener noreferrer"&gt;Distroless Java&lt;/a&gt; image to sanitize our workload.&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%2F1qjax5xrl53le9h72piu.jpeg" 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%2F1qjax5xrl53le9h72piu.jpeg" alt="Nothing in distroless" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Crafting the best Dockerfile possible
&lt;/h1&gt;

&lt;p&gt;The original &lt;strong&gt;Keycloak&lt;/strong&gt; image use a lot of &lt;code&gt;bash&lt;/code&gt; scripts to configure the whole system. This is a good idea, but here, we don't have any shell in our &lt;strong&gt;Distroless&lt;/strong&gt; base image, so we will have to extract the application, and the way to launch it from &lt;strong&gt;scratch&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Keycloak into Distroless
&lt;/h2&gt;

&lt;p&gt;If we analyse the &lt;code&gt;jboss/keycloak:13.0.1&lt;/code&gt; image with &lt;a href="https://github.com/wagoodman/dive" rel="noopener noreferrer"&gt;Dive&lt;/a&gt;, we can see all &lt;strong&gt;Keycloak&lt;/strong&gt; related files are stored into &lt;code&gt;/opt/jboss/&lt;/code&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9kyd201hv91x9uww7sku.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%2F9kyd201hv91x9uww7sku.png" alt="dive" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will copy them into our distroless then, with the following &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;jboss/keycloak:13.0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/java:11-nonroot&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=nonroot:nonroot --from=base /opt/jboss /opt/jboss&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution is pretty simple:&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;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; keycloak-distroless &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;+] Building 0.6s &lt;span class="o"&gt;(&lt;/span&gt;8/8&lt;span class="o"&gt;)&lt;/span&gt; FINISHED
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from Dockerfile                       0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring dockerfile: 37B                                        0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load .dockerignore                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 2B                                            0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;gcr.io/distroless/java:11-nonroot         0.5s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;docker.io/jboss/keycloak:13.0.1           0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;base 1/1] FROM docker.io/jboss/keycloak:13.0.1                        0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;stage-1 1/2] FROM gcr.io/distroless/java:11-nonroot@sha256:07d017944  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-1 2/2] COPY &lt;span class="nt"&gt;--chown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nonroot:nonroot &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;base /opt/jb  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting to image                                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting layers                                                    0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; writing image sha256:06e849f0ab369043be9c071a446484e2a699a114dd988  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/keycloak-distroless                     0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sadly, if we are launching it like this, we will see the following error:&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;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 keycloak-distroless
Error: &lt;span class="nt"&gt;-jar&lt;/span&gt; requires jar file specification
Usage: java &lt;span class="o"&gt;[&lt;/span&gt;options] &amp;lt;mainclass&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;args...]
           &lt;span class="o"&gt;(&lt;/span&gt;to execute a class&lt;span class="o"&gt;)&lt;/span&gt;
   or  java &lt;span class="o"&gt;[&lt;/span&gt;options] &lt;span class="nt"&gt;-jar&lt;/span&gt; &amp;lt;jarfile&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;args...]
           &lt;span class="o"&gt;(&lt;/span&gt;to execute a jar file&lt;span class="o"&gt;)&lt;/span&gt;
   or  java &lt;span class="o"&gt;[&lt;/span&gt;options] &lt;span class="nt"&gt;-m&lt;/span&gt; &amp;lt;module&amp;gt;[/&amp;lt;mainclass&amp;gt;] &lt;span class="o"&gt;[&lt;/span&gt;args...]
       java &lt;span class="o"&gt;[&lt;/span&gt;options] &lt;span class="nt"&gt;--module&lt;/span&gt; &amp;lt;module&amp;gt;[/&amp;lt;mainclass&amp;gt;] &lt;span class="o"&gt;[&lt;/span&gt;args...]
           &lt;span class="o"&gt;(&lt;/span&gt;to execute the main class &lt;span class="k"&gt;in &lt;/span&gt;a module&lt;span class="o"&gt;)&lt;/span&gt;
   or  java &lt;span class="o"&gt;[&lt;/span&gt;options] &amp;lt;sourcefile&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;args]
           &lt;span class="o"&gt;(&lt;/span&gt;to execute a single source-file program&lt;span class="o"&gt;)&lt;/span&gt;

 Arguments following the main class, &lt;span class="nb"&gt;source &lt;/span&gt;file, &lt;span class="nt"&gt;-jar&lt;/span&gt; &amp;lt;jarfile&amp;gt;,
 &lt;span class="nt"&gt;-m&lt;/span&gt; or &lt;span class="nt"&gt;--module&lt;/span&gt; &amp;lt;module&amp;gt;/&amp;lt;mainclass&amp;gt; are passed as the arguments to
 main class.
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the default &lt;code&gt;ENTRYPOINT&lt;/code&gt; of this &lt;strong&gt;distroless&lt;/strong&gt; image want to launch a (fat) &lt;strong&gt;JAR&lt;/strong&gt;, but keycloak is more complex than this, so we will have to find the right &lt;code&gt;ENTRYPOINT&lt;/code&gt; for our use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the ENTRYPOINT
&lt;/h2&gt;

&lt;p&gt;For this one, we will use the original image to &lt;em&gt;see&lt;/em&gt; how Keycloak is launched in its natural state. To do that, we will edit the &lt;code&gt;standalone.sh&lt;/code&gt; file to make it more verbose and copy the &lt;code&gt;java&lt;/code&gt; command generated from it. We will follow &lt;a href="https://hub.docker.com/r/jboss/keycloak/" rel="noopener noreferrer"&gt;the official documentation&lt;/a&gt; to launch keycloak, but we will log into the container to do our magic trick:&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;# Starting the container with the minimal configuration and log into it thanks to the custom entrypoint&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_VENDOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;h2 &lt;span class="nt"&gt;--entrypoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bash jboss/keycloak:13.0.1
&lt;span class="c"&gt;# From here, we are IN the Keycloak image!&lt;/span&gt;
&lt;span class="c"&gt;# The following command update the standalone.sh file to be a lot verbose&lt;/span&gt;
bash-4.4&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; inplace &lt;span class="s1"&gt;'NR==2 {print "set -x"} 1'&lt;/span&gt; /opt/jboss/keycloak/bin/standalone.sh
&lt;span class="c"&gt;# Finally, we will launch keycloak from here and stop it when we found the line starting with "++ java"&lt;/span&gt;
bash-4.4&lt;span class="nv"&gt;$ &lt;/span&gt;/opt/jboss/tools/docker-entrypoint.sh

&lt;span class="o"&gt;=========================================================================&lt;/span&gt;

  Using Embedded H2 database

&lt;span class="o"&gt;=========================================================================&lt;/span&gt;

+ &lt;span class="nv"&gt;DEBUG_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;DEBUG_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8787
+ &lt;span class="nv"&gt;GC_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="nv"&gt;SERVER_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; 3 &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="nv"&gt;SERVER_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
+ &lt;span class="nb"&gt;shift&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; 2 &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="nv"&gt;SERVER_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address.private=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
+ &lt;span class="nb"&gt;shift&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; 1 &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="nv"&gt;SERVER_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address.private=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-c=standalone-ha.xml'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
+ &lt;span class="nb"&gt;shift&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; 0 &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nb"&gt;dirname&lt;/span&gt; /opt/jboss/keycloak/bin/standalone.sh
+ &lt;span class="nv"&gt;DIRNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/bin
++ &lt;span class="nb"&gt;basename&lt;/span&gt; /opt/jboss/keycloak/bin/standalone.sh
+ &lt;span class="nv"&gt;PROGNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;standalone.sh
+ &lt;span class="nv"&gt;GREP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt;
+ &lt;span class="nb"&gt;.&lt;/span&gt; /opt/jboss/keycloak/bin/common.sh
++ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nv"&gt;COMMON_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/bin/common.conf
++ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /opt/jboss/keycloak/bin/common.conf &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;MAX_FD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;maximum
+ &lt;span class="nv"&gt;MALLOC_ARENA_MAX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
+ &lt;span class="nb"&gt;export &lt;/span&gt;MALLOC_ARENA_MAX
+ &lt;span class="nv"&gt;cygwin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;darwin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;solaris&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;freebsd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;other&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
++ &lt;span class="nb"&gt;uname&lt;/span&gt;
+ &lt;span class="nv"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
++ &lt;span class="nb"&gt;cd&lt;/span&gt; /opt/jboss/keycloak/bin/..
++ &lt;span class="nb"&gt;pwd&lt;/span&gt;
+ &lt;span class="nv"&gt;RESOLVED_JBOSS_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x/opt/jboss/keycloak &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nb"&gt;cd&lt;/span&gt; /opt/jboss/keycloak
++ &lt;span class="nb"&gt;pwd&lt;/span&gt;
+ &lt;span class="nv"&gt;SANITIZED_JBOSS_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak
+ &lt;span class="s1"&gt;'['&lt;/span&gt; /opt/jboss/keycloak &lt;span class="s1"&gt;'!='&lt;/span&gt; /opt/jboss/keycloak &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;export &lt;/span&gt;JBOSS_HOME
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;RUN_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/bin/standalone.conf
+ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /opt/jboss/keycloak/bin/standalone.conf &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;.&lt;/span&gt; /opt/jboss/keycloak/bin/standalone.conf
++ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nv"&gt;JBOSS_MODULES_SYSTEM_PKGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman
++ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true'&lt;/span&gt;
++ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true'&lt;/span&gt;
++ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true '&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="s1"&gt;'!='&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;JAVA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java
+ &lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="nv"&gt;CONSOLIDATED_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true   '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address.private=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-c=standalone-ha.xml'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt;
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Xms64m&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt;
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Xmx512m&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Djboss&lt;/span&gt;.bind.address&lt;span class="o"&gt;=&lt;/span&gt;172.17.0.2
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address.private=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-Djboss&lt;/span&gt;.bind.address.private&lt;span class="o"&gt;=&lt;/span&gt;172.17.0.2
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="k"&gt;for &lt;/span&gt;var &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$CONSOLIDATED_OPTS&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-c=standalone-ha.xml'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
++ &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\'&lt;/span&gt;
+ &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;standalone-ha.xml
+ &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;JBOSS_BASE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/standalone
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;JBOSS_LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/standalone/log
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;JBOSS_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/standalone/configuration
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;JBOSS_MODULEPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/modules
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="s1"&gt;'!='&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\-d64'&lt;/span&gt;
+ &lt;span class="nv"&gt;JVM_D64_OPTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\-d32'&lt;/span&gt;
+ &lt;span class="nv"&gt;JVM_D32_OPTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\-server'&lt;/span&gt;
+ &lt;span class="nv"&gt;SERVER_SET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\-client'&lt;/span&gt;
+ &lt;span class="nv"&gt;CLIENT_SET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="s1"&gt;'!='&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="s1"&gt;'!='&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="nt"&gt;-a&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;false&lt;/span&gt;
+ &lt;span class="nv"&gt;PREPEND_JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' -server'&lt;/span&gt;
+ setModularJdk
+ java &lt;span class="nt"&gt;--add-modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.se &lt;span class="nt"&gt;-version&lt;/span&gt;
+ &lt;span class="nv"&gt;MODULAR_JDK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s1"&gt;']'&lt;/span&gt;
+ setDefaultModularJvmOptions &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
+ setModularJdk
+ java &lt;span class="nt"&gt;--add-modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.se &lt;span class="nt"&gt;-version&lt;/span&gt;
+ &lt;span class="nv"&gt;MODULAR_JDK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\-\-add\-modules'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' --add-exports=java.base/sun.nio.ch=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nv"&gt;DEFAULT_MODULAR_JVM_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true   --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'&lt;/span&gt;
+ &lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true   --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-server&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.base/sun.nio.ch&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.misc&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.reflect&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'java\.security\.manager'&lt;/span&gt;
+ &lt;span class="nv"&gt;SECURITY_MANAGER_SET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="s1"&gt;'!='&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;MODULE_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="s1"&gt;']'&lt;/span&gt;
++ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
++ &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\-javaagent:'&lt;/span&gt;
+ &lt;span class="nv"&gt;AGENT_SET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="s1"&gt;'!='&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;=========================================================================&lt;/span&gt;
&lt;span class="o"&gt;=========================================================================&lt;/span&gt;
+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'  JBoss Bootstrap Environment'&lt;/span&gt;
  JBoss Bootstrap Environment
+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'  JBOSS_HOME: /opt/jboss/keycloak'&lt;/span&gt;
  JBOSS_HOME: /opt/jboss/keycloak
+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'  JAVA: java'&lt;/span&gt;
  JAVA: java
+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'  JAVA_OPTS:  -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true   --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED'&lt;/span&gt;
  JAVA_OPTS:  &lt;span class="nt"&gt;-server&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;   &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.base/sun.nio.ch&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.misc&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.reflect&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED
+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;=========================================================================&lt;/span&gt;
&lt;span class="o"&gt;=========================================================================&lt;/span&gt;
+ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

+ &lt;span class="nb"&gt;true&lt;/span&gt;
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x1 &lt;span class="o"&gt;=&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s1"&gt;'"java"'&lt;/span&gt; &lt;span class="s1"&gt;'-D"[Standalone]"'&lt;/span&gt; &lt;span class="nt"&gt;-server&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.base/sun.nio.ch&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.misc&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.reflect&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="s1"&gt;'"-Dorg.jboss.boot.log.file=/opt/jboss/keycloak/standalone/log/server.log"'&lt;/span&gt; &lt;span class="s1"&gt;'"-Dlogging.configuration=file:/opt/jboss/keycloak/standalone/configuration/logging.properties"'&lt;/span&gt; &lt;span class="nt"&gt;-jar&lt;/span&gt; &lt;span class="s1"&gt;'"/opt/jboss/keycloak/jboss-modules.jar"'&lt;/span&gt; &lt;span class="nt"&gt;-mp&lt;/span&gt; &lt;span class="s1"&gt;'"/opt/jboss/keycloak/modules"'&lt;/span&gt; org.jboss.as.standalone &lt;span class="s1"&gt;'-Djboss.home.dir="/opt/jboss/keycloak"'&lt;/span&gt; &lt;span class="s1"&gt;'-Djboss.server.base.dir="/opt/jboss/keycloak/standalone"'&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-Djboss.bind.address.private=172.17.0.2'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'-c=standalone-ha.xml'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="s1"&gt;'&amp;amp;'&lt;/span&gt;
+ &lt;span class="nv"&gt;JBOSS_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;122
+ &lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'kill -HUP  122'&lt;/span&gt; HUP
++ java &lt;span class="s1"&gt;'-D[Standalone]'&lt;/span&gt; &lt;span class="nt"&gt;-server&lt;/span&gt; &lt;span class="nt"&gt;-Xms64m&lt;/span&gt; &lt;span class="nt"&gt;-Xmx512m&lt;/span&gt; &lt;span class="nt"&gt;-XX&lt;/span&gt;:MetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;96M &lt;span class="nt"&gt;-XX&lt;/span&gt;:MaxMetaspaceSize&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="nt"&gt;-Djava&lt;/span&gt;.net.preferIPv4Stack&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.modules.system.pkgs&lt;span class="o"&gt;=&lt;/span&gt;org.jboss.byteman &lt;span class="nt"&gt;-Djava&lt;/span&gt;.awt.headless&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;java.base/sun.nio.ch&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.misc&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;--add-exports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jdk.unsupported/sun.reflect&lt;span class="o"&gt;=&lt;/span&gt;ALL-UNNAMED &lt;span class="nt"&gt;-Dorg&lt;/span&gt;.jboss.boot.log.file&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/standalone/log/server.log &lt;span class="nt"&gt;-Dlogging&lt;/span&gt;.configuration&lt;span class="o"&gt;=&lt;/span&gt;file:/opt/jboss/keycloak/standalone/configuration/logging.properties &lt;span class="nt"&gt;-jar&lt;/span&gt; /opt/jboss/keycloak/jboss-modules.jar &lt;span class="nt"&gt;-mp&lt;/span&gt; /opt/jboss/keycloak/modules org.jboss.as.standalone &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.home.dir&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.server.base.dir&lt;span class="o"&gt;=&lt;/span&gt;/opt/jboss/keycloak/standalone &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.bind.address&lt;span class="o"&gt;=&lt;/span&gt;172.17.0.2 &lt;span class="nt"&gt;-Djboss&lt;/span&gt;.bind.address.private&lt;span class="o"&gt;=&lt;/span&gt;172.17.0.2 &lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;standalone-ha.xml
+ &lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'kill -TERM 122'&lt;/span&gt; INT
+ &lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'kill -QUIT 122'&lt;/span&gt; QUIT
+ &lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'kill -PIPE 122'&lt;/span&gt; PIPE
+ &lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'kill -TERM 122'&lt;/span&gt; TERM
+ &lt;span class="s1"&gt;'['&lt;/span&gt; x &lt;span class="s1"&gt;'!='&lt;/span&gt; x &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nv"&gt;WAIT_STATUS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;128
+ &lt;span class="s1"&gt;'['&lt;/span&gt; 128 &lt;span class="nt"&gt;-ge&lt;/span&gt; 128 &lt;span class="s1"&gt;']'&lt;/span&gt;
+ &lt;span class="nb"&gt;wait &lt;/span&gt;122
18:08:24,393 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.modules] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="p"&gt;)&lt;/span&gt; JBoss Modules version 1.11.0.Final
18:08:25,034 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.msc] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss MSC version 1.4.12.Final
18:08:25,050 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.threads] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss Threads version 2.4.0.Final
18:08:25,219 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-2&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0049: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; starting
18:08:25,412 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.vfs] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-4&lt;span class="o"&gt;)&lt;/span&gt; VFS000002: Failed to clean existing content &lt;span class="k"&gt;for &lt;/span&gt;temp file provider of &lt;span class="nb"&gt;type &lt;/span&gt;temp. Enable DEBUG level log to find what caused this
18:08:26,228 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.wildfly.security] &lt;span class="o"&gt;(&lt;/span&gt;ServerService Thread Pool &lt;span class="nt"&gt;--&lt;/span&gt; 22&lt;span class="o"&gt;)&lt;/span&gt; ELY00001: WildFly Elytron version 1.15.3.Final
^C
bash-4.4&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the huge starting log, we can see the following command, starting with &lt;code&gt;++ java&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;java '-D[Standalone]' -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED -Dorg.jboss.boot.log.file=/opt/jboss/keycloak/standalone/log/server.log -Dlogging.configuration=file:/opt/jboss/keycloak/standalone/configuration/logging.properties -jar /opt/jboss/keycloak/jboss-modules.jar -mp /opt/jboss/keycloak/modules org.jboss.as.standalone -Djboss.home.dir=/opt/jboss/keycloak -Djboss.server.base.dir=/opt/jboss/keycloak/standalone -Djboss.bind.address=172.17.0.2 -Djboss.bind.address.private=172.17.0.2 -c=standalone-ha.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;code&gt;java&lt;/code&gt; command we will put inside our &lt;code&gt;Dockerfile&lt;/code&gt;, as an &lt;code&gt;ENTRYPOINT&lt;/code&gt; to make &lt;strong&gt;Keycloak&lt;/strong&gt; start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;jboss/keycloak:13.0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/java:11-nonroot&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=nonroot:nonroot --from=base /opt/jboss /opt/jboss&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "java", "-D[Standalone]", "-server", "-Xms64m", "-Xmx512m", "-XX:MetaspaceSize=96M", "-XX:MaxMetaspaceSize=256m", "-Djava.net.preferIPv4Stack=true", "-Djboss.modules.system.pkgs=org.jboss.byteman", "-Djava.awt.headless=true", "--add-exports=java.base/sun.nio.ch=ALL-UNNAMED", "--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED", "--add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED", "-Dorg.jboss.boot.log.file=/opt/jboss/keycloak/standalone/log/server.log", "-Dlogging.configuration=file:/opt/jboss/keycloak/standalone/configuration/logging.properties", "-jar", "/opt/jboss/keycloak/jboss-modules.jar", "-mp", "/opt/jboss/keycloak/modules", "org.jboss.as.standalone", "-Djboss.home.dir=/opt/jboss/keycloak", "-Djboss.server.base.dir=/opt/jboss/keycloak/standalone", "-Djboss.bind.address=0.0.0.0", "-Djboss.bind.address.private=1720.0.0.0", "-c=standalone.xml" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can tune this command to increase or decrease the memory setup, the private/public bind address of your keycloak instance and many other parameters. Here, we changed the configuration file used (&lt;code&gt;-c=standalone.xml&lt;/code&gt; instead of &lt;code&gt;-c=standalone-ha.xml&lt;/code&gt; for simplicity reasons) and the bound ip adresses (to &lt;code&gt;0.0.0.0&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;If we build and run this, we will be able to access the Keycloak UI:&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;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; keycloak-distroless &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;+] Building 0.6s &lt;span class="o"&gt;(&lt;/span&gt;8/8&lt;span class="o"&gt;)&lt;/span&gt; FINISHED
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from Dockerfile                       0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring dockerfile: 37B                                        0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load .dockerignore                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 2B                                            0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;gcr.io/distroless/java:11-nonroot         0.5s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;docker.io/jboss/keycloak:13.0.1           0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;base 1/1] FROM docker.io/jboss/keycloak:13.0.1                        0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;stage-1 1/2] FROM gcr.io/distroless/java:11-nonroot@sha256:07d017944  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-1 2/2] COPY &lt;span class="nt"&gt;--chown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nonroot:nonroot &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;base /opt/jb  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting to image                                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting layers                                                    0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; writing image sha256:100908720c19018f2408bb53a5d78ef3d9eb51391b165  0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/keycloak-distroless                     0.0s

&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 keycloak-distroless
18:15:22,645 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.modules] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss Modules version 1.11.0.Final
18:15:23,283 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.msc] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss MSC version 1.4.12.Final
18:15:23,292 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.threads] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss Threads version 2.4.0.Final
18:15:23,452 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-2&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0049: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; starting
18:15:23,694 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.vfs] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-5&lt;span class="o"&gt;)&lt;/span&gt; VFS000002: Failed to clean existing content &lt;span class="k"&gt;for &lt;/span&gt;temp file provider of &lt;span class="nb"&gt;type &lt;/span&gt;temp. Enable DEBUG level log to find what caused this
18:15:24,457 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.wildfly.security] &lt;span class="o"&gt;(&lt;/span&gt;ServerService Thread Pool &lt;span class="nt"&gt;--&lt;/span&gt; 22&lt;span class="o"&gt;)&lt;/span&gt; ELY00001: WildFly Elytron version 1.15.3.Final
...
...
18:15:44,642 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.wildfly.extension.undertow] &lt;span class="o"&gt;(&lt;/span&gt;ServerService Thread Pool &lt;span class="nt"&gt;--&lt;/span&gt; 66&lt;span class="o"&gt;)&lt;/span&gt; WFLYUT0021: Registered web context: &lt;span class="s1"&gt;'/auth'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;server &lt;span class="s1"&gt;'default-server'&lt;/span&gt;
18:15:44,778 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as.server] &lt;span class="o"&gt;(&lt;/span&gt;ServerService Thread Pool &lt;span class="nt"&gt;--&lt;/span&gt; 46&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0010: Deployed &lt;span class="s2"&gt;"keycloak-server.war"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;runtime-name : &lt;span class="s2"&gt;"keycloak-server.war"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
18:15:44,886 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as.server] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0212: Resuming server
18:15:44,892 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0025: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; started &lt;span class="k"&gt;in &lt;/span&gt;22800ms - Started 692 of 977 services &lt;span class="o"&gt;(&lt;/span&gt;686 services are lazy, passive or on-demand&lt;span class="o"&gt;)&lt;/span&gt;
18:15:44,896 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
18:15:44,896 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we try to access &lt;code&gt;http://localhost:8080/&lt;/code&gt;, we can see the following page 🎉.&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%2F4lk0ikvc8fpzw4yl1uby.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%2F4lk0ikvc8fpzw4yl1uby.png" alt="keycloak-ui-from-distroless" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a good start, but this is just the minimal setup with &lt;code&gt;H2&lt;/code&gt; database, we often want something more robust for production!&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the perfect configuration
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;jboss/keycloak&lt;/code&gt; image use a lot of environment variables to configure keycloak (and the underlying &lt;code&gt;standalone.xml&lt;/code&gt;) for you… but in our case, we can't use that because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don't have a &lt;code&gt;shell&lt;/code&gt; to run those scripts.&lt;/li&gt;
&lt;li&gt;We don't want to run those scripts at every startup / scale-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, we will have to &lt;em&gt;steal&lt;/em&gt; the generated &lt;code&gt;standalone.xml&lt;/code&gt; file from the original container, post start-up, and include it in our container. For this example, I will use &lt;code&gt;PostgreSQL&lt;/code&gt; as our main database.&lt;/p&gt;

&lt;p&gt;To do this, I will use two shells side-by-side, one to launch Keycloak, and the other one to fetch the configuration.&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;# In the first shell&lt;/span&gt;
&lt;span class="c"&gt;# Creation of a docker network&lt;/span&gt;
first-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create keycloak-network
4da77163731b584bef2c6d0b00386b9d62e31fa216204c6c6795f66e109ba1a6
&lt;span class="c"&gt;# Launching PostgreSQL linked to the network previously created&lt;/span&gt;
first-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="nt"&gt;--net&lt;/span&gt; keycloak-network &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password postgres
229816da42707e772542f1b089c616a2333a6fbe1aea2be7efe658d6f2c934a1
first-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bar &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--net&lt;/span&gt; keycloak-network jboss/keycloak:13.0.1

&lt;span class="o"&gt;=========================================================================&lt;/span&gt;

  Using PostgreSQL database

&lt;span class="o"&gt;=========================================================================&lt;/span&gt;

18:32:25,172 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.modules] &lt;span class="o"&gt;(&lt;/span&gt;CLI &lt;span class="nb"&gt;command &lt;/span&gt;executor&lt;span class="o"&gt;)&lt;/span&gt; JBoss Modules version 1.11.0.Final
18:32:25,279 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.msc] &lt;span class="o"&gt;(&lt;/span&gt;CLI &lt;span class="nb"&gt;command &lt;/span&gt;executor&lt;span class="o"&gt;)&lt;/span&gt; JBoss MSC version 1.4.12.Final
18:32:25,302 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.threads] &lt;span class="o"&gt;(&lt;/span&gt;CLI &lt;span class="nb"&gt;command &lt;/span&gt;executor&lt;span class="o"&gt;)&lt;/span&gt; JBoss Threads version 2.4.0.Final
18:32:25,453 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-2&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0049: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; starting
...
18:32:59,128 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
18:32:59,129 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0051: Admin console listening on http://127.0.0.1:9990 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another shell, while the previous is still running, we will execute the following command to get the &lt;code&gt;standalone.xml&lt;/code&gt; file used to configure &lt;strong&gt;Keycloak&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;second-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;cp &lt;/span&gt;keycloak:/opt/jboss/keycloak/standalone/configuration/standalone.xml &lt;span class="nb"&gt;.&lt;/span&gt;
second-shell&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;standalone.xml
&lt;span class="c"&gt;# We can now stop the keycloak container&lt;/span&gt;
second-shell&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop keycloak
keycloak
second-shell&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we will start the &lt;strong&gt;Distroless Keycloak&lt;/strong&gt; and mount the &lt;code&gt;standalone.xml&lt;/code&gt; inside the 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;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="nt"&gt;--net&lt;/span&gt; keycloak-network &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/standalone.xml:/opt/jboss/keycloak/standalone/configuration/standalone.xml &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 keycloak-distroless
19:42:20,707 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.modules] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss Modules version 1.11.0.Final
19:42:21,317 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.msc] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss MSC version 1.4.12.Final
19:42:21,329 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.threads] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; JBoss Threads version 2.4.0.Final
19:42:21,470 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-2&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0049: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; starting
19:42:21,651 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.vfs] &lt;span class="o"&gt;(&lt;/span&gt;MSC service thread 1-1&lt;span class="o"&gt;)&lt;/span&gt; VFS000002: Failed to clean existing content &lt;span class="k"&gt;for &lt;/span&gt;temp file provider of &lt;span class="nb"&gt;type &lt;/span&gt;temp. Enable DEBUG level log to find what caused this
19:42:22,577 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.wildfly.security] &lt;span class="o"&gt;(&lt;/span&gt;ServerService Thread Pool &lt;span class="nt"&gt;--&lt;/span&gt; 20&lt;span class="o"&gt;)&lt;/span&gt; ELY00001: WildFly Elytron version 1.15.3.Final
...
19:43:58,356 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0025: Keycloak 13.0.1 &lt;span class="o"&gt;(&lt;/span&gt;WildFly Core 15.0.1.Final&lt;span class="o"&gt;)&lt;/span&gt; started &lt;span class="k"&gt;in &lt;/span&gt;17828ms - Started 595 of 873 services &lt;span class="o"&gt;(&lt;/span&gt;584 services are lazy, passive or on-demand&lt;span class="o"&gt;)&lt;/span&gt;
19:43:58,362 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
19:43:58,363 INFO  &lt;span class="o"&gt;[&lt;/span&gt;org.jboss.as] &lt;span class="o"&gt;(&lt;/span&gt;Controller Boot Thread&lt;span class="o"&gt;)&lt;/span&gt; WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;strong&gt;Voila&lt;/strong&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8o9no0uv4590wnqpnra.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%2Fl8o9no0uv4590wnqpnra.png" alt="keycloak-auth" width="800" height="564"&gt;&lt;/a&gt;&lt;br&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%2Fjky6d1pt4milf243xcup.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%2Fjky6d1pt4milf243xcup.png" alt="keycloak-login" width="800" height="564"&gt;&lt;/a&gt;&lt;br&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%2F6stjtu120ximoqrklovd.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%2F6stjtu120ximoqrklovd.png" alt="keycloak-ui" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  What about security?
&lt;/h1&gt;

&lt;p&gt;The original and main purpose of this manipulation is to reduce the number of CVEs present in our image. We will be able to compare it using &lt;a href="https://github.com/aquasecurity/trivy" rel="noopener noreferrer"&gt;trivy&lt;/a&gt; again on our newly image.&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;$ &lt;/span&gt;trivy image keycloak-distroless
2021-05-26T21:11:15.959+0200    INFO    Detected OS: debian
2021-05-26T21:11:15.959+0200    INFO    Detecting Debian vulnerabilities...
2021-05-26T21:11:15.963+0200    INFO    Number of PL dependency files: 621
2021-05-26T21:11:15.963+0200    INFO    Detecting jar vulnerabilities...

keycloak-distroless &lt;span class="o"&gt;(&lt;/span&gt;debian 10.9&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=================================&lt;/span&gt;
Total: 27 &lt;span class="o"&gt;(&lt;/span&gt;UNKNOWN: 0, LOW: 23, MEDIUM: 3, HIGH: 1, CRITICAL: 0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see, our image contain fewer vulnerabilities, at &lt;code&gt;LOW&lt;/code&gt;, &lt;code&gt;MEDIUM&lt;/code&gt; or &lt;code&gt;HIGH&lt;/code&gt; level. Again, this depends on &lt;em&gt;when&lt;/em&gt; you are doing this analysis. With the solution provided in this article, you'll be able to rebuild your keycloak on a new, up-to-date, &lt;strong&gt;Distroless base&lt;/strong&gt; image without &lt;em&gt;updating&lt;/em&gt; keycloak. With the original keycloak image, the keycloak version is tied to the OS version (and security flaws).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;code&gt;jboss/keycloak:13.0.1&lt;/code&gt; was released few hours before the creation of this article while the &lt;code&gt;distroless/java-debian10:non-root&lt;/code&gt; was released 1 month ago. This is the worst comparison scenario possible for the &lt;strong&gt;Distroless&lt;/strong&gt; base image.&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%2Fxg69znq0rbkaxirtjxky.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%2Fxg69znq0rbkaxirtjxky.png" alt="dive-distroless" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another benefit of this alternative is to create a smaller image for keycloak. The previous &lt;code&gt;dive&lt;/code&gt; reports stated &lt;code&gt;698 MB&lt;/code&gt; for the official image when our custom image weight only &lt;code&gt;519 MB&lt;/code&gt;, so around &lt;code&gt;179 MB&lt;/code&gt; reduction 🏋️‍♂️, and I'm sure we can remove almost &lt;code&gt;100MB&lt;/code&gt; by removing all useless binaries in the image (useless drivers, command line tools, documentation…).&lt;/p&gt;

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

&lt;p&gt;With this article, you should be able to build, from the official &lt;code&gt;jboss/keycloak&lt;/code&gt; image a custom one based on the &lt;strong&gt;Distroless/java&lt;/strong&gt; and even fix CVEs by doing it again when a new version of &lt;strong&gt;Distroless/java&lt;/strong&gt; image is released.&lt;/p&gt;

&lt;p&gt;I hope you liked it, you can find all the sample files from this article in this &lt;strong&gt;GitLab&lt;/strong&gt; repository: &lt;a href="https://gitlab.com/davinkevin/keycloak-distroless" rel="noopener noreferrer"&gt;davinkevin/keycloak-distroless&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>docker</category>
      <category>security</category>
      <category>java</category>
    </item>
    <item>
      <title>Anthos Service Mesh, Istio on Google Cloud ⛵️</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Tue, 18 May 2021 05:55:46 +0000</pubDate>
      <link>https://dev.to/stack-labs/anthos-service-mesh-istio-on-google-cloud-51eo</link>
      <guid>https://dev.to/stack-labs/anthos-service-mesh-istio-on-google-cloud-51eo</guid>
      <description>&lt;h1&gt;
  
  
  Anthos Service Mesh, a managed Istio ⛵️
&lt;/h1&gt;

&lt;p&gt;Istio is one of the most advanced pieces of software in the Kubernetes ecosystem. It allows to redefine the way our services are communicating with each other, without being invasive. Istio works by taking control over the network of your Kubernetes cluster and allows applying configurations (through YAML). If you want to discover Istio, I invite you to read the excellent documentation provided in &lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;istio.io&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzelgop9oyvoaxqs0iy3s.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%2Fzelgop9oyvoaxqs0iy3s.png" alt="istio-logo" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main problem with Istio, is the complexity to manage and configure it. Like kubernetes, this system is complex, and errors could lead to downtime in your cluster… 😓. Lots of software-company starts to provide a &lt;strong&gt;pre-configured&lt;/strong&gt; version of Istio, and here we will talk about &lt;strong&gt;Anthos Service Mesh&lt;/strong&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsq6z72sfsix480wuj1n.jpeg" 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%2Fbsq6z72sfsix480wuj1n.jpeg" alt="anthos-logo" width="200" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anthos Service Mesh&lt;/strong&gt; is available on &lt;em&gt;Anthos clusters&lt;/em&gt; running in &lt;strong&gt;Google Cloud&lt;/strong&gt;, &lt;strong&gt;AWS&lt;/strong&gt; or &lt;strong&gt;on-premise&lt;/strong&gt; (different features are available depending on the cluster locality). Here, we will describe the &lt;strong&gt;Google Cloud&lt;/strong&gt; version based on &lt;strong&gt;Anthos Service Mesh&lt;/strong&gt; version 1.9.3.asm-2 (the last version may be different when you read this article).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; A fully managed version of &lt;strong&gt;Anthos Service Mesh&lt;/strong&gt; exists but is actually in preview/beta. I prefer, for now, using the standard version of &lt;strong&gt;Anthos Service Mesh&lt;/strong&gt; (aka &lt;em&gt;Customer-managed control plane&lt;/em&gt;).&lt;/p&gt;

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

&lt;p&gt;The installation is pretty straight-forward, &lt;strong&gt;Google&lt;/strong&gt; provides a script &lt;code&gt;install_asm&lt;/code&gt; to automate the installation on an already existing GKE cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./install_asm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project_id&lt;/span&gt; kevin-anthos-asm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster_name&lt;/span&gt; anthos-asm-demo  &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster_location&lt;/span&gt; europe-north1-a  &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mode&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output_dir&lt;/span&gt; ./asm-downloads &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable_all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I am installing the latest version of &lt;strong&gt;ASM&lt;/strong&gt; here, but you can choose a different one with the &lt;code&gt;--revision_name&lt;/code&gt; parameter if required.&lt;/p&gt;

&lt;p&gt;To be executed, the script has some requirements. I invite you to check that &lt;a href="https://cloud.google.com/service-mesh/docs/quickstart-asm#cloud-shell" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The best solution is to use the &lt;strong&gt;Google Cloud Shell&lt;/strong&gt; to do the installation, it fulfills requirements by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;install_asm: Setting up necessary files...
install_asm: Fetching/writing GCP credentials to kubeconfig file...
install_asm: [WARNING]: nc not found, skipping k8s connection verification
install_asm: [WARNING]: (Installation will continue normally.)
install_asm: Checking installation tool dependencies...
install_asm: Getting account information...
install_asm: Confirming cluster information for kevin-anthos-asm/europe-north1-a/anthos-asm-demo...
install_asm: Downloading ASM..
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 41.7M  100 41.7M    0     0  31.5M      0  0:00:01  0:00:01 --:--:-- 31.5M
install_asm: Downloading ASM kpt package...
fetching package "/asm" from "https://github.com/GoogleCloudPlatform/anthos-service-mesh-packages" to "asm"
install_asm: Confirming node pool requirements for kevin-anthos-asm/europe-north1-a/anthos-asm-demo...
install_asm: Checking Istio installations...
install_asm: Enabling required APIs...
install_asm: Binding user:kevin.davin@stack-labs.com to required IAM roles...
install_asm: Checking for project kevin-anthos-asm...
install_asm: Reading labels for europe-north1-a/anthos-asm-demo...
install_asm: Adding labels to europe-north1-a/anthos-asm-demo...
install_asm: Enabling Workload Identity on europe-north1-a/anthos-asm-demo...
install_asm: (This could take awhile, up to 10 minutes)
install_asm: Initializing meshconfig API...
install_asm: Enabling Stackdriver on europe-north1-a/anthos-asm-demo...
install_asm: Querying for core/account...
install_asm: Binding kevin.davin@stack-labs.com to cluster admin role...
clusterrolebinding.rbac.authorization.k8s.io/kevin.davin-cluster-admin-binding created
install_asm: Creating istio-system namespace...
namespace/istio-system created
install_asm: Configuring kpt package...
asm/
set 22 field(s) of setter "gcloud.container.cluster" to value "anthos-asm-demo"
asm/
set 40 field(s) of setter "gcloud.core.project" to value "kevin-anthos-asm"
asm/
set 2 field(s) of setter "gcloud.project.projectNumber" to value "62405001080"
asm/
set 6 field(s) of setter "gcloud.project.environProjectNumber" to value "62405001080"
asm/
set 21 field(s) of setter "gcloud.compute.location" to value "europe-north1-a"
asm/
set 2 field(s) of setter "gcloud.compute.network" to value "kevin-anthos-asm-default"
asm/
set 6 field(s) of setter "anthos.servicemesh.rev" to value "asm-193-2"
asm/
set 2 field(s) of setter "anthos.servicemesh.tag" to value "1.9.3-asm.2"
install_asm: Installing validation webhook fix...
service/istiod created
install_asm: Installing ASM control plane...
install_asm: ...done!
install_asm: Installing ASM CanonicalService controller in asm-system namespace...
namespace/asm-system created
customresourcedefinition.apiextensions.k8s.io/canonicalservices.anthos.cloud.google.com created
role.rbac.authorization.k8s.io/canonical-service-leader-election-role created
clusterrole.rbac.authorization.k8s.io/canonical-service-manager-role created
clusterrole.rbac.authorization.k8s.io/canonical-service-metrics-reader created
serviceaccount/canonical-service-account created
rolebinding.rbac.authorization.k8s.io/canonical-service-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/canonical-service-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/canonical-service-proxy-rolebinding created
service/canonical-service-controller-manager-metrics-service created
deployment.apps/canonical-service-controller-manager created
install_asm: Waiting for deployment...
deployment.apps/canonical-service-controller-manager condition met
install_asm: ...done!
install_asm:
install_asm: *****************************
client version: 1.9.3-asm.2
control plane version: 1.9.3-asm.2
data plane version: 1.9.3-asm.2 (2 proxies)
install_asm: *****************************
install_asm: The ASM control plane installation is now complete.
install_asm: To enable automatic sidecar injection on a namespace, you can use the following command:
install_asm: kubectl label namespace &amp;lt;NAMESPACE&amp;gt; istio-injection- istio.io/rev=asm-193-2 --overwrite
install_asm: If you use 'istioctl install' afterwards to modify this installation, you will need
install_asm: to specify the option '--set revision=asm-193-2' to target this control plane
install_asm: instead of installing a new one.
install_asm: To finish the installation, enable Istio sidecar injection and restart your workloads.
install_asm: For more information, see:
install_asm: https://cloud.google.com/service-mesh/docs/proxy-injection
install_asm: The ASM package used for installation can be found at:
install_asm: /home/kevin_davin/anthos/asm/2021-05-11-apres-midi/asm-downloads/asm
install_asm: The version of istioctl that matches the installation can be found at:
install_asm: /home/kevin_davin/anthos/asm/2021-05-11-apres-midi/asm-downloads/istio-1.9.3-asm.2/bin/istioctl
install_asm: A symlink to the istioctl binary can be found at:
install_asm: /home/kevin_davin/anthos/asm/2021-05-11-apres-midi/asm-downloads/istioctl
install_asm: The combined configuration generated for installation can be found at:
install_asm: /home/kevin_davin/anthos/asm/2021-05-11-apres-midi/asm-downloads/asm-193-2-manifest-raw.yaml
install_asm: The full, expanded set of kubernetes resources can be found at:
install_asm: /home/kevin_davin/anthos/asm/2021-05-11-apres-midi/asm-downloads/asm-193-2-manifest-expanded.yaml
install_asm: *****************************
install_asm: Successfully installed ASM.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will install a custom version of Istio, named &lt;strong&gt;A&lt;/strong&gt;ntos &lt;strong&gt;S&lt;/strong&gt;ervice &lt;strong&gt;M&lt;/strong&gt;esh. At the end, your cluster will have two new namespaces, &lt;code&gt;asm-system&lt;/code&gt; and &lt;code&gt;istio-system&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="nv"&gt;$ &lt;/span&gt;kubectl get ns
NAME              STATUS   AGE
asm-system        Active   127m
default           Active   141m
istio-system      Active   128m
kube-node-lease   Active   141m
kube-public       Active   141m
kube-system       Active   141m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have successfully installed &lt;strong&gt;Anthos Service Mesh&lt;/strong&gt; on your GKE Cluster… we have to use it now!&lt;/p&gt;

&lt;h2&gt;
  
  
  Namespace Activation
&lt;/h2&gt;

&lt;p&gt;Istio is a cluster-wide tool, which can be activated at a namespace level or at a component level (but less common). We have to add a label to our &lt;code&gt;namespace&lt;/code&gt; to trigger Istio functionalities on it:&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;$ &lt;/span&gt;kubectl create namespace workshop
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl label namespace workshop istio.io/rev&lt;span class="o"&gt;=&lt;/span&gt;asm-193-2 &lt;span class="nt"&gt;--overwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;asm-193-2&lt;/code&gt; is the version provided by the &lt;code&gt;install_asm&lt;/code&gt; command logs. With this information, &lt;strong&gt;ASM&lt;/strong&gt; knows it will have to inject side-car container for every component of this namespace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; If you want more details on the installation process, the official documentation is available &lt;a href="https://cloud.google.com/service-mesh/docs/quickstart-asm" rel="noopener noreferrer"&gt;here&lt;/a&gt; and provide a lot of information for various use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functionalities
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Anthos Service Mesh&lt;/strong&gt; is a branded version of Istio. Modifications provided by Google are pretty soft and here mainly to make the system compatible with the cloud console. In &lt;em&gt;Customer-managed control plane&lt;/em&gt;, you have access to every functionality the vast majority of feature provided by Istio 1.9.&lt;/p&gt;

&lt;p&gt;You can consult the complete list of features available &lt;a href="https://cloud.google.com/service-mesh/docs/supported-features" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dashboards
&lt;/h2&gt;

&lt;p&gt;The main advantage of &lt;strong&gt;ASM&lt;/strong&gt; over the OpenSource version of Istio is its integration in the Google Cloud console.&lt;/p&gt;

&lt;p&gt;For this example, I've deployed 3 applications in the &lt;code&gt;workshop&lt;/code&gt; namespace. Those applications came from our Stack Labs Workshop on Istio (accessible &lt;a href="https://istio-in-action.training.stack-labs.com/istio-on-gke/1.3/02_workshop/03_application-bootstrap.html#workshop-resources" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and fully open source). With this, we can use theirs dashboards provided.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global overview
&lt;/h3&gt;

&lt;p&gt;We can have a global point-of-view on our micro-services deployed in our cluster. We have a tabular view, which can be filtered on namespace, providing a clear view of our services status and performance.&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%2F9ubxinjnm7kuc401i5dg.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%2F9ubxinjnm7kuc401i5dg.png" alt="tabular-view" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A global topology view (still in beta 🧪) allowing us to drill down on our services, components, deployments, pods… very useful if we want to understand the communication schema in our 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.amazonaws.com%2Fuploads%2Farticles%2Fv0qwez5eto4ravitcswj.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%2Fv0qwez5eto4ravitcswj.png" alt="topology-raw" width="800" height="521"&gt;&lt;/a&gt;&lt;br&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%2Fqsq44tfkcc2n4uvrxtrw.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%2Fqsq44tfkcc2n4uvrxtrw.png" alt="topology-inside-service" width="800" height="467"&gt;&lt;/a&gt;&lt;br&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%2F7mmcyl9z6ybea8wqig98.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%2F7mmcyl9z6ybea8wqig98.png" alt="topology-inside-deployment" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we want to have deeper understanding on each component, we have access to a service specific view, accessible by clicking on a service on the tabular view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Dashboard
&lt;/h3&gt;

&lt;p&gt;This dashboard will provide a specific point of view on the behaviour of your service. Here, we will focus on the middleware service. For this example, we configure this application to send back 500 errors 50% of the time.&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%2Fya6huuponfig8u8h1jt6.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%2Fya6huuponfig8u8h1jt6.png" alt="middleware-details" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This main view is here to summarize every following dashboards into one. It is an entry point for our service&lt;br&gt;
analysis.&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%2Ff8s3lv9c8t9nkyn1kudn.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%2Ff8s3lv9c8t9nkyn1kudn.png" alt="slo-overview" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Health view is here to present us the &lt;strong&gt;Service Level Objectives&lt;/strong&gt; (SLO) based on &lt;strong&gt;Service Level Indicators&lt;/strong&gt; (SLI) we defined on our service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; If you want to learn more about SLOs and SLIs, this &lt;a href="https://cloud.google.com/blog/products/devops-sre/sre-fundamentals-slis-slas-and-slos" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; summarizes it (a lot) the idea behind it. You can also read the documentation and books provide freely by Google SRE team &lt;a href="https://sre.google/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can define SLOs and SLIs on our service with multiple information gathered by the cloud console and &lt;em&gt;Anthos Service Mesh&lt;/em&gt; for us.&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%2Fa65urdn3a5n0zo46gnj6.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%2Fa65urdn3a5n0zo46gnj6.png" alt="define-sli" width="800" height="661"&gt;&lt;/a&gt;&lt;br&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%2Fozrx932vl4nnxbkvdd70.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%2Fozrx932vl4nnxbkvdd70.png" alt="define-slo" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can define an alerting strategy for each SLO. Multiple systems are available from Slack, Pager-Duty, Email for the most standard to web-hook, cloud function or any other programmating system. I choose email for this example and I receive this after few minutes, because the SLO defined was not reached anymore.&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%2Fdunbo9a3g2s92c5acmqi.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%2Fdunbo9a3g2s92c5acmqi.png" alt="alert by email" width="800" height="1209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have access to a global metrics view of the service. CPU, RAM, requests by seconds… all the required information to be able to follow the health of the application.&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%2Fcqevzfoxmto1p605ywd7.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%2Fcqevzfoxmto1p605ywd7.png" alt="metrics" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can analyse all the connectivity of our service. Here, we have a complete list of every services connecting to our service (&lt;strong&gt;inbound&lt;/strong&gt;) or services reached by our service (&lt;strong&gt;outbound&lt;/strong&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxqkf2hl2qqfsddqyc0fs.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%2Fxqkf2hl2qqfsddqyc0fs.png" alt="inbound" width="800" height="375"&gt;&lt;/a&gt;&lt;br&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%2Fary9gt9w1ijgdt1trzl4.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%2Fary9gt9w1ijgdt1trzl4.png" alt="outbound" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The infrastructure pan allows us to see every instance of our service over time. Each of them has its own metrics (CPU, RAM, error rate…) and so, we can analyse at a fine grain level the performance and behaviour of our system.&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%2F5tlkrdwfy5u3x417tthc.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%2F5tlkrdwfy5u3x417tthc.png" alt="infrastructure" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Security pane allows analysing communication security level between services. Istio and &lt;strong&gt;ASM&lt;/strong&gt; provides a built-in way to communicate with &lt;strong&gt;mTLS&lt;/strong&gt; between components. Here, you will see if exchanges are made "in clear" or with a secure protocol. I didn't configure anything and communications between &lt;em&gt;frontend&lt;/em&gt;, &lt;em&gt;middleware&lt;/em&gt; and &lt;em&gt;database&lt;/em&gt; are secured by default.&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%2Fvyqx027rgdpi4dg83lyz.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%2Fvyqx027rgdpi4dg83lyz.png" alt="security inbound" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, a useful view is available to consult YAML resources deployed in the cluster corresponding to this service. Deployment, VirtualService, DestinationRoute… All resources are available from the web-ui, simplifying analysis again.&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%2Fzeyh48bzj3v4nfgttel9.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%2Fzeyh48bzj3v4nfgttel9.png" alt="resources" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline everywhere!
&lt;/h3&gt;

&lt;p&gt;When you analyze production, especially when a problem occurs, you can't only use &lt;strong&gt;current&lt;/strong&gt; data, you have to compare data with past data of the same system to elaborate a conclusion. Here, in every dashboard I introduce to you, you have the capacity to activate &lt;strong&gt;a timeline&lt;/strong&gt; and narrow down your observations to a specific period of time.&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%2Fzat0rwz6o254btzfh4tg.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%2Fzat0rwz6o254btzfh4tg.png" alt="timeline in metrics" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every table, graph, metrics will be adapted to the given time span to present you the information at a specific moment. This will be convenient when you will want to compare the behavior of a service before and after an upgrade, for example.&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%2Fk37g5sbbdc20hte2i2o0.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%2Fk37g5sbbdc20hte2i2o0.png" alt="timeline in metrics" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This feature is really awesome because you won't have to configure a dashboard for each needs. The system provided to you is made for operator, no need to customize a &lt;em&gt;PromQL&lt;/em&gt; or &lt;em&gt;Grafana&lt;/em&gt; query to analyse what's currently happening in production…&lt;/p&gt;

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

&lt;p&gt;I focused this article mainly on &lt;strong&gt;Observability&lt;/strong&gt;, because &lt;strong&gt;Anthos Service Mesh&lt;/strong&gt; provides it out of the box. Even if &lt;strong&gt;Istio&lt;/strong&gt; has wonderful features for traffic splitting, mirroring, authorization… the first reason you will want to use it for is &lt;strong&gt;Observability&lt;/strong&gt; 🕵️‍♂️.&lt;/p&gt;

&lt;p&gt;Google is now doing with &lt;strong&gt;Istio&lt;/strong&gt; what it did with &lt;strong&gt;Kubernetes&lt;/strong&gt; many years ago. It integrated it and simplify its usage to make it available for everyone with ease. The future version, with a &lt;strong&gt;Google Managed Control Plane&lt;/strong&gt; should simplify it even more.&lt;/p&gt;

&lt;p&gt;If you want to increase your observability with a managed and preconfigured system, I advise you to test &lt;strong&gt;Anthos Service Mesh&lt;/strong&gt;!&lt;/p&gt;

</description>
      <category>istio</category>
      <category>anthos</category>
      <category>googlecloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>Manage your secrets in Git with SOPS for Kubectl &amp; Kustomize 🔧</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Sat, 20 Jun 2020 10:55:53 +0000</pubDate>
      <link>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-for-kubectl-kustomize-45b5</link>
      <guid>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-for-kubectl-kustomize-45b5</guid>
      <description>&lt;p&gt;In previous parts, we see how to manage our secrets in Kubernetes format directly from Git with SOPS. For that we use the standard Kubernetes format, and some SOPS parameters to encrypt only some keys. We will go further here and see how to do that with if we want to use &lt;code&gt;kubectl -k&lt;/code&gt; or &lt;code&gt;kustomize&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DISCLAIMER&lt;/em&gt; If you want to discover &lt;code&gt;kustomize&lt;/code&gt;, I've written an article about it available &lt;a href="https://dev.to/stack-labs/kustomize-the-right-way-to-do-templating-in-kubernetes-3ohp"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Simpler solutions
&lt;/h1&gt;

&lt;p&gt;We will use here the solution provided by &lt;code&gt;kubectl&lt;/code&gt; / &lt;code&gt;kustomize&lt;/code&gt; to generate a secret from a secret generator (see the &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/#creating-a-secret-from-a-generator" rel="noopener noreferrer"&gt;official documentation about it&lt;/a&gt;).&lt;br&gt;
This solution induces two steps, one to decrypt the secret and another to produce the &lt;code&gt;YAML&lt;/code&gt;. &lt;/p&gt;
&lt;h2&gt;
  
  
  📄 With files
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SecretGenerator&lt;/code&gt; allows us to include secret from files. So, we can use &lt;code&gt;SOPS&lt;/code&gt; to decrypt the file, and then we can use the &lt;code&gt;files&lt;/code&gt; directive to include them into manifests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Devon&lt;/code&gt; has the following files:&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="c1"&gt;# kustomization.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;app-secret&lt;/span&gt;
  &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;secret&lt;/span&gt; &lt;span class="c1"&gt;# sops encrypted file&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;another-secret&lt;/span&gt; &lt;span class="c1"&gt;# sops encrypted file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;secret&lt;/code&gt; file before encryption:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TlZSNk1sRk9lR3RwTnpnNVdVWkVZUT09
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And &lt;code&gt;another-secret&lt;/code&gt; file before encryption:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CWNRUt3MPSTX3TizkhX2GVh5pN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After encryption with command &lt;code&gt;sops -i -e secret&lt;/code&gt;, the file look 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="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&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;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ENC[AES256_GCM,data:FUiwHLcn9X1Js+4w5CxQ4Qh+SF3VC4AHGcxT9W3Taqmy,iv:Jh/QcOpcWelFr8cwpv2VtzJQ8/67aam9peVGImHZQdg=,tag:rzGayLJ1Blcymkk7R/Iq9A==,type:str]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sops"&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;"kms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"gcp_kms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"azure_kv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastmodified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:05Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mac"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ENC[AES256_GCM,data:3aHK0NLSTT+XdFi44y4xCAfoXy+1PSc+FVTkBe8EpGCl3HlROoUbdjj/nDJTaAHQBctGg1E2U0pSgY2cIx/tWHR61XCtAZf59CL2pzMNeSRSuwJr1Atqq0ltonk08VWDVQ4DpVWBRvUPl5oxAvZpPmSk0LeuHVGeqXO2cTNa5gs=,iv:zUpUHeXghIs+/9hsHQuZtZ5UcgBzC/sF1ccJ6JHzN/U=,tag:rcwzfioUgN6H4wLcZtcrQw==,type:str]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"pgp"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:04Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA4Hzarga0atVAQv9EQ4B8KqC41L1h4N+1jpApv/wHeDUMzsp1q5VisRndjHU&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;cqmE0YtaJsPbIAMt1cdGZr+koZG5PH25Q6bby4lph3zMFQHZhyrKnGzMftsbCE92&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;DPK64vmGQ1QLpJ/3897acu1NtKJicigi14Dr18ujv9kDG4HV1EeqI8o0ylycpUDr&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;VVctbUP7WNKd23ShXTymVOJjjNgH4fZxoCHXnf5ndEaVKGcM6wLkPO5VtZBI0D1N&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;cyy+UxWV3fZRXqjWx/qFwVGfs9wayRSy16WfSlFCNFzM4bztAxb+bLzNPWVQR4Q3&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Sf7eDPVbPHF2VWvmNsODVkg8kr+flf0yKD2T1BOZ9uc+fQiZW6FQYY7qP9fSgPjk&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;8Bto7rtqbcCqyQE7i7E7Xuw6hVjw8dB3nIUnr5WcLD7uuHOFTfk1YNPFV+DLdT4L&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ch4bFkYLtlAz1EGavZuuGwe6vod/anS3BQyp1dNc34Z4Xoc2fZ7G3oeBCXzVlFK4&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;bYLba8pAfYfVQOoMKepp0l4BtV7JTcdNWB+hCCRfPy1nAmWS8SJYvZqFhPzL9VrY&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;L1kPWZ74Ooz6H1srlnNHR4YmxUHqtkHnkDv2GADZxvq0JPpKSv0fpHUi2cCnPO6d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;7VQQaUr53Gl2blMJJPBg&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=MdW8&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5844C613B763F4374BAB2D2FC735658AB38BF93A"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:04Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA+itJvd1gkukAQv7BzY7dpzpafy1CPpyEaUyZLHkr32KPWypU+x5Mgs1BXFo&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;7+Zo92bmnidK6SAgMTJFJZlZP/81AM9s9VYXkbTmpS6OaKgloPsCe4/TQYA7ktAa&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;c9mRAESQSt1gDX0xupfWqdmS8yUqnAM6bULNvRkwFgV8Fjz9014sE9yM5DZckLfY&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;9FxinBvEfnB2kg/8f82hu7/JIqbvc5uweUCfRglqORHx9L6wrx0suGnBeyWacNB8&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;F3Dw4ICMxOBLrvohomqjGQqX7uzmYF8akZVl1vqtHz6vLRu818NIpap9xuyPMzpJ&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;iXiC+NthfVCAAlKOtUxWMSC+ptZu2JtPaU+WALEwLhAGj26UVsghn1w3u2cEtNwi&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;09odd63jt6vXyT7KXPPyJN5stSuHZnRveN5pmXXY/+ZE4VNQXxBW8XzEW1jXJNWH&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;8153wy1ObJUG5vOftt8L/NjpYEOBh9TRK+W+DIjGXCcunAHJ2MYH4JRQCvZvinZK&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;sw27mdhGe1MTyfPUsLvp0l4BxIvJoO/ac/JcsWwUZBhNDynNzOObL++E0TMoJUmE&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;69Gn3UmPwNcFBVHjZRBZh2IpYL5J0Hp+SBxQtKf/XI5AuLQ3jL504teiXt1YuWf4&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;9JiEUVhwnGheaWIIWHCc&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=cDXu&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AE0D6FD0242FF896BE1E376B62E1E77388753B8E"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:04Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA1ZWtaFCHvLMAQv+NkbxKcDPgNdtTs/n6OaXiFpCgCPRsjuXOmCFaZou33Od&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;tNCPTHeAiMseq+Hxl3c0cdLh8wj1C5Sgqc6OBGaTOki3ZKfWA7H6DyPlbFR2d+af&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;TZRQc6j7uGISJkPRVGQ5X5rJ1BcoT19rhUQm4vr3x3LHHbLvRXccKugiI7fxpDo4&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;rVl7Dc9jhwz8XlV1WdalBeXvupvgxV+6aMqsnt6TFKc0vS/ECSRNoGD/cp2ZnEb8&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;NRgIQXH7jv6cYSl1is7VIHvRIxyCukRznlQc6Bhcu/nDI2+vPJEIdiIX8qgG/AF7&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;lfFPg8S6FALWcjNmCZksInIGltAIaQir0D2RxPfH7JdYu34c0TwgZCjMow1fBMiS&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;82sa9cTWQStGh6lJALTlGWTmZeDtbL22+NX0tG0gtVGlnAmJqZbDCWunL61qrt1n&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;1vvAPiYM/KZJOUibn5/gVyyb8PuVyY5Y0GahiTPPXL5h2gFo11xYpHsA9Ir0r9iB&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;B2/9gyR8dqaI6xoLWJnc0l4BDdKoO+4+hhkkSneEC5FG6p0u0a2E+bjkfNKGuPas&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;EnlIaWEYPh4Ndz2Km/GbII0kOuXvxdt9fxBTmB2k7/weDusUhNYY00sNxLd6rGW4&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;iQeX7+n5ajUO1efzqmXM&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=B2bM&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3FF9E6938905023D25AB56EEFFEFFE9451381735"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:04Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA5/a4uV8wP1EAQv/YmHFgZNe/FgrL9etsny96gztis5qnIDCus+GdZam6j+K&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;mVJ21tt5lqqPi3eXm/1OyEQKBgYjQzjDvLNFJRpQMyxogAgD062jKOOHooZWsGaA&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;uhQ8Pz05pzMAdmCS2YBQmKKBHbzcue8g0iiJCKd/LAr5Ude8VTAa+Z/Re5fbZAK7&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Y7UX/uz7dg0msAF8PyYxovZG907Dsin0FC/ObAcyL5GW24YOWeEHp5VCwpnyWa6v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Vr3uopiWn0n9J1sqhlb9ESq/M5qz8tqBLh1Z0yw8en6Bite+kEiDNW1o16XH4zKA&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;B+SN0R/jS5uutijrneAUOQulYN148W9md1rvbL5U9VT5pJXEKPj55rSogvQkv6tK&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;PH1m4WiEu7GeujO4oTD2gL5k17c5VzSLYDg3QmkYsJqLh0Z6/DvYELTKo9icMhrb&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ZUj2DhTB9RtctJAbfGhZHeRp+V2Lt6at3c/bt7lINAqkEAEUqymrRu9CZwhdDcM9&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;zDoanJrDqAInhUyYo7sq0l4Bbd0eyO2FNqsHTlwASkSNgP3bFdSGL5u2t4WlK+gx&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;yQd/AZO8C1dcd5VWLi90j6gYe/8zrxrfnnzY1E0CvYRL21YxI2lUeS7+7EAnGmXz&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;vw3ZXpnrNX44jC4RZ8FX&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=NTXR&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"57E6DA39E907744429FB07871141FE9F63986243"&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;"unencrypted_suffix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"_unencrypted"&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;"3.5.0"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;another-secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&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;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ENC[AES256_GCM,data:5MqAdBMFuvXolRkAiiRZzEEaOt6Tm7HU1aOd,iv:7Z7DGCzH9pQJfuMKs9/BJWsHNfK2RX+xg4/GZgQXWxg=,tag:uXwTc+2fzXmixc27RuhhCQ==,type:str]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sops"&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;"kms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"gcp_kms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"azure_kv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastmodified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:08Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"mac"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ENC[AES256_GCM,data:HMjZeBiuiN5oW6Z56Sd4O1+1stR7EyO1cNfAmlEUL8XSR97vSJ8mD+It0yZf7Y2/qcTBi1TQS+0Ic6YRL5RycBbRXzCwdVdAUsrQ0netosv1Zcz+jF+sRwzbtJY72hVA16XnAB/vGOBqdAOzzYt60yMI2Hw56AYvK2tnYihGJdk=,iv:bPdVIcUaNifmavFmcHroVU24dj6gqz3jLZHUIKupt5o=,tag:XucX0NIdbXxN3mNXE08Q8Q==,type:str]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"pgp"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:07Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA4Hzarga0atVAQwAmfkQySL6NV4gJL57RKTgxsJA9VYCZeww4vhZcCCNy1pN&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;zJF97UZSjrO9Pn5LZKgAHi4Ao0RANCH5S2+Se8t3t43SktJdN+TEBIG9gWa3WJ4W&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;A0yyR1Xx/bD81ogYmU1CNBxbxF4QO7wZcPtBwhN7whDDLsSvd1ETOR0ZifhhGXDw&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;WMieRpf3VannmCu8BugQBqYILyHCwkL/PFzZhVjBT08Vvy9EJwd0oSOKQ4LajDli&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;hJkiWwyzoyLmwqZDxSOhOhjF3FHc3cowAwrNDnXs0W1k+NZE+97M96Bkd4JcRvdX&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;6K7zD9oKQMT8jUToD6qyzyFGdGOnqh9odTInRDhcWGahbp8vkBsGPWrLFVxboRUy&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;1yT92AbWjfLsmhkPQOfpHZcy5zqg8/lrnuYtuw7i5v3OEGjEOoJA5ddjB16roX3I&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;JbyHCgrO+60dbDLFKDyYyt9kNBwo7tsFwVdoCtLdvs1/y4jwvRGR9udLaapZV/3H&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;zOwFzDpLDblM/eMjIB4G0l4B0dFU0EpjTwX6XDOjf2NVDJYhIu9hwBbqQiTQ2Fhe&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;lfVdSLMBwsgTmYmZsTz6Vm4QdwOY9gfNhZm6wrutJ7ZqKb0mZCYi5qtZIa96C2cG&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;/pRUsrtTnp8gZao/XjAR&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=Lj0c&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5844C613B763F4374BAB2D2FC735658AB38BF93A"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:07Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA+itJvd1gkukAQwAhdEWK1TK+FsnqQAsD5KVCYNxySWfDUqoRgD5QWI6Axnm&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;TXo9d5g5qUsz6NrSjMIMQEZ4injIGj0aezxrrAQ+A5h2KnNNc76/Mhy1HdvrlnRz&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;qhP0pfwyB0MFyNEoD2nJoGONzg69Ob2mMBULsVxja4p+nl7V9xcNe8o7bgfbdobJ&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;YtkgNEJxfahMamQecED8/r7rUhbxxuc3yYx4ijU96dcjpnPl2HpXiD9kUVImMRuQ&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;wGdV1GrUYBYFb5U/PgD24Vz2V1D5hIGifbG5MLa0ddOkiKAlz6IXj8HKbR6nNklK&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;TGhs9FunpGuwgowef3BMwrKmQocvsHRgrv6Tu/sndwh7ThnV2tH0yoYOz3vaM57+&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;1VDGMZSe9QHJjsgaU36lgJBgq4SfzYeWs8zE08QzjZe07iAg6YKms2M+giZ0FDNx&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;sHwtCf6p432NTYokYd1NHS09c8s+fyxr271muNC5Q10+NltVJNRYuqfNPdZ9A9Aw&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;UBvdyLs9X6fSs+0yPCyq0l4BbF3pL5KfR+lRXkwZmqFr5yUfkaykCpF1sIGwHbfj&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;5VAnzcwTgcpSFdc9rqVq4E+HH4V5DZYbXTvqQ9vJGOZeqwUdhYF5NpRTDntzbqoe&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;2PolvaOWzvbCGu+DP4Ga&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=6Tfo&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AE0D6FD0242FF896BE1E376B62E1E77388753B8E"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:07Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA1ZWtaFCHvLMAQv+Ndwgiv2ch5F/3FcIEtzqlxoq4yuy4L/xzD9Brhoqfgqx&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ZPQsrSIez93SlR7JBBGmSaPZiCeUtNuQapi70f25LugGb3HHNVs6Brss05AabTUJ&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;WMNjlMBl7uk03bGtx83xNoJAnI8BGzkmrE5JlY3bYE+fTfzxY3CP8L0Tf/7lzbKy&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;9uTF3Jf3sWWX0bvSJY7KKuELImTJynfBj8z29DKl6Tmt15LbwVGpqSknhsuZBMBx&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;EI9JmwfPnA233jAz+f6QCDodx4GDv9+Ak5CJL9NleMBGJdG6VONrKRe8a8yNCI1k&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Vyqgx9oWzpEZK9Go4eBIrbv9GpnyRDo7ULX6luqAeys0co0Cp8N/f8T20K24vEm+&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Ghi1dIwcIj3gyFZZ+o7OkI/355CAdmYAldyEkBycZMV+oC7r3zyLniLRrTEzrtwt&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;QZCdu/lejBoUK/fWY++L8slZjTL8jVX9lVMHKQZqmC4LMsXtoPv9uS6xlA9I8usp&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;OzhrdGCiapQVfPAvZ3Fa0l4BHpAWInZcfWjYXCWfOfis+ygb/YV4I9BLRFTzRiRR&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Epug1dNDEDX3uYO5BsQBt8qMPMsH7T+XI21081oLIH95UD5iOSiYsu600whQV7XO&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;g6p/T5LaVdoreMBDcyy/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=DSoD&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3FF9E6938905023D25AB56EEFFEFFE9451381735"&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;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-20T10:12:07Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"enc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-----BEGIN PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;hQGMA5/a4uV8wP1EAQv/Z3Z2n4inarsPkhSQllWWG7+O7nDwdOK7c6S01f/eGBQ9&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;bzwhsZmyuYQ9ReKHRpxhbLjYnQBgc8JqDHCDgznZ8aIFp4XQ3zZeZwBzWYX2D+Ok&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;dXt2Ht1yd1zkhMGgrj55yjojnFZRRuNclXFZtypkVFzlU7trrBknlCjQDGIDqOS4&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ZXUQ3IHeDOW7F0+wPxz96i4ZfxcZcOtRhsGGqxrMPAXRZGwgRwdOSJNC1a5iwEsc&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;k9DPcluf9+1Px/eOW2lmExntqkscNCgkjgT4ACsUCmI4p1V/LUrwvkPDPCH0EE4h&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;9MuBKQNuqp+gjjPwR9NOLOtoa/Njsp2V0vZsQ39Y+nfkqtE9w1bAX/R1VDABU+Bm&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ml0WjT0eqGQwnZsBY4pfEvRwIS4JVQCa8sGhZXS4iax0uXgEFh4jma2uWBWKBnGd&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;D9mJ53RC6Xb4eTSkvNtF2VBFtqdu0N587xhykrprxVf7q/s2RXu3haP+JmdJHfMW&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;x2utciyuNIWAGm13u3Cn0l4BK5z9hlBxZfOeGAIyQIWQjYn/ZDG8smHmo8jGUPIR&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;ki/bs+uzo5Fnyk7OJhr0+bkQjG6weVoZJIUxZr61Tb0JhV4+xXaPVMZoZpg96Bii&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;+VWaRWaE+nTfH4wrXwDT&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;=svPU&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;-----END PGP MESSAGE-----&lt;/span&gt;&lt;span class="se"&gt;\n&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;"fp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"57E6DA39E907744429FB07871141FE9F63986243"&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;"unencrypted_suffix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"_unencrypted"&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;"3.5.0"&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;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;After decryption of the secret and execution of the command &lt;code&gt;kubectl apply -k . --dry-run -o yaml&lt;/code&gt; or &lt;code&gt;kustomize build .&lt;/code&gt;, we have the following YAML&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="c1"&gt;# Generated manifest&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;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;another-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Q1dOUlV0M01QU1RYM1RpemtoWDJHVmg1cE4K&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VGxaU05rMXNSazlsUjNSd1RucG5OVmRWV2tWWlVUMDkK&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;app-secret-8m97554t4c&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;default&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;You see here, the &lt;code&gt;secret&lt;/code&gt; and &lt;code&gt;another-secret&lt;/code&gt; keys used in the file &lt;code&gt;kustomization.yaml&lt;/code&gt; (&lt;code&gt;$.secretGenerator[*].files[*]&lt;/code&gt;) are both filenames on the file-system, and keys in the generated manifests (&lt;code&gt;$.data[*]&lt;/code&gt;). This could lead to problems if key name can't be a file (unauthorized characters for example). &lt;/p&gt;
&lt;h2&gt;
  
  
  🗂 With &lt;code&gt;.env&lt;/code&gt; files
&lt;/h2&gt;

&lt;p&gt;A feature available into &lt;code&gt;kustomize&lt;/code&gt; but not yet in &lt;code&gt;kubectl&lt;/code&gt; (see &lt;a href="https://github.com/kubernetes/kubernetes/issues/82905" rel="noopener noreferrer"&gt;issue&lt;/a&gt;) is the possibility to use &lt;code&gt;.env&lt;/code&gt; file as secret source.&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;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;app-secret&lt;/span&gt;
  &lt;span class="na"&gt;envs&lt;/span&gt;&lt;span class="pi"&gt;:&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;The &lt;code&gt;secrets.env&lt;/code&gt; before encryption:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;secret=TlZSNk1sRk9lR3RwTnpnNVdVWkVZUT09
another-secret=CWNRUt3MPSTX3TizkhX2GVh5pN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After encryption with command &lt;code&gt;sops -i -e 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;secret=ENC[AES256_GCM,data:LZxAINIj7/HOIqSJ/M1YTjGmjGIFk24KKEKyXxcFHvQ=,iv:OJNKetoqlJdmYKFpFsfr1ihQjpZ6nJfm8INW474EfE8=,tag:Ea8G+IGH+aORM0sePL9Njw==,type:str]
another-secret=ENC[AES256_GCM,data:OFgAiBkTzEjPwxpo0qn17wvSXm3T164f1Wg=,iv:+kvczR+vGDl6W/UG8KOl++cCKDmx3zA4cGC2vIsge7w=,tag:n7Qbs/I/qo4uy4FfCOMQkg==,type:str]
sops_pgp__list_3__map_fp=57E6DA39E907744429FB07871141FE9F63986243
sops_pgp__list_2__map_enc=-----BEGIN PGP MESSAGE-----\n\nhQGMA1ZWtaFCHvLMAQv/S/gV3liTwmA8Rm3v08kjV39mfBF66mSbjgljoVMvlgoQ\nZ6l2djvzUfzTpgdfACwVVbzQ+KWYfYWnHtOlEHKGQ1abi4Ik/WRS9J6xfzf0aPj0\n9GAy43wCqblvKLKBPHzfYXsQQLP/IA3gHgj1T0hRMx0Nbv+bHi16DEilzxyhCQQf\nzV+w0IMfF1CJrTvTgn4ohaN1KvuQcqOJNlWyr0/fIL420QYL09rQvjA20ds8Jwaq\nZ61O6XOtjyCVAB1nAYfCIOtd0t/+ApnI6Qwe5FKKcN1GrCiqJ4rt4mCK9/nLRcqg\nF1h825h5yuAWYjIyIb8zbulfLxUUN8ztbn4myIUKTcXu+Onq6dq2jaCKOceVc90m\njpXwvcDKYRdeG1HUcmOeQu9ioyt+4xGxOkWY8/hg1YJp9lA+/n4uU5OE9Su0o8LP\n71EzXfISx2PeROkY2W83e8qhzgQQNsCOI9Y5k8Tmd8ctgI4i9Pw0j1S2+sdiD5Y9\njOW7hPVLNiQ/avAkfKVr0lwBZSwkvC3d0Rbw4Wskj8/meRNMYf8DsbFOYi05ysjO\nwTvWQFYPy4cvMtaIvMKn05f+dQIm/6jwOwu6tyytKxqYuq/vEPOrUMQU72wA25eS\nTyXEd7AjE4boT/hQ9g==\n=gUpS\n-----END PGP MESSAGE-----\n
sops_pgp__list_3__map_enc=-----BEGIN PGP MESSAGE-----\n\nhQGMA5/a4uV8wP1EAQv8DQgTw5K4u3E+J9rRxiNK3AvdM+ajifrhZ/NEXu1hgj6g\nMhp8eqQoG1P7/1OJAYL1B20fTatol6oHXgpE7/+f8W6pY+wLfo4d6JSL+KSAAxJk\nWizkR87JxWZFyQB109vgZRDagPylA02yq/2XSgI47JFGVPB14/MAxJkr3O47Si3D\nc3NkqUew8jwPNAToLxM/oCnnDuEoSKmB6smSiL5UHk2/04h3PqbcwUnEPheGR9LK\neA6LVhQ/AiEtbZqAeSU46KGLVHFvQdQVKO+sAHkia21Y041tfS1HDiijTk3UxYTV\nSIB5OKln0qVdDZPYDg4y6Pc/qvj+DHUE+gDoSFOOaGPl1BMw5m9AoY/UQ+Tip66k\nBpG54b20GNmlCuI+7N2QG/lpoML4CGDP2AJ40o6KbZrlo4iwGieg9gdSAstJKlvg\nPpi/p/Fo2zyWin1Gjf/T3QYk5PjbeDkqDqyehToI90qDv8KF7ZDf/t3bz9Geitef\nt9Vg74WCXK/yNcyGAGbC0lwBaqxUf2erAGoTDBkmjjlyEGiEsIgpZzhNj85/G1Mp\n1NxM7zM+5nJGEquzXe/FEoOVX4+LRm1W8+y0T2FRxiOUhlE3anWs+576BYZ+1zZl\nourCUh/MJPpDz3R6Cg==\n=D5A/\n-----END PGP MESSAGE-----\n
sops_pgp__list_1__map_enc=-----BEGIN PGP MESSAGE-----\n\nhQGMA+itJvd1gkukAQv+NZtoRyHQfmOEA2zLM3HaK2HDofMNqcA8Gy66sOfMRwM0\nDvnyu7gcurDE75hYiY4Dd/k5wy+HE7c6fITIWZ62nGAU33RQKgWzZ/vuZX2JnzcA\n8d2XsyoBLe3KGOB75tfepYVkPuRdzDqCY+IH6bHvr9MtNW/CGL58OBXfGTKs1NnI\nSpBWPhF/8Zj0l9S5QkA5ENYGM2u/yutav1z281PNLmZmTrmZ9VYnytwwPX2dY9LS\nupyJnywKqwuw1iaFh7f8BhbuqTjNzAkmjycL8ZFLQB1uGI5CwxCGZ9a3KByNJ6Eh\nH/KVrK5rBBP09ByoIAYoiSBraejoovNFns9O2oUin8HEjv4tziuZ3KeqR51uJCS0\n31nZ/JBlx7GyJ7WGKoVsthlrOebpUuDRbmcKhzhNZT7umTCOYCHdTvGH5p6nMvsE\ngcuuqiTJjrp1WECkr1mMhQqD1Ef708Cw9TAK1LbzdHz5ePBWZ9b+FP0PpBUBHZOc\npWlzd8nYATEK7kQsRbUw0lwB5XQZZKltYrzy1n0dmI98HpqWUpx/f0BX9WRQMkgz\nmbZ8iEQIOBSexNvcM3yA9RMVaTW6M1WoHPGoQdiz4kw4gWtaKqxkK/RlBa6YPnwG\ncrToyqNOO5+HxrSIJg==\n=3esV\n-----END PGP MESSAGE-----\n
sops_pgp__list_1__map_fp=AE0D6FD0242FF896BE1E376B62E1E77388753B8E
sops_pgp__list_2__map_created_at=2020-06-20T10:30:18Z
sops_pgp__list_0__map_created_at=2020-06-20T10:30:18Z
sops_pgp__list_0__map_fp=5844C613B763F4374BAB2D2FC735658AB38BF93A
sops_unencrypted_suffix=_unencrypted
sops_lastmodified=2020-06-20T10:30:19Z
sops_pgp__list_3__map_created_at=2020-06-20T10:30:18Z
sops_pgp__list_2__map_fp=3FF9E6938905023D25AB56EEFFEFFE9451381735
sops_version=3.5.0
sops_pgp__list_0__map_enc=-----BEGIN PGP MESSAGE-----\n\nhQGMA4Hzarga0atVAQv/cIT+IM2097+hQ5NWuod9D7WamqfLTkYeCA+eQqMWRy7l\nBeUGX+JNrh8Wzmq3jAT4NP61/WZiPdX/sqmw/VKd7f+RoLoT5vvT/omHqXgt07Qv\nG0Sp/lz5KZF9d5ujiEmvOTbHZgFhA/8CzLl6rfWLzyHocdV5585vcnRK6RUa8Ezh\nUG9+nLCecq5xIAiqUfWpwdt+bznHAgb9VMW8m8T3UFDnUDa0Y/ankLJ9wIaReoVB\nTCRpJnr5fKxUw4r+kJDFxhnSg21Iw5Rx4rGJHAlDmazXGHazXD5z7f9g3ZiHxrOQ\n6WpoxZw/X/d3697viqe/yemp+CDQJUWQBolhAqfrMN95KllNo7vb7dOpNYG4PFIK\nRLNSTgg+2Ph4mE7AQHTqCdV1jPhtONApcf7CQmkQG/KqyJ5YCfwDLzfo+7+pv1Xx\nhSYEGwMVX+jBIW7ZlLsqcYUaJNQeF3DYNrfgdsTiMB7aGdhohOJvpL/771t37P2s\nyJ/rgu/K8J1InTcEfRPs0lwBeXjohJIpTOL09QgXNSQ0Dsio19VhPjWTr0QI6cAP\nYedVlwrAebeQAOkBlC8RPSUG35CYUqnSRpGHKrl03xCP0Z4TXZ+v60XphehDpdOn\nROm168cPbVF+K2BTTQ==\n=Pdik\n-----END PGP MESSAGE-----\n
sops_mac=ENC[AES256_GCM,data:kXxkiJmgK5nyDiEwStytEHwKLw5JIN1Y1xiNUAXKJLNGEk4VyZ+VkqMNBcUED8HQycHIRle550/Mtov4aVtviM9jM7MnTMIdtg/bp6f0AKiXcl5nCWt26b3JP2nvLN/COMUnvxHxW3uwhdnrsJupBMNDSVdDOv2wXbyazfuor+U=,iv:LR2dRc054UDQoWCVJ+bynri5j5zFKvE69ggLh19sXLY=,tag:nvYpc7ez5Hb4jYLNUnd9Fw==,type:str]
sops_pgp__list_1__map_created_at=2020-06-20T10:30:18Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After decryption of the &lt;code&gt;secrets.env&lt;/code&gt; and execution of the command &lt;code&gt;kustomize build .&lt;/code&gt;, we have the following YAML&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;another-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Q1dOUlV0M01QU1RYM1RpemtoWDJHVmg1cE4=&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VGxaU05rMXNSazlsUjNSd1RucG5OVmRWV2tWWlVUMDk=&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;app-secret-4g57fkmb8b&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;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;Both way can be used without any problem (&lt;code&gt;files&lt;/code&gt; or &lt;code&gt;envs&lt;/code&gt;). This depends of the restriction on tooling you have to generate/deploy your manifests. &lt;/p&gt;

&lt;h1&gt;
  
  
  🔧 Alternative with Kustomize Plugins
&lt;/h1&gt;

&lt;p&gt;The main goal about using the &lt;code&gt;kustomize&lt;/code&gt; plugin system is to be able to remove the step of files decryption before manifests creation. Thanks to that, you will be able to use &lt;code&gt;SOPS&lt;/code&gt; secrets with some tools like skaffold and or others using&lt;code&gt;kustomize&lt;/code&gt; under the hood. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;kustomize&lt;/code&gt; has two plugins system (&lt;a href="https://kubernetes-sigs.github.io/kustomize/guides/plugins/" rel="noopener noreferrer"&gt;official plugin documentation&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;native &lt;code&gt;Go&lt;/code&gt; extension&lt;/li&gt;
&lt;li&gt;exec &lt;code&gt;shell&lt;/code&gt; extension&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This &lt;code&gt;native&lt;/code&gt; solution is very useful because all can be done directly from &lt;code&gt;kustomize&lt;/code&gt; code, and we don't need to have &lt;code&gt;SOPS&lt;/code&gt; installed (for local or CI execution). &lt;/p&gt;

&lt;p&gt;But this solution is complex and requires compiling &lt;code&gt;kustomize&lt;/code&gt; &amp;amp; plugins from sources 😓... with all the Go build system. There is some &lt;code&gt;SOPS&lt;/code&gt; plugins available: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/viaduct-ai/kustomize-sops" rel="noopener noreferrer"&gt;KSOPS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Agilicus/kustomize-sops" rel="noopener noreferrer"&gt;Agilicus/kustomize-sops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/barlik/kustomize-sops" rel="noopener noreferrer"&gt;barlik/kustomize-sops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/monopole/sopsencodedsecrets" rel="noopener noreferrer"&gt;sopsencodedsecrets&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;shell&lt;/code&gt; solution is simpler to manage, but it will require installing &lt;code&gt;SOPS&lt;/code&gt; or other binary to perform decrypt operation. I found only one implementation of shell plugin for &lt;code&gt;kustomize&lt;/code&gt; available: &lt;a href="https://github.com/goabout/kustomize-sopssecretgenerator" rel="noopener noreferrer"&gt;kustomize-sopssecretgenerator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both solutions have another downside, it requires extracting secrets configuration outside the &lt;code&gt;kustomization.yaml&lt;/code&gt; (in a &lt;code&gt;generator.yaml&lt;/code&gt; file), so it multiplies files and make it harder to read / manage 😓. Depending on your needs, I advise you to use &lt;code&gt;KSOPS&lt;/code&gt; or &lt;code&gt;kustomize-sopssecretgenerator&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;We have seen here a “simpler” solution to manage our secret and to use them with Kubernetes relying on the builtin Kustomize system. The plugin ecosystem is still young, so I hope integrations will be simpler in futur. &lt;/p&gt;

&lt;p&gt;You can find the source code of this article, files and scripts in this &lt;a href="https://gitlab.com/davinkevin/sops-blog-post" rel="noopener noreferrer"&gt;GitLab repository&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>security</category>
      <category>sops</category>
    </item>
    <item>
      <title>Manage your secrets in Git with SOPS for Kubernetes ☸️</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Sat, 13 Jun 2020 13:25:56 +0000</pubDate>
      <link>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-for-kubernetes-57me</link>
      <guid>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-for-kubernetes-57me</guid>
      <description>&lt;p&gt;In previous parts, we see how to manage our secrets in Git and usable by humans and CI. Right now, we will discover how&lt;br&gt;
to integrate it with Kubernetes, and especially with &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/" rel="noopener noreferrer"&gt;Kubernetes Secrets&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  😊 Naive usage
&lt;/h1&gt;

&lt;p&gt;Since the beginning, &lt;code&gt;Alice&lt;/code&gt;, &lt;code&gt;Bobby&lt;/code&gt; and &lt;code&gt;Devon&lt;/code&gt; are using only &lt;code&gt;*.env&lt;/code&gt; file to store secrets. They have to use them&lt;br&gt;
in a Kubernetes context, to create a secret named &lt;code&gt;app-secret&lt;/code&gt;. The simple (and naive way) to do this is to use&lt;br&gt;
the &lt;code&gt;kubectl&lt;/code&gt; application to create the secret from an env files: &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;This solution is pretty basic and rely on the capacity of the &lt;code&gt;kubectl&lt;/code&gt; cli to create secret from an &lt;code&gt;*.env&lt;/code&gt; file. The main problem here is the fact we have to generate our secret from the env file. In Kubernetes context, when we have multiple manifests, we like to have the same semantic and organisation, which is simpler to read and debug. &lt;/p&gt;

&lt;h1&gt;
  
  
  🔒 Sops &amp;amp; YAML
&lt;/h1&gt;

&lt;p&gt;To match the team expectation, &lt;code&gt;Devon&lt;/code&gt; will move to a Kubernetes &lt;code&gt;YAML&lt;/code&gt; format for secrets instead of relying on &lt;code&gt;*.env&lt;/code&gt; files. To do so, he will use the output generated from the previous example and use sops to encrypt it.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;The SOPS output is like this:&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;ENC[AES256_GCM,data:gEA=,iv:2jeZ0y0SEPXZTgrsmYXc7LgPydoPwnKzF4GVqykSw5M=,tag:YzEMrctU19sUIqat+LUFIw==,type:str]&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;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENC[AES256_GCM,data:uJIDP3yMdFyW/7bnBAU0MJp07xmF0nlh,iv:ardoVW+p9HLNpaWfXOWrevZFdNJqGJvt00jPbkrr7p4=,tag:mB3wqa7kNE7gZ9J87IboNg==,type:str]&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;ENC[AES256_GCM,data:tu+OHbXI,iv:QfqZeYOkTPTzJ618gh+/zGWPMDBJfjp+GwM8yBZCD+Y=,tag:XrJfNhmyDhOQNncd/uNvxA==,type:str]&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;ENC[AES256_GCM,data:X8WMTByIcUrjag==,iv:pQ6xt5DPchrxwXtt98l4mgCixpcpaA2ewST0lAqYY4E=,tag:fqj/1PbzbhjIhRx9Ogitxg==,type:str]&lt;/span&gt;
&lt;span class="na"&gt;sops&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;gcp_kms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;azure_kv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;lastmodified&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2020-06-13T12:21:16Z'&lt;/span&gt;
    &lt;span class="na"&gt;mac&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENC[AES256_GCM,data:dU1bm2GBwdkqMQqEa+b0EfuMG5BRiiHDxNjB3tvB4JTVxqamQ17A47tzMld93/lZnJWRb4BtDFUI2xHh/5BCpxdI4J67AvWdv91usRgpgh7z7SF4pK4UUVah3WDYd3tvo+VzugvN6b4V+oGZlfJM789dJrTDOjSCAyyaaa/Qfb0=,iv:Cdaet+2Sowq50TDIkDO1RXi4vgaVqe7rYz08PHybAv0=,tag:1aeR/f4NnydiP2Vuuvljvw==,type:str]&lt;/span&gt;
    &lt;span class="na"&gt;pgp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;   &lt;span class="na"&gt;created_at&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2020-06-13T12:21:16Z'&lt;/span&gt;
        &lt;span class="na"&gt;enc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;-----BEGIN PGP MESSAGE-----&lt;/span&gt;

            &lt;span class="s"&gt;hQGMA5/a4uV8wP1EAQwAshoGsWCKiYnsi1Rr/HoMtvp8Tx5cmUWLPz0OsGsQUFc/&lt;/span&gt;
            &lt;span class="s"&gt;HQ48h7KUAjemTBnyQ6SpZUK2uI5YOLnOIqNcQJvNb4o30o0M3kuD2KNzSbsRGt3j&lt;/span&gt;
            &lt;span class="s"&gt;vODVKcs699sT1mX/rc6/EzpLy0NUgrAlXn7IUG+rhnk97OvfMdjP6+i77OpSU9Uc&lt;/span&gt;
            &lt;span class="s"&gt;5l6FWokBzFBvL6EENay6EO54C17MBXijoM4h6x7YajOjk5VNFtUl7wh039VGWZpQ&lt;/span&gt;
            &lt;span class="s"&gt;7vMuTbla06DWAHIFKIOs54yEh+vMTMgnsg8NrPzz4xqKDMcmZoQIm1jwc8EvxYK4&lt;/span&gt;
            &lt;span class="s"&gt;nGI4LpXO8VMgCyHBEcMjCGhZ5xXAiMRzF9M8XAWRfDFgYjDosL8QOQkl4vVnsf4e&lt;/span&gt;
            &lt;span class="s"&gt;5bhITFz1ZGaJwEeVzlt82cxWy4+BNq+lfAghqEnHGAjLGFIgTiRtVB47NLk9WPIT&lt;/span&gt;
            &lt;span class="s"&gt;T7Qv5gvATdW0rNHecfoGCKWx15ZJezXSo8Vtt5eVPgdYd3wO76Uw6XWhkxwSgQNT&lt;/span&gt;
            &lt;span class="s"&gt;vE7W5qvLoqxZrJs4Kyd20l4BQzSEiHJRg7wmWx9Y0BhyzF1Zh6F0oQBxTzGjP0f1&lt;/span&gt;
            &lt;span class="s"&gt;J9THfwIOhyOBwm8bWkEz7ac84o2NEItdrvlhrB0Y61ZfRl6xZKWqFxJ3N1yVHxxM&lt;/span&gt;
            &lt;span class="s"&gt;MWRYzaQcuoh5670lRhY5&lt;/span&gt;
            &lt;span class="s"&gt;=KjAd&lt;/span&gt;
            &lt;span class="s"&gt;-----END PGP MESSAGE-----&lt;/span&gt;
        &lt;span class="na"&gt;fp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;57E6DA39E907744429FB07871141FE9F63986243&lt;/span&gt;
    &lt;span class="na"&gt;unencrypted_suffix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;_unencrypted&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;3.5.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;SOPS is encrypting every value of the previously generated &lt;code&gt;YAML&lt;/code&gt;, including &lt;code&gt;kind&lt;/code&gt; or &lt;code&gt;apiVersion&lt;/code&gt; and &lt;code&gt;metadata.name&lt;/code&gt;. This is problematic because the whole team need to be able to read keys, which are not sensible in this Kubernetes secret. &lt;/p&gt;
&lt;h1&gt;
  
  
  🔏 Encrypt specific keys only
&lt;/h1&gt;

&lt;p&gt;SOPS provides an handy parameter to choose which keys should be encrypted. This could be used directly from the command line &lt;code&gt;sops --encrypt --encrypted-regex '^(data|stringData)$' app-secret.yaml&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt;: For concision, I've removed a part of the generated files. SOPS parameters are included into the YAML and are verbose.&lt;/p&gt;

&lt;p&gt;The file looks like this now:&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;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENC[AES256_GCM,data:RLfYML8bJ0d5kN0nudgGgJJQIJMHK9MB,iv:6rurG5rErwn2O7PrHVoPjRnkMT5MjenhRmF3Vxh/cVw=,tag:4B5NeHbTmm0yl5VFlA9fww==,type:str]&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;app-secret&lt;/span&gt;
&lt;span class="na"&gt;sops&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;gcp_kms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;azure_kv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
    &lt;span class="na"&gt;lastmodified&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2020-06-13T12:32:40Z'&lt;/span&gt;
    &lt;span class="na"&gt;mac&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENC[AES256_GCM,data:jl00u8YtegEi3lvuHv70PtI6Zywo8tSDH2DKk2jw+b4yiUGU2y9T82t3bE6ahY1MWy1U6CRK9NYKBS8p5HJqDjH/N21rhQy7k3g4BuzG/CJt2S/xio0KaAwwGXURUtXbHpXLrko22q0cgqCrsr/IFvz65r6WNhji0utSC1FKCPQ=,iv:XoilcDIZR8ir/ySbu+xVGNO0NkEkQr/IlyKXvAyyeyM=,tag:Hraabep8cq85M3OjvjoQBA==,type:str]&lt;/span&gt;
    &lt;span class="na"&gt;pgp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# Removed for concision&lt;/span&gt;
    &lt;span class="na"&gt;encrypted_regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^(data|stringData)$&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;3.5.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can do better, because SOPS provides us a solution to store configuration required for every secret directly into the &lt;code&gt;.sops.yaml&lt;/code&gt;. We can add the key &lt;code&gt;encrypted-regex&lt;/code&gt; to simplify the command line. This is helpful when you need to add another secret key in an existing secret.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;Every team member can now read and edit Kubernetes secret simply 🎉.&lt;/p&gt;

&lt;h1&gt;
  
  
  🦊 CI Integration
&lt;/h1&gt;

&lt;p&gt;The CI needs to be adjusted to be able to deploy those manifests. We are using the base described in &lt;a href="https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-gitlab-ci-2jnd"&gt;the previous article&lt;/a&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;deploy int&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="s"&gt;registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*auth&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sops -d app-secret.yaml | kubectl apply -f -&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If you are using Kubernetes, and you want to manage your secrets in Git, SOPS will be a good match ❤️. You will be able to encrypt only desired value in a &lt;code&gt;YAML&lt;/code&gt; file to keep it readable, by a human and other tools working on Kubernetes manifests. &lt;/p&gt;

&lt;p&gt;You can find the source code of this article, files and scripts in this &lt;a href="https://gitlab.com/davinkevin/sops-blog-post" rel="noopener noreferrer"&gt;GitLab repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sops</category>
      <category>kubernetes</category>
      <category>git</category>
      <category>devops</category>
    </item>
    <item>
      <title>Manage your secrets in Git with SOPS &amp; GitLab CI 🦊</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Sat, 06 Jun 2020 12:50:18 +0000</pubDate>
      <link>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-gitlab-ci-2jnd</link>
      <guid>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-gitlab-ci-2jnd</guid>
      <description>&lt;p&gt;In this series, we saw how to create and manage our secrets in git and restrict their access to team members. &lt;/p&gt;

&lt;p&gt;What if I want do use my secret from my CI for Continuous Deployment? This is exactly what we will see in this article, based on &lt;a href="https://docs.gitlab.com/ee/ci/" rel="noopener noreferrer"&gt;GitLab CI 🦊&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt;: Every CI system should be compatible, you just need to have access to CI secrets and be able to fetch them during execution. &lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 Creation of the CI identity
&lt;/h2&gt;

&lt;p&gt;To be able to decrypt secrets within Continuous Integration, we should create an identity for our CI. &lt;code&gt;Alice&lt;/code&gt;, &lt;code&gt;Bobby&lt;/code&gt; and &lt;code&gt;Devon&lt;/code&gt; will have a new friend named &lt;code&gt;CI&lt;/code&gt; 🤖 ! &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Devon&lt;/code&gt;, in charge of the CI, will create a new key-pair for this new special user. He will use the &lt;code&gt;gpg --full-gen-key&lt;/code&gt; command and answer questions like if it was for a real human.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;Then, he will extract a backup of the private/public key with the command &lt;code&gt;gpg -o ci.key --armor --export-secret-keys ci@domain.fr&lt;/code&gt;. This key is in a textual format, which is easy to use as an environment variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: This key and the passphrase should be stored securely, in a keypass, vault or something else, because they represent the identity of your CI. By design, it will be able to decrypt all secrets from the repository.&lt;/p&gt;

&lt;p&gt;We also need to extract the public key separately to distribute it to every team members. Without it, they won't be able to encrypt a secret and include the &lt;code&gt;CI&lt;/code&gt; key in the process. &lt;/p&gt;

&lt;p&gt;To do so, &lt;code&gt;Devon&lt;/code&gt; will use the command &lt;code&gt;gpg -o ci.public.key --armor --export&lt;/code&gt;.    &lt;/p&gt;

&lt;h2&gt;
  
  
  🔑 Import public key of CI
&lt;/h2&gt;

&lt;p&gt;Each team member should import the public key extracted by &lt;code&gt;Devon&lt;/code&gt; in the previous step. Here, &lt;code&gt;Booby&lt;/code&gt; will use the &lt;code&gt;gpg --import ci.public.key&lt;/code&gt; command to perform this. &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;At the end, he, and every other team members should have in their key list (&lt;code&gt;gpg --list-keys&lt;/code&gt;) the public key of &lt;code&gt;CI&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔏 Update keys of every secret
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CI&lt;/code&gt; is a new team member, as any other human. The team has to let this new "person" access and decrypt every secret. So we will use the &lt;code&gt;sops updatekeys &amp;lt;secret&amp;gt;&lt;/code&gt; on every secret.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;This requires a correctly configured &lt;code&gt;.sops.yaml&lt;/code&gt; file. For more information about this, you can read the &lt;a href="https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-common-operations-118g"&gt;second part&lt;/a&gt; of this series.  &lt;/p&gt;

&lt;h2&gt;
  
  
  🦊 GitLab CI Configuration
&lt;/h2&gt;

&lt;p&gt;Now, the CI is ready to be configured. &lt;code&gt;Devon&lt;/code&gt;, in charge of this, will import into GitLab secret pane (in &lt;em&gt;Settings&lt;/em&gt; &amp;gt; &lt;em&gt;CI/CD&lt;/em&gt; &amp;gt; &lt;em&gt;Variables&lt;/em&gt;) the content of the &lt;code&gt;ci.key&lt;/code&gt; created previously and the passphrase defined for it.&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%2Fi%2Ff51lanyy7mfvm24hoe1y.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%2Fi%2Ff51lanyy7mfvm24hoe1y.png" alt="passphrase &amp;amp; key in gitlab parameters" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The variable &lt;code&gt;Type&lt;/code&gt; is defined to &lt;code&gt;File&lt;/code&gt; for the &lt;code&gt;KEY&lt;/code&gt;. For more information about this, look at &lt;a href="https://docs.gitlab.com/ee/ci/variables/#custom-environment-variables-of-type-file" rel="noopener noreferrer"&gt;the official GitLab documentation about file variable&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, &lt;code&gt;Devon&lt;/code&gt; will define a GitLab CI job able to read the value from the encrypted secret. For our example, this will just do a &lt;code&gt;cat&lt;/code&gt; on the standard output. Of course, you can do what you want with those values in your CI pipeline.&lt;/p&gt;

&lt;p&gt;We need to take care of the &lt;code&gt;image&lt;/code&gt; used in our Continuous Integration. We should be able to access the &lt;code&gt;gpg&lt;/code&gt; command. In this example, we will install it.&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;deploy int&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="s"&gt;google/cloud-sdk&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;1&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y curl gnupg&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;2&amp;gt;&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl -qsL https://github.com/mozilla/sops/releases/download/v3.5.0/sops-v3.5.0.linux -o /usr/local/bin/sops&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;3&amp;gt;&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chmod +x /usr/local/bin/sops&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat $KEY | gpg --batch --import&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;4&amp;gt; &lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo $PASSPHRASE | gpg --batch --always-trust --yes --passphrase-fd 0 --pinentry-mode=loopback -s $(mktemp)&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;5&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sops -d int.encrypted.env &amp;gt; int.env&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;6&amp;gt;&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat int.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Define a job image based required for our deploy phase... it should fit team needs, this is just for example here.&lt;/li&gt;
&lt;li&gt;We install &lt;code&gt;gpg&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt; thanks to &lt;code&gt;apt&lt;/code&gt; on debian distribution. Should be adapted if you are using another distribution.&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;curl&lt;/code&gt; to download &lt;code&gt;sops&lt;/code&gt; binary from GitHub. Take care to get the latest version 😉.&lt;/li&gt;
&lt;li&gt;Import the &lt;code&gt;ci.key&lt;/code&gt; into our &lt;code&gt;gpg&lt;/code&gt; toolchain.&lt;/li&gt;
&lt;li&gt;Provide the &lt;code&gt;PASSPHRASE&lt;/code&gt; to the &lt;code&gt;gpg&lt;/code&gt; toolchain to be totally "not interactive".&lt;/li&gt;
&lt;li&gt;Then, the CI can decrypt the &lt;code&gt;int.encrypted.env&lt;/code&gt; file and use it where we want in our deploy phase.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running with gitlab-runner 13.0.0 (c127439c)
   on docker-auto-scale 0277ea0f
Preparing the "docker+machine" executor
 Using Docker executor with image google/cloud-sdk ...
 Pulling docker image google/cloud-sdk ...
 Using docker image sha256:5d096c48b3b4bab72df240dcf94940be3e0397606bb4cc94143f2a12189dba1f for google/cloud-sdk ...
Preparing environment
 Running on runner-0277ea0f-project-19225532-concurrent-0 via runner-0277ea0f-srm-1591443365-4e9ad7c3...
Getting source from Git repository
 $ eval "$CI_PRE_CLONE_SCRIPT"
 Fetching changes with git depth set to 50...
 Initialized empty Git repository in /builds/davinkevin/sops-blog-post-repository/.git/
 Created fresh repository.
 From https://gitlab.com/davinkevin/sops-blog-post-repository
  * [new ref]         refs/pipelines/153546622 -&amp;gt; refs/pipelines/153546622
  * [new branch]      master                   -&amp;gt; origin/master
 Checking out 84edc518 as master...
 Skipping Git submodules setup
Restoring cache
00:02
Downloading artifacts
00:01
Running before_script and script
 $ apt-get update &amp;amp;&amp;amp; apt-get install -y curl gnupg
 Hit:1 http://deb.debian.org/debian buster InRelease
 Get:2 http://deb.debian.org/debian buster-updates InRelease [49.3 kB]
 Get:3 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
 Hit:4 https://packages.cloud.google.com/apt cloud-sdk-buster InRelease
 Get:5 http://deb.debian.org/debian sid InRelease [146 kB]
 Get:6 http://security.debian.org/debian-security buster/updates/main amd64 Packages [201 kB]
 Get:7 http://deb.debian.org/debian sid/main amd64 Packages.diff/Index [27.9 kB]
 Get:8 http://deb.debian.org/debian sid/main amd64 Packages 2020-06-05-2003.49.pdiff [23.8 kB]
 Get:9 http://deb.debian.org/debian sid/main amd64 Packages 2020-06-06-0208.52.pdiff [16.2 kB]
 Get:10 http://deb.debian.org/debian sid/main amd64 Packages 2020-06-06-0803.34.pdiff [12.5 kB]
 Get:10 http://deb.debian.org/debian sid/main amd64 Packages 2020-06-06-0803.34.pdiff [12.5 kB]
 Fetched 543 kB in 2s (243 kB/s)
 Reading package lists...
 Reading package lists...
 Building dependency tree...
 Reading state information...
 The following packages were automatically installed and are no longer required:
   dh-python libperl5.28 libpython3.7 libpython3.7-minimal libpython3.7-stdlib
   perl-modules-5.28 python3.7-minimal
 Use 'apt autoremove' to remove them.
 The following additional packages will be installed:
   dirmngr gnupg-l10n gnupg-utils gpg gpg-agent gpg-wks-client gpg-wks-server
   gpgconf gpgsm gpgv libbrotli1 libcurl4
 Suggested packages:
   dbus-user-session libpam-systemd pinentry-gnome3 tor parcimonie xloadimage
   scdaemon
 The following NEW packages will be installed:
   libbrotli1
 The following packages will be upgraded:
   curl dirmngr gnupg gnupg-l10n gnupg-utils gpg gpg-agent gpg-wks-client
   gpg-wks-server gpgconf gpgsm gpgv libcurl4
 13 upgraded, 1 newly installed, 0 to remove and 132 not upgraded.
 Need to get 8559 kB of archives.
 After this operation, 1241 kB of additional disk space will be used.
 Get:1 http://deb.debian.org/debian sid/main amd64 gpg-wks-client amd64 2.2.20-1 [507 kB]
 Get:2 http://deb.debian.org/debian sid/main amd64 dirmngr amd64 2.2.20-1 [740 kB]
 Get:3 http://deb.debian.org/debian sid/main amd64 gnupg-utils amd64 2.2.20-1 [889 kB]
 Get:4 http://deb.debian.org/debian sid/main amd64 gpg-wks-server amd64 2.2.20-1 [500 kB]
 Get:5 http://deb.debian.org/debian sid/main amd64 gpg-agent amd64 2.2.20-1 [641 kB]
 Get:6 http://deb.debian.org/debian sid/main amd64 gpg amd64 2.2.20-1 [894 kB]
 Get:7 http://deb.debian.org/debian sid/main amd64 gpgconf amd64 2.2.20-1 [532 kB]
 Get:8 http://deb.debian.org/debian sid/main amd64 gnupg-l10n all 2.2.20-1 [1035 kB]
 Get:9 http://deb.debian.org/debian sid/main amd64 gnupg all 2.2.20-1 [749 kB]
 Get:10 http://deb.debian.org/debian sid/main amd64 gpgsm amd64 2.2.20-1 [627 kB]
 Get:11 http://deb.debian.org/debian sid/main amd64 gpgv amd64 2.2.20-1 [608 kB]
 Get:12 http://deb.debian.org/debian sid/main amd64 libbrotli1 amd64 1.0.7-6.1 [267 kB]
 Get:13 http://deb.debian.org/debian sid/main amd64 curl amd64 7.68.0-1 [249 kB]
 Get:14 http://deb.debian.org/debian sid/main amd64 libcurl4 amd64 7.68.0-1 [321 kB]
 debconf: delaying package configuration, since apt-utils is not installed
 Fetched 8559 kB in 0s (22.3 MB/s)
 (Reading database ... 85863 files and directories currently installed.)
 Preparing to unpack .../00-gpg-wks-client_2.2.20-1_amd64.deb ...
 Unpacking gpg-wks-client (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../01-dirmngr_2.2.20-1_amd64.deb ...
 Unpacking dirmngr (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../02-gnupg-utils_2.2.20-1_amd64.deb ...
 Unpacking gnupg-utils (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../03-gpg-wks-server_2.2.20-1_amd64.deb ...
 Unpacking gpg-wks-server (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../04-gpg-agent_2.2.20-1_amd64.deb ...
 Unpacking gpg-agent (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../05-gpg_2.2.20-1_amd64.deb ...
 Unpacking gpg (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../06-gpgconf_2.2.20-1_amd64.deb ...
 Unpacking gpgconf (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../07-gnupg-l10n_2.2.20-1_all.deb ...
 Unpacking gnupg-l10n (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../08-gnupg_2.2.20-1_all.deb ...
 Unpacking gnupg (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../09-gpgsm_2.2.20-1_amd64.deb ...
 Unpacking gpgsm (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Preparing to unpack .../10-gpgv_2.2.20-1_amd64.deb ...
 Unpacking gpgv (2.2.20-1) over (2.2.12-1+deb10u1) ...
 Setting up gpgv (2.2.20-1) ...
 Selecting previously unselected package libbrotli1:amd64.
 (Reading database ... 85881 files and directories currently installed.)
 Preparing to unpack .../libbrotli1_1.0.7-6.1_amd64.deb ...
 Unpacking libbrotli1:amd64 (1.0.7-6.1) ...
 Preparing to unpack .../curl_7.68.0-1_amd64.deb ...
 Unpacking curl (7.68.0-1) over (7.64.0-4+deb10u1) ...
 Preparing to unpack .../libcurl4_7.68.0-1_amd64.deb ...
 Unpacking libcurl4:amd64 (7.68.0-1) over (7.64.0-4+deb10u1) ...
 Setting up libbrotli1:amd64 (1.0.7-6.1) ...
 Setting up gnupg-l10n (2.2.20-1) ...
 Setting up gpgconf (2.2.20-1) ...
 Setting up libcurl4:amd64 (7.68.0-1) ...
 Setting up curl (7.68.0-1) ...
 Setting up gpg (2.2.20-1) ...
 Setting up gnupg-utils (2.2.20-1) ...
 Setting up gpg-agent (2.2.20-1) ...
 Installing new version of config file /etc/logcheck/ignore.d.server/gpg-agent ...
 Setting up gpgsm (2.2.20-1) ...
 Setting up dirmngr (2.2.20-1) ...
 Setting up gpg-wks-server (2.2.20-1) ...
 Setting up gpg-wks-client (2.2.20-1) ...
 Setting up gnupg (2.2.20-1) ...
 Processing triggers for libc-bin (2.30-8) ...
 $ curl -qsL https://github.com/mozilla/sops/releases/download/v3.5.0/sops-v3.5.0.linux -o /usr/local/bin/sops
 $ chmod +x /usr/local/bin/sops
 $ cat $KEY | gpg --batch --import
 gpg: directory '/root/.gnupg' created
 gpg: keybox '/root/.gnupg/pubring.kbx' created
 gpg: /root/.gnupg/trustdb.gpg: trustdb created
 gpg: key FFEFFE9451381735: public key "continuous-integration &amp;lt;ci@domain.fr&amp;gt;" imported
 gpg: key FFEFFE9451381735: secret key imported
 gpg: Total number processed: 1
 gpg:               imported: 1
 gpg:       secret keys read: 1
 gpg:   secret keys imported: 1
 $ echo $PASSPHRASE | gpg --batch --always-trust --yes --passphrase-fd 0 --pinentry-mode=loopback -s $(mktemp)
 $ sops -d int.encrypted.env &amp;gt; int.env
 $ cat int.env
 secret=5Tz2QNxki789YFDa
Running after_script
00:01
Saving cache
00:02
Uploading artifacts for successful job
00:01
 Job succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like we see here (and in the &lt;a href="https://gitlab.com/davinkevin/sops-blog-post-repository/-/jobs/584226660" rel="noopener noreferrer"&gt;GitLab job view&lt;/a&gt;), every time the pipeline is triggered, it will re-install every software, from &lt;code&gt;gpg&lt;/code&gt; to &lt;code&gt;sops&lt;/code&gt;, which can be time-consuming and source of error. &lt;/p&gt;

&lt;h2&gt;
  
  
  🦊 GitLab CI Optimization
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Devon&lt;/code&gt; will create another project just for &lt;code&gt;CI&lt;/code&gt; tooling image, and he will prepare the image of the CI to be able to have &lt;code&gt;gpg&lt;/code&gt; and &lt;code&gt;sops&lt;/code&gt; installed. Here is for example, the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;tutum/curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;downloader&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-qsL&lt;/span&gt; https://github.com/mozilla/sops/releases/download/v3.5.0/sops-v3.5.0.linux &lt;span class="nt"&gt;-o&lt;/span&gt; /opt/sops &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /opt/sops

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;google/cloud-sdk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;final&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=downloader /opt/sops /usr/local/bin/sops&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gnupg &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, he will activate a schedule to build this image every day and publish it in their own docker registry. Thanks to this, he can improve the previous &lt;code&gt;.gitlab-ci.yml&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;deploy int&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="s"&gt;registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops&lt;/span&gt; &lt;span class="c1"&gt;# Image created by the previous Dockerfile 🐳&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat $KEY | gpg --batch --import&lt;/span&gt; 
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo $PASSPHRASE | gpg --batch --always-trust --yes --passphrase-fd 0 --pinentry-mode=loopback -s $(mktemp)&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sops -d int.encrypted.env &amp;gt; int.env&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat int.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see, the job execution is much straightforward now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running with gitlab-runner 13.0.0 (c127439c)
   on docker-auto-scale ed2dce3a
Preparing the "docker+machine" executor
 Using Docker executor with image registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops ...
 Authenticating with credentials from job payload (GitLab Registry)
 Pulling docker image registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops ...
 Using docker image sha256:ec7e941ae9f66656c9c0b7b4ce61b229eeb215492c65ebfe21c69026aa166013 for registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops ...
Preparing environment
00:05
 Running on runner-ed2dce3a-project-19225532-concurrent-0 via runner-ed2dce3a-srm-1591446538-6f33846d...
Getting source from Git repository
00:03
 $ eval "$CI_PRE_CLONE_SCRIPT"
 Fetching changes with git depth set to 50...
 Initialized empty Git repository in /builds/davinkevin/sops-blog-post-repository/.git/
 Created fresh repository.
 From https://gitlab.com/davinkevin/sops-blog-post-repository
  * [new ref]         refs/pipelines/153552640 -&amp;gt; refs/pipelines/153552640
  * [new branch]      master                   -&amp;gt; origin/master
 Checking out 625b7479 as master...
 Skipping Git submodules setup
Restoring cache
00:01
Downloading artifacts
00:02
Running before_script and script
00:04
 Authenticating with credentials from job payload (GitLab Registry)
 $ cat $KEY | gpg --batch --import
 gpg: directory '/root/.gnupg' created
 gpg: keybox '/root/.gnupg/pubring.kbx' created
 gpg: /root/.gnupg/trustdb.gpg: trustdb created
 gpg: key FFEFFE9451381735: public key "continuous-integration &amp;lt;ci@domain.fr&amp;gt;" imported
 gpg: key FFEFFE9451381735: secret key imported
 gpg: Total number processed: 1
 gpg:               imported: 1
 gpg:       secret keys read: 1
 gpg:   secret keys imported: 1
 $ echo $PASSPHRASE | gpg --batch --always-trust --yes --passphrase-fd 0 --pinentry-mode=loopback -s $(mktemp)
 $ sops -d int.encrypted.env &amp;gt; int.env
 $ cat int.env
 secret=5Tz2QNxki789YFDa
Running after_script
00:02
Saving cache
00:01
Uploading artifacts for successful job
00:02
 Job succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can use this method with any docker registry, not only the one from GitLab 🦊.&lt;/p&gt;

&lt;p&gt;Of course, the &lt;code&gt;before_script&lt;/code&gt; should be copy/paste for every job requiring access to secrets. &lt;code&gt;Devon&lt;/code&gt; will refactor this to optimize the &lt;code&gt;Don't Repeat Yourself&lt;/code&gt; aspect:&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;.auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;auth&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;1&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;function gpg_auth() {&lt;/span&gt;
    &lt;span class="s"&gt;cat $KEY | gpg --batch --import &amp;gt; /dev/null&lt;/span&gt;
    &lt;span class="s"&gt;echo $PASSPHRASE | gpg --batch --always-trust --yes --passphrase-fd 0 --pinentry-mode=loopback -s $(mktemp) &amp;gt; /dev/null&lt;/span&gt;
  &lt;span class="s"&gt;}&lt;/span&gt;

  &lt;span class="s"&gt;gpg_auth&lt;/span&gt;

&lt;span class="na"&gt;deploy alice&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="s"&gt;registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*auth&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;2&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sops -d dev_a.encrypted.env &amp;gt; dev_a.env&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat dev_a.env&lt;/span&gt;

&lt;span class="na"&gt;deploy int&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="s"&gt;registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*auth&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;3&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sops -d int.encrypted.env &amp;gt; int.env&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cat int.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;YAML&lt;/code&gt; anchor used to set a custom shell function and calling it right away.&lt;/li&gt;
&lt;li&gt;Usage of the anchor as a &lt;code&gt;before_script&lt;/code&gt; step in the job.&lt;/li&gt;
&lt;li&gt;Re-use of the &lt;code&gt;auth&lt;/code&gt; anchor in another job.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see &lt;a href="https://gitlab.com/davinkevin/sops-blog-post-repository/-/jobs/584245508" rel="noopener noreferrer"&gt;&lt;code&gt;deploy alice&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://gitlab.com/davinkevin/sops-blog-post-repository/-/jobs/584245509" rel="noopener noreferrer"&gt;&lt;code&gt;deploy int&lt;/code&gt;&lt;/a&gt; logs in the GitLab UI.&lt;/p&gt;

&lt;p&gt;Of course, this is just an extract of the real &lt;code&gt;.gitlab-ci.yaml&lt;/code&gt;, which includes linting, test, build &amp;amp; cie 👍. &lt;/p&gt;

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

&lt;p&gt;Here, we learn how to configure our CI identity and configure the GitLab CI 🦊 to be able to decrypt secrets. We also see how to optimize it in a GitLab environment leveraging usage of home made docker image. &lt;/p&gt;

&lt;p&gt;You can find the source code of this article, files and scripts in this &lt;a href="https://gitlab.com/davinkevin/sops-blog-post" rel="noopener noreferrer"&gt;GitLab repository&lt;/a&gt;. The CI is triggered in &lt;a href="https://gitlab.com/davinkevin/sops-blog-post-repository" rel="noopener noreferrer"&gt;this GitLab repository&lt;/a&gt; &lt;/p&gt;

</description>
      <category>git</category>
      <category>sops</category>
      <category>devops</category>
      <category>ci</category>
    </item>
    <item>
      <title>Kustomize - The right way to do templating in Kubernetes</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Thu, 04 Jun 2020 05:56:02 +0000</pubDate>
      <link>https://dev.to/stack-labs/kustomize-the-right-way-to-do-templating-in-kubernetes-3ohp</link>
      <guid>https://dev.to/stack-labs/kustomize-the-right-way-to-do-templating-in-kubernetes-3ohp</guid>
      <description>&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: this article was originally published on &lt;a href="https://blog.stack-labs.com/code/kustomize-101/" rel="noopener noreferrer"&gt;Stack Labs blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We always need to customize our deployment with Kubernetes and, I don't know why but the main tool around for now is HELM which throws away all the logic we learn on docker and Kubernetes. &lt;br&gt;
Here I will introduce to you an alternative called &lt;strong&gt;Kustomize&lt;/strong&gt; ❤️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kubernetes-sigs/kustomize" rel="noopener noreferrer"&gt;Kustomize&lt;/a&gt; isn't a new tool, it is under construction since 2017 and has been introduced as a native &lt;code&gt;kubectl&lt;/code&gt; sub-command in the version &lt;code&gt;1.14&lt;/code&gt;. Yeah, you've heard correctly, this is now embedded directly inside the tool you use everyday... so you will be able to throw that &lt;code&gt;helm&lt;/code&gt; command away 😉. &lt;/p&gt;
&lt;h2&gt;
  
  
  Philosophy
&lt;/h2&gt;

&lt;p&gt;Kustomize tries to follow the philosophy you are using in your everyday job when using Git as VCS, creating Docker images or declaring your resources inside Kubernetes.&lt;/p&gt;

&lt;p&gt;So, first of all, Kustomize is like Kubernetes, it is totally &lt;strong&gt;declarative&lt;/strong&gt; ! &lt;br&gt;
You say what you want and the system provides it to you. You don't have to follow the &lt;strong&gt;imperative&lt;/strong&gt; way and describe how you want it to build the thing.&lt;/p&gt;

&lt;p&gt;Secondly, it works like Docker. You have many layers and each of those is modifying the previous ones. &lt;br&gt;
Thanks to that, you can constantly write things above others without adding complexity inside your configuration. &lt;br&gt;
The result of the build will be the addition of the base and the different layers you applied over it.&lt;/p&gt;

&lt;p&gt;Lastly, like Git, you can use a remote base as the start of your work and add some customization on it.  &lt;/p&gt;
&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Of course, for 🍎 Mac users, you can use &lt;code&gt;brew&lt;/code&gt; to install it :&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;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;kustomize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are on another operating system, you can directly download the binary from the &lt;a href="https://github.com/Kubernetes-sigs/kustomize/blob/master/docs/INSTALL.md" rel="noopener noreferrer"&gt;release page&lt;/a&gt; and add it to your path.&lt;/p&gt;

&lt;p&gt;For the others, you also can build it from source, why not 😅.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your base
&lt;/h2&gt;

&lt;p&gt;To start with Kustomize, you need to have your original yaml files describing any resources you want to deploy into your cluster. &lt;br&gt;
Those files will be stored for this example in the folder &lt;code&gt;./k8s/base/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those files will NEVER (EVER) be touched, we will just apply customization above them to create new resources definitions &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can build base templates (e.g. for dev environment) at any point in time using the command kubectl apply -f ./k8s/base/.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this example, we will work with a &lt;code&gt;service&lt;/code&gt; and a &lt;code&gt;deployment&lt;/code&gt; resources:&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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;sl-demo-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;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;sl-demo-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;sl-demo-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;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;foo/bar: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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&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;8080&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We wil add a new file inside this folder, named &lt;code&gt;kustomization.yaml&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;apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - service.yaml
  - deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file will be the central point of your base and it describes the resources you use. &lt;br&gt;
Those resources are the path to the files relatively to the current file. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This &lt;code&gt;kustomization.yaml&lt;/code&gt; file could lead to errors when running &lt;code&gt;kubectl apply -f ./k8s/base/&lt;/code&gt;, you can either run it with the parameter &lt;code&gt;--validate=false&lt;/code&gt; or simply not running the command against the whole folder&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To apply your base template to your cluster, you just have to execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl apply -k k8s/base
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see what will be applied in your cluster, we will mainly use in this article the command &lt;code&gt;kustomize build&lt;/code&gt; instead of &lt;code&gt;kubectl apply -k&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The result of &lt;code&gt;kustomize build k8s/base&lt;/code&gt; command will be the following, which is for now only the two files previously seen, concatenated:&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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sl-demo-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;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;sl-demo-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;sl-demo-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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo/bar:latest&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;app&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;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;K&lt;/strong&gt;ustomization
&lt;/h2&gt;

&lt;p&gt;Now, we want to kustomize our app for a specific case, for example, for our &lt;code&gt;prod&lt;/code&gt; environement. &lt;br&gt;
In each step, we will see how to enhance our base with some modification. &lt;/p&gt;

&lt;p&gt;The main goal of this article is not to cover the whole set of functionnalities of Kustomize but to be a standard example to show you the phiplosophy behind this tool.&lt;/p&gt;

&lt;p&gt;First of all, we will create the folder &lt;code&gt;k8s/overlays/prod&lt;/code&gt; with a &lt;code&gt;kustomization.yaml&lt;/code&gt; inside it. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;k8s/overlays/prod/kustomization.yaml&lt;/code&gt; has the following content:&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;bases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../../base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we build it, we will see the same result as before when building the base.&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;$ &lt;/span&gt;kustomize build k8s/overlays/prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output the following &lt;code&gt;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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sl-demo-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;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;sl-demo-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;sl-demo-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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo/bar:latest&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;app&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;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are now ready to apply kustomization for our &lt;code&gt;prod&lt;/code&gt; env&lt;/p&gt;

&lt;h3&gt;
  
  
  Define Env variables for our deployment
&lt;/h3&gt;

&lt;p&gt;In our &lt;code&gt;base&lt;/code&gt;, we didn't define any env variable. We will now add those &lt;code&gt;env&lt;/code&gt; variables above our base.&lt;br&gt;
To do so, it's very simple, we just have to create the chunk of yaml we would like to apply above our base and referece it inside the &lt;code&gt;kustomization.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This file &lt;code&gt;custom-env.yaml&lt;/code&gt; containing env variables will look like this:&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;sl-demo-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;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;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;app&lt;/span&gt; &lt;span class="c1"&gt;# (1)&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;CUSTOM_ENV_VARIABLE&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;Value defined by Kustomize ❤️&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 &lt;code&gt;name&lt;/code&gt; &lt;strong&gt;(1)&lt;/strong&gt; key here is very important and allow Kustomize to find the right container which need to be modified.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can see this &lt;code&gt;yaml&lt;/code&gt; file isn't valid by itself but it describes only the addition we would like to do on our previous base.&lt;/p&gt;

&lt;p&gt;We just have to add this file to a specific entry in the &lt;code&gt;k8s/overlays/prod/kustomization.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;bases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../../base&lt;/span&gt;

&lt;span class="na"&gt;patchesStrategicMerge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;custom-env.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we build this one, we will have the following result:&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;$ &lt;/span&gt;kustomize build k8s/overlays/prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sl-demo-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;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;sl-demo-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;sl-demo-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;CUSTOM_ENV_VARIABLE&lt;/span&gt; &lt;span class="c1"&gt;# (1)&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;Value defined by Kustomize ❤️&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;foo/bar:latest&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;app&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;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see our &lt;code&gt;env&lt;/code&gt; block has been applied above our base and now the &lt;code&gt;CUSTOM_ENV_VARIABLE&lt;/code&gt; (1) will be defined inside our deployment.yaml.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change the number of replica
&lt;/h3&gt;

&lt;p&gt;Like in our previous example, we will extend our base to define variables not already defined&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can also override some variables already present in your base files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here, we would like to add information about the number of replica. Like before, a chunk or &lt;code&gt;yaml&lt;/code&gt; with just the extra info needed for defining replica will be enought:&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;sl-demo-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;10&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rollingUpdate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;maxSurge&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;maxUnavailable&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And like before, we add it to the list of &lt;code&gt;patchesStrategicMerge&lt;/code&gt; in the &lt;code&gt;kustomization.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;bases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../../base&lt;/span&gt;

&lt;span class="na"&gt;patchesStrategicMerge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;custom-env.yaml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;replica-and-rollout-strategy.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result of the command &lt;code&gt;kustomize build k8s/overlays/prod&lt;/code&gt; give us the following result&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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sl-demo-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;10&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;sl-demo-app&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rollingUpdate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;maxSurge&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;maxUnavailable&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&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;sl-demo-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;CUSTOM_ENV_VARIABLE&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;Value defined by Kustomize ❤️&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;foo/bar:latest&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;app&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;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can see the replica number and rollingUpdate strategy have been applied above our base.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a secret define through command line
&lt;/h3&gt;

&lt;p&gt;One of the things we often do is to set some variables as secret from command-line. &lt;br&gt;
In our case, we are doing this directly from our Gitlab-CI on &lt;a href="https://gitlab.com" rel="noopener noreferrer"&gt;Gitlab.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But you can do this from anywhere else, the main purpose here is to define Kubernetes Secret without putting them inside Git 😱.&lt;/p&gt;

&lt;p&gt;To do so, &lt;code&gt;kustomize&lt;/code&gt; has a sub-command to edit a &lt;code&gt;kustomization.yaml&lt;/code&gt; and create a secret for you. You just have to use it in your deployment like if it already exists.&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;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;k8s/overlays/prod
&lt;span class="nv"&gt;$ &lt;/span&gt;kustomize edit add secret sl-demo-app &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db-password&lt;span class="o"&gt;=&lt;/span&gt;12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will modify your &lt;code&gt;kustomization.yaml&lt;/code&gt; and add a &lt;code&gt;SecretGenerator&lt;/code&gt; inside it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can also use secret comming from properties file (with &lt;code&gt;--from-file=file/path&lt;/code&gt;) or from env file (with &lt;code&gt;--from-env-file=env/path.env&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;bases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../../base&lt;/span&gt;

&lt;span class="na"&gt;patchesStrategicMerge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;custom-env.yaml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;replica-and-rollout-strategy.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;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-password=12345&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;sl-demo-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;If you run the &lt;code&gt;kustomize build k8s/overlays/prod&lt;/code&gt; from the root folder of the example project, you will have the following output&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;db-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MTIzNDU=&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;sl-demo-app-6ft88t2625&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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sl-demo-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;10&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;sl-demo-app&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rollingUpdate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;maxSurge&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;maxUnavailable&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&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;sl-demo-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;CUSTOM_ENV_VARIABLE&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;Value defined by Kustomize ❤️&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;foo/bar:latest&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;app&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;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&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 secret name is &lt;code&gt;sl-demo-app-6ft88t2625&lt;/code&gt; instead of &lt;code&gt;sl-demo-app&lt;/code&gt;, it's normal and this is made to trigger a rolling update of the deployment if secrets content is changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we want to use this secret from our deployment, we just have, like before, to add a new layer definition which uses the secret.&lt;/p&gt;

&lt;p&gt;For example, this file will mount the &lt;code&gt;db-password&lt;/code&gt; value as environement variables&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;sl-demo-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;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;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;app&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD"&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;sl-demo-app&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;db.password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, like before, we add this to the &lt;code&gt;k8s/overlays/prod/kustomization.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;bases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../../base&lt;/span&gt;

&lt;span class="na"&gt;patchesStrategicMerge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;custom-env.yaml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;replica-and-rollout-strategy.yaml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database-secret.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;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-password=12345&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;sl-demo-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;If we build the whole &lt;code&gt;prod&lt;/code&gt; files, we now have&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;db-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MTIzNDU=&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;sl-demo-app-6ft88t2625&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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sl-demo-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;10&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;sl-demo-app&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rollingUpdate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;maxSurge&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;maxUnavailable&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&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;sl-demo-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;DB_PASSWORD&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;db.password&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;sl-demo-app-6ft88t2625&lt;/span&gt; &lt;span class="c1"&gt;# (1)&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;CUSTOM_ENV_VARIABLE&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;Value defined by Kustomize ❤️&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;foo/bar:latest&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;app&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;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the &lt;code&gt;secretKeyRef.name&lt;/code&gt; used is automatically modified to follow the name defined by Kustomize (1)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Don't forget, the command to put the secret inside the &lt;code&gt;kustomization.yaml&lt;/code&gt; file should be made only from safe env and should not be commited.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same logic exists with ConfigMap with hash at the end to allow redeployement of your app if ConfigMap changes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Change the image of a deployment
&lt;/h3&gt;

&lt;p&gt;Like for secret, there is a custom directive to allow changing of image or tag directly from the command line. &lt;br&gt;
This is very useful if you need to deploy the image previously tagged by your continuous build system.&lt;/p&gt;

&lt;p&gt;To do that, you can use 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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;k8s/overlays/prod
&lt;span class="nv"&gt;$ TAG_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.4.5 &lt;span class="c"&gt;# (1)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kustomize edit add secret sl-demo-app &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db-password&lt;span class="o"&gt;=&lt;/span&gt;12345 &lt;span class="c"&gt;# To create my required secret&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kustomize edit &lt;span class="nb"&gt;set &lt;/span&gt;image foo/bar&lt;span class="o"&gt;=&lt;/span&gt;foo/bar:&lt;span class="nv"&gt;$TAG_VERSION&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 &lt;code&gt;TAG_VERSION&lt;/code&gt; here is usualy defined by your CI/CD system&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;k8s/overlays/prod/kustomization.yaml&lt;/code&gt; will be modified with those values:&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;bases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../../base&lt;/span&gt;

&lt;span class="na"&gt;patchesStrategicMerge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;custom-env.yaml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;replica-and-rollout-strategy.yaml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database-secret.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;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-password=12345&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;sl-demo-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;span class="na"&gt;images&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;foo/bar&lt;/span&gt;
  &lt;span class="na"&gt;newName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo/bar&lt;/span&gt;
  &lt;span class="na"&gt;newTag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.4.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we build it, with the &lt;code&gt;kustomize build k8s/overlays/prod/&lt;/code&gt; we have the following result:&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;db-password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MTIzNDU=&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;sl-demo-app-6ft88t2625&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;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;sl-demo-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;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;http&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;8080&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;sl-demo-app&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sl-demo-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;10&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;sl-demo-app&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rollingUpdate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;maxSurge&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;maxUnavailable&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&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;sl-demo-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;DB_PASSWORD&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;db.password&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;sl-demo-app-6ft88t2625&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;CUSTOM_ENV_VARIABLE&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;Value defined by Kustomize ❤️&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;foo/bar:3.4.5&lt;/span&gt; &lt;span class="c1"&gt;# (1)&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;app&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;8080&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;http&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see the first &lt;code&gt;container.image&lt;/code&gt; of the deployment have been modified to be run with the version &lt;code&gt;3.4.5&lt;/code&gt; (1).&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;We see in these examples how we can leverage the power of Kustomize to define your Kubernetes files without even using a templating system. &lt;br&gt;
All the modification files you made will be applied above the original files without altering it with curly braces and imperative modification.&lt;/p&gt;

&lt;p&gt;There is a lot of advanced topic in Kustomize, like the mixins and inheritance logic or other directive allowing to define a name, label or namespace to every created object... &lt;br&gt;
You can follow the &lt;a href="https://github.com/kubernetes-sigs/kustomize" rel="noopener noreferrer"&gt;official Kustomize github repository&lt;/a&gt; to see advanced examples and documentation. &lt;/p&gt;




&lt;p&gt;Note: You can find all code from this article in this &lt;a href="https://gitlab.com/davinkevin/kustomize-blog-demo" rel="noopener noreferrer"&gt;Gitlab project&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>cloud</category>
      <category>yaml</category>
    </item>
    <item>
      <title>Manage your secrets in Git with SOPS - Common operations</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Sat, 30 May 2020 14:27:23 +0000</pubDate>
      <link>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-common-operations-118g</link>
      <guid>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-common-operations-118g</guid>
      <description>&lt;p&gt;We saw in the previous article &lt;a href="https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-g0a"&gt;how to use SOPS&lt;/a&gt; to store our secrets in Git. Here, we will see some common operations required when you are using SOPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit a secret
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Alice&lt;/code&gt; would like to change the value in &lt;code&gt;dev_a&lt;/code&gt; secret. To do so, she can use the &lt;code&gt;sops dev_a.encrypted.env&lt;/code&gt; command to open the &lt;code&gt;$EDITOR&lt;/code&gt; and allow in-place changes. &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;After the edition, the secret is encrypted back, and she can commit the file in Git. &lt;/p&gt;

&lt;h2&gt;
  
  
  Add secret access to someone else
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Alice&lt;/code&gt; would like to let &lt;code&gt;Bobby&lt;/code&gt; read the &lt;code&gt;dev_a&lt;/code&gt; secret. To do that, she will use &lt;code&gt;sops --rotate --in-place --add-pgp &amp;lt;bobby-key-id&amp;gt; dev_a.encrypted.env&lt;/code&gt; command.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;After this modification, &lt;code&gt;Bobby&lt;/code&gt; can fetch modifications. He is now able to read (and modify) the secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove secret access to someone else
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Alice&lt;/code&gt; now wants to remove &lt;code&gt;Bobby&lt;/code&gt; access to &lt;code&gt;dev_a&lt;/code&gt; secret. She is able to do this by using the &lt;code&gt;sops --rotate --in-place --rm-pgp &amp;lt;bobby-key-id&amp;gt; dev_a.encrypted.env&lt;/code&gt;. &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;After this, &lt;code&gt;Bobby&lt;/code&gt; is unable to decrypt the secret anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure automatic key selection
&lt;/h2&gt;

&lt;p&gt;Like we saw before, &lt;code&gt;sops&lt;/code&gt; commands often requires references to &lt;code&gt;key-id&lt;/code&gt; of people concerned by the modification... and this is error prone and hard to manage if you share access with a lot of people. &lt;/p&gt;

&lt;p&gt;To simplify this, the team can create a file, named &lt;code&gt;.sops.yaml&lt;/code&gt; and placed it in the root of our Git repository.&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;creation_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# Specific to `dev_a` env&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path_regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev_a\.encrypted\.env$&lt;/span&gt;
    &lt;span class="c1"&gt;# Here, only the `Alice` key-id&lt;/span&gt;
    &lt;span class="na"&gt;pgp&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;5844C613B763F4374BAB2D2FC735658AB38BF93A&lt;/span&gt;

  &lt;span class="c1"&gt;# Specific to `int` env&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path_regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;int\.encrypted\.env$&lt;/span&gt;
    &lt;span class="c1"&gt;# Here, we have :&lt;/span&gt;
    &lt;span class="c1"&gt;# * `Alice` key-id: 5844C613B763F4374BAB2D2FC735658AB38BF93A&lt;/span&gt;
    &lt;span class="c1"&gt;# * `Bobby` key-id: AE0D6FD0242FF896BE1E376B62E1E77388753B8E&lt;/span&gt;
    &lt;span class="c1"&gt;# * `Devon` key-id: 57E6DA39E907744429FB07871141FE9F63986243&lt;/span&gt;
    &lt;span class="na"&gt;pgp&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;5844C613B763F4374BAB2D2FC735658AB38BF93A,&lt;/span&gt;
      &lt;span class="s"&gt;AE0D6FD0242FF896BE1E376B62E1E77388753B8E,&lt;/span&gt;
      &lt;span class="s"&gt;57E6DA39E907744429FB07871141FE9F63986243&lt;/span&gt;

  &lt;span class="c1"&gt;# Specific for new env `dev_a_and_b`&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path_regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev_a_and_b\.encrypted.env$&lt;/span&gt;
    &lt;span class="c1"&gt;# Here, we have only `Alice` and `Bobby` :&lt;/span&gt;
    &lt;span class="c1"&gt;# * `Alice` key-id: 5844C613B763F4374BAB2D2FC735658AB38BF93A&lt;/span&gt;
    &lt;span class="c1"&gt;# * `Bobby` key-id: AE0D6FD0242FF896BE1E376B62E1E77388753B8E&lt;/span&gt;
    &lt;span class="na"&gt;pgp&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;5844C613B763F4374BAB2D2FC735658AB38BF93A,&lt;/span&gt;
      &lt;span class="s"&gt;AE0D6FD0242FF896BE1E376B62E1E77388753B8E&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, &lt;code&gt;Bobby&lt;/code&gt; will create a new secret for &lt;code&gt;dev_a_and_b&lt;/code&gt; env just with the command &lt;code&gt;sops dev_a_and_b.encrypted.env&lt;/code&gt;. No more &lt;code&gt;--pgp &amp;lt;key-id&amp;gt;&lt;/code&gt;, &lt;code&gt;sops&lt;/code&gt; automatically selects the closest &lt;code&gt;.sops.yaml&lt;/code&gt; file from the &lt;code&gt;CWD&lt;/code&gt; (see &lt;a href="https://github.com/mozilla/sops#using-sops-yaml-conf-to-select-kms-pgp-for-new-files" rel="noopener noreferrer"&gt;this&lt;/a&gt;).&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;Keys are selected by matching a &lt;code&gt;regex&lt;/code&gt; againt the path of the file, so possibilities are wide and this is simpler than using parameters in command line ! &lt;/p&gt;

&lt;h2&gt;
  
  
  Add or Remove access with &lt;code&gt;.sops.yaml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If a &lt;code&gt;.sops.yaml&lt;/code&gt; file is used, &lt;code&gt;Alice&lt;/code&gt; can simplify the &lt;code&gt;add-pgp&lt;/code&gt; or &lt;code&gt;rm-pgp&lt;/code&gt; command previously seen. She just need to change the &lt;code&gt;.sops.yaml&lt;/code&gt; and use the command &lt;code&gt;sops updatekeys dev_a.encrypted.env&lt;/code&gt; to update who can decrypt the file.&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


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

&lt;p&gt;Those are the most common operations required to use &lt;code&gt;sops&lt;/code&gt; in your project. This is simple and still helps you to keep your secrets in sync with your code !&lt;/p&gt;

&lt;p&gt;You can find the source code of this article, files, and scripts in this &lt;a href="https://gitlab.com/davinkevin/sops-blog-post" rel="noopener noreferrer"&gt;GitLab repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>git</category>
      <category>sops</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Manage your secrets in Git with SOPS</title>
      <dc:creator>Kevin Davin</dc:creator>
      <pubDate>Sat, 23 May 2020 10:35:16 +0000</pubDate>
      <link>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-g0a</link>
      <guid>https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-g0a</guid>
      <description>&lt;p&gt;We use Git for everything now, from code source to organization, history, and even for Kubernetes Cluster Management (aka GitOps). &lt;/p&gt;

&lt;p&gt;But, there is still something not widely adopted... managing our secrets in Git. Some tools like &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;, &lt;a href="https://cloud.google.com/solutions/secrets-management" rel="noopener noreferrer"&gt;Google Secret Management&lt;/a&gt;, or &lt;a href="https://aws.amazon.com/fr/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secret Manager&lt;/a&gt; provide us a solution to manage our secrets in a dedicated system, but they are still not in sync with our source code.&lt;/p&gt;

&lt;p&gt;We will see here, thanks to &lt;a href="https://github.com/mozilla/sops" rel="noopener noreferrer"&gt;Mozilla SOPS&lt;/a&gt; how to integrate our secrets management directly in Git. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/getsops" rel="noopener noreferrer"&gt;
        getsops
      &lt;/a&gt; / &lt;a href="https://github.com/getsops/sops" rel="noopener noreferrer"&gt;
        sops
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Simple and flexible tool for managing secrets
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="rst"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;SOPS: Secrets OPerationS&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;SOPS&lt;/strong&gt; is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY
formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, age, and PGP
(&lt;a href="https://www.youtube.com/watch?v=YTEVyLXFiq0" rel="nofollow noopener noreferrer"&gt;demo&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/1ea5f0bb71c611d225ac52c8a003530292add9989abaf48894abbade6348c139/68747470733a2f2f692e696d6775722e636f6d2f5830544d354e492e676966"&gt;&lt;img alt="https://i.imgur.com/X0TM5NI.gif" src="https://camo.githubusercontent.com/1ea5f0bb71c611d225ac52c8a003530292add9989abaf48894abbade6348c139/68747470733a2f2f692e696d6775722e636f6d2f5830544d354e492e676966"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;a href="https://pkg.go.dev/github.com/getsops/sops/v3" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2e45ef5eb16abb1b72b56b9b3731554aca5d70368eccd57e8fc24fb5c0b9d054/68747470733a2f2f706b672e676f2e6465762f62616467652f6769746875622e636f6d2f676574736f70732f736f70732f76332e737667"&gt;
&lt;/a&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;a href="https://github.com/getsops/sops#id2" rel="noopener noreferrer"&gt;1   Download&lt;/a&gt;&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;a href="https://github.com/getsops/sops#id3" rel="noopener noreferrer"&gt;1.1   Stable release&lt;/a&gt;&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Binaries and packages of the latest stable release are available at &lt;a href="https://github.com/getsops/sops/releases" rel="noopener noreferrer"&gt;https://github.com/getsops/sops/releases&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;a href="https://github.com/getsops/sops#id4" rel="noopener noreferrer"&gt;1.2   Development branch&lt;/a&gt;&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;For the adventurous, unstable features are available in the main branch, which you can install from source:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ mkdir -p &lt;span class="pl-smi"&gt;$GOPATH&lt;/span&gt;/src/github.com/getsops/sops/
$ git clone https://github.com/getsops/sops.git &lt;span class="pl-smi"&gt;$GOPATH&lt;/span&gt;/src/github.com/getsops/sops/
$ &lt;span class="pl-c1"&gt;cd&lt;/span&gt; &lt;span class="pl-smi"&gt;$GOPATH&lt;/span&gt;/src/github.com/getsops/sops/
$ make install&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;(requires Go &amp;gt;= 1.19)&lt;/p&gt;
&lt;p&gt;If you don't have Go installed, set it up with:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ {apt,yum,brew} install golang
$ &lt;span class="pl-c1"&gt;echo&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;export GOPATH=~/go&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-k"&gt;~&lt;/span&gt;/.bashrc
$ &lt;span class="pl-c1"&gt;source&lt;/span&gt; &lt;span class="pl-k"&gt;~&lt;/span&gt;/.bashrc
$ mkdir &lt;span class="pl-smi"&gt;$GOPATH&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Or whatever variation of the above fits your system and shell.&lt;/p&gt;
&lt;p&gt;To use &lt;strong&gt;SOPS&lt;/strong&gt; as a library, take a look at the &lt;a href="https://pkg.go.dev/github.com/getsops/sops/v3/decrypt" rel="nofollow noopener noreferrer"&gt;decrypt package&lt;/a&gt;.&lt;/p&gt;
&lt;div id="user-content-table-of-contents"&gt;&lt;p&gt;…&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/getsops/sops" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt;: I've previously written &lt;a href="https://blog.stack-labs.com/code/keep-your-kubernetes-secrets-in-git-with-kubesec/" rel="noopener noreferrer"&gt;an article&lt;/a&gt; on the same subject about a project named &lt;code&gt;kubesec&lt;/code&gt; specialized in Kubernetes Secret. The project seems to be stopped and &lt;a href="https://github.com/mozilla/sops" rel="noopener noreferrer"&gt;Mozilla SOPS&lt;/a&gt; is a better alternative right now, because it can manage every kind of secrets, not only Kubernetes ones. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sops Installation
&lt;/h2&gt;

&lt;p&gt;Sops is very simple to install, like every &lt;code&gt;golang&lt;/code&gt; application, you just have to download the binary for your specific Operating System (Linux, Mac, Windows) directly from the &lt;a href="https://github.com/mozilla/sops/releases" rel="noopener noreferrer"&gt;release page&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;By the way, you can install it thanks to &lt;code&gt;brew&lt;/code&gt; on Mac &amp;amp; Linux (&lt;a href="https://formulae.brew.sh/formula/sops" rel="noopener noreferrer"&gt;sops formuale&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case
&lt;/h2&gt;

&lt;p&gt;What we will try to achieve is to store secrets in Git but with restrictions on "who can access what". &lt;/p&gt;

&lt;p&gt;For example, we have 4 environments, &lt;code&gt;dev_a&lt;/code&gt;, &lt;code&gt;dev_b&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, and &lt;code&gt;prod&lt;/code&gt; and 3 team members, &lt;code&gt;Alice&lt;/code&gt;, &lt;code&gt;Bobby&lt;/code&gt;, and &lt;code&gt;Devon&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We want to restrict secrets access with the following requirements: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dev_a&lt;/code&gt; secrets should be only accessible by &lt;code&gt;Alice&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dev_b&lt;/code&gt; secrets should be only accessible by &lt;code&gt;Bobby&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;int&lt;/code&gt; secrets should be accessible by &lt;code&gt;Alice&lt;/code&gt;, &lt;code&gt;Bobby&lt;/code&gt;, and &lt;code&gt;Devon&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prod&lt;/code&gt; secrets should be accessible only by &lt;code&gt;Devon&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of them already has configured their GPG key pairs. If you need to set them up, you can follow the &lt;a href="https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/#generating-a-gpg-key" rel="noopener noreferrer"&gt;official GitLab documentation 🦊&lt;/a&gt; about this.&lt;/p&gt;

&lt;p&gt;In this example, secrets are just plain old &lt;code&gt;env&lt;/code&gt; files.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;dev_a&lt;/code&gt; configuration
&lt;/h2&gt;

&lt;p&gt;Alice will generate a file containing a secret: &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;Thanks to the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sops -pgp 5844C613B763F4374BAB2D2FC735658AB38BF93A -e dev_a.env &amp;gt; dev_a.encrypted.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Alice&lt;/code&gt; has encrypted the file &lt;code&gt;dev_a.env&lt;/code&gt; and stored the result in &lt;code&gt;dev_a.encrypted.env&lt;/code&gt;. She is the only one able to decrypt it. &lt;/p&gt;

&lt;p&gt;Let's see, if we run this as &lt;code&gt;Bobby&lt;/code&gt;: &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dev_b&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt; configurations are similar to the one created by &lt;code&gt;Alice&lt;/code&gt;. Then, &lt;code&gt;Bobby&lt;/code&gt; and &lt;code&gt;Devon&lt;/code&gt; will respectively create &lt;code&gt;dev_b&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;int&lt;/code&gt; configuration
&lt;/h2&gt;

&lt;p&gt;In this configuration, we would like every developers to be able to read this file. But, only developers from the project and not everyone with access to the git repository... so we still have to encrypt this file. &lt;/p&gt;

&lt;p&gt;To do so, &lt;code&gt;Devon&lt;/code&gt; will execute the following commands: &lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Devon&lt;/code&gt; has to create the secret with the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sops &lt;span class="nt"&gt;--pgp&lt;/span&gt; 57E6DA39E907744429FB07871141FE9F63986243,&lt;span class="se"&gt;\&lt;/span&gt;
           5844C613B763F4374BAB2D2FC735658AB38BF93A,&lt;span class="se"&gt;\&lt;/span&gt;
           AE0D6FD0242FF896BE1E376B62E1E77388753B8E &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;-e&lt;/span&gt; int.env &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; int.encrypted.env&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command contains every public key ids, comma sparated. &lt;/p&gt;

&lt;p&gt;We can check that both &lt;code&gt;Alice&lt;/code&gt; and &lt;code&gt;Bobby&lt;/code&gt; can decrypt the &lt;code&gt;int.encrypted.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;alice &lt;span class="nv"&gt;$ &lt;/span&gt;sops &lt;span class="nt"&gt;-d&lt;/span&gt; int.encrypted.env
&lt;span class="nv"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5Tz2QNxki789YFDa
&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;bobby &lt;span class="nv"&gt;$ &lt;/span&gt;sops &lt;span class="nt"&gt;-d&lt;/span&gt; int.encrypted.env
&lt;span class="nv"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5Tz2QNxki789YFDa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the &lt;code&gt;*.encrypted.env&lt;/code&gt; files are now stored in &lt;code&gt;Git&lt;/code&gt; and can be managed like any other resources, with history and diff in commits. Only those defined during encryption can read them edit them.&lt;/p&gt;

&lt;p&gt;You can find the source code of this article, files, and scripts in this &lt;a href="https://gitlab.com/davinkevin/sops-blog-post" rel="noopener noreferrer"&gt;GitLab repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this will help you to use &lt;code&gt;Git&lt;/code&gt; &amp;amp; &lt;code&gt;SOPS&lt;/code&gt; to manage your secrets.&lt;/p&gt;

</description>
      <category>git</category>
      <category>sops</category>
      <category>security</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
