Helm Charts — Kubernetes

There are different ways of running production services at a high scale. One popular solution for running containers in production is Kubernetes. But interacting with Kubernetes directly comes with some caveats. Kubernetes is a very helpful tool for cloud-native developers. But it doesn’t cover all the bases on its own — there are some things that Kubernetes cannot solve or that are outside its scope.

What is Helm?

Helm is a Kubernetes package and operations manager. The name “kubernetes” is derived from the Greek word for “pilot” or “helmsman”, making Helm its steering wheel. Using a packaging manager, Charts, Helm allows us to package Kubernetes releases into a convenient zip (.tgz) file. A Helm chart can contain any number of Kubernetes objects, all of which are deployed as part of the chart. A Helm chart will usually contain at least a Deployment and a Service, but it can also contain an Ingress, Persistent Volume Claims, or any other Kubernetes object.

Why Do We Need Helm?

Kubernetes objects are challenging to manage. With helpful tools, the Kubernetes learning curve becomes smooth and manageable. Helm automates maintenance of YAML manifests for Kubernetes objects by packaging information into charts and advertises them to a Kubernetes cluster.

The Purpose of Helm

Helm is a tool for managing Kubernetes packages called charts. Helm can do the following:

  • Package charts into chart archive (tgz) files
  • Interact with chart repositories where charts are stored
  • Install and uninstall charts into an existing Kubernetes cluster
  • Manage the release cycle of charts that have been installed with Helm
  1. The config contains configuration information that can be merged into a packaged chart to create a releasable object.
  2. A release is a running instance of a chart, combined with a specific config.

Components

Helm is an executable which is implemented into two distinct parts:

  • Managing repositories
  • Managing releases
  • Interfacing with the Helm library
  • Sending charts to be installed
  • Requesting upgrading or uninstalling of existing releases
  • Installing charts into Kubernetes, and providing the subsequent release object
  • Upgrading and uninstalling charts by interacting with Kubernetes

Implementation

The Helm client and library is written in the Go programming language.

How Does Helm Work?

Helm and Kubernetes work like a client/server application. The Helm client pushes resources to the Kubernetes cluster. The server-side depends on the version: Helm 2 uses Tiller while Helm 3 got rid of Tiller and entirely relies on the Kubernetes API.

Charts

Helm uses a packaging format called charts. A chart is a collection of files that describe a related set of Kubernetes resources. A single chart might be used to deploy something simple, like a memcached pod, or something complex, like a full web app stack with HTTP servers, databases, caches, and so on.

How to Create a Helm Chart

It’s pretty easy to create a chart in Helm. First, you need to have Helm installed. Then, just type in helm create <chart name> and it will create a directory filled with files and other directories. Those files are required for Helm to create a chart.

  • values.yaml: Like we saw before, this is the file that contains defaults for variables.
  • templates (dir): This is the place where you’ll put all your manifest files. Everything in here will be passed on and created in Kubernetes.
  • charts: If your chart depends on another chart you own, or if you don’t want to rely on Helm’s default library (the default registry where Helm pull charts from), you can bring this same structure inside this directory. Chart dependencies are installed from the bottom to the top, which means if chart A depends on chart B, and B depends on C, the installation order will be C ->B ->A.

How to Deploy a Simple Helm Application

Let’s get our hands dirty and make sure Helm is ready to use.

$ kubectl config use-context docker-desktop  Switched to context "docker-desktop".$ kubectl get node

NAME STATUS ROLES AGE VERSION
docker-desktop Ready master 20d v1.19.3
$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm install my-apache bitnami/apache --version 8.0.2
$ kubectl get pods  NAME                               READY   STATUS    RESTARTS   AGE
my-apahe-apache-589b8df6bd-q6m2n 1/1 Running 0 2m27s
$ helm list NAME     	REVISION	STATUS  	CHART       	VERSION
my-apache 1 deployed apache-8.0.2 2.4.46

How to Upgrade a Helm Application

We can upgrade our deployed application to a new version like this:

$ helm upgrade my-apache bitnami/apache --version 8.0.3$ helm listNAME     	REVISION	STATUS  	CHART      	VERSION
my-apache 2 deployed apache-8.0.3 2.4.46

