Simplifying Kubernetes Configuration Management Using Helm

Piotr Stroz
DevBulls

--

In this tutorial, I will guide you through deploying a simple Apache web server to a Kubernetes cluster, illustrating how Helm’s templating capabilities can be used to apply distinct configurations across development, staging, and production environments. This approach eliminates the need for repeated code or manually editing configuration files before applying them, making the deployment process more efficient and manageable.

Prerequisites

  • A Kubernetes cluster
  • Helm installed and configured
  • Basic understanding of Kubernetes objects (Deployments, Services, ConfigMaps)

Overview

We will deploy an Apache web server that displays a custom message, which will vary depending on the environment (development, staging, production). We will demonstrate how Helm templating can be leveraged to manage this scenario. As we go through the example, we will gradually increase the complexity.

Step 1: Creating the Helm Chart

First, we need to create a Helm chart for our Apache web server.

Initialize Chart

Create a new Helm chart:

helm create apache-chart

This command creates a new directory apache-chart with the basic structure Helm chart structure.

Customize Chart

Remove the default templates generated by Helm:

cd apache-chart
rm -rf templates/*

Step 2: Create required Kubernetes objects

ConfigMap

The ConfigMap will store the HTML content served by the Apache server:

# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-app-config
data:
index.html: |
<html>
<head><title>App Message</title></head>
<body><h1>{{ .Values.message }}</h1></body>
</html>

Notice template directive {{ .Release.Name }} and {{ .Values.message }}. They inject the release name and message into the ConfigMap.

Deployment

Define the Apache web server Deployment, mounting the ConfigMap with the message that will get displayed:

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-web-server
labels:
version: {{ .Chart.Version }}
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web-server
image: httpd
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /usr/local/apache2/htdocs
volumes:
- name: config-volume
configMap:
name: {{ .Release.Name }}-app-config

Service

Expose the web server within the cluster via a ClusterIP service:

# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-web-server
spec:
type: ClusterIP
selector:
app: web
ports:
- protocol: TCP
port: 80

Step 3: Create Values File

Create a values.yaml file for default values and placeholders for environment-specific configurations:

# values.yaml
message: "Welcome to our website!"

Step 4: Configure for Different Environments

For each environment (development, staging, production), create a values file with specific configurations.

Development Environment Values

Create a values-development.yaml:

# values-development.yaml
message: "Development Environment Message"

Repeat this step for staging and production environments, customizing the message and namespace as necessary.

Step 5: Deploy to Your Cluster

First create a development namespace:

kubectl create namespace development

Next deploy the chart to the development namespace using Helm:

helm install apache-dev ./apache-chart -f ./apache-chart/values-development.yaml --namespace development

Repeat for staging and production by using their respective values files.

Advanced Helm Templating Features

To take full advantage of Helm’s templating capabilities, consider incorporating the following features:

Using values.yaml for Environment-Specific Overrides

Create environment-specific values.yaml files to override settings for different environments:

# values-staging.yaml
message: "Staging Environment Message"

Conditional Template Logic

Use conditional logic to include or exclude resources based on values:

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-web-server
labels:
version: {{ .Chart.Version }}
spec:
replicas: {{ .Values.replicaCount | default 1 }} # <- conditionals
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web-server
image: httpd
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /usr/local/apache2/htdocs
volumes:
- name: config-volume
configMap:
name: {{ .Release.Name }}-app-config

Using Helm Functions

Utilize Helm functions to manipulate strings, lists, and other data types:

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-web-server
labels:
app: {{ .Chart.Name }}
spec:
replicas: {{ .Values.replicaCount | default 1 }}
selector:
matchLabels:
app: {{ .Chart.Name }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: {{ required "image.repository is required" .Values.image.repository }}:{{ .Values.image.tag | default "latest" | quote }}
ports:
- containerPort: 80
resources:
limits:
cpu: {{ .Values.resources.limits.cpu | default "0.5" | quote }}
memory: {{ .Values.resources.limits.memory | default "512Mi" | quote }}
volumeMounts:
- name: config-volume
mountPath: /usr/local/apache2/htdocs
volumes:
- name: config-volume
configMap:
name: {{ .Release.Name }}-app-config

Using Templates and Subcharts

Break down complex charts into smaller, reusable templates or subcharts for better manageability and reusability:

# templates/_helpers.tpl
{{- define "apache-chart.fullname" -}}
{{- .Release.Name }}-{{ .Chart.Name }}
{{- end -}}

Configuring Helm Hooks

Use Helm hooks to manage pre-install, post-install, and other lifecycle events for your Helm releases:

# templates/job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ .Release.Name }}-pre-install-job"
annotations:
"helm.sh/hook": pre-install
spec:
template:
spec:
containers:
- name: pre-install-job
image: busybox
command: ["sh", "-c", "echo Hello, Helm!"]
restartPolicy: Never

Conclusion

This tutorial demonstrated how to use Helm’s templating engine and values files to manage configurations for Kubernetes applications across multiple environments. By leveraging advanced Helm features such as conditional logic, functions, templates, subcharts, and hooks, you can create flexible and maintainable Helm charts for complex application deployments.

--

--