Helm: Getting Started with Using Helm to Deploy Apps on Kubernetes

Tyler Britten

Helm is a tool to help you define, install, and upgrade applications running on Kubernetes. For more information, be sure to check out Helm: What Is It?

In this guide you’ll deploy a simple application using Helm to a Kubernetes cluster.

Before You Begin

There are a few things you need to do before getting started with Helm:

  • Have access to a Kubernetes cluster. If you don’t, you can use local options like Docker Desktop or Minikube.

  • Check out Kubernetes 101 on KubeAcademy, particularly if you’ve never worked with Kubernetes before.

  • Follow the documentation for installing Helm.

Helm leverages your local Kubernetes context to operate, so it will have whatever permissions the account you’re using for kubectl does.

If you read about Helm and come across references to tiller, previous versions (before version 3) required an extra component installed on the Kubernetes cluster.

Initial Helm Setup

You’re going to need a chart to deploy with Helm, so the easiest thing is to connect to a chart repository with the helm repo command.

$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository

Now that you have a repo connected, you need to see which charts you have available to deploy.

$ helm search repo bitnami
NAME                            	CHART VERSION	APP VERSION            	DESCRIPTION
bitnami/bitnami-common          	0.0.8        	0.0.8                  	Chart with custom templates used in Bitnami cha...
bitnami/airflow                 	5.0.3        	1.10.9                 	Apache Airflow is a platform to programmaticall...
bitnami/apache                  	7.3.9        	2.4.41                 	Chart for Apache HTTP Server
---- Truncated ----

You can see a whole list of charts, but the output above shows the first three. It shows the name of the chart, the versions, and the descriptions. As you’ll see, there’s both a chart version and an app version. That’s because a chart may be updated and changed separately from the underlying application it is deploying.

Time to Deploy a Chart (Create a Release)

Now that you have Helm configured with a repo, you can deploy a chart. In Helm lingo that’s called creating a release. In this example, you’ll deploy a pretty simple one, like nginx. You can supply a name for your app like you’re going to do here (my app) or you can use the --generate-name CLI option to have Helm generate one for you.

$ helm install my-app bitnami/nginx
NAME: my-app
LAST DEPLOYED: Mon Mar 9 07:37:28 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Get the NGINX URL:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w my-app-nginx'

  export SERVICE_IP=$(kubectl get svc --namespace default my-app-nginx --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
  echo "NGINX URL: http://$SERVICE_IP/"

After your release is successfully created, you’ll see an output like this with the name, namespace, status, etc. The NOTES section has specific information about your install; that’s because it’s generated by Helm using a template, too.

You can see what was deployed by using kubectl.

$ kubectl get all
NAME                                READY   STATUS    RESTARTS   AGE
pod/my-app-nginx-655b5cfc8c-mfhcb   1/1     Running   0          2m38s

NAME                   TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)                      AGE
service/my-app-nginx   LoadBalancer   10.0.2.51    104.197.x.x   80:30291/TCP,443:31827/TCP   2m38s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-app-nginx   1/1     1            1           2m38s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/my-app-nginx-655b5cfc8c   1         1         1       2m38s

You can see the external IP of your application listed, but if you follow the instructions in the notes, you should see the same as well.

$ export SERVICE_IP=$(kubectl get svc --namespace default my-app-nginx --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")

$ echo "NGINX URL: http://$SERVICE_IP/"
NGINX URL: http://104.197.x.x/

$ curl $SERVICE_IP
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>

You can see which releases are deployed using helm list.

NAME  	NAMESPACE	REVISION	UPDATED                            	STATUS  	CHART      	APP VERSION
my-app	default     	1       	2020-03-09 08:07:53.54657 -0400 EDT	deployed	nginx-5.1.9	1.16.1

It shows all the relevant information. Anytime you update a release, the revision number will increment.

You can clean up by removing the app with uninstall.

$ helm uninstall my-app
release "my-app" uninstalled

Changing the Values

Now you have a working nginx app, but maybe you don’t want it exposed externally via a load balancer. You can delete this app and redeploy it with ClusterIP instead of LoadBalancer.

Helm charts have a set of default values; the ones for this chart can be seen in its GitHub repository. If you look there, you’ll see the value you want to change is service.type. You can now install that same chart using the --set flag to configure it.

$ helm install my-app bitnami/nginx --set service.type=ClusterIP
NAME: my-app
LAST DEPLOYED: Mon Mar 9 08:07:53 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Get the NGINX URL:

  echo "NGINX URL: http://127.0.0.1:8080/"
  kubectl port-forward --namespace blog svc/my-app-nginx 8080:80

Notice how the NOTES section changed? It’s a template, too. You can see here that changing the service type changed the output.

In this instance you supplied the value via the CLI, but you could have also put it into a values.yaml file and used the --values CLI option. This is a common practice for when you want to supply numerous values to the chart, and/or you want to keep track of what you’re deploying by checking the file into a version control system. The easiest way to get started with your values file is to download the default one from the chart repository, like this one for the nginx chart you deployed. Any value you aren’t changing can be deleted from the file as it will be supplied by the default values. If you wanted to do that to get the same results as above you’d create a my-app-values.yaml file with these contents:

## NGINX Service properties
##
service:
  ## Service type
  ##
  type: ClusterIP

The command to create the release would then be:

$ helm install my-app bitnami/nginx --values my-app-values.yaml

Upgrading a Release

Anytime you want to change anything about a release—be it a configuration value for the chart, an upgrade to the chart itself, or the application version—you’ll run helm upgrade.

For your nginx chart, you can try this by changing a configuration value. Currently the default image pullPolicy for this chart is IfNotPresent. You can change that to Always via an upgrade.

$ helm upgrade my-app bitnami/nginx --set service.type=ClusterIP,image.pullPolicy=Always

Release "my-app" has been upgraded. Happy Helming!
NAME: my-app
LAST DEPLOYED: Wed Mar 11 13:50:05 2020
NAMESPACE: blog
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
Get the NGINX URL:

  echo "NGINX URL: http://127.0.0.1:8080/"
  kubectl port-forward --namespace blog svc/my-app-nginx 8080:80

You can see the revision has been incremented. If you get the nginx pod you can see the change of configuration:

$ kubectl get pod my-app-nginx-5bd7878597-pc8jp -o yaml

spec:
  containers:
  - image: docker.io/bitnami/nginx:1.16.1-debian-10-r46
    imagePullPolicy: Always

Why did you have to supply both service.type and image.pullPolicy? Because if you hadn’t supplied both, the service type would have tried to revert to the default.

Rollback

What happens if you didn’t want that change or it didn’t work the way you expected? Remember the revision of the releases? You can rollback to a previous revision with helm rollback. If you want, you can do a --dry-run first to see if the rollback would even work.

$ helm rollback my-app 1 --dry-run
Rollback was a success! Happy Helming!
$ helm rollback my-app 1
Rollback was a success! Happy Helming!

If you check the pod again, you’ll see pullPolicy is set back to IfNotPreset.

Get Helming

If you’re ready to start trying to deploy more charts, there are a whole bunch of charts available in a number of different repositories. A current list of repositories in a Helm install might look like this:

NAME    	URL
stable  	https://kubernetes-charts.storage.googleapis.com
jetstack	https://charts.jetstack.io
elastic 	https://helm.elastic.co
bitnami 	https://charts.bitnami.com/bitnami

Happy Helming!