The Power of Library Charts in Helm: A Comprehensive Guide

The Power of Library Charts in Helm: A Comprehensive Guide

March 21, 2024
Get tips and best practices from Develeap’s experts in your inbox

Intro

Helm charts have become indispensable tools for packaging, templating, sharing, and deploying applications’ configurations in Kubernetes clusters. However, Helm introduces a fascinating concept that adds another abstraction layer and reusability to Helm charts – the Library Chart.

From my experience as a DevOps Engineer, developing and using a Library chart for my 40+ microservices applications was a game changer. It quickly became super easy to handle, develop, and configure the Helm charts. I was able to teach developers with no Kubernetes experience how to configure their microservices and add new ones without my help.

The Challenge

Suppose a DevOps team needs to create and maintain about 10 similar Microservices. The solution might be like this:

Step 1 – Create one Helm chart that most likely fits all the Microservices configurations.

Step 2 – Copy and paste the Helm chart for each microservice.

Step 3 – Configure and set the correct values for each Microservice.

With that solution, we not only have duplicated the code 10 times (Helm chart), but we will also have to carry the maintenance of these 10 Helm charts. What if tomorrow we will need to add some features to all of our services? Or open, for example, a new port? We will have to add these features to 10 Helm charts. And what if we had 100+ Helm charts? It doesn’t sound so comfortable, right? It will significantly delay the development.

The Solution: Library Charts

The differences between Helm chart and Library chart

While Helm charts encapsulate Kubernetes resources and configurations into a single package, Library Charts take it a step further. Unlike traditional charts, Library Charts aren’t meant for standalone deployment; rather, they are designed to be imported and utilized by other Helm charts. The key distinction lies in their purpose: Library Charts are building blocks rather than standalone applications. 

When you have many microservices requiring different configurations, you will have to create a Helm chart for each microservice; keeping the same structure and values file for each microservice will be almost impossible, especially when more than one person is writing these Helm charts.


The solution for that is a Library chart. A Library chart will define how your Helm chart values file, and templates will look. You can import the Library chart into the Helm chart and use the Library’s templates for all of your microservices. Think about the Library chart as a final “key: value” template for all of your Helm charts.

Why opt for Library Charts? The things Library Chart brings to the table

Reusability: Share common functionalities and configurations across multiple charts, promoting code reuse and reducing duplication efforts.

Consistency: Ensure consistency in resource definitions and configurations across various Helm charts, simplifying maintenance and updates.

Maintainability: Centralize changes and updates in one location, impacting all charts using the Library Chart. This significantly streamlines maintenance efforts.

Best practices for  Library Charts

Creating a Library Chart involves a structured approach:

• Organize Your Library: Keep your Library Chart well-organized with a clear directory structure. Leverage subdirectories to categorize templates logically.

• Parameterization: Effectively utilize Helm’s parameterization capabilities. Make your Library Chart customizable by defining parameters for dynamic values.

• Readability: Create at least one values file as an example for others to see and understand how to use your Library chart. Additionally, you can kill two birds with one stone—you can follow a specific guide (helm-docs) on how to write comments or remarks on the code itself. Implementing this guide’s instructions will allow you to automatically create a very nice-looking README.md file for your Library chart with just one CLI command.

• Version Control: From time to time, you will have to fix, upgrade, or add a feature to your Library Chart. Therefore, remember to update your README.md using helm-docs and release a new version.

There are several ways to manage the versions of Library charts. The first and most simple way is to control the versions via git tags. The tags should look like this: x.y.z, where ‘x’ stands for Major update, ‘y’ stands for minor changes, and ‘z’ stands for fixes.


However, there is a better way to manage and control Library chart versions – I recommend this way:

On your Library’s Chart.yaml keep updating the .version field (e.g. version: 1.2.3) 

Create a CI Pipeline that tests, packages and pushes the Library with the version field from Chart.yaml file to a designated Helm registry (OCI-based registries) such as ACR, jFrog or Harbor.

Implementation

In this section, I will show you how to create a Library chart, import it to your Helm chart, use a values file, etc.

Step 1: Create the library chart.
A library chart is a type of Helm chart. This means that you can start off by creating a scaffold chart:

helm create library

