Helm application manager for Kubernetes. Using professional and hand-crafted charts.
Helm is the most widely used package manager for Kubernetes. It is difficult to imagine Docker without https://hub.docker.com/ and the relation is pretty similar in the case of Kubernetes and https://helm.sh/.
Cloud Native Computing Foundation depicts Helm as a major player in Application Definition & Image Build category by awarding it with the “Graduated” honor, leading position, and large size.
My plan is to construct an artificial application example consisting of a mixture of RabbitMQ message broker, wrapped in Helm package and maintained by Bitnami (so, I do not need to worry about this part myself but rather trust to maintainer) and very naive Rest API server (so, I put there my business-related logic) connecting to RabbitMQ and sending/receiving messages.
This plan is nothing too unusual for software developers, like writing Node.js application and digging into npm packages for libraries or coding .Net Core application and searching nuget for the same purpose. One part of the project I will conduct with my hands, other part is already done by somebody else. Contrary to these examples, my tiny project will be delivered by Helm to the Kubernetes cluster!
My Kubernetes playground will be minikube. The screenshots below reveal the fact I use Windows host machine for this article. Please consider this as firm guarantee that any Kubernetes cluster (cloud or host-based) and any host OS will be suitable for this task. Windows OS is known for its tricks to run Linux native stuff. In the very last remark, I assume that the reader has installed both minikube and Helm as these tasks are straightforward (old but still mostly actual video tutorial is here).
Good. So, let’s create new folder (/c/Temp/helmarticle), run Kubernetes cluster with minikube and make sure none of Helm packages installed so far.
Open Helm site, select Charts tab and got redirected to https://artifacthub.io/ which is a public repository, and search there for RabbitMQ. The result page is RabbitMQ packaged by Bitnami.
Doing the first instruction to point local Helm where to get packages for Bitnami:
However, the plan is not to install RabbitMQ from the remote repository, but to pull it locally to investigate it(remember, I am going to create my very own chart soon). Another important reason is to stay independent from the remote registry as it can have new incompatible version of the package in the future, it can be unreachable or unexisting at all. Helm chart pulling allows to have all package locally and to store it in the source code repository as the best practice approach suggests.
Pulling chart content and untarring it into local subfolder:
At first glance, the ./rabbitmq subfolder content is somewhat scary:
It’s the right time to read Helm chart getting started pages. At this time it’s important to look at two areas.
First is the content of templates folder. The YAML files describe concrete pieces of Kubernetes deployment. svc.yaml is for Service, ingress.yaml is nginx routes etc. In fact, the structure of that files is similar to Kubernetes manifest files with the majority of lines “tweaked” with double curly braces {{ … }}. These braces are Go template language injections and YAML files are passed through the transformation pipeline which “executed” Go constructions inside braces and substitutes curly braces with the execution results. In such a way, output files are pure Kubernetes manifest files, which are applied to Cluster. To make it simpler, better to concentrate on files structure here.
Second is value values.yaml. It has more than 1000 lines mostly with documentation what which value is responsible for what (same details can be found on the official Helm chart description page). Basically, all these values are used in transformation pipeline as input to Go template injections and imply on result manifest files.
Both areas look very big and complicated indeed but bare with me until I create a hand-crafted chart myself and this cart will be simple but touches both menationd areas. In addition, what is shown are really professional and well-maintained chart which does a lot more than just run RabbitMQ server.
I, as the end-user, will only need to modify some out of the many values that the maintainer is proposed to me in the chart description. This correction can be done in two ways. First, and I am going to show it now, via helm command parameters. Second approach, and I will show it later, is with help of custom YAML files which can be created (and stored in the repository along with other files) to overwrite any variable value from values.yaml. Given the fact of good chart values documentation existence, this should not be hard, if needed at all.
My plan is to overwrite RabbitMQ instance user and password and cluster cookie. It can be done with — set parameter followed by overwriting value pairs(RTFM for chart to understand what each value does).
Important command pieces:
my-rabbitmq is future Helm chart application name, Kubernetes object name parts, network address to talk to RabbitMQ.
— set is the value is admin credentials.
./rabbitmq/ is subfolder pass where pulled chart is located.
And the output in my console:
Please pay attention to the output and read carefully through it. Especially, in case of reinstallation or having trouble connecting to RabbitMQ Admin UI!!!
Enter RabbitMQ Admin UI port-forward command from installation output:
Opening in browser http://localhost:15672/ should result in credentials enter window:
So nice, that it has been overwritten in Helm chart install command and credentials are known now. After entering it, the RabbitMQ Admin UI is shown:
With that, the first half of the plan is implemented, Helm is used to deploy RabbitMQ in Kubernetes cluster. Bitnami organization, as a professional chart maintainer, helped a lot.
helm list shows that one application with name my-rabbitmq is deployed. Name specified during installation and takes part into Kubernetes cluster objects naming;
kubectl get all reveals what is in cluster at the moment. There are two RabbitMQs services, a single file system to cope with statefulness and a single pod as compute entity.
There is a wide range of options in Helm chart (remeber, Values.yaml and documentation) to add “steroids” to RabbitMQ, like increase reliability with multiple pods, create RabbitMQ cluster or join existing cluster and many more things. That highlights the power of Helm: setting parameters of chart is an easy thing, while taking bare RabbitMQ Docker image and supporting it with setup scripts, configurations and other things to achieve your targets is time consuming process.
Now, let’s dive into our hand-crafted Helm chart creation adventure.
The idea is to have a trivial REST API service talking to RabbitMQ. Whole sources is here. It has single /heartbeats REST API get point to return all messages found in queue ‘queue#1’ of sibling RabbiMQ. To make thing a bit dynamic, this service also has sendHeartbeat() function which runs every second in a loop. Function populates ‘queue#1’ with message which includes current UTC time and valid for 30 seconds. That naive server implementation is baked into Docker container and uploaded to Docker hub.
My Helm chart is going to wrap ‘heartbeat’ server Docker image. Command helm create produces a skeleton folder structure for the new Helm chart.
helm create my-serverhb scaffolds new chart with name my-serverhb (where serverhb is for ‘server heartbeat’ and suffix ‘my’ puts a stress on custom nature)in subfolder my-serverhb;
mv ./my-serverhb/ ./serverhb renames subfolder with custom chart to be consistent with ‘rabbitmq’ sibling;
ls ./serverhb/ shows an initial chart folder structure (which is similar to RabbitMQ by Bitnami chart from above)
Let’s clean not needed stuff and greatly simplify the chart. For that, remove values.yaml and clean everything inside templates folder. Due to the fact charts folder is empty, the last file standing is Chart.yaml.
General 5 lines Helm chart description which does not even require editing.
Next step is to put into templates folder the one and only one yaml file taken from here. In fact, this file is standard Kubernetes manifest file (it even can be run with kubectl apply -f serverhb-k8s.manifest.yaml straightaway resulting with very same, as later we will do with Helm chart, Kubernetes resources created in cluster).
Above Kubernetes manifest file creates k8s service (#24) with name heartbeat-webapp (#26) which consists of any selector matching app: webapp (#30) and exposed internal IP port 3000 (#34) as 30080 (#35) as NodePort (#37). On the resource side of the application (based on selector app: webapp (#8)) the deployment (#2) found. This deployment has one replica (#9) of container (#16) based on Docker image yurgenwk/demo-server-hb:v2 (#17) from Docker hub repository with overriding container environment MQ_URL (#19) to value amqp://supervisor:supervisor@my-rabbitmq:5672/ (#20).
Thus, the initial version of custom Helm chart has only two files (renamed manifest file to main.yaml):
Install hand-crafted Helm chart and see Kubernetes resources:
helm install my-serverhb ./serverhb/ installs chart with name my-serverhb from folder ./serverhb;
helm list shows that Helm maintains two applications now;
kubectl get all lists all resources in the cluster.
To access NodePort in minukube need to enter:
In my case access URL on localhost was http://127.0.0.1:56975 which along with suffix path, showed current heartbeats which were get from RabbitMQ deployed as sibling Helm chart:
To recap the achievement: there are are two Helm charts installed in one Kubernetes cluster. The first is RabbitMQ message bus wrappered by the professional team to be highly configurable while staying easy to install and use. The second is naive REST API server baked inside Docker image which communicates with RabbitMQ server to send and get messages from it.
The last piece of change here is to start using Helm template functionality in the just-built custom chart. This step is not only to add respectable configurability to it but shows the approach how to dig into other third-party charts, even RabbitMQ packaged by Bitnami which plays now an essential role for us.
Changed main.yaml file:
#19 makes Docker image tag dynamic, so it can be specified somewhere else and there is no need to change current file whenever new version of heartbeat server released. Intro into syntax of template directive can be found here. In addition, this line uses default template function and pipeline.
#23 uses the whole value specified somewhere outside as the RabbitMQ address (my-rabbitmq in current example) will be different in each deployment and credentials to connect (supervisor:supervisor) might vary. For sure, all sensitive information should be sorted and referenced accordingly and Kubernetes Secret is to the rescue.
Looking above at RabbitMQ professional chart, the file values.yaml can be easily spotted. That is default location where Helm searches for values as soon as it finds {{ .Values.xxx }} in any template YAML file. Overall Helm has a sophisticated mechanism to supply values, but here the default values.yaml is more than enough.
YAML has a hierarchical structure. Thus, {{ .Values.app.version | default “latest”}} from main.yaml, transforms into v2 with this values.yaml or to latest with different/missing values.yaml. Second substitution case {{ .Values.app.env.rabbitmq }} is even simplier.
The final custom Helm chart configuration is still simple: static templates folder with only one main.yaml which has content very similar to classic Kubernetes manifest file, except two lines which might be dynamic, thus having Helm templates in it. The second file values.yaml is in root folder and has values to be placed in dynamic places in template. The third file Chart.yaml has obvious chart file description (let’s set version of chart to 1.17.0, since it has more dynamic parameters in it).
Next, the old version (1.16.0) of heartbeat server needs to be removed:
And new Helm chart version(1.17.0) should be installed:
And the access to heartbeat service NodePort can be granted with port forward command same as earlier
Since functionality has not changed, the access to /heartbeats endpoint should show “pulses” stored in RabbitMQ queue.
Final Helm charts can be found here and whole article related files are here.
Helm has great documentation for sure. Majority of modern stuff is wrapped as Helm charts and published on https://artifacthub.io/. Own components delivering is also easier with Helm and it is a nice learning path to understand how to tune any professional and well-maintained Helm charts.
In the next article, I will modify current Helm charts to handle secrets properly: