Helm Guide: Commands, Templates, and Best Practices

Introduction

Helm is a package manager for Kubernetes that simplifies the process of defining, installing, and upgrading even the most complex Kubernetes applications. In this post, we’ll explore Helm commands, dive into writing Helm templates, and discuss best practices for using Helm in your Kubernetes workflows.

Why Use Helm?

Before we dive into the technical details, let’s understand why Helm is so valuable:

  1. Simplifies Complex Deployments: Helm allows you to define, install, and upgrade even the most complex Kubernetes applications.
  2. Promotes Reusability: With Helm charts, you can package applications and make them easily shareable and reusable.
  3. Manages Dependencies: Helm can manage charts that depend on each other, simplifying the deployment of applications with multiple components.
  4. Facilitates Version Control: Helm charts can be version controlled, making it easier to track changes and roll back if necessary.
  5. Enhances Collaboration: Teams can share and collaborate on Helm charts, standardizing deployment processes across an organization.

Helm vs. Regular Kubernetes YAML Files

While you can deploy applications to Kubernetes using regular YAML files, Helm offers several advantages:

  1. Templating: Helm allows you to use variables and functions in your YAML files, making them more flexible and reusable.
  2. Packaging: Helm bundles all related Kubernetes resources into a single package (chart), making it easier to manage and distribute.
  3. Versioning: Helm charts are versioned, allowing for easier upgrades and rollbacks.
  4. Lifecycle Management: Helm provides commands for installing, upgrading, and uninstalling applications, simplifying the management of application lifecycles.

Basic Helm Commands

Let’s start with some essential Helm commands:

# Install a chart
helm install [RELEASE_NAME] [CHART]

# Upgrade a release
helm upgrade [RELEASE_NAME] [CHART]

# Rollback a release
helm rollback [RELEASE_NAME] [REVISION]

# List releases
helm list

# Uninstall a release
helm uninstall [RELEASE_NAME]

# Create a new chart
helm create [CHART_NAME]

# Package a chart
helm package [CHART_PATH]

Writing Helm Templates

Helm uses Go templates to create dynamic Kubernetes manifest files. Let’s explore some key concepts in Helm templating.

Variable Declaration

Variables in Helm templates are typically defined in the values.yaml file and can be accessed using the .Values object.

# values.yaml
replicaCount: 3
image:
  repository: nginx
  tag: latest

In your template files, you can reference these values like this:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-nginx
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

Using Arrays

Arrays in Helm can be defined in the values.yaml file and iterated over in templates.

# values.yaml
env:
  - name: DEBUG
    value: "true"
  - name: LOG_LEVEL
    value: "info"

You can use these in your template like this:

env:
{{- range .Values.env }}
  - name: {{ .name }}
    value: {{ .value | quote }}
{{- end }}

Conditions

Conditional statements in Helm templates allow you to include or exclude parts of your manifest based on certain conditions.

{{- if .Values.persistence.enabled }}
volumes:
  - name: data
    persistentVolumeClaim:
      claimName: {{ .Release.Name }}-data
{{- end }}

Loops

Loops in Helm templates allow you to iterate over arrays or maps.

{{- range $key, $value := .Values.configMap }}
{{ $key }}: {{ $value | quote }}
{{- end }}

Best Practices

  1. Use Named Templates: Break down complex templates into smaller, reusable named templates.
  2. Leverage Helper Functions: Helm provides many built-in functions. Use them to keep your templates clean and readable.
  3. Provide Default Values: Always provide sensible default values in your values.yaml file.
  4. Use Conditional Blocks Wisely: Use conditions to make your charts more flexible and adaptable to different environments.
  5. Document Your Chart: Use the NOTES.txt file to provide usage instructions and important information about your chart.
  6. Version Your Charts: Use semantic versioning for your charts to make upgrades and rollbacks easier.

Example: A Complete Helm Chart

Let’s put it all together with a simple example of a Helm chart for a web application.

# values.yaml
replicaCount: 2
image:
  repository: nginx
  tag: latest
service:
  type: ClusterIP
  port: 80
ingress:
  enabled: false
  hosts:
    - host: chart-example.local
      paths: ["/"]
env:
  - name: LOG_LEVEL
    value: "info"
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "mychart.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          env:
            {{- range .Values.env }}
            - name: {{ .name }}
              value: {{ .value | quote }}
            {{- end }}
# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "mychart.selectorLabels" . | nindent 4 }}
# templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  rules:
    {{- range .Values.ingress.hosts }}
    - host: {{ .host | quote }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ . }}
            pathType: Prefix
            backend:
              service:
                name: {{ include "mychart.fullname" $ }}
                port: 
                  number: {{ $.Values.service.port }}
          {{- end }}
    {{- end }}
{{- end }}

This example demonstrates the use of variables, arrays, conditions, and loops in a practical Helm chart. It includes a deployment, a service, and an optional ingress, all of which can be customized through the values.yaml file.

Conclusion

Helm is a powerful tool that can significantly simplify your Kubernetes deployments. By mastering Helm commands and template writing, you can create flexible, reusable charts that make deploying and managing applications on Kubernetes a breeze. Remember to follow best practices, leverage Helm’s built-in functions, and always test your charts thoroughly before using them in production environments.