Configuration as Code (CaC): Helms
1. Configuration as Code in the Cloud Stack
- IaC: clusters, networks, VMs (Terraform, Crossplan)
- CaC: runtime app/platform configuration (Helm, Kustomize, YAML overlays)
- Why CaC:
- Reproducibility
- Reviewability
- "Diff-able" ops
- Promotion across environments
2. Helm Fundamentals
What Helm does?
- packaging (charts),
- templating (Go templates),
- release management (history/rollback)
Helm as a Packaging System
- Helm packages Kubernetes manifests into reusable bundles called
charts. - Charts contains
- Metadata
- Templates
- Default Configurations
Key objects of a Chart
- Chart.yaml
- templates/
- values.yaml
- values.schema.json
Release lifecycle
- Install
- Upgrade --reuse-values
- Rollback
- Uninstall
3. Nginx Chart Components
Chart: Bitnami Secure Images Helm chart for NGINX Open Source
Chart.yaml
The Helm manifest defining the chart (identity, version, dependencies, and metadata)
annotations
annotations:
images: |
- name: git
image: docker.io/bitnami/git:2.51.0-debian-12-r0
- name: nginx
image: docker.io/bitnami/nginx:1.29.1-debian-12-r0
- name: nginx-exporter
image: docker.io/bitnami/nginx-exporter:1.4.2-debian-12-r9
licenses: Apache-2.0
tanzuCategory: clusterUtility
images: a list of container images used by this chartlicenses: declares the chart’s open-source license.tanzuCategory: a VMware Tanzu-specific tag grouping the chart (here: cluster utilities).
dependencies
dependencies:
- name: common
repository: oci://registry-1.docker.io/bitnamicharts
tags:
- bitnami-common
version: 2.x.x
- This means the NGINX chart depends on another chart called common from Bitnami.
- This is not an application, but it is a library chart, which defines a collection of reusable template helpers, functions, and YAML snippets.
- This is for Bitnami only, and is not applicable for charts created by others.
- Other items in
Chart.yaml,description,home,icon,keywordsare more for descriptive/categorical purposes.
templates/
- Helm templates are the parameterized YAML manifests that define Kubernetes objects (like Deployments, Services, ConfigMaps, etc.) for your application.
- They live under the templates/ directory inside a Helm chart and use Go’s templating language (
{{ ... }}) to inject dynamic values.
Rendering final manifests from templates
- The structure inside the
templatesdirectory depends on how the application needs to be deployed. - Structure for Bitname's NGINX templates
Portion of templates/deployment.yaml
{{- /*
Copyright Broadcom, Inc. All Rights Reserved.
SPDX-License-Identifier: APACHE-2.0
*/}}
apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }}
kind: Deployment
metadata:
name: {{ include "common.names.fullname" . }}
namespace: {{ include "common.names.namespace" . | quote }}
labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }}
{{- if .Values.commonAnnotations }}
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
labels:: Standard YAML key, indicating that the following lines will definelabels.{{- ... }}: Go template syntax for an action or function call.-: tells the template engine to remove any extra white space (like blank lines or spaces) immediately before or after this command to keep the final output clean.include "common.labels.standard": fetches pre-defined text from another part (a "partial template") of the Helm chart named "common.labels.standard". This is a way to reuse the same set of labels across many different files.- This is how Bitnami's
commonchart is used.
- This is how Bitnami's
( dict "customLabels" .Values.commonLabels "context" $ ): Creates a new, specific package of data (a dictionary or map) for that template to use. This package contains two items:"customLabels" .Values.commonLabels: Creates a field named "customLabels" and fills it with values found in the main configuration under.Values.commonLabels(Seevalues.yaml)."context" $: Creates a field named "context" and assigns it the value of$. In Helm, the dollar sign ($) is a special variable that always refers to the main, top-level set of all data in the chart (the "root context"). This ensures the included template can still access global information like the Chart name or Release name, even though we are primarily passing it a limited dictionary.
| nindent 4: Is a "pipe" command that takes the output of the include function and applies an operation to it.- The
nindent 4function adds a new line before the included text and indents every single line of that text by 4 spaces. - This is crucial for making sure the output is valid YAML, as indentation matters in YAML
- The
values.yaml
- Configuration file that provides default values for the chart's templates.
- Help customizting how the application should be deployed.
- Steps:
- Create a custom yaml file,
- Specify this file when launching Helm using
--valuesflag.
values.schema.json
- Optional
- Containing schematic of
values.yaml. - Ensuring the custom yaml file match the keys in
values.yamlprior to deployment.
4. Hands-on
Info
Helm is already installed in CloudLab's class profile. The installation script is
located in install_helm.sh. This script needs to be run as root.
Deploy Nginx from Helm as-is
- Update repo
- Create a namespace
- Preview the chart template (no installation yet)
- Examine manifests for deployment and service
mkdir my-nginx
cd my-nginx
helm template my-nginx bitnami/nginx -n web > nginx-template.yaml
cat nginx-template.yaml
- Install chart
- Check chart status
- Read the status message and find out how to test the my-nginx server from a browser.
- Check Kubernetes Services and Pods
- Uninstall Helm chart
Deploy customed Nginx from Helm
- Inside
my-nginxdirectory, create a file calledmy-values.yamlwith the following content
image:
registry: docker.io
repository: nginx
tag: "1.25.5"
pullPolicy: IfNotPresent
containerPorts:
http: 80
service:
type: NodePort
ports:
http: 8080 # Service port inside cluster (arbitrary)
nodePorts:
http: 32080 # External NodePort (within default range)
containerSecurityContext:
enabled: true
runAsNonRoot: false
runAsUser: 0
- Regenerate template file
- Compare the differences between the two templates
- Install the new nginx with the customized environment
- Confirm that the new nginx is accessible via a custom predefined port 32080