Writing a bunch of Kubernetes configuration files is not so much fun. For a few containers, you will end up with 10+ yaml files. Maintaining them is only one issue, but running in different environments or using the same files for CI/CD is a nightmare. You can always use some bash tricks to replace some values, but that is a bad practice. Here is where Helm comes into place. In this post, I will show you why Helm is a must have for Kubernetes applications, the procedure of packaging Kubernetes applications with Helm, and also how to use Helm for deployment of some complex applications you might have.
The application that I was working on lately is complex and I had a directory of 65+ Kubernetes config files. You feel my pain :). The main question was, how I'm gonna deploy this application to many environments? Or how to make a CI/CD with Kubernetes? Doing some bash tricks was not an option, at least I didn't like the idea. Then I started to research the CI/CD pipelines with Kubernetes and I found that some teams are integrating Helm into the process. Reading the official Helm documents and doing some research was very convincing that this is what we need for Kubernetes applications.
Think of Helm charts like application package where you have a dependency management, different types of hooks (pre-install, pre-upgrade, post-install, etc.) with easy upgrades or rollbacks. Yes, Helm supports revisions and you can see them if you list all installed charts on cluster. But first, you need to install the server component - Tiller, and the client - Helm. Tiller runs inside of your Kubernetes cluster and manages releases (installations) of your charts. Let's install the Helm and run the list command after I installed some charts:
⚡ brew install kubernetes-helm ⚡ helm init $HELM_HOME has been configured at /Users/alen/.helm. Happy Helming! ⚡ helm list NAME REVISION UPDATED STATUS CHART NAMESPACE autoscaler 2 Fri Jan 26 13:32:53 2018 DEPLOYED cluster-autoscaler-0.4.0 kube-system es-operator 1 Wed Feb 28 16:53:39 2018 DEPLOYED elasticsearch-operator-0.1.1 kube-system
Also, you only have one file with all values that are used in different templates. Let's create a example Helm chart to see the directory structure:
⚡ helm create test-app Creating test-app
Ok, Helm created the following files for us:
⚡ tree test-app/ test-app/ ├── Chart.yaml ├── charts ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── ingress.yaml │ └── service.yaml └── values.yaml
You use the
Chart.yaml file to describe this chart and to version it. Charts directory is where your dependency goes, but more on that later. As you can see, all Kubernetes config files are templates. You have one file which configures the app and where you store all values -
values.yaml. Isn't this great? You can provide your own values file during the installation of the package or you can pass some values you want to change as parameters. For example, to pass username and password for a private Docker registry you need to use
⚡ helm install --name test --set imageCredentials.username=TEST,imageCredentials.password=TEST \ test-app/
And to pass a complete values file which you might have per environment:
⚡ helm install --name test -f /path/to/values-test.yaml test-app/
NOTE: In those examples my application is available in directory
test-app/ on my local machine. This is the Helm chart directory. Having the charts in local directory is great for testing, but usually, you will add it to the remote chart repository and install the application from there.
When you run the Helm install command you will get some message displayed. This message is a result of NOTES.txt template and it can display messages according to the values after you install the chart. For example, if there is some required value which is missing you could display ERROR message here, or you could provide Ingress URL, etc. It can be useful if you are installing official charts from kubeapps. The best thing about Helm is that applications are sharable and you can find many of them at official kubeapps page. So, if you want to install, for example, Nginx Ingress controller you don't need to write your own chart. Here is the example:
⚡ helm install stable/nginx-ingress --namespace kube-system
During the chart development, you want to see how populated templates look like. You will actually see the usual Kubernetes config files, but without applying them to the cluster. To do that use
⚡ helm install --name test --debug --dry-run test-app/
Also, you can see that I set the name to the Helm install command. This is a release name. With different release names, you can install the same chart in the same namespace. If not set, Helm will autogenerate it.
I hope I gave you some good examples of why Helm is so great. To get started with Helm please check official quickstart guide. The last but not least, there are some concerns about Helm and security. I recommend that you take a look at this great post Exploring The Security Of Helm before using it in production.
Installing Complex Applications with Helm
After I created my first chart I was researching how to install a complex application with Helm. You know, the application which has many dependencies. Installing one Helm chart should install all required services to run the complex application.
There is something called umbrella chart. You can create one big chart and everything else is a dependency on that chart. You can put all dependencies into this chart and also create some config maps and secrets that would be available in all charts under the umbrella. You can take a look at EFK stack umbrella chart as the real world example. The values file then can configure any dependent chart, so again you have one file which can manage all the charts for this application. For example:
⚡ tree umbrella-chart/ umbrella-chart/ ├── Chart.yaml ├── requirements.yaml ├── templates │ ├── _helpers.tpl │ ├── configmap.yaml │ └── registry-secret.yaml └── values.yaml
When you add all dependencies to the
requirements.yaml file and run
helm dep update umbrella-chart/ you will add them to the main chart. Finally, the chart will look like this:
⚡ tree umbrella-chart/ umbrella-chart/ ├── Chart.yaml ├── charts │ ├── app-0.1.0.tgz │ ├── app2-0.1.0.tgz │ ├── app3-0.1.0.tgz ├── requirements.lock ├── requirements.yaml ├── templates │ ├── _helpers.tpl │ ├── configmap.yaml │ └── registry-secret.yaml └── values.yaml
The umbrella values file can configure all dependencies. You need to use the dependency chart name to distinguish multiple applications. This overrides values file for a particular application:
app: image: test tag: latest replicas: 1 app2: image: test2 tag: latest replicas: 1 app3: image: test3 tag: latest replicas: 1
When everything is set you can run the install command for your complex application and all its dependencies:
⚡ helm install --name test \ --set imageCredentials.username=TEST,imageCredentials.password=TEST \ -f my-values.yaml \ umbrella-chart/
Then, if you want to install the same chart on another environment, but you also want to change some values, you need to create only one values file per environment even if you have 20+ services under the umbrella chart. Another environment can be in the same cluster and namespace. And, if you want to upgrade a Helm release, after changing some values or versions run the upgrade command:
⚡ helm upgrade test -f my-values.yaml umbrella-chart/
Depending of the resource type you have this command will do rolling updates.
I try to contribute to the official repository to make the official Helm charts even better. My first contribution was small, but you need to start somewhere:
Bitnami is a Helm supporter among others and you can find a lot of charts in the official charts repository which you can use. Or, you can explore them to get some ideas for your project. I hope that I gave you the short introduction to Helm and shortly explained why you should use it for Kubernetes applications. For me, now it is the time to explore ksonnet and compare it in some future blog post. Stay tuned for the next one.