Deploying packaged Kubernetes apps with Helm
What is Helm ? How Helm works ? How can we use Helm to deploy and manage packaged Kubernetes applications ? Let's get familiar with Helm and learn how to create and distribute our own Helm packages.

What is Helm
Helm is a tool that can be used to create and install ready to use, configurable and sharable packages of Kubernetes resources manifests for specific applications.
Those packaged Kubernetes resources manifests are called Helm Charts. Helm Charts are distributed via Charts repositories.
ArtifactHub is a web-based application that can be used to explore Helm Charts from many public Helm Charts repositories.
The installation of a specific Helm Chart inside a Kubernetes cluster creates a Helm Release.
How Helm works
Helm configuration settings and files
When the Helm command line utility is invoked to manage (install, uninstall, get status...) Helm releases, by default, it uses the $HOME/.kube/config
configuration file to discover currently configured Kubernetes clusters endpoints and get the necessary info to securely communicate with their API servers.
The same configuration file is by default used by 'kubectl' and therefore, configuration setups performed with 'kubectl config' will by default be reused by Helm.
The environment variable $KUBECONFIG
, or the '--kubeconfig' command line flag can be used to change the default path to Kubernetes clusters configuration file.
To list the other available configuration environment variables and command line flags for configuring Helm, and also, the default directory paths where Helm stores its data, configurations and caches, use 'helm --help'.
Helm Releases states
Helm releases states are stored as Kubernetes 'secrets' resources inside target clusters. Each time a Helm release is created or updated, a new 'secret' resource version is created in the release namespace.
Helm gets the releases history from those 'secrets' and use them for performing releases rollbacks. Deleting those 'secret' resources will make Helm forget anything about the previously managed releases.
Here are example Helm state 'secrets' resources for two versions of a Helm release named 'grafana':
$ kubectl get secret
NAME TYPE DATA AGE
sh.helm.release.v1.grafana.v1 helm.sh/release.v1 1 3m1s
sh.helm.release.v1.grafana.v2 helm.sh/release.v1 1 5s
Installing Helm
- Helm releases assets can be found here
- To install Helm on Linux, do the following:
$ helm_version=v3.15.3 # choose version
$ linux_arch=amd64 # choose OS arch
# Download Helm binary
$ wget https://get.helm.sh/helm-${helm_version}-linux-${linux_arch}.tar.gz
# Install Helm binary
$ sudo tar xzvf helm-${helm_version}-linux-${linux_arch}.tar.gz -C /usr/local/bin/
# Verify
$ helm version
Managing Helm Charts repositories
# Adding Helm Charts repositories
$ repo_name=grafana ; repo_url=https://grafana.github.io/helm-charts
$ helm repo add $repo_name $repo_url
"grafana" has been added to your repositories
# Listing added repositories
$ helm repo list
NAME URL
grafana https://grafana.github.io/helm-charts
# Synchronize/update local repo info from remote
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "grafana" chart repository
Update Complete. ⎈Happy Helming!⎈
# Removing repositories
$ helm repo remove $repo_name
"grafana" has been removed from your repositories
Searching for Helm charts
# Searching through public artifacthub.io repositories
$ search_keyword=grafana
$ helm search hub $search_keyword
URL CHART VERSION APP VERSION DESCRIPTION
https://artifacthub.io/packages/helm/grafana/gr... 8.3.6 11.1.0 The leading tool for querying and visualizing t...
https://artifacthub.io/packages/helm/saurabh6-g... 0.2.0 1.1 This is a Helm Chart for Grafana Setup.
(...)
# Searching through locally added repositories
$ search_keyword=grafana/grafana
## Search for lastest versions of Charts matching the search keyword
$ helm search repo $search_keyword
NAME CHART VERSION APP VERSION DESCRIPTION
grafana/grafana 8.3.6 11.1.0 The leading tool for querying and visualizing t...
grafana/grafana-agent 0.42.0 v0.42.0 Grafana Agent
grafana/grafana-agent-operator 0.4.1 0.42.0 A Helm chart for Grafana Agent Operator
grafana/grafana-sampling 0.1.1 v0.40.2 A Helm chart for a layered OTLP tail sampling a...
(...)
## Search for all versions of Charts matching the search keyword
$ helm search repo $search_keyword -l
NAME CHART VERSION APP VERSION DESCRIPTION
grafana/grafana 8.3.6 11.1.0 The leading tool for querying and visualizing t...
grafana/grafana 8.3.5 11.1.0 The leading tool for querying and visualizing t...
(...)
grafana/grafana-agent 0.42.0 v0.42.0 Grafana Agent
grafana/grafana-agent 0.41.0 v0.41.1 Grafana Agent
(...)
grafana/grafana-agent-operator 0.4.1 0.42.0 A Helm chart for Grafana Agent Operator
grafana/grafana-agent-operator 0.4.0 0.41.1 A Helm chart for Grafana Agent Operator
(...)
# Download Charts files
$ helm pull --untar $chart_url
## Download from locally added repositories
$ helm pull --untar grafana/grafana
$ ls grafana/
Chart.yaml ci dashboards README.md templates values.yaml
Managing Helm releases
Show Helm Charts default values
- Helm Charts 'values' are variables/parameters we can set in order to customize the manifests that will be deployed by the Charts, during installation or updates
- To get all the possible 'values' that can be provided to Charts, do the following:
$ chart_name=grafana/grafana
$ helm show values $chart_name
global:
# -- Overrides the Docker registry globally for all images
imageRegistry: null
(...)
# global:
# imagePullSecrets:
# - pullSecret1
# - pullSecret2
imagePullSecrets: []
rbac:
create: true
(...)
The output of the previous command could be redirected to a 'values.yaml' file, that will then be customized and used as input values file during Charts releases creation.
Get Helm releases values, manifests, hooks and notes
We can get the following data from a Helm release:
- values: user-supplied values
- hooks: Helm hooks in use
- manifest: the manifests of resources created by the release
- notes: the content of the Charts NOTES.txt file
We could also get all for all currently used values and manifests, plus hooks currently in use plus the content of the Charts NOTES.txt file.
Here are examples:
$ chart_name=grafana/grafana
$ release_name=grafana
$ helm get values $release_name [-n $namespace]
USER-SUPPLIED VALUES:
null
$ helm get manifest $release_name
---
# Source: grafana/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
automountServiceAccountToken: false
metadata:
labels:
helm.sh/chart: grafana-8.4.4
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
app.kubernetes.io/version: "11.1.3"
app.kubernetes.io/managed-by: Helm
name: grafana
namespace: default
---
# Source: grafana/templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: grafana
(...)
$ helm get hooks $release_name
(...)
---
# Source: grafana/templates/tests/test-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-test
namespace: default
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
helm.sh/chart: grafana-8.4.4
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
app.kubernetes.io/version: "11.1.3"
app.kubernetes.io/managed-by: Helm
data:
run.sh: |-
@test "Test Health" {
url="http://grafana/api/health"
code=$(wget --server-response --spider --timeout 90 --tries 10 ${url} 2>&1 | awk '/^ HTTP/{print $2}')
[ "$code" == "200" ]
}
---
(...)
$ helm get all $release_name
COMPUTED VALUES:
admin:
existingSecret: ""
passwordKey: admin-password
userKey: admin-user
adminUser: admin
affinity: {}
alerting: {}
assertNoLeakedSecrets: true
automountServiceAccountToken: true
autoscaling:
behavior: {}
enabled: false
maxReplicas: 5
minReplicas: 1
targetCPU: "60"
targetMemory: ""
containerSecurityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
(...)
Create or update Helm releases from a Chart
helm upgrade --install $release_name $chart_name --create-namespace [-n $namespace] [--version $chart_version] [-f values.yaml] [--set key=value] [--wait]
- The '-n' and '--version' options can be used to repectively specify the release namespace and Chart version to use
- The '--create-namespace' option is used to automatically create the target namespace for the release if it does not exist
- The '-f' or '--values' option can be used to set custom values for the Chart. That option can be used multiple times with multiple values files. Values from lastest specified files will have higher priorities
- Chart values can also be specified directly at the command line using '--set key1=value1,key2=value2...'. Values specified with the '--set' option have higher priority over those specified with '-f' or '--values'. The backslash character can be used to escape some characters.
Example:--set nodeSelector."kubernetes\.io/role"=master
- The '--wait' flag can be used to wait for all pods to be at a ready state. Helm will wait the amount of time specified by the '--timeout' flag (Default value: '5 minutes')
List and get the status of Helm releases
$ helm list [-n $namespace]
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
grafana default 2 <release_creation_date> deployed grafana-8.3.6 11.1.0
$ release_name=grafana
$ helm status $release_name [-n $namespace]
NAME: grafana
(...)
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:
kubectl get secret --namespace default grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
(...)
View Helm releases history
$ release_name=grafana
$ helm history $release_name [-n $namespace]
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 <release_creation_date> superseded grafana-8.3.6 11.1.0 Install complete
2 <release_creation_date> deployed grafana-8.3.6 11.1.0 Upgrade complete
Rollback Helm releases
# Rolling back
$ release_name=grafana
# Rollback to previous release
$ helm rollback $release_name [-n $namespace]
# Rollback to specific revision of a release
# Revision numbers are shown in release history
$ revision_number=1
$ helm rollback $release_name $revision_number [-n $namespace]
Rollback was a success! Happy Helming!
# Show release created after rollback
$ helm history $release_name
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
(...)
3 <release_creation_date> deployed grafana-8.3.6 11.1.0 Rollback to 1
Uninstall Helm releases
$ release_name=grafana
$ helm uninstall $release_name [--keep-history -n $namespace]
release "grafana" uninstalled
The optional '--keep-history' flag can be used to keep deletion records. Doing that allows showing deleted releases with the '--uninstalled' or '--all' flags when listing releases with the 'helm list' command.
$ helm list --uninstalled
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
grafana default 1 <release_creation_date> uninstalled grafana-8.4.4 11.1.3
Creating Helm charts
Helm Charts structure
Here is the basic structure of Helm Charts sources:
$ tree mychart/
mychart/
├── charts
├── Chart.yaml
├── NOTES.txt
├── templates
│ ├── deployment.yaml
│ └── _helpers.tpl
└── values.yaml
- Prefer using '.yaml' extension over '.yml' when developing Helm Charts
- To quickly generate an example Chart that can be used as development base, use the following command:
helm create $chart_directory_path
The Chart.yaml file
The 'Charts.yaml' file contains the Charts metadata. Here is an example content:
apiVersion: v2
version: 0.0.1 # Sementic Versioning (SemVer)
name: myapp
description: Helm Chart for myapp
type: application # or 'library'
appVersion: 10.2.3
All the available fields for that file are listed here. The 'type' field indicates the type of the chart we are creating. There are two types of charts: 'application' or 'library'.
Application charts are the classic charts we use for deploying Kubernetes apps. That's the one we are going to create in this post.
Library charts are useful only during charts development for adding new utilities or functions into the rendering pipeline. They do not contain any Kubernetes resources templates and therefore cannot be deployed.
The fields apiVersion
, name
and version
are mandatory.
The charts directory
The 'charts' directory is used to store dependencies charts packages. The required dependencies charts are declared using the 'dependencies' field inside the Chart.yaml file.
Custom 'values' for the dependencies Charts are set under the key corresponding to the name of the dependencies. Here is an example:
# File: Charts.yaml
(...)
dependencies:
- name: ingress-nginx
repository: https://helm.nginx.com/stable
(...)
(...)
# File: values.yaml
nginx:
# Charts values from the ingress-nginx Helm Charts repository:
# https://github.com/kubernetes/ingress-nginx/blob/main/charts/ingress-nginx/values.yaml
namespaceOverride: mynamespace
controller:
name: mycontroller
(...)
The templates directory
The 'templates' directory contains the '.yaml' Kubernetes resources manifests files. Those files contain the definition of the Kubernetes resources that could be deployed using the Chart.
The Go templating engine syntax and functions can be used inside those files. Sprig functions and additional Helm built-in objects can also be used. For a list of all functions available inside templated Kubernetes resources manifests, have a look at Helm chart template functions list.
Note that template files prefixed with the underscore (_
) sign won't generate an interpreted output, but their content could be used inside other templated manifests. This is the case for the '_helpers.tpl' file for instance, that is used for storing reusable templates blocks. Here is an example:
# File: templates/_helpers.tpl
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end -}}
# File: templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "name" . }}
(...)
The NOTES.txt file
The content of the 'NOTES.txt' file will be printed to standard output after a release has been created from the Chart. That file is also compatible with the Go templating language. Here is an example content:
=> Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "..name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
The values.yaml file
The 'values.yaml' file contains the Chart default configuration parameters that could be set by the Chart users to customize their releases. Releases customizations could be for instance specifying a custom name for all the releases resources, enabling/disabling specific resources or features, setting custom values for specific fields of certain resources. Here is an example content:
# values.yaml
nameOverride: myapp
image:
repository: selenium/node-firefox
tag: 3.141.59
replicaCount: 3
autoscaling:
enabled: false
(...)
All the Charts parameters specified as values (via values.yaml or the '--set' Helm CLI flag) are exported into the Helm '.Values' object that is accessible in templates files. Here is an example of using Charts values parameters inside a deployment resource manifest template:
# File: templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
(...)
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
(...)
Helm Charts templating basics
- To render Kubernetes resources manifests from a Helm Chart directory, use:
helm template $path_to_chart_directory --debug
- Templating directives and functions are put inside double curly braces
{{ }}
- Here is how we can use comments inside templates files, single or multiline:
{{/* my comment */}}
- The
-
sign is used at the begining of the double curly braces like this{{- }}
or at the end{{ -}}
in order to respectively remove all whitespaces characters before or after the templating directives. Here are examples:
# Without '-', whitespaces before and after
# the double curly braces '{{ }}' are preserved
# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ "myapp" }}
labels:
{{ "app.kubernetes.io/name: myapp" }}
# Result
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app.kubernetes.io/name: myapp
# Removing all whitespaces before the label
# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ "myapp" }}
labels:
{{- "app.kubernetes.io/name: myapp" }}
# Result
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:app.kubernetes.io/name: myapp
- The
{{- }}
is commonly used for templating blocks defined inside the '_helpers.tpl' file. It is also commonly used inside Kubernetes resources templates, combined with thenindent
function, that add a new line and the desired number of spaces for indentation. Here is an example:
# Removing all whitespaces before the label
# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ "myapp" }}
labels:
{{- "app.kubernetes.io/name: myapp" }}
# Result
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:app.kubernetes.io/name: myapp
# Adding a new line + 4 spaces for indentation
# File: templates/test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ "myapp" }}
labels:
{{- "app.kubernetes.io/name: myapp" | nindent 4 }}
# Result
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app.kubernetes.io/name: myapp
- The
{{ -}}
is most of the time used when defining templating blocks inside the '_helpers.tpl' file. Here is an example:
# File: templates/_helpers.tpl
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end }}
- The dot
{{ . }}
is an object containing all the values and other Helm built-in objects or a portion of the values depending on the location from where it is called from inside a template file. Here is an example where we get all the objects:
# File: values.yaml
(...)
autoscaling:
enabled: true
env:
- name: HUB_HOST
value: "selenium-hub"
- name: HUB_PORT
value: "4444"
- name: NODE_MAX_INSTANCES
value: "1"
- name: NODE_MAX_SESSION
value: "1"
(...)
# File: templates/test.yaml
{{ toYaml . }}
# Result
$ helm template . --debug
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /home/gmkziz/helm-charts/mychart
---
# Source: myapp/templates/test.yaml
Capabilities:
APIVersions:
- v1
(...)
HelmVersion:
git_commit: 414ff28d4029ae8c8b05d62aa06c7fe3dee2bc58
(...)
KubeVersion:
Major: "1"
(...)
IsRoot: true
apiVersion: v2
appVersion: 10.2.3
description: Helm Chart for myapp
name: myapp
type: application
version: 0.0.1
Files:
(...)
Release:
IsInstall: true
(...)
Subcharts: {}
Template:
BasePath: myapp/templates
Name: myapp/templates/test.yaml
Values:
autoscaling:
enabled: true
env:
- name: HUB_HOST
value: selenium-hub
- name: HUB_PORT
value: "4444"
- name: NODE_MAX_INSTANCES
value: "1"
- name: NODE_MAX_SESSION
value: "1"
(...)
- Now, let's change the scope with a
range
block and see that the{{ . }}
contains only objects inside that scope:
# File: values.yaml
(...)
autoscaling:
enabled: true
env:
- name: HUB_HOST
value: "selenium-hub"
- name: HUB_PORT
value: "4444"
- name: NODE_MAX_INSTANCES
value: "1"
- name: NODE_MAX_SESSION
value: "1"
(...)
# File: templates/test.yaml
{{- range .Values.env }}
{{ toJson . }}
{{- end }}
# Result
$ helm template . --debug
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /home/gmkziz/helm-charts/mychart
---
# Source: myapp/templates/test.yaml
{"name":"HUB_HOST","value":"selenium-hub"}
{"name":"HUB_PORT","value":"4444"}
{"name":"NODE_MAX_INSTANCES","value":"1"}
{"name":"NODE_MAX_SESSION","value":"1"}
- Objects we get when using
{{ . }}
depend on the scope or context we are currently in - The scope from where we get all the Helm built-in objects is called the root scope
- We can get the root scope objects from anywhere inside template files by using the
$
sign. Example:
# File: values.yaml
(...)
autoscaling:
enabled: true
env:
- name: HUB_HOST
value: "selenium-hub"
- name: HUB_PORT
value: "4444"
- name: NODE_MAX_INSTANCES
value: "1"
- name: NODE_MAX_SESSION
value: "1"
(...)
# File: templates/test.yaml
{{- with .Values.autoscaling }}
{{ toJson . }}
{{ toJson $.Values.env }}
{{- end }}
# Result
$ helm template . --debug
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /home/gmkziz/helm-charts/mychart
---
# Source: myapp/templates/test.yaml
{"enabled":true}
[{"name":"HUB_HOST","value":"selenium-hub"},{"name":"HUB_PORT","value":"4444"},{"name":"NODE_MAX_INSTANCES","value":"1"},{"name":"NODE_MAX_SESSION","value":"1"}]
Conditions and variables in Helm Charts
# File: templates/deployment.yaml
# if autoscaling.enabled is not set to true,
# set the number of replicas from the replicaCount value
apiVersion: apps/v1
kind: Deployment
metadata:
(...)
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
(...)
# Set resource type depending on the podController value
apiVersion: apps/v1
{{- if eq .Values.podController "Deployment" }}
kind: Deployment
{{- else if eq .Values.podController "Statefulset" }}
kind: Statefulset
{{- else }}
kind: Deployment
{{- end }}
metadata:
(...)
# File: templates/ingress.yaml
{{/* Defining variables */}}
{{- $svcPort := .Values.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
(...)
{{/* Referencing variables */}}
(...)
spec:
(...)
rules:
- host: myhost.local
http:
paths:
- path: /
backend:
service:
name: myservice
port:
number: {{ $svcPort }}
Commonly used Helm Charts actions
define
include
with
range
Commonly used Helm Charts functions
nindent
toYaml
trunc
trimSuffix
replace
default
printf
Templating Helm Charts resources names
A common way to define Charts resources names is this:
- '.Values.fullnameOverride' if defined, otherwise
- a combination of '.Release.Name' and '.Values.nameOverride' if '.Values.nameOverride' is defined and does not contain '.Release.Name', otherwise
- A combination of '.Release.Name' and '.Chart.Name' if '.Chart.Name' does not contain '.Release.Name', otherwise
- '.Release.Name'
The resources names should also be truncated at 63 characters because of a limitation of some of the Kubernetes name fields (DNS naming spec) and the -
suffix trimmed.
Here is an example templating block for defining resources names:
# File: templates/_helpers.tpl
{{- define "fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
Templating Helm Charts resources labels
Here are common labels keys we can use in our Charts:
helm.sh/chart
: a combination of the Chart name and versionapp.kubernetes.io/version
: the Chart application versionapp.kubernetes.io/managed-by
: Helmapp.kubernetes.io/name
: the name of the Chart applicationapp.kubernetes.io/instance
: the name of the release
app.kubernetes.io/name
and app.kubernetes.io/instance
are commonly used as selector labels for Deployments and StatefulSets type resources.
Here are labels usage examples in Helm Charts:
# File: templates/_helpers.tpl
# Define reusable templating blocks
{{/*
Return .Values.nameOverride if defined,
otherwise .Chart.name
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end -}}
{{- define "labels" -}}
helm.sh/chart: {{ .Chart.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "selectorLabels" . }}
{{- end }}
{{- define "selectorLabels" -}}
app.kubernetes.io/name: {{ include "name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
# File: templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
(...)
labels:
{{- include "labels" . | nindent 4 }}
spec:
(...)
selector:
matchLabels:
{{- include "selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "selectorLabels" . | nindent 8 }}
(...)
Creating and packaging a simple Helm Chart
Let's create an example Helm chart that will be used to create a deployment type resource. Here is the content of the Chart folder:
$ tree helm-chart-example/
helm-chart-example/
├── charts
├── Chart.yaml
├── NOTES.txt
├── templates
│ ├── deployment.yaml
│ └── _helpers.tpl
└── values.yaml
- Content of the Chart.yaml file
apiVersion: v2
version: 0.0.1
name: myapp
description: Helm Chart for myapp
type: application
appVersion: 10.2.3
- Content of the _helpers.tpl file
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride }}
{{- end -}}
{{/*
The fullname tempating block will be
used to set resources names
*/}}
{{- define "fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := include "name" . }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name }}
{{- end }}
{{- end }}
{{- end }}
{{- define "chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "labels" -}}
helm.sh/chart: {{ include "chart" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "selectorLabels" . }}
{{- end }}
{{- define "selectorLabels" -}}
app.kubernetes.io/name: {{ include "name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
- Content of the deployment.yaml file
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "fullname" . }}
labels:
{{- include "labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ include "name" . }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.env }}
env:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 10 }}
{{- end }}
- Content of the values.yaml file
nameOverride: myapp2
fullnameOverride:
image:
repository: selenium/node-firefox
tag: 3.141.59
replicaCount: 3
autoscaling:
enabled: true
volumes:
- name: dshm
emptyDir:
medium: Memory
volumeMounts:
- name: dshm
mountPath: /dev/dshm
env:
- name: HUB_HOST
value: "selenium-hub"
- name: HUB_PORT
value: "4444"
- name: NODE_MAX_INSTANCES
value: "1"
- name: NODE_MAX_SESSION
value: "1"
resources:
limits:
memory: "2000Mi"
requests:
memory: "2000Mi"
- To output the resulting manifests contents to stdout from the Chart, we use:
# From inside the helm-chart-example directory
$ helm template --debug .
- To create a Chart package, we use:
$ helm package helm-chart-example
Successfully packaged chart and saved it to: /home/gmkziz/helm-charts/myapp-0.0.1.tgz
Creating a release using the example Helm Chart
$ helm show values myapp-0.0.1.tgz > values.yaml
nameOverride: primary
fullnameOverride:
image:
repository: selenium/node-firefox
tag: 3.141.59
replicaCount: 3
(...)
# Set the custom Charts values as desired inside the values.yaml file,
# then:
$ helm upgrade --install myapp -f values.yaml myapp-0.0.1.tgz
NAME: myapp
(...)
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
myapp default 1 <release_creation_date> deployed myapp-0.0.1 10.2.3
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
myapp-primary 1/1 1 1 84s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-primary-65596d9948-k7m2z 1/1 Running 0 79s
$ kubectl get deploy -o yaml
apiVersion: v1
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
meta.helm.sh/release-name: myapp
meta.helm.sh/release-namespace: default
creationTimestamp: "***"
generation: 1
labels:
app.kubernetes.io/instance: myapp
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: primary
app.kubernetes.io/version: 10.2.3
helm.sh/chart: myapp
name: myapp-primary
namespace: default
resourceVersion: "990"
uid: bf577704-2d19-4b43-88ac-a7450c32ced1
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/instance: myapp
app.kubernetes.io/name: primary
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app.kubernetes.io/instance: myapp
app.kubernetes.io/name: primary
spec:
containers:
- env:
- name: HUB_HOST
value: selenium-hub
- name: HUB_PORT
value: "4444"
- name: NODE_MAX_INSTANCES
value: "1"
- name: NODE_MAX_SESSION
value: "1"
image: selenium/node-firefox:3.141.59
imagePullPolicy: IfNotPresent
name: primary
resources:
limits:
memory: 2000Mi
requests:
memory: 2000Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /dev/dshm
name: dshm
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- emptyDir:
medium: Memory
name: dshm
status:
availableReplicas: 1
conditions:
- lastTransitionTime: "***"
lastUpdateTime: "***"
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: "***"
lastUpdateTime: "***"
message: ReplicaSet "myapp-primary-74fdbf6bd" has successfully progressed.
reason: NewReplicaSetAvailable
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 1
replicas: 1
updatedReplicas: 1
kind: List
metadata:
resourceVersion: ""