How to Rollback a Helm Application

So let’s try to rollback to the first deployed version:

$ helm rollback my-apache 1                    Rollback was a success! Happy Helming!$ helm list

NAME REVISION STATUS CHART VERSION
my-apache 3 deployed apache-8.0.2 2.4.46
$ kubectl get secretNAME		  		  TYPE        	   	  	DATA   AGE
default-token-nc4hn kubernetes.io/sat 3 20d
sh.helm.release.v1.my-apache.v1 helm.sh/release.v1 1 1m
sh.helm.release.v1.my-apache.v2 helm.sh/release.v1 1 1m
sh.helm.release.v1.my-apache.v3 helm.sh/release.v1 1 1m

How to Remove a Deployed Helm Application

Let’s clean up our Kubernetes by removing the my-apache release:

$ helm delete my-apache

release "my-apache" uninstalled

How to Access Production-Ready Helm Charts

You can search public hubs for Charts that enable you to quickly deploy your desired application with a customizable configuration.

  • Instances of MariaDB for persistence and
  • Prometheus sidecar containers for each WordPress container to expose health metrics.

How to Deploy WordPress and MariaDB

There are different public hubs for Helm Charts. One of them is artifacthub.io. We can search for “WordPress” and find an interesting WordPress Chart.

$ helm repo add bitnami https://charts.bitnami.com/bitnami$ helm install my-wordpress bitnami/wordpress --version 10.1.4
$ echo Username: user
$ echo Password: $(kubectl get secret --namespace default my-wordpress-3 -o jsonpath="{.data.wordpress-password}" | base64 --decode)
Username: user
Password: sZCa14VNXe
$pw=kubectl get secret --namespace default my-wordpress -o jsonpath="{.data.wordpress-password}"
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($pw))
### Start 3 WordPress instances that will all receive 
### requests from our visitors. A load-balancer will distribute calls
### to all containers evenly.
replicaCount: 3
### start a sidecar container that will expose metrics for your wordpress container
metrics:
enabled: true
image:
registry: docker.io
repository: bitnami/apache-exporter
tag: 0.8.0-debian-10-r243
$ helm delete my-wordpress

release "my-wordpress" uninstalled

How to Start a Multi-instance WordPress and MariaDB Deployment

Deploy a new release using the production values:

$ helm install my-wordpress-prod bitnami/wordpress --version 10.1.4 -f values-production.yaml
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-wordpress-prod-5c9776c976-4bs6f 2/2 Running 0 103s
my-wordpress-prod-5c9776c976-9ssmr 2/2 Running 0 103s
my-wordpress-prod-5c9776c976-sfq84 2/2 Running 0 103s
my-wordpress-prod-mariadb-0 1/1 Running 0 103s

How to Access Exposed Health Metrics

We can check the exposed health metrics by proxying to one of the running pods:

kubectl port-forward my-wordpress-prod-5c9776c976-sfq84 9117:9117
apache_cpuload 1.2766
process_resident_memory_bytes 1.6441344e+07
$ helm delete my-wordpress-prod  release "my-wordpress-prod" uninstalled

MariaDB Helm Chart

The persistence of the helm Chart for WordPress depends on MariaDB. It builds on another Helm Chart for MariaDB that you can configure and scale to your needs by, for example, starting multiple replicas.

How to Create a Template for Custom Applications

Helm adds a lot more flexibility to your Kubernetes deployment files. Kubernetes deployment files are static by their nature. This means, adjustments like

  • environment variables or
  • CPU and memory limit

How to Start a Ghost Blog Using Docker

A simple instance can be started using pure Docker:

docker run --rm -p 2368:2368 --name my-ghost ghost
$ docker rm -f my-ghost  my-ghost
# file 'application/deployment.yaml'apiVersion: apps/v1
kind: Deployment
metadata:
name: ghost-app
spec:
selector:
matchLabels:
app: ghost-app
replicas: 2
template:
metadata:
labels:
app: ghost-app
spec:
containers:
- name: ghost-app
image: ghost
ports:
- containerPort: 2368
# file 'application/service.yaml'apiVersion: v1
kind: Service
metadata:
name: my-service-for-ghost-app
spec:
type: LoadBalancer
selector:
app: ghost-app
ports:
- protocol: TCP
port: 80
targetPort: 2368
$ kubectl apply -f ./appplication/deployment.yaml -f ./appplication/service.yaml  deployment.apps/ghost-app created
service/my-service-for-ghost-app created
$ kubectl delete -f ./appplication/deployment.yaml -f ./appplication/service.yaml  deployment.apps/ghost-app delete
service/my-service-for-ghost-app delete
$ helm create my-ghost-app  Creating my-ghost-app
# Chart.yamlapiVersion: v2
name: my-ghost-app
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: 1.16.0
# templates/deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: ghost-app
spec:
selector:
matchLabels:
app: ghost-app
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
app: ghost-app
spec:
containers:
- name: ghost-app
image: ghost
ports:
- containerPort: 2368
env:
- name: url
{{- if .Values.prodUrlSchema }}
value: http://{{ .Values.baseUrl }}
{{- else }}
value: http://{{ .Values.datacenter }}.non-prod.{{ .Values.baseUrl }}
{{- end }}
# templates/service.yamlapiVersion: v1
kind: Service
metadata:
name: my-service-for-my-webapp
spec:
type: LoadBalancer
selector:
app: ghost-app
ports:
- protocol: TCP
port: 80
targetPort: 2368
# values.yamlreplicaCount: 1
prodUrlSchema: false
datacenter: us-east
baseUrl: myapp.org
  • my-ghost-app/templates/serviceaccount.yaml
  • my-ghost-app/templates/ingress.yaml
  • my-ghost-app/templates/hpa.yaml
  • my-ghost-app/templates/NOTES.txt.
$ helm template --debug my-ghost-appinstall.go:159: [debug] Original chart version: ""
install.go:176: [debug] CHART PATH: /helm/my-ghost-app
---
# Source: my-ghost-app/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service-for-my-webapp
spec:
type: LoadBalancer
selector:
app: my-example-app
ports:
- protocol: TCP
port: 80
targetPort: 2368
---
# Source: my-ghost-app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ghost-app
spec:
selector:
matchLabels:
app: ghost-app
replicas: 1
template:
metadata:
labels:
app: ghost-app
spec:
containers:
- name: ghost-app
image: ghost
ports:
- containerPort: 2368
env:
- name: url
value: us-east.non-prod.myapp.org
# values.us-east.yaml
datacenter: us-east
# values.us-west.yaml
datacenter: us-west
# values.nonprod.yaml
replicaCount: 1
prodUrlSchema: false
# values.prod.yaml
replicaCount: 3
prodUrlSchema: true
$ helm template --debug my-ghost-app -f my-ghost-app/values.nonprod.yaml  -f my-ghost-app/values.us-east.yaml install.go:159: [debug] Original chart version: ""
install.go:176: [debug] CHART PATH: /helm/my-ghost-app
---
# Source: my-ghost-app/templates/service.yaml
# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service-for-my-webapp
spec:
type: LoadBalancer
selector:
app: my-example-app
ports:
- protocol: TCP
port: 80
targetPort: 2368
---
# Source: my-ghost-app/templates/deployment.yaml
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ghost-app
spec:
selector:
matchLabels:
app: ghost-app
replicas: 1
template:
metadata:
labels:
app: ghost-app
spec:
containers:
- name: ghost-app
image: ghost
ports:
- containerPort: 2368
env:
- name: url
value: http://us-east.non-prod.myapp.org
$ helm install -f my-ghost-app/values.prod.yaml my-ghost-prod ./my-ghost-app/NAME: my-ghost-prod
LAST DEPLOYED: Mon Dec 21 00:09:17 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ helm delete my-ghost-prod                                                 
release "my-ghost-prod" uninstalled
$ helm install -f my-ghost-app/values.nonprod.yaml -f my-ghost-app/values.us-east.yaml my-ghost-nonprod ./my-ghost-app
$ helm delete my-ghost-nonprod

How to Host a Helm Chart

Ok, you created your chart, now what? Do we have to download the entire repository to install those charts? No! Helm has a public library for the most used charts, which kind of works like Docker Hub.

ARTH | IIEC Rise | GoogleCloudReady Facilitator | Proficient Photographer | Quantum Computing Enthusiast