Helm Charts

Good afternoon

Helm is a package manager for Kubernetes. This tool allows us to wrap Kubernetes applications in convenient packages called charts that can be easily deployed, updated, and managed at any given time.

Charts are packages that can include everything to run an application on Kubernetes, from deployments to services. All this makes it possible to work with applications as a single entity, and not as a set of separate resources that also need to be configured manually…

Also Helm simplifies dependency management between applications, allows you to easily parameterize application settings through values.yaml files and makes it possible to reuse charts using templates.

In addition, you can easily roll back to the previous version of our application.

Helm Chart Structure

The Helm Chart structure is a directory that contains a set of mechanisms for managing applications, packaging them into charts that can be easily installed, updated and managed.

The first and most important file – Chart.yaml, it contains metadata: chart name, version, description, dependency information, etc. This file is required and helps identify the chart:

apiVersion: v2
name: mychart
version: 1.0.0
description: "helm chart"
keywords:
  - mykeyword
home: http://example.com/
sources:
  - https://github.com/example/mychart
dependencies:
  - name: nginx
    version: "1.14.0"
    repository: "https://charts.helm.sh/stable"

Values.yaml defines configuration parameters that can be overridden during chart installation or update. These values ​​are used in chart templates to dynamically generate Kubernetes manifests:

replicaCount: 2
image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent
service:
  type: NodePort
  port: 80

Directory templates/ contains Kubernetes manifest templates that use both static and dynamic data from values.yaml and Chart.yaml to generate final manifests. Helm processes each file in this directory, using templating to create Kubernetes resources. For example, 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: 80
  selector:
    app.kubernetes.io/name: {{ include "mychart.name" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}

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:
      app.kubernetes.io/name: {{ include "mychart.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "mychart.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
      containers:
        - name: nginx
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: 80

templates/_helpers.tpl:

{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

Charts/ – this is the directory for subcharts, i.e., the dependencies of your chart. If an application requires other charts for its operation, they are placed here

.helmignore – a file similar to .gitignore, which allows you to exclude files and directories from the chart package, which reduces its size and prevents unnecessary data from being included in the chart, etc.:

# игнорить все файлы .git и .svn
.git
.svn

# игнорить все файлы .md, кроме README.md
*.md
!README.md

# игнорить специфичные файлы и директории
tmp/
temp/
secrets.yaml

File NOTES.txt in the templates directory provides the user with information after installing the chart, such as how to connect to the application or next steps after installation:

1. Get the application URL by running these commands:
  {{- if .Values.service.type == "NodePort" }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "mychart.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
  {{- end }}

This file also supports templating.

Storage management

Helm uses a store to store release information, and by default in Helm 3 this store is implemented using ConfigMaps. However, there are alternative storage options, such as Secrets or storage in a SQL database, which are much more convenient.

Secrets is the basic choice for storing sensitive information: passwords or keys associated with Helm releases.

Setting up Secrets:

# values.yaml для чарта

secrets:
  enabled: true
  backend: secrets
# файл templates/secrets.yaml

apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-secrets
type: Opaque
data:
  # можно указать данные, к примеру пароль

For other scenarios where larger and more functional storage is required, you can use a SQL database. For example, you can use PostgreSQL:

# values.yaml для чарта

sql:
  enabled: true
  backend: postgresql
  postgresqlHost: my-postgresql-host
  postgresqlDatabase: helm_releases
  postgresqlUsername: helm
  postgresqlPassword: qazwsxedcdanormparol
CREATE TABLE IF NOT EXISTS releases (
    release_name VARCHAR(255) PRIMARY KEY,
    chart_name VARCHAR(255),
    chart_version VARCHAR(255),
    release_version INT,
    status VARCHAR(255),
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);

Post-Renderers

Post-Renderers provide the ability to modify Kubernetes manifests after they are generated by the Helm template engine, but before they are applied to Kubernetes. This provides opportunities for customization, allowing you to integrate additional tools and processes into the deployment workflow (for example, kustomize)

Let’s say I have a Helm chart that deploys an application to Kubernetes, and I want to modify the generated manifests before deployment, using Kustomize to add specific security settings or change resource configuration.

First a directory is created, for example kustomizeinside the project and add the file to it kustomization.yaml with all the necessary settings. For example, you can add new labels or annotations to the Deployment:

# kustomize/kustomization.yaml
commonLabels:
  environment: production

patchesStrategicMerge:
  - patch.yaml

Besides we will definitely create patch.yaml in the same directory to change the Deployment specification:

# kustomize/patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-application
spec:
  template:
    spec:
      containers:
      - name: my-application
        resources:
          limits:
            cpu: "500m"
            memory: "128Mi"

Let’s create a script that will be used as a post-renderer. This script should read manifests from STDIN, apply Kustomize to them and output the result to STDOUT:

#!/bin/bash

# сейвим входные данные во временный файл
INPUT=$(mktemp)
cat > $INPUT

# юзаем Kustomize
kustomize build kustomize | cat

# дел временных файлов
rm $INPUT

Making the script executable:

chmod +x post-render.sh

Now you can use this script as a post-renderer when installing or updating a chart:

helm install my-chart ./mychart --post-renderer ./post-render.sh

Or when updating:

helm upgrade my-chart ./mychart --post-renderer ./post-render.sh

Helm Dependencies

Often an application or service may depend on other charts. For example, an application may require a Redis database or a RabbitMQ message server. Instead of manually installing and configuring these dependencies each time, Helm allows you to automatically manage them through a file Chart.yaml chart.

Dependencies are defined in a file Chart.yaml or requirements.yaml on the Helm chart. It specifies the dependency chart name, version, and repository from which Helm can download the chart:

apiVersion: v2
name: my-application
version: 1.0.0
dependencies:
  - name: redis
    version: "^6.0.1"
    repository: "https://charts.bitnami.com/bitnami"
  - name: rabbitmq
    version: "8.0.2"
    repository: "https://charts.bitnami.com/bitnami"

After determining the dependency in Chart.yamlyou can use Helm commands to manage these dependencies:

  • helm dependency list shows a list of dependencies and their status.

  • helm dependency update downloads and places dependencies in a directory charts/ chart.

  • helm dependency build updates dependencies and regenerates the file charts.lockwithout downloading packages again.

  • helm dependency update does the same thing as buildbut also downloads all the necessary charts.

Let’s say we’re working on an application that requires Redis. Instead of manually deploying Redis each time, you can simply define it as a dependency in the chart:

  1. determine the dependence in Chart.yaml.

  2. launch helm dependency update to download and package the Redis chart into a chart.

  3. when you launch a chart using helm installHelm will also install Redis using certain settings.

Helm SDK

golang has a Helm SDK that allows you to programmatically manage Helm charts, repositories, and releases in Kubernetes.

Working with the Helm SDK is based on the client configuration action.Configuration which includes settings for interacting with the Kubernetes cluster and Tiller or directly with the Kubernetes API. action.Configuration used to initialize various clients to perform Helm operations such as installing action.Installupdate action.Upgradedeletion action.Uninstall and many others.

The SDK provides functions for working with charts, including searching, installing, updating and deleting them. You can use these functions to automate application deployment, as well as to create complex pipelines that automatically update charts based on changes in source code or configuration.

Using the SDK, you can manage Helm releases, including getting information about current releases, their history, and rolling back to previous versions. This is the main opportunity in the helm for implementing Blue/Green or Canary deployment strategies

The first step is to initialize the Helm client:

package main

import (
    "helm.sh/helm/v3/pkg/action"
    "helm.sh/helm/v3/pkg/cli"
    "k8s.io/client-go/util/homedir"
    "path/filepath"
)

func main() {
    settings := cli.New()
    actionConfig := new(action.Configuration)

    kubeconfig := filepath.Join(homedir.HomeDir(), ".kube", "config")
    if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
        fmt.Printf(format, v...)
    }); err != nil {
        log.Fatalf("Failed to initialize Helm client: %v", err)
    }
}

To install a chart using the Go SDK, you can use the client action.Install and configure its parameters, similar to how you would do it using the CLI command helm install:

install := action.NewInstall(actionConfig)
install.ReleaseName = "my-release"
install.Namespace = "default"
chartPath, err := install.LocateChart("mychart", settings)
if err != nil {
    log.Fatalf("Failed to locate chart: %v", err)
}

_, err = install.Run(chartPath, nil) // Второй параметр может содержать значения для переопределения (values)
if err != nil {
    log.Fatalf("Failed to install chart: %v", err)
}

Chart update works similar to installation, but uses a client action.Upgrade:

upgrade := action.NewUpgrade(actionConfig)
upgrade.Namespace = "default"
if _, err := upgrade.Run("my-release", chartPath, nil); err != nil {
    log.Fatalf("Failed to upgrade chart: %v", err)
}

Conclusion

Helm Charts allow you to simplify application management and automate many processes.

And my friends from OTUS talk more about application infrastructure and practical tools as part of practical online courses. With a complete catalog of courses you can check out the link.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *