Community post originally published on DEV.to by Sunny Bhambhani

In this article we will be talking about Approach 2 i.e. how to get multiple helm charts installed using helmfile.

If you haven’t read the previous article where I discussed about Approach 1, feel free to read it over.

Ref: https://dev.to/aws-builders/installing-multiple-helm-charts-in-one-go-approach-1-5d1p

Prerequisites:

Installation of helmfile is pretty straight forward, we just need to grab its binary and put that in /usr/local/bin and we are done (Ref: https://helmfile.readthedocs.io/en/latest/#installation for operating system other than linux).

Grab the latest one from the assets section from here: https://github.com/helmfile/helmfile/releases.

sudo wget https://github.com/helmfile/helmfile/releases/download/v0.159.0/helmfile_0.159.0_linux_amd64.tar.gz
sudo tar -xxf helmfile_0.159.0_linux_amd64.tar.gz
sudo rm helmfile_0.159.0_linux_amd64.tar.gz
sudo mv helmfile /usr/local/bin/

Once helmfile is installed, we need to initialize it so that it can download necessary helm plugins.

helmfile init

If you want to list it out what all plugins init installs, it can be found using below command:

$ helm plugin list
NAME            VERSION DESCRIPTION
diff            3.8.1   Preview helm upgrade changes as a diff
helm-git        0.12.0  Get non-packaged Charts directly from Git.
s3              0.14.0  Provides AWS S3 protocol support for charts and repos. https://github.com/hypnoglow/helm-s3
secrets         4.1.1   This plugin provides secrets values encryption for Helm charts secure storing

Note: A notice to upgrade helm may appear if the helm version is 3.10 or lower.

helm version is too low, the current version is 3.10.1+g9f88ccb, the required version is 3.12.3
use: ‘https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3‘ [y/n]: y

Approach 2:

helmfile is pretty intresting and a very useful utility when it comes to get multiple helm charts installed. Using a single file helmfile.yaml we can manage N number of charts.

When it comes to installing mutiple helm charts, helmfile is a really helpful and interesting tool. The helmfile.yaml values file and helmfile binary is all that is needed to manage N number of charts. Internally helmfile invokes helm command to manage Kubernetes objects/charts. Also you can think of helmfile as a wrapper around helm together with some helm plugins.

Below is a quick example, wherein I have a 3-Tier application comprising of a frontend, backend, and database and for all of them I have 3 individual charts.

$ tree
.
├── backend
│   ├── Chart.yaml
│   ├── templates
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── hpa.yaml
│   │   ├── ingress.yaml
│   │   ├── service.yaml
│   │   └── serviceaccount.yaml
│   └── values.yaml
├── database
│   ├── Chart.yaml
│   ├── templates
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── hpa.yaml
│   │   ├── ingress.yaml
│   │   ├── service.yaml
│   │   └── serviceaccount.yaml
│   └── values.yaml
└── webapp
    ├── Chart.yaml
    ├── templates
    │   ├── _helpers.tpl
    │   ├── deployment.yaml
    │   ├── hpa.yaml
    │   ├── ingress.yaml
    │   ├── service.yaml
    │   └── serviceaccount.yaml
    └── values.yaml

6 directories, 24 files

Now, we will use helmfile (that too a single command to get these charts installed).

In the same directory where my charts are there, lets create a new file called helmfile.yaml. Just to add here we are referring local charts now, but we will refer charts from helm repositories in next examples.

Location: Local helm charts

$ cat helmfile.yaml
releases:
  - name: webapp
    namespace: default
    chart: ./webapp
    version: "0.1.0"
    wait: true
    installed: true
  - name: backend
    namespace: default
    chart: ./backend
    wait: true
  - name: database
    namespace: default
    chart: ./database
    wait: true

Lets break this file now:

Now, lets see helmfile in action:

$ helm list -A
NAME    NAMESPACE       REVISION        UPDATED STATUS  CHART   APP VERSION
$
$ helmfile init
helmfile initialization completed!
$
$ helmfile list
NAME            NAMESPACE       ENABLED INSTALLED       LABELS  CHART           VERSION
webapp          default         true    true                    ./webapp        0.1.0
backend         default         true    true                    ./backend
database        default         true    true                    ./database
$
$ helmfile apply
Building dependency release=webapp, chart=webapp
Building dependency release=backend, chart=backend
Building dependency release=database, chart=database
Comparing release=webapp, chart=webapp
********************

        Release was not present in Helm.  Diff will show entire contents as new.

********************
default, webapp, Deployment (apps) has been added:
-
+ # Source: webapp/templates/deployment.yaml
+ apiVersion: apps/v1

#.........
#.........
# OUTPUT TRIMMED

UPDATED RELEASES:
NAME       CHART        VERSION   DURATION
webapp     ./webapp     0.1.0           3s
database   ./database   0.1.0           3s
backend    ./backend    0.1.0           3s
$ helm list -A
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
backend         default         1               2023-12-11 16:13:24.769966852 +0530 IST deployed        backend-0.1.0   1.16.0
database        default         1               2023-12-11 16:13:24.771955874 +0530 IST deployed        database-0.1.0  1.16.0
webapp          default         1               2023-12-11 16:13:24.771767015 +0530 IST deployed        webapp-0.1.0    1.16.0
$ k get pods
NAME                       READY   STATUS    RESTARTS   AGE
backend-7f458d4566-zwcz2   1/1     Running   0          3m47s
database-b4f679788-z6r84   1/1     Running   0          3m47s
webapp-7f6ffdc676-g5w7j    1/1     Running   0          3m47s
$
$ helmfile status
Getting status backend
Getting status webapp
Getting status database
NAME: backend
LAST DEPLOYED: Mon Dec 11 16:13:24 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

NAME: webapp
LAST DEPLOYED: Mon Dec 11 16:13:24 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

NAME: database
LAST DEPLOYED: Mon Dec 11 16:13:24 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

By default, similar to helm, this will also use helm chart’s default values.yaml file which is a part of helm chart itself. So either you can update that and fulfill your requirement (which idelly is not a good practice, since in real world scenario that is most unlikely to happen and ideally we should to override intended keys).

To cater this need we have something called values and set which we can specify in the state file and helmfile will consider those values and will override them.

Lets use the same example, but now I have added few additional line for backend as well as for the database item in our releases array.

releases:
  - name: webapp
    namespace: default
    chart: ./webapp
    version: "0.1.0"
    wait: true
    installed: true
  - name: backend
    namespace: default
    chart: ./backend
    wait: true
    set:
      - name: replicaCount
        value: 2
  - name: database
    namespace: default
    chart: ./database
    wait: true
    values:
      - "./db-values.yaml"
$ cat db-values.yaml
replicaCount: 2
$ helmfile diff
Building dependency release=webapp, chart=webapp
Building dependency release=backend, chart=backend
Building dependency release=database, chart=database
Comparing release=webapp, chart=webapp
Comparing release=backend, chart=backend
default, backend, Deployment (apps) has changed:
  # Source: backend/templates/deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: backend
# ......
# OUTPUT TRIMMED

-   replicas: 1
+   replicas: 2
# ......
# OUTPUT TRIMMED

Comparing release=database, chart=database
default, database, Deployment (apps) has changed:
  # Source: database/templates/deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: database
# ......
# OUTPUT TRIMMED

-   replicas: 1
+   replicas: 2
# ......
# OUTPUT TRIMMED

Note: helmfile apply basically calls two operations helmfile diff and helmfilesync.

$ helmfile apply
Building dependency release=webapp, chart=webapp
Building dependency release=backend, chart=backend
Building dependency release=database, chart=database
Comparing release=webapp, chart=webapp
default, backend, Deployment (apps) has changed:
# ..........
# OUTPUT TRIMMED

-   replicas: 1
+   replicas: 2

default, database, Deployment (apps) has changed:
# ..........
# OUTPUT TRIMMED

-   replicas: 1
+   replicas: 2

Upgrading release=database, chart=database
Upgrading release=backend, chart=backend
Release "backend" has been upgraded. Happy Helming!
NAME: backend
LAST DEPLOYED: Mon Dec 11 16:37:27 2023
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None

Listing releases matching ^backend$
Release "database" has been upgraded. Happy Helming!
NAME: database
LAST DEPLOYED: Mon Dec 11 16:37:27 2023
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None

UPDATED RELEASES:
NAME       CHART        VERSION   DURATION
backend    ./backend    0.1.0           3s
database   ./database   0.1.0           3s
$ helm list
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
backend         default         2               2023-12-11 16:37:27.366097438 +0530 IST deployed        backend-0.1.0   1.16.0
database        default         2               2023-12-11 16:37:27.366586959 +0530 IST deployed        database-0.1.0  1.16.0
webapp          default         1               2023-12-11 16:13:24.771767015 +0530 IST deployed        webapp-0.1.0    1.16.0
$ k get pods
NAME                       READY   STATUS    RESTARTS   AGE
backend-7f458d4566-6jgxk   1/1     Running   0          12s
backend-7f458d4566-zwcz2   1/1     Running   0          24m
database-b4f679788-9kxhl   1/1     Running   0          12s
database-b4f679788-z6r84   1/1     Running   0          24m
webapp-7f6ffdc676-g5w7j    1/1     Running   0          24m

In the above example we referred the helm charts which were placed locally, now lets see how to consume the helm charts which are a part of OCI or HTTPS repositories.

Location: helm charts from OCI repositories

releases:
  - name: nginx
    namespace: default
    chart: oci://registry-1.docker.io/bitnamicharts/nginx
    version: "15.4.4"
    wait: true
    set:
      - name: service.type
        value: ClusterIP
$ helmfile apply
Pulling registry-1.docker.io/bitnamicharts/nginx:15.4.4
Comparing release=nginx, chart=/tmp/helmfile976774944/default/nginx/nginx/15.4.4/nginx
********************

        Release was not present in Helm.  Diff will show entire contents as new.

********************
default, nginx, Deployment (apps) has been added:
# ......
# ......
# OUTPUT TRIMMED

Location: helm charts from HTTPS repositories

repositories:
 - name: prometheus-community
   url: https://prometheus-community.github.io/helm-charts

releases:
- name: prom-norbac-ubuntu
  namespace: prometheus
  chart: prometheus-community/prometheus
  set:
  - name: rbac.create
    value: false
$ helmfile apply
Adding repo prometheus-community https://prometheus-community.github.io/helm-charts
"prometheus-community" has been added to your repositories

Comparing release=prom-norbac-ubuntu, chart=prometheus-community/prometheus
********************

        Release was not present in Helm.  Diff will show entire contents as new.

********************
prometheus, prom-norbac-ubuntu-alertmanager, ConfigMap (v1) has been added:
# ......
# ......
# OUTPUT TRIMMED

Repositories

repositories:
  - name: ocirepo
    url: registry-1.docker.io/bitnamicharts
    oci: true
releases:
  - name: nginx
    namespace: default
    chart: ocirepo/nginx
    version: 15.4.4
    wait: true
    set:
      - name: service.type
        value: ClusterIP
$ helmfile apply
Pulling registry-1.docker.io/bitnamicharts/nginx:15.4.4
Comparing release=nginx, chart=/tmp/helmfile4237214626/default/nginx/nginx/15.4.4/nginx
********************

        Release was not present in Helm.  Diff will show entire contents as new.

********************
default, nginx, Deployment (apps) has been added:
# ......
# ......
# OUTPUT TRIMMED

Feel free to read it over and share your thoughts and comments.

Happy Learning!

References: