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. I should mention that there is also another interesting tool ksonnet which does 'the same', of course in its own way. I didn't try the ksonnet yet, so I can not comment on how good it is or to compare it to the Helm. 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 this great video by Lachlan Evenson
You need to watch this! It gave me some ideas and I started to look into the Helm. Reading the official Helm docs 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 your 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 (truncated and 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 es-operator 1 viable-unicorn 1
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 NOTES.txt template and it can display some 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. 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 your 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 many apps. 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 your 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 repo 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 repo 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 your Kubernetes apps. For me, now it is the time to explore ksonnet and compare it in some future blog post, so stay tuned.