Let's Write Our First Helm Chart!

Illustration of a person tying the laces of an orange shoe, surrounded by colorful cargo containers and diagrams on the wall, suggesting logistics planning or creativity in a workplace. Illustration of a person tying the laces of an orange shoe, surrounded by colorful cargo containers and diagrams on the wall, suggesting logistics planning or creativity in a workplace.

In the next two lessons of the Helm Lightning Course, we will talk about three core components of Helm:

  • Charts;
  • Templates;
  • Values;

Let’s start with Charts.

Chart is a packaging format of Helm. It’s the top-level collection of files that defines your Kubernetes resources. It’s similar to operating system packages — for example, RPM and DEB packages contain all the files and scripts required to run some application on Linux.

There are multiple ways to create a new chart. One of them is to run helm create chart_name command — this command creates a new directory with a basic Helm chart inside. The problem with this command is that it creates a bit too many files for the beginner to understand. Another way to create a new Helm Chart is to do it manually.

Let’s create a new directory called “mkdev-pgadmin” — this directory will contain our Helm Chart that deploys PgAdmin for PostgreSQL database management.

mkdir mkdev-pgadmin
cd mkdev-pgadmin

Each Chart requires a Chart.yaml file, that sets some metadata.

I need to set apiVersion to v2, the latest version of Helm api.

The name would be mkdev-pgadmin, and description is just “A Helm Chart for pgAdmin”.

Next I need to set the chart type. There are two types — application charts, that are used to package Kubernetes applications, and library charts, that only packages various functions and helpers. Library charts are on the advanced side of Helm usage and won’t be covered in this course. For this chart, we will pick the application type.

Finally, we need to set the Chart version and an appVersion. appVersion is the version of the application your Chart deploys. For example, we might get a new version of pgAdmin, but we don’t need to change any other files in the Chart — for such cases, appVersion is used.

apiVersion: v2
name: mkdev-pgadmin
description: A Helm chart for pgAdmin
type: application
version: 0.1.0
appVersion: 1.16.0

After we have our Chart.yaml, it’s time to define some templates. Helm expects all of your Kubernetes resources to be defined inside the templates/ directory (mkdir templates). There is no limitation around how you name the files in this directory, neither there is a limit of how many resources you can define in a single file.

Let’s start by creating a deployment.yaml with the following contents:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pgadmin
  labels:
    app: pgadmin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pgadmin
  template:
    metadata:
      labels:
        app: pgadmin
    spec:
      containers:
      - name: pgadmin
        image: dpage/pgadmin4
        ports:
        - containerPort: 80
        env:
          - name: PGADMIN_DEFAULT_PASSWORD
            value: secrets123
          - name: PGADMIN_DEFAULT_EMAIL
            value: kirill@mkdev.me

As you can see, we don’t really template anything — it’s a pretty basic Kubernetes deployment, with 2 environment variables and a publicly available pgAdmin image.

Installing the chart means that Helm will process our templates and apply them to the Kubernetes cluster. We can do that with the “helm install” command. This command requires two arguments — the name of the Helm release — pgadmin in this case — and the location of the chart, which is just a local directory where our Chart.yaml is located. We will also pass the wait argument, to tell Helm to wait for the pods of our Deployment to enter the Ready state.

Let’s run the command:

helm install pgadmin ./ --wait

We get a nice info about the status of our release. We can verify that our pgadmin is working by listing the running pods:

kubectl get pods

I’ve mentioned something called “Helm release”. When you run “helm install”, Helm doesn't simply apply your yamls - it also tracks what it has installed by creating a “release”. Release is an instance of your Chart deployed to the cluster. You can deploy same Chart multiple times, but with different configurations and different release names — for example, we could deploy two independent pgAdmin installations for two different development teams, or we can test new versions in a development release, and then promote them to staging or production release.

To list the releases, we can run helm list — it shows us when application was deployed the last time, and the current revision of the release. Every time we upgrade the release, it will increase the revision.

Let’s create another file, templates/service.yaml, with a very simple Service definition:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: pgadmin
  name: pgadmin
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: pgadmin
  sessionAffinity: None
  type: ClusterIP

To upgrade the release, we run helm upgrade pgadmin ./. Now we can forward the ports of this Service to verify that pgAdmin is running:

kubectl port-forward service/pgadmin 8080:80

This is looking good! Two more things about Helm releases that you should know.

First, the way Helm tracks releases is by using Kubernetes Secrets. If we run kubectl get secrets, we will see 2 secrets with the type helm.sh/release.v1. There is no other database or custom resources that Helm needs to operate - only those special Secrets for release tracking.

And second, we can easily rollback releases to the previous revision by using helm rollback command and passing the release name. If I run this command and check for the services, I see that there is no more pgadmin service in the list - this is because we’ve added it in the previous release revision, that was then rolled back.

With this simple Chart format and a couple of commands to install, upgrade and rollback the releases, Helm becomes a very convenient tool to package and deploy applications to Kubernetes. But simply packaging YAML files is not enough — we want to parametrize our templates, so that we can deploy slightly different configurations to different environments.

We also want to reduce the duplication in our code and keep some things defined just once. For this, we are going to use templating features of Helm, as well as values files. This will be the topic of the next lesson.

Cloud Native consulting: regardless if you are just getting started, or looking for a big shift from the old ways to the future, we are here to help. About consulting


Here's the same article in video form for your convenience: