03-04 Helm charts

Docker/Kubernetes workshop

03-04 Helm charts

You will learn about:

Start

You are provided with:

Helm charts

Helm is an application package manager for Kubernetes.

In principle, it provides similar features to other existing package managers:

You may be already using a package manager for:

Helm charts can be installed from and stored in repositories. Tools like Artifactory support the storage of private Helm charts in addition to private Docker images.

Helm charts also provide ways to configure the applications with default values or overrides making them customisable.

Install the Helm CLI

Windows

Install the following packages with Chocolatey:

  choco upgrade kubernetes-helm

MacOS

Install the following packages with Homebrew:

  brew install kubernetes-helm

Installing a Helm chart from a public repository

To get familiar with the Helm CLI, we are going to install the metabase application in our cluster.

We first need to add the official Helm chart repository to our list of available repositories:

helm repo add stable https://kubernetes-charts.storage.googleapis.com

Let’s search for it in the public stable chart repository with:

helm search repo metabase

A complete list of all the available charts is also available at https://github.com/helm/charts/tree/master/stable or with helm search repo.

The name of the metabase chart is stable/metabase. Download it to look at its content:

helm fetch --untar stable/metabase
cd metabase

The structure of the chart is as following:

Metabase has no dependencies. For Charts containing a requirements.yaml, it is possible to list and update dependencies with:

helm dep list
helm dep update

Let’s install the metabase chart. Each unique installation of a Helm chart is identified by a name. We can let Helm autogenerate one for us, but it’ll be easier if we set our own name:

# Windows only
$env:NAMESPACE="[username-placeholder]" # should be the same as what you used in exercise 03-01
$env:RELEASE_NAME=$env:NAMESPACE + "-metabase"

# MacOS only
export NAMESPACE=[username-placeholder] # should be the same as what you used in exercise 03-01
export RELEASE_NAME="$NAMESPACE-metabase"

Install stable/metabase in the cluster with:

# Windows only
helm install $env:RELEASE_NAME stable/metabase

# MacOS only
helm install $RELEASE_NAME stable/metabase

Helm keeps track of the version installed by creating releases, which we can see by running:

helm ls

The output should be similar to this:

NAME        REVISION    UPDATED                     STATUS      CHART           APP VERSION NAMESPACE
my-release  1           Sat Jul 27 00:08:58 2019    DEPLOYED    metabase-0.5.0  v0.31.2     team-trainers

Wait for the chart to be installed and follow the instructions in the NOTES:

# Windows only
export POD_NAME=$(kubectl get pods --namespace will -l "app=metabase,release=v1-metabase" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward --namespace $env:NAMESPACE $POD_NAME 8080:3000

# MacOS only
export POD_NAME=$(kubectl get pods --namespace will -l "app=metabase,release=v1-metabase" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward --namespace ${NAMESPACE} $POD_NAME 8080:3000

Access metabase on http://127.0.0.1:8080!

We’re using kubectl port-forward here to proxy the Kubernetes traffic of the metabase container

This is another useful debugging tool and is not limited to pods created with Helm.

Exit the port-forwarding command with CRTL-C.

Let’s delete our release before deploying another application:

# Windows only
helm delete $env:RELEASE_NAME

# MacOS only
helm delete $RELEASE_NAME

The metabase application has been undeployed.

Why use Helm charts?

The following YAML definitions provide everything we need to create the DockerCoins application without Helm:

apiVersion: v1
kind: Service
metadata:
  name: dockercoins
  labels:
    app: dockercoins
    service: dockercoins
spec:
  type: LoadBalancer
  ports:
  - port: 80
    name: http
  selector:
    app: dockercoins
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dockercoins
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dockercoins
  labels:
    app: dockercoins
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dockercoins
      version: v1
  template:
    metadata:
      labels:
        app: dockercoins
        version: v1
    spec:
      containers:
      - name: dockercoins
        image: rotcaus/dockercoins_webui:v1
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /
            port: http
        readinessProbe:
          httpGet:
            path: /
            port: http
      - name: rng
        image: rotcaus/dockercoins_rng:v1
        ports:
        - containerPort: 3001
      - name: hasher
        image: rotcaus/dockercoins_hasher:v1
        ports:
        - containerPort: 3000
      - name: worker
        image: rotcaus/dockercoins_worker:v1
      - name: redis
        image: redis
        ports:
          - containerPort: 6379
---

If we were to install or update its Kubernetes resources, we could just run the following (don’t run it):

kubectl apply -f dockercoins-v1.yaml

When working with multiple applications, maintaining these YAML templates for each resource (Service, Deployment, ServiceAccount, Volumes, ConfigMaps etc.) … tends to become a tedious task :(

The approach of forking/versioning templates per context/per environment is an option but generally leads to an explosion of the number of templates which does not scale well and quickly becomes difficult to manage.

Helm charts solve some of these challenges by providing an abstraction layer on top of Kubernetes YAML definitions. Kubernetes YAML definitions can be templatised and then bundled up as a package.

So, what are the benefits of using a package manager?

What is in a Helm chart?

We are not going to create the DockerCoins chart from scratch. However, for learning purposes, create a new chart to see what the default skeleton of a chart looks like:

cd exercise/
helm create dockercoins

The structure of the chart is as following:

To transform our Kubernetes YAML definitions present in dockercoins-v1.yaml into a Helm chart, we would have to:

  - name: redis
    image: redis

to this dynamic Helm templatised version of it:

  - name: redis
    image: ":"

The next steps would be to declare a default value for the Redis image and version, using the same path we’ve used in the template. values.yaml would look like:

image:
  redis:
    repository: redis
    tag: latest

When executing the Helm chart in the next steps, you will see how the Redis image can be changed dynamically when installing the chart.

Search for a Helm chart in a private repository

In addition to using the public Helm chart repository, we can also use private repositories.

We’ve set up one for you using Google Cloud Storage (similar to AWS S3), but tools like Artifactory can also be used to host private repositories.

Add the rotcaus repository to the list of Helm repositories on your machine:

helm repo add rotcaus https://rotcaus-helm.storage.googleapis.com

Update the local repository index and list all the charts available in the newly added repository:

helm repo update
helm search repo rotcaus

The output should be similar to this:

NAME                    CHART VERSION   APP VERSION     DESCRIPTION                
rotcaus/dockercoins     0.1.0           1.0             A Helm chart for Kubernetes```

Inspect the chart to show its metadata and the possible values that can be configured on it:

```console
helm inspect chart rotcaus/dockercoins

In this previous command rotcaus is the name of the Helm repository we added earlier and dockercoins the name of the chart.

Cool, our chart is there and we can change a few options on it such as the version of the Docker images to run and the type of service to use (LoadBalancer being the default for a cloud load balancer).

Run a Helm chart

Install the chart in Kubernetes by running:

# Windows only
$env:RELEASE_NAME=$env:NAMESPACE + "-coins"
helm install --wait $env:RELEASE_NAME rotcaus/dockercoins --version 0.1.0

# MacOS only
export RELEASE_NAME="$NAMESPACE-coins"
helm install --wait $RELEASE_NAME rotcaus/dockercoins --version 0.1.0

This may take a few moments.

Once the chart is installed, you should be able to access it by its public IP. Allow some time for the service to be allocated a public IP, use the watch mode -w to following the change from “no IP” to “a public IP is assigned to the Kubernetes service”:

# Windows only
kubectl get svc -w

# MacOS only
kubectl get svc -w

Once the column IP goes from pending to an available IP, do Ctrl+C and access the app with:

# Windows only
$env:SERVICE_IP=$(kubectl get svc ${env:RELEASE_NAME}-dockercoins -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$env:SERVICE_IP:80

# MacOS only
export SERVICE_IP=$(kubectl get svc $RELEASE_NAME-dockercoins -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:80

Upgrade and rollback a Helm chart

Run the following command and keep it open in a separate terminal. This will give us the current version of the webui (version 1):

# Windows only
while ($true) {start-sleep 1;(iwr http://$env:SERVICE_IP/info).content;}

# MacOS only
while sleep 1; do echo -e && curl http://$SERVICE_IP/info; done

Upgrade the chart with:

# Windows only
helm upgrade --wait --set image.webui.tag=v2 $env:RELEASE_NAME rotcaus/dockercoins

# MacOS only
helm upgrade --wait --set image.webui.tag=v2 $RELEASE_NAME rotcaus/dockercoins

Version 2 of the web UI image adds a new HTTP endpoint, /info, to the API.

If you run kubectl get pods -w during the upgrade process, you should see that a new dockercoins pod is created. Once the new pod is created, the old one gets terminated:

dockercoins-84bb95c9fb-jf6qh   5/5     Terminating        0          2m12s
dockercoins-868cc48795-pbtjz   5/5     Running            0          16s

After waiting for the upgrade to be completed, the version in the terminal should go from an error to {"version":3}! This happened without interruption of service, the requests were routed to the new pod transparently.

Looking at the Helm history for this release, we should see 2 revisions:

# Windows only
helm history $env:RELEASE_NAME

# MacOS only
helm history $RELEASE_NAME

Expected output:

REVISION	UPDATED                 	STATUS    	CHART         	DESCRIPTION
1       	Sun Jul 28 11:12:17 2019	SUPERSEDED	dockercoins-0.1.0	Install complete
2       	Sun Jul 28 11:14:13 2019	DEPLOYED  	dockercoins-0.1.0	Upgrade complete

If version 2 does not operate as expected, you can always rollback to the first version:

# Windows only
helm rollback $env:RELEASE_NAME 1

# MacOS only
helm rollback $RELEASE_NAME 1

You should see output similar to: Rollback was a success! Happy Helming!

A process similar to the upgrade happened during the rollback. The history has also now a third revision:

# Windows only
helm history $env:RELEASE_NAME

# MacOS only
helm history $RELEASE_NAME

Expected output:

REVISION	UPDATED                 	STATUS    	CHART         	DESCRIPTION
1       	Sun Jul 28 11:12:17 2019	SUPERSEDED	dockercoins-0.1.0	Install complete
2       	Sun Jul 28 11:14:13 2019	SUPERSEDED	dockercoins-0.1.0	Upgrade complete
3       	Sun Jul 28 11:18:24 2019	DEPLOYED  	dockercoins-0.1.0	Rollback to 1

After the deployment has completed, the version from the API should go from version 2 to the error we were seeing with the original image version (no /info endpoint).

Cleanup

# Windows only
helm delete --purge $env:RELEASE_NAME
kubectl delete all --all -n "$env:NAMESPACE"

# MacOS only
helm delete --purge $RELEASE_NAME
kubectl delete all --all -n "${NAMESPACE}"