Easier Kubernetes Deployments with Helm3 & ChartMuseusm
Helm is a package manager for Kubernetes. It helps you define, install, and upgrade even the most complex Kubernetes application. Helm is a graduated project in the CNCF and is maintained by the Helm community. It has been around for some time now. Recently the v3 was released which has the tiller removed and made Helm more flexible and user friendly. Helm can be used to:
- create, share, and host your own packages
- install packages and query any k8s cluster to see what packages are installed and running (The Helm Community provides 800+ Helm charts!)
- upgrade, delete, rollback, or view the history of installed packages
This article is an introduction to what is Helm and what it can do for you and how to store the Helm packages on a repository provided by Chartmuseum.
Prerequisites
You should have access to a Kubernetes cluster and have a local configured kubectl. For the latest release of Helm, it is recommended to have the latest stable release of Kubernetes, which in most cases is the second-latest minor release. You should also be confident with Kubernetes Objects
You should also install Helm which can be done using tools like homebrew on a Mac. Follow the installation guide on the Helm documentation page.
Introduction
The Kubernetes Objects of an application are defined in separate YAML files, and with the kubectl command line tool are deployed on a cluster.
A Helm chart encapsulates each of these YAML definitions, provides a mechanism for configuration at deploy-time, and allows you to define metadata and documentation that might be useful when sharing the package.
Creating a chart
The helm create command is the best way to bootstrap your first chart. It creates the directory structure and files you can then build on.
$ helm create mychart $ tree ./mychart ./mychart ├── Chart.yaml ├── charts ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── ingress.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── tests │ └── test-connection.yaml └── values.yaml 3 directories, 9 files
The most important part of a chart is the templates directory. You can edit or replace the files in that directory with your own. In this directory, you can create subdirectories which will also be processed by Helm. In the end, you will have a working chart that can be deployed using the helm install command.
Helm uses the Go template rendering engine to process those templates. If we take a look at the service.yaml in the templates directory you will almost immediately note special tags that Helm will use to generate a valid Service YAML.
apiVersion: v1 kind: Service metadata: name: {{ include "mychart.fullname" . }} labels: {{- include "mychart.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port }} targetPort: http protocol: TCP name: http selector: {{- include "mychart.selectorLabels" . | nindent 4 }}}
Now if we run helm install with the --dry-run flag we will notice that the produced service.yaml file has all the labels populated, the port and selector configured:
$ helm install --generate-name --dry-run --debug ./mychart ... --- # Source: mychart/templates/service.yaml apiVersion: v1 kind: Service metadata: name: mychart-1588661695 labels: helm.sh/chart: mychart-0.1.0 app.kubernetes.io/name: mychart app.kubernetes.io/instance: mychart-1588661695 app.kubernetes.io/version: "1.16.0" app.kubernetes.io/managed-by: Helm spec: type: ClusterIP ports: - port: 80 targetPort: http protocol: TCP name: http selector: app.kubernetes.io/name: mychart app.kubernetes.io/instance: mychart-1588661695 --- ...
The Helm-specific object .Chart is used to provide the metadata about the chart to the definitions such as name, or version. The .Values is used to expose configuration that can be set at the time of deployment. The defaults are defined in the values.yaml file provided with the chart.
Now if we change in the values.yaml file the port of the Service definition and run again the helm install with dry-run we will find the targetPort in the Service and containerPort in the Deployment changed to the value you specified. The use of templating can greatly reduce boilerplate and simplify your definitions.
The user of the chart could override the values with a command like this:
$ helm install --generate-name --dry-run --debug ./mychart --set service.port=8080 ... --- # Source: mychart/templates/service.yaml apiVersion: v1 kind: Service metadata: ... spec: type: ClusterIP ports: - port: 8080 targetPort: http protocol: TCP name: http selector: ...
The partials and functions defined in _helpers.tpl are also used in the service.yaml. You should take a look at the Helm Chart Template Guide for detailed reference on how to use functions, partials, and flow control when developing a chart.
The NOTES.txt file is a templated, plaintext file that gets printed out after the chart is successfully deployed. It is commonly used to describe the next steps for using a chart. NOTES.txt is run through the template engine so you can use templating to print out working commands for obtaining an IP address, or getting a password from a Secret object etc.
Deploying
Let's try to deploy this chart on a Kubernetes cluster with:
$ helm install example ./mychart --set service.type=NodePort NAME: example LAST DEPLOYED: Tue May 5 09:36:31 2020 NAMESPACE: default STATUS: deployed REVISION: 1 NOTES: 1. Get the application URL by running these commands: export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services example-mychart) export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT
The output of helm install displays a summary of the state of the release, what objects were created, and the rendered NOTES.txt file to explain what to do next.
Now let's run the commands in the output to get a URL to access the NGINX service and open it in our browser and see the NGINX welcome page.
To delete the Helm chart from the k8s cluster we just run:
$ helm delete example release "example" uninstalled
Packaging
Now that we have a working chart we can package it to be able to share it. We've been using the local, unpackaged chart so far and the helm install command to run it. The Helm package is just a gzipped tar of our directory. We can package our example chart like this:
$ helm package ./mychart Successfully packaged chart and saved it to: /Users/sosic/projects/mychart-0.1.0.tgz
To install the chart via Helm package we run:
$ helm install example mychart-0.1.0.tgz --set service.type=NodePort NAME: example LAST DEPLOYED: Tue May 5 09:57:42 2020 NAMESPACE: default STATUS: deployed REVISION: 1 NOTES: 1. Get the application URL by running these commands: export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services example-mychart) export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath= "{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT
Chartmuseum
We now have a highly customized chart ready. The only thing that remains is to distribute it on repositories. That's where Chartmuseum comes to action.
ChartMuseum is an open-source Helm Chart Repository written in Go (Golang), with support for cloud storage backends, including Google Cloud Storage, Amazon S3, Microsoft Azure Blob Storage, Alibaba Cloud OSS Storage and Openstack Object Storage. ChartMuseum is a child project under the Helm umbrella, maintained primarily by Codefresh.
Why use Chartmuseum?
- It' easier to distribute your charts across different deployment pipelines or repositories
- It exposes an API for chart manipulation
- Reduces maintenance - you can share charts or templates easily
- Requires no effort to set it up on k8s
Installing
To deploy Chartmuseum we can use Helm obviously ;) By default the Helm chart of Chartmuseum installs with DISABLE_API set to true, we will override it with the false value for the purpose of this tutorial.
$ helm install chartmuseum --namespace default stable/chartmuseum --version 2.7.1 --set env.open.DISABLE_API=false NAME: chartmuseum LAST DEPLOYED: Tue May 5 10:15:07 2020 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: ** Please be patient while the chart is being deployed ** Get the ChartMuseum URL by running: export POD_NAME=$(kubectl get pods --namespace default -l "app=chartmuseum" -l "release=chartmuseum" -o jsonpath="{.items[0].metadata.name}") echo http://127.0.0.1:8080/ kubectl port-forward $POD_NAME 8080:8080 --namespace default
To see the contents of the package and defaults please refer to the Chartmuseum chart repository on GitHub.
Charmuseum can be configured with persistence storage and ingress to be reachable from the internet. If you want a private helm repository it is best to secure the ingress with basic HTTP access authentication or used it on a private LAN.
How to use
For this tutorial I am installing the chartmuseum on my local k8s cluster and forwarding the port manually to use the repo:
$ kubectl port-forward chartmuseum-chartmuseum-7bf84f4d57-hmbts 8080:8080 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080 Handling connection for 8080
We can now add the repo to helm with:
$ helm repo add demo http://localhost:8080 "demo" has been added to your repositories
To list the repositories we run:
$ helm repo list NAME URL demo http://localhost:8080
Now let's upload our chart to the repo with:
$ curl --data-binary "@mychart-0.1.0.tgz" http://localhost:8080/api/charts {"saved":true}
We need to update the repo with:
$ helm repo update Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "demo" chart repository Update Complete. ⎈ Happy Helming!⎈
To list the available packages on the repository:
$ helm search repo demo NAME CHART VERSION APP VERSION DESCRIPTION demo/mychart 0.1.0 1.16.0 A Helm chart for Kubernetes
And finally to install our package from the Chartmuseum repo we can run:
$ helm install mychart demo/mychart NAME: mychart LAST DEPLOYED: Tue May 5 10:53:44 2020 NAMESPACE: default STATUS: deployed REVISION: 1 NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=mychart" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace default port-forward $POD_NAME 8080:80
Versioning
During its life, our app will have different versions. We will use helm package and curl to upload the new package to our repository. The helm search will always show the last version available on the Chartmuseum repo.
We can also visualize the various versions available on the repository adding the -l flag:
$ helm search repo demo/mychart -l NAME CHART VERSION APP VERSION DESCRIPTION demo/mychart 0.2.0 1.16.0 A Helm chart for Kubernetes demo/mychart 0.1.0 1.16.0 A Helm chart for Kubernetes
The latest version of the demo/mychart package differs from the one installed on our cluster:
$ helm list NAME NAMESPACE REVISION STATUS CHART APP VERSION mychart default 1 deployed mychart-0.1.0 1.16.0
To upgrade our app now we simply run:
$ helm upgrade mychart demo/mychart Release "mychart" has been upgraded. Happy Helming! NAME: mychart LAST DEPLOYED: Tue May 5 11:03:58 2020 NAMESPACE: default STATUS: deployed REVISION: 2 NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=mychart" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace default port-forward $POD_NAME 8080:80
The whole deploying and upgrading/downgrading subject is complex and I'm not going to describe it here, but there is a detailed tutorial on deploying, scaling and upgrading an application on Kubernetes using Helm on the Bitnami documentation pages.
Conclusions
As seen in this brief article using Helm has multiple advantages on how we handle our applications in Kubernetes. With the addition of Chartmuseum storing and versioning the packages of our application becomes a piece of cake. Also, the whole deployment of this tool is simple and easy to do that's why I recommend it to all of my clients.
Happy Helming!