You will first remove all the files in the templates directory as we will create our own template definitions in this example.
rm -rf library/templates/*

The values file will not be required either.rm -f library/values.yaml

Step 2: Create a template.

In the templates/ directory, any template file name should begin with an underscore(_); this is a convention to indicate a template.
For this example, I will create a Service template.

touch library/templates/_service.yaml
Currently, our library directory should look like this:
library/

├── Chart.yaml

├── README.md

└── templates

    └── _service.yaml
1 directory, 3 files

Let’s add code to _service.yaml file.
# Code Example

{{- define "lib.service" }}
{{- if .Values.service.enabled }}
---
apiVersion: v1
kind: Service
metadata:
  name: "{{ .Values.service.name }}"
  annotations: {{ toYaml .Values.service.annotations | nindent 4 }}
  labels: {{ toYaml .Values.service.labels | nindent 4 }}
spec:
  type: {{ .Values.service.type }}


  ports:  
  {{- $type := .Values.service.type }}
  {{- range $port := .Values.service.ports }}
    - name: {{ $port.name }}
      port: {{ $port.port }}
      {{- if $port.targetPort }}
      targetPort: {{ $port.targetPort }}
      {{- else }}
      targetPort: {{ $port.port }}
      {{- end }}
      {{- if $port.protocol }}
      protocol: {{ $port.protocol }}
      {{- end }}
      {{- if and (eq $type "NodePort") (hasKey $port "nodePort" ) ($port.nodePort) }}
      nodePort: {{ $port.nodePort }}
      {{- end }}
  {{- end }}
 
  selector: {{ toYaml .Values.service.selector | nindent 4 }}
---
{{- end }}
{{- end }}

Finally, let’s change the chart type to library. This requires editing a field in the library/Chart.yaml file. Change “type: application” to “type: library” as follows:
type: library

Step 3: Import the Library chart into the Helm chart

For this example, I already created a Helm chart called “example”.
For importing the library chart into our Helm chart, we are required to add a dependency to example/Chart.yaml file as follows:

dependencies:
- name: library
  repository: file://../library
  version: 0.1.0

After adding a dependency, we will have to update our Helm chart dependencies:

helm dependency update.
Right after the update is finished, you will see that 2 files were added to your Helm chart: ‘example/charts/library-0.1.0.tar’ and ‘example/Chart.lock’

All the chart’s dependencies are stored in example/charts directory, library-0.1.0.tar is the Library chart itself, archived with a format common to Linux and Unix-based operating systems. We can open and extract the library using tar -zxvf library-0.1.0.tar.

The example/Chart.lock file lists the exact versions of immediate dependencies and their dependencies and so on.

Step 4: Create a service in the Helm chart using the Library chart

Let’s create the yaml file for the service:
touch example/templates/service.yaml

In the service.yaml file we will have to include the library’s service template as follows:

{{- include "lib.service" . }}

Step 5: Add values to the Helm chart

It is very important to know that the keys in the values file need to be matched to the keys that the Library chart expects. So let’s add these values to example/values.yaml file:

service:
  enabled: true
  name: example-svc
  annotations: {}
  labels:
    service: example
  type: NodePort
  ports:
  - name: service-port
    protocol: TCP
    port: 80
    targetPort: 3000
    nodePort: 30123
  selector:
    app: example
    tier: develeap-magazine

So now our Helm chart Directory looks like this:
example/

├── Chart.lock

├── Chart.yaml

├── charts

│   └── library-0.1.0.tgz

├── templates

│   └── service.yaml

└── values.yaml

2 directories, 5 files

Step 6: Rendering and testing

It’s time to compile and test all use cases in the Helm chart. In this example, I will render and test the Helm chart one time.

Render:

helm template .

Output:
---

# Source: example/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: "example-svc"
  annotations:
    {}
  labels:
    service: example
spec:
  type: NodePort


  ports:
    - name: service-port
      port: 80
      targetPort: 3000
      protocol: TCP
      nodePort: 30123
 
  selector:
    app: example
    tier: develeap-magazine

Now, Let’s deploy the Helm chart on the actual Kubernetes cluster and check that it is ok:

helm install example ./example -n test

After installing the Helm chart, check for the service:
kubectl get service -n test

Output:

example-svc   NodePort   10.0.171.238   <none>        80:30123/TCP   6s

kubectl get service -n test example-svc -o yaml

Output:

apiVersion: v1
kind: Service
metadata:
  annotations:
    meta.helm.sh/release-name: example
    meta.helm.sh/release-namespace: test
  creationTimestamp: "2024-02-28T14:20:42Z"
  labels:
    app.kubernetes.io/managed-by: Helm
    service: example
  name: example-svc
  namespace: test
  resourceVersion: "53318351"
  uid: 91a3d280-3fc6-4838-b98f-08e2457b4ebd
spec:
  clusterIP: 10.0.171.238
  clusterIPs:
  - 10.0.171.238
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: service-port
    nodePort: 30123
    port: 80
    protocol: TCP
    targetPort: 3000
  selector:
    app: example
    tier: develeap-magazine
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

Summary

Library Charts are vital in promoting configurations, modularity, and reusability in the Helm ecosystem. They allow you to abstract away common patterns, configurations, or resource definitions into reusable components. This modular approach enhances collaboration and reduces redundancy. For instance, if you have repeated Kubernetes objects across multiple charts, a Library Chart can efficiently consolidate and manage those shared resources.


If you, or anyone you know, maintains or develops Helm Charts, I strongly recommend using Library Charts, it will make your life easier.

In the foreseeable future, there is no doubt Helm Charts will have a huge role in how we organize, configure, and deploy our microservices. Helm Charts are here to stay, and Library Charts are here to make it better.

We’re Hiring!
Develeap is looking for talented DevOps engineers who want to make a difference in the world.