Using Kubernetes Helm to install applications

After reading this introduction to Kubernetes Helm, you will know how to:

  • Install Helm
  • 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.

Next week, my colleague Maciej Kwiek will be giving a talk at Kubecon about Boosting Helm with AppController, so we thought this might be a good time to give you an introduction to what it is and how it works.

Let’s take a quick look at how to install, configure, and utilize Helm.

Install Helm

Installing Helm is actually pretty straightforward.  Follow these steps:

  1. 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.)
  2. Unpack the archive:
    $ gunzip helm-v2.2.3-darwin-amd64.tar.gz
    $ tar -xvf helm-v2.2.3-darwin-amd64.tar
    x darwin-amd64/
    x darwin-amd64/helm
    x darwin-amd64/LICENSE
    x darwin-amd64/README.md
  3. Next move the helm executable to your path:
    $ mv dar*/helm /usr/local/bin/.
  4. Finally, initialize helm to both set up the local environment and to install the server portion, Tiller, on your cluster.  (Helm will use the default cluster for Kubernetes, unless you tell it otherwise.)
    $ helm init
    Creating /Users/nchase/.helm 
    Creating /Users/nchase/.helm/repository 
    Creating /Users/nchase/.helm/repository/cache 
    Creating /Users/nchase/.helm/repository/local 
    Creating /Users/nchase/.helm/plugins 
    Creating /Users/nchase/.helm/starters 
    Creating /Users/nchase/.helm/repository/repositories.yaml 
    Writing to /Users/nchase/.helm/repository/cache/stable-index.yaml
    $HELM_HOME has been configured at /Users/nchase/.helm.
    
    Tiller (the helm server side component) has been instilled into your Kubernetes Cluster.
    Happy Helming!

Note that you can also upgrade the Tiller component using:

helm init --upgrade

That’s all it takes to install Helm itself; now let’s look at using it to install an application.

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                          VERSION DESCRIPTION                                       
stable/aws-cluster-autoscaler 0.2.1   Scales worker nodes within autoscaling groups.    
stable/chaoskube              0.5.0   Chaoskube periodically kills random pods in you...
stable/chronograf             0.1.2   Open-source web application written in Go and R...
...

