{"id":557,"date":"2019-11-23T11:21:12","date_gmt":"2019-11-23T19:21:12","guid":{"rendered":"http:\/\/blog.nillsf.com\/?p=557"},"modified":"2019-11-23T11:21:23","modified_gmt":"2019-11-23T19:21:23","slug":"writing-a-helm-v3-chart","status":"publish","type":"post","link":"https:\/\/nillsf.com\/index.php\/2019\/11\/23\/writing-a-helm-v3-chart\/","title":{"rendered":"Writing a Helm v3 chart"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Helm v3 recently became generally available. This looks like a great time to discover what Helm can help you do in your journey building cloud-native applications.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;ve been playing around with Helm charts (using v2) for a while, but I have never written a chart from zero (I typically use charts I find online and just play around with those).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this post, we&#8217;ll first explore what Helm is, what is new in Helm v3 and then we&#8217;ll build and deploy a Helm chart to Kubernetes. As a basis for our charts, we&#8217;ll use the <a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/11\/10\/simple-kubernetes-blue-green-deployments\/\">blue\/green deployment<\/a> we built a couple of weeks ago as native kubernetes YAML, and transform that into a Helm chart.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But let&#8217;s start at the beginning:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Helm?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/helm.sh\/\">Helm <\/a>is called the package manager for Kubernetes. Helm helps you deploy, update and manage Kubernetes applications. For this, you write something called Helm Charts. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I like to explain Helm Charts as <em>parameterized <\/em>kubernetes YAML files. If you think about a typical Kubernetes YAML file, those files are very static. You need to go into the file and edit the file to make changes. Or you make imperative changes through edit\/update.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Helm charts allow you write YAML files with certain parameters in them, which you can dynamically set. This setting of the parameters can be done through a values file or as a command line variable when you deploy the chart.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, with Helm you don&#8217;t necesarily have to write Helm Charts, you can also use a rich library of pre-written Helm charts and install popular software in your cluster through a simple command such as <code>helm&nbsp;install&nbsp;--name my-release stable\/mysql <\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is new in Helm 3?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In Helm 2 a component called Tiller was used in the architecture. Tiller was responsible to managing state of Helm charts in a cluster. It was a pod installed in a cluster that needed permissions on your cluster to execute the actual Helm charts. Tiller also managed the state of the installed Charts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Helm 3, Tiller is no longer part of the architecture. Instead of relying on Tiller for state, Helm will fetch information directly from the Kubernetes API server. The biggest benefit of this approach is that Helm will now use the user&#8217;s credentials from the kubeconfig file, and a user will only be able to deploy what he has RBAC rights to deploy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Another benefit of this approach is that you can now have the same release names across namespaces.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, with that knowledge, let&#8217;s first go ahead and create a dummy chart &#8211; which we&#8217;ll later update to reflect our blue-green deployment.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting started with Helm 3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I still have Helm v2 on my machine, and on my cluster:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm version\nClient: &amp;version.Version{SemVer:\"v2.14.3\", GitCommit:\"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085\", GitTreeState:\"clean\"}\nServer: &amp;version.Version{SemVer:\"v2.14.3\", GitCommit:\"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085\", GitTreeState:\"clean\"}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As my cluster is only used for testing and experimenting, I&#8217;ll nuke Helm from my cluster, and then update my binary locally. Let&#8217;s start with removing Helm from my cluster:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm reset --force #will remove tiller<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now go ahead and cleanup Helm 2 and install Helm 3.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mv \/usr\/local\/bin\/helm \/usr\/local\/bin\/helm2 #keep a copy of helm 2, just in case\nwget https:\/\/get.helm.sh\/helm-v3.0.0-linux-amd64.tar.gz\nsudo tar -xvzf helm-v3.0.0-linux-amd64.tar.gz\ncd linux-amd64\nsudo cp helm \/usr\/local\/bin\/helm\ncd ~\nhelm version\n#version.BuildInfo{Version:\"v3.0.0\", GitCommit:\"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6\", GitTreeState:\"clean\", GoVersion:\"go1.13.4\"}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now get started with a demo chart. Helm can create a boilerplate template for you. Let&#8217;s have a look at what this creates for us:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm create helmdemo\ncd helmdemo\ntree<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>.\n\u251c\u2500\u2500 Chart.yaml\n\u251c\u2500\u2500 charts\n\u251c\u2500\u2500 templates\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 NOTES.txt\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 _helpers.tpl\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 deployment.yaml\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 ingress.yaml\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 service.yaml\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 serviceaccount.yaml\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 tests\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 test-connection.yaml\n\u2514\u2500\u2500 values.yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s a couple of files there. We see a master <code>Chart.yaml<\/code>, we see a <code>values.yaml<\/code> file and then a couple of templates.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The actual Chart.yaml doesn&#8217;t contain much, except for version numbers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v2\nname: helmdemo\ndescription: A Helm chart for Kubernetes\n\n# A chart can be either an 'application' or a 'library' chart.\n#\n# Application charts are a collection of templates that can be packaged into versioned archives # to be deployed.\n#\n# Library charts provide useful utilities or functions for the chart developer. They're included as\n# a dependency of application charts to inject those utilities and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed.        type: application\n\n# This is the chart version. This version number should be incremented each time you make changes\n# to the chart and its templates, including the app version.\nversion: 0.1.0\n\n# This is the version number of the application being deployed. This version number should be   # incremented each time you make changes to the application.\nappVersion: 1.16.0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The values.yaml file actually contains some meaningful configuration. Remember what I explained in the introduction here: when deploying a chart, you can specify some of the parameters either via a values file (this file) or you can set them at runtime in the command line. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Default values for helmdemo.\n# This is a YAML-formatted file.\n# Declare variables to be passed into your templates.\n\nreplicaCount: 1\n\nimage:\n  repository: nginx\n  pullPolicy: IfNotPresent\n\nimagePullSecrets: &#91;]\nnameOverride: \"\"\nfullnameOverride: \"\"\n\nserviceAccount:\n  # Specifies whether a service account should be created\n  create: true\n  # The name of the service account to use.\n  # If not set and create is true, a name is generated using the fullname template\n  name:\n\npodSecurityContext: {}\n  # fsGroup: 2000\n\nsecurityContext: {}\n  # capabilities:\n  #   drop:\n  #   - ALL\n  # readOnlyRootFilesystem: true\n  # runAsNonRoot: true\n  # runAsUser: 1000\n\nservice:\n  type: ClusterIP\n  port: 80\n\ningress:\n  enabled: false\n  annotations: {}\n    # kubernetes.io\/ingress.class: nginx\n    # kubernetes.io\/tls-acme: \"true\"\n  hosts:\n    - host: chart-example.local\n      paths: &#91;]\n  tls: &#91;]\n  #  - secretName: chart-example-tls\n  #    hosts:\n  #      - chart-example.local\n\nresources: {}\n  # We usually recommend not to specify default resources and to leave this as a conscious        # choice for the user. This also increases chances charts run on environments with little       # resources, such as Minikube. If you do want to specify resources, uncomment the following     # lines, adjust them as necessary, and remove the curly braces after 'resources:'.\n  # limits:\n  #   cpu: 100m\n  #   memory: 128Mi\n  # requests:\n  #   cpu: 100m\n  #   memory: 128Mi\n\nnodeSelector: {}\n\ntolerations: &#91;]\n\naffinity: {}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now have a look at the <code>deployment.yaml<\/code> file to see an actual Helm template:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: {{ include \"helmdemo.fullname\" . }}\n  labels:\n    {{- include \"helmdemo.labels\" . | nindent 4 }}\nspec:\n  replicas: {{ .Values.replicaCount }}\n  selector:\n    matchLabels:\n      {{- include \"helmdemo.selectorLabels\" . | nindent 6 }}\n  template:\n    metadata:\n      labels:\n        {{- include \"helmdemo.selectorLabels\" . | nindent 8 }}\n    spec:\n    {{- with .Values.imagePullSecrets }}\n      imagePullSecrets:\n        {{- toYaml . | nindent 8 }}\n    {{- end }}\n      serviceAccountName: {{ include \"helmdemo.serviceAccountName\" . }}\n      securityContext:\n        {{- toYaml .Values.podSecurityContext | nindent 8 }}\n      containers:\n        - name: {{ .Chart.Name }}\n          securityContext:\n            {{- toYaml .Values.securityContext | nindent 12 }}\n          image: \"{{ .Values.image.repository }}:{{ .Chart.AppVersion }}\"\n          imagePullPolicy: {{ .Values.image.pullPolicy }}\n          ports:\n            - name: http\n              containerPort: 80\n              protocol: TCP\n          livenessProbe:\n            httpGet:\n              path: \/\n              port: http\n          readinessProbe:\n            httpGet:\n              path: \/\n              port: http\n          resources:\n            {{- toYaml .Values.resources | nindent 12 }}\n      {{- with .Values.nodeSelector }}\n      nodeSelector:\n        {{- toYaml . | nindent 8 }}\n      {{- end }}\n    {{- with .Values.affinity }}\n      affinity:\n        {{- toYaml . | nindent 8 }}\n    {{- end }}\n    {{- with .Values.tolerations }}\n      tolerations:\n        {{- toYaml . | nindent 8 }}\n    {{- end }}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It might be a bit difficult to read, but have a look at the image section of this deployment.yaml file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>         image: \"{{ .Values.image.repository }}:{{ .Chart.AppVersion }}\"\n          imagePullPolicy: {{ .Values.image.pullPolicy }}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can see that this refers to both <code>.Values.image.repository<\/code>, <code>.Chart.AppVersion<\/code> and <code>.Values.image.pullPolicy<\/code>. If we check our values file, we can see the following sections:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>image:\n  repository: nginx\n  pullPolicy: IfNotPresent<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And in our Chart.yaml we can see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>appVersion: 1.16.0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So, the image that this chart will create is essentially:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>         image: \"nginx:1.16.0\"\n          imagePullPolicy: IfNotPresent<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can now go ahead and install our Chart on our cluster:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm install helmdemo .<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>NAME: helmdemo\nLAST DEPLOYED: Sat Nov 23 09:09:04 2019\nNAMESPACE: default\nSTATUS: deployed\nREVISION: 1\nNOTES:\n1. Get the application URL by running these commands:\n  export POD_NAME=$(kubectl get pods --namespace default -l \"app.kubernetes.io\/name=helmdemo,app.kubernetes.io\/instance=helmdemo\" -o jsonpath=\"{.items&#91;0].metadata.name}\")\n  echo \"Visit http:\/\/127.0.0.1:8080 to use your application\"\n  kubectl --namespace default port-forward $POD_NAME 8080:80<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If we execute those commands, we can connect from our localhost to that webserver:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export POD_NAME=$(kubectl get pods --namespace default -l \"app.kubernetes.io\/name=helmdemo,app.kubernetes.io\/instance=helmdemo\" -o jsonpath=\"{.items&#91;0].metadata.name}\")\necho \"Visit http:\/\/127.0.0.1:8080 to use your application\"\nkubectl --namespace default port-forward $POD_NAME 8080:80<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"532\" height=\"289\" src=\"\/wp-content\/uploads\/2019\/11\/image-52.png\" alt=\"\" class=\"wp-image-558\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-52.png 532w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-52-300x163.png 300w\" sizes=\"auto, (max-width: 532px) 100vw, 532px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s enough trouble for our demo chart. Let&#8217;s now go ahead and update our blue\/green deployment to be deployable via Helm:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Updating our blue\/green deployment to Helm charts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To get started with updating our blue\/green deployment, let&#8217;s revisit the architecture:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"566\" height=\"420\" src=\"\/wp-content\/uploads\/2019\/11\/image-15.png\" alt=\"\" class=\"wp-image-500\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-15.png 566w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-15-300x223.png 300w\" sizes=\"auto, (max-width: 566px) 100vw, 566px\" \/><figcaption>Our blue-green architecture <\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And for this we created a couple of files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u251c\u2500\u2500 blue\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 configmap.sh\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 deployment.yaml\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 index.html\n\u251c\u2500\u2500 green\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 configmap.sh\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 deployment.yaml\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 index.html\n\u251c\u2500\u2500 service-prod.yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Looking back at this, and at the fact that we want to update this via Helm, we&#8217;ll have to change the way we create our configmap to also become part of a Helm chart. We&#8217;ll afterwards update our blue and green deployment files to also take certain inputs from the values file. Finally, we&#8217;ll update our <code>service-prod.yaml<\/code> to take in a parameter as well.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In terms of deploying a new version of the blue-green app &#8211; we&#8217;ll still do a two-step deployment. First we&#8217;ll update the deployment itself, and then we do the blue-green flip. This &#8220;wait&#8221; period is not something we&#8217;ll code into Helm, this is part of the manual process (and later of a CI\/CD pipeline, but that&#8217;s a topic for later).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll do a <code>helm create blue-green<\/code> to create some boilerplate Chart and values file. In what is created, we&#8217;ll delete the templates and tests via:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>rm templates\/*.*\nrm -rf tests<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll update our values file to look like this, to keep track of different versions that will be running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>blue:\n  version: 1\n\ngreen:\n  version: 1\n\nproduction: blue<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s start at the beginning, and creating our updated configmap. We&#8217;ll create this in<code> templates\/configmap.blue.yaml<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I installed a configmap first to get an overview of what the yaml should look like (just execute the configmap.sh script from before). This looks something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get configmap index-blue-v1 -o yaml\napiVersion: v1\ndata:\n  index.html: \"&lt;html>\\r\\n &lt;head>\\r\\n  &lt;title>Blue&lt;\/title>\\r\\n &lt;\/head>\\r\\n &lt;body bgcolor=\\\"#11a7f7\\\">\\r\\n\n    \\ &lt;h1>V1&lt;\/h1>\\r\\n &lt;\/body>\\r\\n&lt;\/html>\"\nkind: ConfigMap\nmetadata:\n  creationTimestamp: \"2019-11-10T15:53:25Z\"\n  name: index-blue-v1\n  namespace: default\n  resourceVersion: \"4195496\"\n  selfLink: \/api\/v1\/namespaces\/default\/configmaps\/index-blue-v1\n  uid: 3acf62ff-03d2-11ea-a11b-b2bece276eec<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will boil down to useful yaml like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\ndata:\n  index.html: \"&lt;html>\\r\\n &lt;head>\\r\\n  &lt;title>Blue&lt;\/title>\\r\\n &lt;\/head>\\r\\n &lt;body bgcolor=\\\"#11a7f7\\\">\\r\\n\n    \\ &lt;h1>V1&lt;\/h1>\\r\\n &lt;\/body>\\r\\n&lt;\/html>\"\nkind: ConfigMap\nmetadata:\n  name: index-blue-v1<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll pass in the version via our values file. So our chart for blue will look like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\ndata:\n  index.html: \"&lt;html>\\r\\n &lt;head>\\r\\n  &lt;title>Blue&lt;\/title>\\r\\n &lt;\/head>\\r\\n &lt;body bgcolor=\\\"#11a7f7\\\">\\r\\n\n    \\ &lt;h1>V{{ .Values.blue.version }}&lt;\/h1>\\r\\n &lt;\/body>\\r\\n&lt;\/html>\"\nkind: ConfigMap\nmetadata:\n  name: index-blue-v{{ .Values.blue.version }}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And similar for <code>configmap.green.yaml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\ndata:\n  index.html: \"&lt;html>\\r\\n &lt;head>\\r\\n  &lt;title>Green&lt;\/title>\\r\\n &lt;\/head>\\r\\n &lt;body bgcolor=\\\"#6dc066\\\">\\r\\n\n    \\ &lt;h1>V{{ .Values.green.version }}&lt;\/h1>\\r\\n &lt;\/body>\\r\\n&lt;\/html>\"\nkind: ConfigMap\nmetadata:\n  name: index-green-v{{ .Values.green.version }}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next up, we&#8217;ll need a deployment object. As a reminder, our <code>deployment.yaml<\/code> looked like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1beta1\nkind: Deployment\nmetadata:\n  name: blue\nspec:\n  replicas: 10\n  template:\n    metadata:\n      labels:\n        app: web-version\n        color: blue\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n        ports:\n        - containerPort: 80\n        volumeMounts:\n        - name: config-volume\n          mountPath: \/usr\/share\/nginx\/html\n      volumes:\n      - name: config-volume\n        configMap:\n          name: index-blue-v1\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: blue\nspec:\n  type: LoadBalancer\n  ports:\n  - port: 80\n  selector:\n    color: blue<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll need to update this to take the version from a values file. e.g. deployment.blue.yaml looks like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1beta1\nkind: Deployment\nmetadata:\n  name: blue\nspec:\n  replicas: 10\n  template:\n    metadata:\n      labels:\n        app: web-version\n        color: blue\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n        ports:\n        - containerPort: 80\n        volumeMounts:\n        - name: config-volume\n          mountPath: \/usr\/share\/nginx\/html\n      volumes:\n      - name: config-volume\n        configMap:\n          name: index-blue-v{{ .Values.blue.version }}\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: blue\nspec:\n  type: LoadBalancer\n  ports:\n  - port: 80\n  selector:\n    color: blue<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, we&#8217;ll also turn our production-service into a Helm chart. For this, we introduced value, called <code>production <\/code>to reference which color is now in production.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Service\nmetadata:\n  name: production\nspec:\n  type: LoadBalancer\n  ports:\n  - port: 80\n  selector:\n    color: {{ .Values.production }}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With our templates at the ready, we can go ahead and run our end-to-end example for a first time. We&#8217;ll install via <code>helm install bluegreen .<\/code> (this has to be executed from the directory that contains Chart.yaml)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Doing a <code>kubectl get all<\/code> will show us that we have all our services and deployments running. Let&#8217;s wait a minute for our production service to get a public ip address.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once we have a production IP address, we can see that blue is now our production service. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2019\/11\/image-53.png\" alt=\"\" class=\"wp-image-564\" width=\"417\" height=\"127\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-53.png 417w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-53-300x91.png 300w\" sizes=\"auto, (max-width: 417px) 100vw, 417px\" \/><figcaption>Blue is our production to start with.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s do an update to green via helm. This time, without touching the values.yaml file, but by setting a runtime value in helm: <code>helm upgrade bluegreen . --set green.version=1.1<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This again has no impact on our production service, which we can flip around with yet another helm command: <code>helm upgrade bluegreen . --set production=green<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Looking at our production service now, this doesn&#8217;t look like expected. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"406\" height=\"117\" src=\"\/wp-content\/uploads\/2019\/11\/image-54.png\" alt=\"\" class=\"wp-image-565\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-54.png 406w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-54-300x86.png 300w\" sizes=\"auto, (max-width: 406px) 100vw, 406px\" \/><figcaption>After setting green to production, we&#8217;re back to v1, not v1.1.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This isn&#8217;t version 1.1 anymore. What happened here is that Helm reverted to the values file, as we no longer set v1.1 via the command line. We can force Helm to use server values via the flag <code>--reuse-values<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s try this out by updating blue:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm upgrade bluegreen . --set blue.version=1.1\nkubectl rollout status deploy\/blue #wait for blue to finish deploying\nhelm upgrade bluegreen . --set production=blue --reuse-values<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"407\" height=\"124\" src=\"\/wp-content\/uploads\/2019\/11\/image-55.png\" alt=\"\" class=\"wp-image-566\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-55.png 407w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/image-55-300x91.png 300w\" sizes=\"auto, (max-width: 407px) 100vw, 407px\" \/><figcaption>Looking better now!<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">With all of this done, we have turned our blue-green deployment into Helm charts, and now have a better update mechanism. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">During this blog post we explored Helm v3. We updated our deployment for blue-green deployments into Helm charts. I personally learned two important gotchas with Helm:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Don&#8217;t dynamically name object names in Kubernetes is you don&#8217;t want Helm to delete the old ones. In the first passthrough of this post, I didn&#8217;t have a deployment.green.yaml and a deployment.blue.yaml, I just had a deployment.yaml and passed in the color via a value. That worked well to deploy just one, but to deploy the other color, Helm actually deleted the other color. So, we ended up with both a blue and a green deployment and configmap.<\/li><li>Be careful with setting values via the command-line and use <code>--reuse-values<\/code> to avoid mistakes.<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">With that out the way, we can now use these Helm charts to do a blue-green deployment from a CI\/CD pipeline. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Helm v3 recently became generally available. This looks like a great time to discover what Helm can help you do in your journey building cloud-native applications. I&#8217;ve been playing around with Helm charts (using v2) for a while, but I have never written a chart from zero (I typically use charts I find online and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":568,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[3,58,5],"tags":[51,59,18],"class_list":["post-557","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-kubernetes","category-open-source","tag-blue-green","tag-helm","tag-kubernetes"],"jetpack_featured_media_url":"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/11\/2019-11-23-11_20_16-Window.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/557","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=557"}],"version-history":[{"count":7,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/557\/revisions"}],"predecessor-version":[{"id":569,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/557\/revisions\/569"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media\/568"}],"wp:attachment":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=557"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=557"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=557"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}