{"id":588,"date":"2023-06-28T15:52:45","date_gmt":"2023-06-28T15:52:45","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=588"},"modified":"2023-06-28T15:52:45","modified_gmt":"2023-06-28T15:52:45","slug":"kuztomize-configmap-generators","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=588","title":{"rendered":"Kuztomize Secret &amp; Configmap Generators [Practical Examples]"},"content":{"rendered":"<p>In this guide, we will look at how to generate Kubernetes Configmaps and Secrets using Kustomize.<\/p>\n<p>If you are new to Kustomize, please check out the <a href=\"https:\/\/devopscube.com\/kustomize-tutorial\/\">Kustomize tutorial <\/a>to learn the basics.<\/p>\n<h2 id=\"configmap-secret-generator-use-case\">Configmap &amp; Secret Generator Use Case<\/h2>\n<p>Before looking into how a secret\/config generator works, let&#8217;s understand what problem it solves.<\/p>\n<p>When you update a configmap attached to a pod as a volume, the configmap data gets propagated to the pod automatically. However, the <strong>pod does not get the latest data<\/strong> in the configmap in the following scenarios.<\/p>\n<ol>\n<li>If the pod gets environment variables from the configmap.<\/li>\n<li>If the configmap is mounted as a volume using a subpath.<\/li>\n<\/ol>\n<p>In the above cases, the pod will continue using the old configmap data until we restart the pod. Because the <strong>pod is unaware<\/strong> of what got changed in configMap.<\/p>\n<p>Essentially, the data from the ConfigMaps (such as properties, environment variables, etc.) is <strong>used by applications during their startup<\/strong>. So even if the updated configmap data is projected to the pod, if the application running inside the pod doesn&#8217;t have any <strong>hot reload mechanism<\/strong>, you will have to restart the pod for the changes to take place.<\/p>\n<p>What options do we have to solve this issue?<\/p>\n<ol>\n<li>You can use <a href=\"https:\/\/github.com\/stakater\/Reloader?ref=devopscube.com\" rel=\"noreferrer noopener\">Reloader<\/a> Controller.<\/li>\n<li>Using Kustomize ConfigMap Generator<\/li>\n<\/ol>\n<p>If you already use Kustomize for <a href=\"https:\/\/devopscube.com\/kubernetes-deployment-tutorial\/\">Kubernetes Deployments<\/a> or planning to use it, there is no need for any extra controllers to take care of Configmap rollouts.<\/p>\n<h2 id=\"kustomize-configmap-secret-generator\">Kustomize Configmap &amp; Secret Generator<\/h2>\n<p>Here is how the Kustoimize Configmap\/Secret generator work.<\/p>\n<ol>\n<li>Kustomize generator creates a configMap and Secret with a unique name(hash) at the end. For example, if the name of the configmap is <strong>app-configmap<\/strong>, the generated one would have the name <strong>app-configmap-7b58b6ct6d<\/strong>. Here <strong>7b58b6ct6d<\/strong> is the appended hash.<\/li>\n<li>If you update the configmap\/Secret, it will <strong>create a new configMap\/Secret <\/strong>with the same name with a different hash(random sets of characters) at the end.<\/li>\n<li>Kustomize will <strong>automatically update<\/strong> the Deployment with the new configmap name.<\/li>\n<li>The moment Deployment is updated by Kustomize, a <strong>rollout will be triggered<\/strong> and the application runs on the pod and gets the updated configmap\/secret data. In this way, we don&#8217;t need to redeploy or restart the deployment.<\/li>\n<\/ol>\n<p>The following image shows the Configmap create and update workflow with changes in hash during create and update stages.<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-36-19.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"1324\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-36-19.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-36-19.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/image-36-19.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-36-19.png 2345w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<p>Following are the important points you should know about the Kustomize generators.<\/p>\n<ol>\n<li>Since Kustomize creates a new configmap every time there is an update, you need to <strong>garbage-collect your old orphaned Configmaps<\/strong>. If you have resource quota limits set for namespace, orphaned Configmaps could be an issue. Or you should use the <strong>&#8211;prune <\/strong>flag with labels in the <strong>kubectl apply <\/strong>command. Also, GitOps tools like <strong>ArgoCD<\/strong> offer Orphaned resource monitoring mechanisms.<\/li>\n<li>You can use the <code>disableNameSuffixHash: true<\/code> flag to disable creating new Configmaps on every update, but it does not trigger a pod rollout. You need to manually trigger a rollout for pods to get the latest configmap data. Or the application running inside the pod should have a hot-reload mechanism.<\/li>\n<\/ol>\n<p>Now let&#8217;s look at practically how to use Configmap and Secret Generators.<\/p>\n<h2 id=\"generate-configmap-using-kustomize\">Generate Configmap Using Kustomize<\/h2>\n<p>We will look at an Nginx example where it uses a configmap content for its index.html<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note:<\/strong><\/b> The base and overlay YAMLs are part of the <a href=\"https:\/\/github.com\/techiescamp\/kustomize?ref=devopscube.com\" rel=\"noreferrer noopener\">Kustomize Github repo<\/a>. Clone the repo to follow along the tutorial<\/div>\n<\/div>\n<p>Here is the file structure of the repository. To understand the generators, we will use the generators overlay folder.<\/p>\n<pre><code>\u251c\u2500\u2500 base\n\u2502   \u251c\u2500\u2500 deployment.yaml\n\u2502   \u251c\u2500\u2500 kustomization.yaml\n\u2502   \u2514\u2500\u2500 service.yaml\n\u2514\u2500\u2500 overlays\n    \u251c\u2500\u2500 dev\n    \u2502   \u251c\u2500\u2500 deployment-dev.yaml\n    \u2502   \u251c\u2500\u2500 kustomization.yaml\n    \u2502   \u2514\u2500\u2500 service-dev.yaml\n    \u251c\u2500\u2500 generators\n    \u2502   \u251c\u2500\u2500 deployment.yaml\n    \u2502   \u251c\u2500\u2500 files\n    \u2502   \u2502   \u2514\u2500\u2500 index.html\n    \u2502   \u251c\u2500\u2500 kustomization.yaml\n    \u2502   \u2514\u2500\u2500 service.yaml\n    \u2514\u2500\u2500 prod\n        \u251c\u2500\u2500 deployment-prod.yaml\n        \u251c\u2500\u2500 kustomization.yaml\n        \u2514\u2500\u2500 service-prod.yaml<\/code><\/pre>\n<p><strong><code>generators\/deployment.yaml<\/code><\/strong><\/p>\n<p>Here is the Overlay nginx <strong><code>deployment.yaml <\/code><\/strong>that uses a configmap named <strong><code>index-html-configmap<\/code><\/strong> mounted as a volume and <strong>env variable <\/strong>derived from a configmap named <strong><code>endpoint-configmap<\/code><\/strong>. I have highlighted the configs in bold.<\/p>\n<pre><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: web-deployment\nspec:\n  replicas: 3\n  template:\n    spec:\n      containers:\n      - name: nginx\n        resources:\n          limits:\n            cpu: \"200m\"\n            memory: \"256Mi\"\n          requests:\n            cpu: \"100m\"\n            memory: \"128Mi\"\n        env:\n        - name: ENDPOINT\n          valueFrom:\n            configMapKeyRef:\n              name: endpoint-configmap\n              key: endpoint\n        volumeMounts:\n        - name: nginx-index-file\n          mountPath: \/usr\/share\/nginx\/html\/\n      volumes:\n      - name: nginx-index-file\n        configMap:\n          name: index-html-configmap<\/code><\/pre>\n<p><strong><code>files\/index.html<\/code><\/strong><\/p>\n<p>We have the configmap file content in the index.html file under the files directory<\/p>\n<pre><code>&lt;html&gt;\n    &lt;h1&gt;Welcome&lt;\/h1&gt;\n    &lt;\/br&gt;\n    &lt;h1&gt;Hi! This is the Configmap Index file &lt;\/h1&gt;\n    &lt;\/html<\/code><\/pre>\n<p><strong><code>generators\/kustomization.yaml<\/code><\/strong><\/p>\n<p>The configmap generation options should be added to the <strong><code>kustomization.yaml<\/code><\/strong> file under <strong>configMapGenerator<\/strong> field.<\/p>\n<p>In this example,  we are generating two types of Configmaps.<\/p>\n<ol>\n<li>Configmap from a file (index.html) that will be mounted to the nginx <strong>\/usr\/share\/nginx\/html\/ <\/strong>directory.<\/li>\n<li>Configmap from literals, that will set an environment variable named <strong>ENDPOINTS<\/strong><\/li>\n<\/ol>\n<p>Under <strong>generatorOptions<\/strong> field, you can add the common labels that needs to be added to the Configmaps.<\/p>\n<pre><code>apiVersion: kustomize.config.k8s.io\/v1beta1\nkind: Kustomization\n\nresources:\n- ..\/..\/base\n\npatches:\n- path: deployment.yaml\n- path: service.yaml\n\ngeneratorOptions:\n  labels:\n    app: web-service\n\nconfigMapGenerator:\n- name: index-html-configmap\n  behavior: create\n  files:\n  - files\/index.html\n- name: endpoint-configmap\n  literals:\n  - endpoint=\"api.example.com\/users\"<\/code><\/pre>\n<p>Let&#8217;s run the deployment using Kustomize.<\/p>\n<pre><code>kustomize build overlays\/generators | k apply  -f -<\/code><\/pre>\n<p>Now if you list the Configmaps, you can see two Configmaps created with a hash appended to their name as shown below.<\/p>\n<pre><code>kubectl get cm <\/code><\/pre>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-32-26.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"498\" height=\"163\"><\/figure>\n<p>As the deployment has a NodePort service, you can access the Nginx webpage that shows the content from the Configmap as shown below.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-33-22.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"551\" height=\"321\"><\/figure>\n<p>Also, if you login to the pod and echo the <strong>ENDPOINT<\/strong> environment variable, you will see the data from the configmap as shown below.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/literal-configmap-1.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"675\" height=\"220\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/literal-configmap-1.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/literal-configmap-1.png 675w\"><\/figure>\n<p>Now, to test the configmap update through the generator, lets update the <strong>index.html<\/strong> data to the following.<\/p>\n<pre><code>&lt;html&gt;\n    &lt;h1&gt;Welcome&lt;\/h1&gt;\n    &lt;\/br&gt;\n    &lt;h1&gt;Hi! This is the Updated Configmap Index file &lt;\/h1&gt;\n&lt;\/html\n<\/code><\/pre>\n<p>Let&#8217;s update the deployment using the following command.<\/p>\n<pre><code>kustomize build overlays\/generators | k apply  -f -<\/code><\/pre>\n<p>Now if you list the Configmaps, you will see two index Configmaps as shown below. This is because, for every configmap update, Kustomize will create a new configmap.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-34-16.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"509\" height=\"179\"><\/figure>\n<p>If you want to prune the orphaned Configmaps, use the <strong>&#8211;prune<\/strong> flag with the configmap label as shown below. The <code>--prune<\/code> flag instructs Kustomize to remove any resources from the final output that is no longer referenced or required.<\/p>\n<pre><code>kustomize build overlays\/generators | kubectl apply --prune -l app=web-service  -f -<\/code><\/pre>\n<p>Now, due to the new configmap created by Kustomize, the deployment triggers a rollout and nginx will use the updated configmap. If you check the Nginx NodePort service, you will see the updated index page.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-35-18.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"462\" height=\"227\"><\/figure>\n<p>Next, we will look at how to disable Hashed configmap.<\/p>\n<h2 id=\"disabling-hashed-configmap\">Disabling Hashed ConfigMap<\/h2>\n<p>If you don&#8217;t want to create hashed Configmaps using the ConfigMap generator, you can disable it by setting the <strong><code>disableNameSuffixHash<\/code><\/strong> flag to true under <strong><code>generatorOptions<\/code><\/strong>. It will disable the hash for all the Configmaps mentioned in the <strong><code>kustomization.yaml <\/code><\/strong>file.<\/p>\n<p>Here is an example.<\/p>\n<pre><code>generatorOptions:\n  labels:\n    app: web-service\n  disableNameSuffixHash: true<\/code><\/pre>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note<\/strong><\/b>: If you disable Configmap hash, you need to manually restart the pods for the configmap data to be consumed by the application.<\/div>\n<\/div>\n<h2 id=\"generate-secrets-using-kustomize\">Generate Secrets Using Kustomize<\/h2>\n<p>You can generate secrets the same way you generate Configmaps.<\/p>\n<p>For generating secrets, you need to use the secretGenerator field.<\/p>\n<p>Here is an example of generating a secret object from a file.<\/p>\n<pre><code>secretGenerator:\n- name: nginx-secret\n  files:\n  - files\/secret.txt<\/code><\/pre>\n<p>If you want to generate secrets from literals, use the following format.<\/p>\n<pre><code>secretGenerator:\n- name: nginx-api-password\n  literals:\n  - password=\"myS3cret\"<\/code><\/pre>\n<p>You can mount the secret as a volume or propagate it as an environment variable as per your requirements.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>In this blog, we looked at,<\/p>\n<ol>\n<li>Generating Configmaps and secrets using Kustomize generator.<\/li>\n<li>Configmap generator considerations related to orphaned Configmaps.<\/li>\n<li>A practical example implementing the Kustomize Configmap generator.<\/li>\n<\/ol>\n<p>Also, if you are learning Kubernetes, check out my 40+ comprehensive <a href=\"https:\/\/devopscube.com\/kubernetes-tutorials-beginners\/\">Kubernetes tutorials.<\/a><\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/kuztomize-configmap-generators\/\" target=\"_blank\" rel=\"noopener noreferrer\">Kuztomize Secret &amp;amp; Configmap Generators [Practical Examples] \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/kuztomize-configmap-generators\/<\/p>\n","protected":false},"author":1,"featured_media":589,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-588","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/588","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=588"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/588\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/589"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=588"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=588"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=588"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}