In our case, we’re going to install MySQL from the stable/mysql chart. Follow these steps:

  1. 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!⎈ 
  2. 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:   lucky-wildebeest
    LAST DEPLOYED: Thu Mar 16 16:13:50 2017
    NAMESPACE: default
    STATUS: DEPLOYED

    As you can see, it’s called lucky-wildebeest, 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:
    ==> v1/Secret
    NAME                    TYPE    DATA  AGE
    lucky-wildebeest-mysql  Opaque  2     0s
    
    ==> v1/PersistentVolumeClaim
    NAME                    STATUS  VOLUME                                    CAPACITY  ACCESSMODES  AGE
    lucky-wildebeest-mysql  Bound   pvc-11ebe330-0a85-11e7-9bb2-5ec65a93c5f1  8Gi       RWO          0s
    
    ==> v1/Service
    NAME                    CLUSTER-IP  EXTERNAL-IP  PORT(S)   AGE
    lucky-wildebeest-mysql  10.0.0.13   <none>       3306/TCP  0s
    
    ==> extensions/v1beta1/Deployment
    NAME                    DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
    lucky-wildebeest-mysql  1        1        1           0          0s

    This is a good example because we can see that this chart configures multiple types of resources: a Secret (for passwords), a persistent volume (to store the actual data), a Service (to serve requests) and a Deployment (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:
    lucky-wildebeest-mysql.default.svc.cluster.local
    
    To get your root password run:
        kubectl get secret --namespace default lucky-wildebeest-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo
    
    To connect to your database:
     Run an Ubuntu pod that you can use as a client:
        kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il
    
     Install the mysql client:
        $ apt-get update && apt-get install mysql-client -y
    
     Connect using the mysql cli, then provide your password:
    $ mysql -h lucky-wildebeest-mysql -p

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:
lucky-wildebeest-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 lucky-wildebeest-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 lucky-wildebeest-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
lucky-wildebeest-mysql   Opaque                                2         20m

It’s the second, lucky-wildebeest-mysql that we’re interested in. Let’s look at the information it contains:

$ kubectl get secret lucky-wildebeest-mysql -o yaml
apiVersion: v1
data:
  mysql-password: a1p1THdRcTVrNg==
  mysql-root-password: REJUem1iQWlrTw==
kind: Secret
metadata:
  creationTimestamp: 2017-03-16T20:13:50Z
  labels:
    app: lucky-wildebeest-mysql
    chart: mysql-0.2.5
    heritage: Tiller
    release: lucky-wildebeest
  name: lucky-wildebeest-mysql
  namespace: default
  resourceVersion: "43613"
  selfLink: /api/v1/namespaces/default/secrets/lucky-wildebeest-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 iwt 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 lucky-wildebeest-mysql.default.svc.cluster.local -p
Enter password: 
ERROR 2005 (HY000): Unknown MySQL server host 'lucky-wildebeest-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
lucky-wildebeest-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 lucky-wildebeest-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 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 revisions. 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 Revision.  By deploying MySql, we created a Revision, which we can see in this list:

NAME              REVISION UPDATED                  STATUS CHART         NAMESPACE
lucky-wildebeest     1        Sun Mar 19 22:07:56 2017 DEPLOYEmysql-0.2.5   default  
operatic-starfish    2        Thu Mar 16 17:10:23 2017 DEPLOYEredmine-0.4.0 default  

As you can see, we created a revision called lucky-wildebeest, based on the mysql-0.2.5 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 intended-mule
LAST DEPLOYED: Sun Mar 19 22:07:56 2017
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Secret
NAME                 TYPE    DATA  AGE
intended-mule-mysql  Opaque  2     43m

==> v1/PersistentVolumeClaim
NAME                 STATUS  VOLUME                                    CAPACITY  ACCESSMODES  AGE
intended-mule-mysql  Bound   pvc-08e0027a-0d12-11e7-833b-5ec65a93c5f1  8Gi       RWO          43m
...

Now, if we wanted to, we could go ahead and delete the revision:

$ helm delete lucky-wildebeest

Now if you list all of the active revisions, it’ll be gone.

$ helm ls

However, even though the revision s gone, you can still see the status:

$ helm status lucky-wildebeest
LAST DEPLOYED: Sun Mar 19 22:07:56 2017
NAMESPACE: default
STATUS: DELETED

NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
lucky-wildebeest-mysql.default.svc.cluster.local

To get your root password run:

    kubectl get secret --namespace default lucky-wildebeest-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo

To connect to your database:

 Run an Ubuntu pod that you can use as a client:

    kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

 Install the mysql client:

    $ apt-get update && apt-get install mysql-client -y

 Connect using the mysql cli, then provide your password:

    $ mysql -h lucky-wildebeest-mysql -p

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 lucky-wildebeest 1
Rollback was a success! Happy Helming!

We can see that the application is back, and the revision has been incremented:

NAME              REVISION UPDATED                  STATUS CHART         NAMESPACE
lucky-wildebeest     2        Sun Mar 19 23:46:52 2017 DEPLOYEmysql-0.2.5   default  

We can also check the status:

$ helm status intended-mule
LAST DEPLOYED: Sun Mar 19 23:46:52 2017
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Secret
NAME                 TYPE    DATA  AGE
intended-mule-mysql  Opaque  2     21m

==> v1/PersistentVolumeClaim
NAME                 STATUS  VOLUME                                    CAPACITY  ACCESSMODES  AGE
intended-mule-mysql  Bound   pvc-dad1b896-0d1f-11e7-833b-5ec65a93c5f1  8Gi       RWO          21m
...

Next time, we’ll talk about how to create charts for Helm.  Meanwhile, if you’re going to be at Kubecon, don’t forget Maciej Kwiek’s talk on Boosting Helm with AppController.

Leave a Reply

Your email address will not be published. Required fields are marked *

NEWS VIA EMAIL

Recommendations

Archive

LIVE DEMO
Mirantis Cloud Platform
WEBINAR
Automate Upgrades with Mirantis DriveTrain
WEBINAR
Kubernetes & Docker Mini-Bootcamp