
Kubernetes Helm Tutorial: A quick and dirty guide to installing applications
After reading this introduction to Kubernetes Helm, you will know how to:
- Install the Helm software
- Configure Helm
- Use Helm to determine available packages
- Use Helm to install a software package
- Retrieve a Kubernetes Secret
- Use Helm to delete an application
- Use Helm to roll back changes to an application
Difficulty is a relative thing. Deploying an application using containers can be much easier than trying to manage deployments of a traditional application over different environments, but trying to manage and scale multiple containers manually is much more difficult than orchestrating them using Kubernetes. But even managing Kubernetes applications looks difficult compared to, say, “apt-get install mysql”. Fortunately, the container ecosystem has now evolved to that level of simplicity. Enter Helm.
Helm is a Kubernetes-based package installer. It manages Kubernetes “charts”, which are “preconfigured packages of Kubernetes resources.” Helm enables you to easily install packages, make revisions, and even roll back complex changes.
Last year, when my colleague Maciej Kwiek gave a talk at Kubecon about Boosting Helm with AppController, we thought this might be a good time to give you an introduction to what it is and how it works. Now, as we get ready to talk about Helm and other applications, we thought we’d revisit and update those instructions in this Kubernetes helm tutorial.
Let’s take a quick look at how to install, configure, and utilize Helm as well as answer some questions such as “what does Helm install do?
Install Helm
Our Kubernetes Helm tutorial, as with any other software tutorial, starts with how to install Helm in the first place. Luckily, it’s is actually pretty straightforward. Follow these steps:
- Download the latest version of Helm from https://github.com/kubernetes/helm/releases. (Note that if you are using an older version of Kubernetes (1.4 or below) you might have to downgrade Helm due to breaking changes.)
- Unpack the archive:
$ gunzip helm-v2.8.1-linux-amd64.tar.gz
$ tar -xvf helm-v2.8.1-linux-amd64.tar
x linux-amd64/
x linux-amd64/helm
x linux-amd64/LICENSE
x linux-amd64/README.md - Next move the helm executable to your path:
$ sudo mv l*/helm /usr/local/bin/.
- Finally, initialize helm for both local environment configuration and to install the server portion, Tiller, on your cluster. (Helm will use the default Kubernetes cluster, unless you tell it otherwise.)
$ helm init Creating /home/nick/.helm Creating /home/nick/.helm/repository Creating /home/nick/.helm/repository/cache Creating /home/nick/.helm/repository/local Creating /home/nick/.helm/plugins Creating /home/nick/.helm/starters Creating /home/nick/.helm/cache/archive Creating /home/nick/.helm/repository/repositories.yaml Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com Adding local repo with URL: http://127.0.0.1:8879/charts $HELM_HOME has been configured at /home/nick/.helm. Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster. Happy Helming!
Note that you can also upgrade the Tiller component using:
helm init --upgrade
Finally, you need to add the appropriate Kubernetes credentials:
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule \
--clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy \
-p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
That’s all it takes to install Helm itself on your system; now let’s look at some Helm install examples.
Install an application with Helm
One of the things that Helm does is enable authors to create and distribute their own applications using charts; to get a full list of the charts that are available, you can simply ask:
$ helm search NAME CHART VERSION APP VERSION DESCRIPTION stable/acs-engine-autoscaler 2.1.3 2.1.1 Scales worker nodes within agent pools stable/aerospike 0.1.7 v3.14.1.2 A Helm chart for Aerospike in Kubernetes stable/anchore-engine 0.1.3 0.1.6 Anchore container analysis and policy evaluatio... stable/artifactory 7.0.3 5.8.4 Universal Repository Manager supporting all maj... ...
In our case, we’re going to install MySQL from the stable/mysql chart. Follow these steps:
- First update the repo, just as you’d do with apt-get update:
$ helm repo update Hang tight while we grab the latest from your chart repositories... ...Skip local chart repository Writing to /Users/nchase/.helm/repository/cache/stable-index.yaml ...Successfully got an update from the "stable" chart repository Update Complete. ⎈ Happy Helming!⎈
- Next, we’ll do the actual install:
$ helm install stable/mysql
This command produces a lot of output, so let’s take it one step at a time. First, we get information about the release that’s been deployed:
NAME: inky-manta LAST DEPLOYED: Thu Mar 1 03:10:58 2018 NAMESPACE: default STATUS: DEPLOYED
As you can see, it’s called inky-manta, and it’s been successfully DEPLOYED.
Your release will, of course, have a different name. Next, we get the resources that were actually deployed by the stable/mysql chart:
RESOURCES: ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE inky-manta-mysql 1 1 1 0 1s ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE inky-manta-mysql-588bf547d6-4vvqk 0/1 Pending 0 0s ==> v1/Secret NAME TYPE DATA AGE inky-manta-mysql Opaque 2 1s ==> v1/PersistentVolumeClaim NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE inky-manta-mysql Pending 1s ==> v1/Service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE inky-manta-mysql ClusterIP 10.102.240.155 <none> 3306/TCP 1s
This is a good example because we can see that this chart configures multiple resource types : Secrets (for passwords), persistent volume claims (to store the actual data), Services (to serve requests) and a Deployments (to manage it all).
The chart also enables the developer to add notes:
NOTES: MySQL can be accessed via port 3306 on the following DNS name from within your cluster: inky-manta-mysql.default.svc.cluster.local To get your root password run: MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default inky-manta-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo) To connect to your database: 1. Run an Ubuntu pod that you can use as a client: kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il 2. Install the mysql client: $ apt-get update && apt-get install mysql-client -y 3. Connect using the mysql cli, then provide your password: $ mysql -h inky-manta-mysql -p To connect to your database directly from outside the K8s cluster: MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 # Execute the following commands to route the connection: export POD_NAME=$(kubectl get pods --namespace default -l "app=inky-manta-mysql" -o jsonpath="{.items[0].metadata.name}") kubectl port-forward $POD_NAME 3306:3306 mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}
These notes are the basic documentation a user needs to use the actual application. There let’s see how we put it all to use.
Connect to mysql
The first lines of the notes make it seem deceptively simple to connect to MySql:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
inky-manta-mysql.default.svc.cluster.local
Before you can do anything with that information, however, you need to do two things: get the root password for the database, and get a working client with network access to the pod hosting it.
Get the mysql password
Most of the time, you’ll be able to get the root password by simply executing the code the developer has left you:
$ kubectl get secret --namespace default inky-manta-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo DBTzmbAikO
Some systems — notably MacOS — will give you an error:
$ kubectl get secret --namespace default inky-manta-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo Invalid character in input stream.
This is because of an error in base64 that adds an extraneous character. In this case, you will have to extract the password manually. Basically, we’re going to execute the same steps as this line of code, but one at a time.
Start by looking at the Secrets that Kubernetes is managing:
$ kubectl get secrets NAME TYPE DATA AGE default-token-0q3gy kubernetes.io/service-account-token 3 145d inky-manta-mysql Opaque 2 20m
It’s the second, inky-manta-mysql that we’re interested in. Let’s look at the information it contains:
$ kubectl get secret inky-manta-mysql -o yaml apiVersion: v1 data: mysql-password: a1p1THdRcTVrNg== mysql-root-password: REJUem1iQWlrTw== kind: Secret metadata: creationTimestamp: 2017-03-16T20:13:50Z labels: app: inky-manta-mysql chart: mysql-0.2.5 heritage: Tiller release: inky-manta name: inky-manta-mysql namespace: default resourceVersion: "43613" selfLink: /api/v1/namespaces/default/secrets/inky-manta-mysql uid: 11eb29ed-0a85-11e7-9bb2-5ec65a93c5f1 type: Opaque
You probably already figured out where to look, but the developer’s instructions told us the raw password data was here:
jsonpath="{.data.mysql-root-password}"
So we’re looking for this:
apiVersion: v1 data: mysql-password: a1p1THdRcTVrNg== mysql-root-password: REJUem1iQWlrTw== kind: Secret metadata: ...
Now we just have to go ahead and decode it:
$ echo "REJUem1iQWlrTw==" | base64 --decode DBTzmbAikO
Finally! So let’s go ahead and connect to the database.
Create the mysql client
Now we have the password, but if we try to just connect with the mysql client on any old machine, we’ll find that there’s no connectivity outside of the cluster. For example, if I try to connect with my local mysql client, I get an error:
$ ./mysql -h linky-manta-mysql.default.svc.cluster.local -p Enter password: ERROR 2005 (HY000): Unknown MySQL server host 'inky-manta-mysql.default.svc.cluster.local' (0)
So what we need to do is create a pod on which we can run the client. Start by creating a new pod using the ubuntu:16.04 image:
$ kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never $ kubectl get pods NAME READY STATUS RESTARTS AGE hello-minikube-3015430129-43g6t 1/1 Running 0 1h inky-manta-mysql-3326348642-b8kfc 1/1 Running 0 31m ubuntu 1/1 Running 0 25s
When it’s running, go ahead and attach to it:
$ kubectl attach ubuntu -i -t
Hit enter for command prompt
Next install the mysql client:
root@ubuntu2:/# apt-get update && apt-get install mysql-client -y Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB] Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB] ... Setting up mysql-client-5.7 (5.7.17-0ubuntu0.16.04.1) ... Setting up mysql-client (5.7.17-0ubuntu0.16.04.1) ... Processing triggers for libc-bin (2.23-0ubuntu5) ...
Now we should be ready to actually connect. Remember to use the password we extracted in the previous step.
root@ubuntu2:/# mysql -h inky-manta-mysql -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 410 Server version: 5.7.14 MySQL Community Server (GPL) Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
Of course you can do what you want here, but for now we’ll go ahead and exit both the database and the container:
mysql> exit Bye root@ubuntu2:/# exit logout
So far in our Kubernetes Helm tutorial we’ve successfully installed an application — in this case, MySql, using Helm. But what else can Helm do?
Working with revisions
So now that you’ve seen Helm in action, let’s take a quick look at what you can actually do with it. Helm is designed to let you install, upgrade, delete, and roll back releases. We’ll get into more details about upgrades in a later article on creating charts, but let’s quickly look at deleting and rolling back revisions:
First off, each time you make a change with Helm, you’re creating a Release with a specific Revision number. By deploying MySql, we created a Release, which we can see in this list:
$ helm list
NAME REVISION UPDATED STATUS CHART NAMESPACE
inky-manta 1 Thu Mar 1 03:10:58 2018 DEPLOYED mysql-0.3.4 default
volted-bronco 1 Thu Mar 1 03:06:03 2018 DEPLOYED redis-1.1.13 default
As you can see, we created a revision called inky-manta. It’s based on the mysql-0.3.4 chart, and its status is DEPLOYED.
We could also get back the information we got when it was first deployed by getting the status of the revision:
$ helm status inky-manta LAST DEPLOYED: Thu Mar 1 03:10:58 2018 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1/PersistentVolumeClaim NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE inky-manta-mysql Pending 5h ==> v1/Service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE inky-manta-mysql ClusterIP 10.102.240.155 <none> 3306/TCP 5h ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE inky-manta-mysql 1 1 1 0 5h ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE inky-manta-mysql-588bf547d6-4vvqk 0/1 Init:0/1 0 5h ==> v1/Secret NAME TYPE DATA AGE inky-manta-mysql Opaque 2 5h NOTES: MySQL can be accessed via port 3306 on the ...
Now, if we wanted to, we could go ahead and delete the revision:
$ helm delete inky-manta
Now if you list all of the active revisions, it’ll be gone.
$ helm ls
However, even though the revision is gone, you can still see the status:
$ helm status inky-manta LAST DEPLOYED: Thu Mar 1 03:10:58 2018 NAMESPACE: default STATUS: DELETED NOTES: MySQL can be accessed via port 3306 on the following DNS name from within your cluster: inky-manta-mysql.default.svc.cluster.local ...
OK, so what if we decide that we’ve changed our mind, and we want to roll back that deletion? Fortunately, Helm is designed for that. We can specify that we want to rollback our application to a specific revision (in this case, 1).
$ helm rollback inky-manta 1 Rollback was a success! Happy Helming!
We can see that the application is back, and the revision number has been incremented:
$ helm ls NAME REVISION UPDATED STATUS CHART NAMESPACE inky-manta 2 Thu Mar 1 08:53:05 2018 DEPLOYED mysql-0.3.4 default volted-bronco 1 Thu Mar 1 03:06:03 2018 DEPLOYED redis-1.1.13 default
We can also check the status:
$ helm status inky-manta LAST DEPLOYED: Thu Mar 1 08:53:05 2018 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE inky-manta-mysql 1 1 1 0 48s ...
We hope you found this Kubernetes helm tutorial useful. We’ll talk about how to use custom charts with the Helm architecture and other useful topics in our future tutorials. Meanwhile, don’t forget to join us on March 14, 2018 for What’s New in Kubernetes 1.10.
When you run `helm install` Helm creates Release (not Revision) with name like “lucky-wildebeest”. After upgrade or rollback Release name will be the same but Revision number will increase.