What is Helm?
Helm is a package manager for Kubernetes.
A package manager generally aims to provide a single command to install some software. Installing software likely has dependencies – a package manager should be able to resolve those dependencies. A package manager is supposed to abstract some complexities of installing software and making it easier to update, upgrade and remove the software.
What can Helm do for us?
- Single command installation – helm install
- Ability to roll back – helm rollback
- Get state information – helm status
- Simplified upgrades- helm upgrade
- Single command uninstall – helm uninstall
Want to install helm? Grab the installation script from the official guide, and run it.
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
#Confirm that the installation was successful
helm version
version.BuildInfo{Version:"v3.5.0", GitCommit:"32c22239423b3b4ba6706d450bd044baffdcf9e6", GitTreeState:"clean", GoVersion:"go1.15.6"}
Helm Charts
Charts is the packaging format for Helm. A chart is nothing but a collection of files with a specific file structure that represents resources within Kubernetes. The name of the folder is the name of the chart. A chart contains all the files for a Kubernetes deployment.
Chart Repositories
A chart repository is an HTTP server that serves the packaged charts. A packaged chart is a Helm chart that is processed into an archive (tar) by using the following command and a name.
helm package chartname
Note: A chart repository serves an index.yaml file. This file is the source of truth of packaged charts and where they are. (Example: https://charts.helm.sh/stable/index.yaml)
The following section contains some commonly used commands.
Add a repository:
helm repo add stable https://charts.helm.sh/stable
"stable" has been added to your repositories
# Note: "stable" after the add argument is the name of the repository which can be changed.
List the added repositories if the repositories are already configured:
helm reop list
NAME URL
stable https://charts.helm.sh/stable
Search and Download (or fetch) a chart:
# search a chart
helm search repo apache
# download a chart
helm fetch stable/spark
ls
spark-1.0.5.tgz
Create a repository index from packaged charts:
# We have a number of packaged helm charts in a directory
ls sample
kafka-manager-2.3.5.tgz spark-1.0.5.tgz
# create an index and give the directory as the argument
helm repo index ./sample/
# Notice that now we have an index file in the sample repository
cat sample/index.yaml
apiVersion: v1
entries:
kafka-manager:
- apiVersion: v1
appVersion: 1.3.3.22
created: "2021-05-23T17:36:13.257252559Z"
dependencies:
- condition: zookeeper.enabled
name: zookeeper
repository: https://charts.helm.sh/incubator
version: 2.1.4
deprecated: true
description: DEPRECATED - A tool for managing Apache Kafka.
digest: 2570c27581a7b51e50e5857836aae0c1c86293cd68e36836cd7d91006520b84d
home: https://github.com/yahoo/kafka-manager
icon: https://kafka.apache.org/images/logo.png
keywords:
- kafka
- zookeeper
- kafka-manager
kubeVersion: ^1.8.0-0
name: kafka-manager
sources:
- https://github.com/yahoo/kafka-manager
urls:
- kafka-manager-2.3.5.tgz
version: 2.3.5
spark:
- apiVersion: v1
appVersion: 1.5.1
created: "2021-05-23T17:36:13.258001094Z"
deprecated: true
description: DEPRECATED - Fast and general-purpose cluster computing system.
digest: e0b4862812f85dcf7b92d71ed90e9f894f1aef35bb76f384fe322ba736cd10fc
home: http://spark.apache.org
icon: http://spark.apache.org/images/spark-logo-trademark.png
name: spark
sources:
- https://github.com/kubernetes/kubernetes/tree/master/examples/spark
- https://github.com/apache/spark
urls:
- spark-1.0.5.tgz
version: 1.0.5
Update or remove repositories:
helm repo update .
helm repo remove name
Install, get status or remove a chart:
#search for an example chart, wordpress in our case.
helm search repo wordpress
NAME CHART VERSION APP VERSION DESCRIPTION
stable/wordpress 9.0.3 5.3.2 DEPRECATED Web publishing platform for building...
#install
helm install wordpress stable/wordpress
#You'll see the pods appear in your Kubernetes cluster
kubectl get pods
NAME READY STATUS RESTARTS AGE
wordpress-6d8b97544f-jt7pc 0/1 Pending 0 60s
wordpress-mariadb-0 0/1 Pending 0 59s
#See the status of helm chart in helm
helm ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
wordpress default 1 2021-05-23 18:07:33.476648857 +0000 UTC deployed wordpress-9.0.3 5.3.2
#Delete the installation
helm delete wordpress
release "wordpress" uninstalled
Releases in Helm
While a chart contains the structure of objects that you’ll need for a Kubernetes deployment, it will require values and other information for deployment. When we install a chart, the structure will be combined with Values to create objects in a Kubernetes cluster. This combination of chart and values is called a release.
Each Chart has a version number and a REVISION number. We saw the following output in the wordpress installation after we installed the wordpress chart. Note the REVISION number.
NAME: wordpress
LAST DEPLOYED: Sun May 23 18:15:24 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
This Helm chart is deprecated
If you look at the Chart.yaml file in the wordpress chart, you’ll notice that the chart version number is 9.0.3.
cat Chart.yaml
apiVersion: v1
appVersion: 5.3.2
deprecated: true
description: DEPRECATED Web publishing platform for building blogs and websites.
engine: gotpl
home: http://www.wordpress.com/
icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png
keywords:
- wordpress
- cms
- blog
- http
- web
- application
- php
name: wordpress
sources:
- https://github.com/bitnami/bitnami-docker-wordpress
version: 9.0.3
# This was the version that was deployed (You might have to install the wordpress chart again if you're following)
helm ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
wordpress default 1 2021-05-23 18:15:24.991379171 +0000 UTC deployed wordpress-9.0.3 5.3.2
Let’s change the above installation. We can do that by using helm upgrade command.
# Let's say in the example, we want to change the mariadb version.
helm upgrade wordpress stable/wordpress --set image.tag='docker.io/bitnami/mariadb:10.3.21-debian-10-r27'
# check the status of wordpress installation
helm status wordpress
NAME: wordpress
LAST DEPLOYED: Sun May 23 18:27:00 2021
NAMESPACE: default
STATUS: deployed
REVISION: 2
NOTES:
This Helm chart is deprecated
#Note that the REVISION is changed to 2.
Note that the Chart.yaml file which is fetched locally remains unchanged by an upgrade command. If you want to change the chart permanently, you need to change the value inside the chart file.
Chart Structure
The following folder structure shows a structure of a chart and how files are laid out.
wordpress/
Chart.yaml # A YAML file containing information about the chart
LICENSE # OPTIONAL: A plain text file containing the license for the chart
README.md # OPTIONAL: A human-readable README file
values.yaml # The default configuration values for this chart
values.schema.json # OPTIONAL: A JSON Schema for imposing a structure on the values.yaml file
charts/ # A directory containing any charts upon which this chart depends.
crds/ # Custom Resource Definitions
templates/ # A directory of templates that, when combined with values,
# will generate valid Kubernetes manifest files.
templates/NOTES.txt # OPTIONAL: A plain text file containing short usage notes
If you want to create a demo chart, you can do that with a helm create command. Also, if you do a dry run on the newly created helm chart, helm will output the yaml file
# Create a demo chart
helm create demo
# You can do a dry run to check how helm converts the chart to a Kubernetes manifest file
helm install demo ./demo --dry-run
NAME: demo
LAST DEPLOYED: Sun May 23 20:45:34 2021
NAMESPACE: default
STATUS: pending-install
REVISION: 1
HOOKS:
---
# Source: demo/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "demo-test-connection"
labels:
helm.sh/chart: demo-0.1.0
app.kubernetes.io/name: demo
app.kubernetes.io/instance: demo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['demo:80']
restartPolicy: Never
MANIFEST:
---
# Source: demo/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: demo
labels:
helm.sh/chart: demo-0.1.0
app.kubernetes.io/name: demo
app.kubernetes.io/instance: demo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
---
# Source: demo/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: demo
labels:
helm.sh/chart: demo-0.1.0
app.kubernetes.io/name: demo
app.kubernetes.io/instance: demo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: demo
app.kubernetes.io/instance: demo
---
# Source: demo/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
labels:
helm.sh/chart: demo-0.1.0
app.kubernetes.io/name: demo
app.kubernetes.io/instance: demo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: demo
app.kubernetes.io/instance: demo
template:
metadata:
labels:
app.kubernetes.io/name: demo
app.kubernetes.io/instance: demo
spec:
serviceAccountName: demo
securityContext:
{}
containers:
- name: demo
securityContext:
{}
image: "nginx:1.16.0"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{}
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=demo,app.kubernetes.io/instance=demo" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
Read Values from an Existing Chart
If you wanted to read just values from an existing chart, you can also use helm show values command. It will show values and strip out other information from a chart which makes it (a little bit) easier to read the values.
helm show values demo
# Default values for demo.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
Chart Modifications
There are three main ways to modify a helm charts.
Inline modification using set
If you want to just test a few values, you can use –set option with the install command. This will override the values from values.yaml file. This method is usually used in a proof of concept type environment.
If you dry run the demo chart that we created earlier (helm install demo demo –dry-run), by default it uses nginx:1.16.0 image.

Now if you want to change this image, you can do that by –set option.
helm install demo demo --set image.tag=latest --dry-run
# Side note: the first "demo" argument is the name of the deployment and the second "demo" argument is the name of our chart folder.
# you can do multiple sets together
Passing a file
You can also pass a file that contains the values that you want to override from the values.yaml.
In the same example that we used above, we can create a yaml file with the image tag that we want to override.
image:
tag: latest
# install the demo chart with input of the above yaml file to override values
helm install demo demo -f override_values.yaml
Change values.yaml
Last but not least, you can modify the values.yaml instead of overriding values via passing a file or setting values inline. This effectively creates a new chart.
Subcharts
A parent chart can have a subchart located in charts directory in the main chart folder. Each chart has its own values file. The parent chart can override the value of a subchart by defining the value in its own (parent’s) values.yaml file.
Continuing our earlier example of wordpress chart, you can see that wordpress chart has a subchart of mariadb.
├── charts
│ └── mariadb
│ ├── Chart.yaml
│ ├── files
│ │ └── docker-entrypoint-initdb.d
│ │ └── README.md
│ ├── OWNERS
│ ├── README.md
│ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── initialization-configmap.yaml
│ │ ├── master-configmap.yaml
│ │ ├── master-pdb.yaml
│ │ ├── master-statefulset.yaml
│ │ ├── master-svc.yaml
│ │ ├── NOTES.txt
│ │ ├── rolebinding.yaml
│ │ ├── role.yaml
│ │ ├── secrets.yaml
│ │ ├── serviceaccount.yaml
│ │ ├── servicemonitor.yaml
│ │ ├── slave-configmap.yaml
│ │ ├── slave-pdb.yaml
│ │ ├── slave-statefulset.yaml
│ │ ├── slave-svc.yaml
│ │ ├── test-runner.yaml
│ │ └── tests.yaml
│ ├── values-production.yaml
│ ├── values.schema.json
│ └── values.yaml
├── Chart.yaml
├── README.md
├── requirements.lock
├── requirements.yaml
├── templates
│ ├── deployment.yaml
│ ├── externaldb-secrets.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── pvc.yaml
│ ├── secrets.yaml
│ ├── servicemonitor.yaml
│ ├── svc.yaml
│ ├── tests
│ │ └── test-mariadb-connection.yaml
│ └── tls-secrets.yaml
├── values.schema.json
└── values.yaml
A child chart cannot access values in the parent folders. If you want a child chart to access value from a parent chart, you need to create a global value. Global values can be accessed by both the parent all subcharts. Global values are defined using the global keyword. In our example, here are some global values in the main values.yaml file. Although commented out, but shows an example.
##
# global:
# imageRegistry: myRegistryName
# imagePullSecrets:
# - myRegistryKeySecretName
# storageClass: myStorageClass
Chart Hooks
Chart hooks provide a way to allow chart developers to have control at certain points during a chart’s lifecycle. Hooks inject a job at certain points in chart lifecycles.
For example, after we complete a release, we need to load some data in that release. We can use post install hook to run a pod that loads data.
These are some of the hook types which are available at the time of the writing.
Hook Type | Description |
Pre-Install | Executes after templates are rendered but before resource creation in k8s. |
Post-Install | After all resources are loaded in k8s. |
Pre-Delete | Before a resource is deleted. |
Post-Delete | After a resource is deleted. |
Pre-Upgrade | After templates are rendered but before resource updates in k8s. |
Post-Upgrade | After all resources are updated. |
Pre-Rollback | After templates are rendered but before resource roll backs. |
Post-Rollback | After all resources are rolled back. |
Test | Executes when helm test is invoked. |
Some pointers on hooks
- A hook is defined in its annotation. It is a template that has some special annotations to convert it into a hook.
- Hooks are part of a chart. A parent chart cannot override subchart’s hooks.
- Hooks have weights. If there are multiple hooks, you can provide it a priority by assigning weights to hooks. The hook with the lowest weight executes first.
- If hooks fail, the release will fail.
- There’s no limit on number of hooks.
- We make to make sure that hook jobs deletes or cleans itself because hook objects stand alone. When a release is deleted, hook resources can persist.
Hook example
Let’s create a demo chart for hooks. We’ll create a file named hook.yaml in templates directory. This example is of a post-install example which means that this pod will run after our release is complete.
helm create demo-hook
nano demo-hook/templates/hook.yaml
#contents of hook.yaml
apiVersion: v1
kind: Pod
metadata:
name: posthook
annotations:
"helm.sh/hook": "post-install"
"helm.sh/hook-weight": "0"
#"helm.sh/hook-delete-policy": hook-succeeded
spec:
containers:
- name: democontainer
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo post-install hook Pod is running && sleep 20']
restartPolicy: Never
terminationGracePeriodSeconds: 0
helm install demo-hook ./demo-hook/
NAME: demo-hook
LAST DEPLOYED: Sat Jun 19 12:39:35 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=demo-hook,app.kubernetes.io/instance=demo-hook" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-hook-795cc96467-xdrwf 1/1 Running 0 30s
posthook 0/1 Completed 0 30s
Note the output of the last command where we can see the posthook pod as a complete pod. This is because hook-delete-policy line is commented out in the YAML file. If we uncomment that line, the posthook pod in the last command will not be part of the output.
Test Hooks
A test is a type of hook with a job definition that specifies a container with a command to run. If the command succeeds, then the container should exist with a success code.
Helm will create an example test by default in In demo-hook/templates/tests/test-connection.yaml as part of chart creation process. Here are the contents of that test-connection.yaml file. You’ll see in annotations that this is a hook of type test.
cat demo-hook/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "demo-hook.fullname" . }}-test-connection"
labels:
{{- include "demo-hook.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "demo-hook.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
Now if you do run helm test with the deployment name, it will create a container that runs that test command for us.
#run the test suite in the demo-hook release
helm test demo-hook
NAME: demo-hook
LAST DEPLOYED: Sat Jun 19 18:10:58 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: demo-hook-test-connection
Last Started: Sat Jun 19 18:11:55 2021
Last Completed: Sat Jun 19 18:13:56 2021
Phase: Succeeded
Library Charts
A library chart is a boilerplate helm chart that defines chart primitives and definitions for sharing templates with the goal of reducing repetition. It is important to note that in the templates directory, any file starting with an underscore (_) does not output to a manifest file. By convention, helper templates are placed in files with a naming pattern of _*.yaml or _*.tpl.
Here’s an example:
# Create a new chart
helm create libdemo
# remove all templates and the value file
rm -rf libdemo/templates/*
rm -f libdemo/values.yaml
# You'll have two directories and one Chart.yaml file
tree libdemo/
libdemo/
├── charts
├── Chart.yaml
└── templates
2 directories, 1 file
We’ll create a common ConfigMap which creates a data resource with a defaultvalue config. This means that any chart that inherits this configmap will inherit this default value.
nano libdemo/templates/_configmap.yaml
{{- define "libdemo.configmap.tpl" -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name | printf "%s-%s" .Chart.Name }}
data:
defaultvalue: defaultvalue
{{- end -}}
{{- define "libdemo.configmap" -}}
{{- include "libdemo.util.merge" (append . "libdemo.configmap.tpl") -}}
{{- end -}}
# Note that the above file includes another named template (libdemo.configmap).
Next, we’ll change the chart type to library in Chart.yaml.
cat libdemo/Chart.yaml
apiVersion: v2
name: libdemo
description: A Helm chart for Kubernetes
type: library
version: 0.1.0
appVersion: "1.16.0"
# We need to ensure that we add the utility function that we're using to merge two YAML templates.
nano libdemo/templates/_util.yaml
{{- /*
libdemo.util.merge will merge two YAML templates and output the result.
This takes an array of three values:
- the top context
- the template name of the overrides (destination)
- the template name of the base (source)
*/}}
{{- define "libdemo.util.merge" -}}
{{- $top := first . -}}
{{- $overrides := fromYaml (include (index . 1) $top) | default (dict ) -}}
{{- $tpl := fromYaml (include (index . 2) $top) | default (dict ) -}}
{{- toYaml (merge $overrides $tpl) -}}
{{- end -}}
# Now if you try to install this chart, you'll get an error confirming that library charts are not installable
helm install libdemo libdemo/
Error: library charts are not installable
Now if you want to use the above library chart, let’s create another chart, and include our common configmap in the new chart. This configmap will get all standard properties defined in the common configmap and just add myvalue in addition to that.
helm create usinglibdemo
#add a config map that will inherit configmap definition that we created earlier.
nano usinglibdemo/templates/configmap.yaml
{{- include "libdemo.configmap" (list . "mychart.configmap") -}}
{{- define "mychart.configmap" -}}
data:
myvalue: "Hello World"
{{- end -}}
Before we try generating the above configmap, we will add the earlier libdemo chart as a dependency in usinglibdemo chart.
nano usinglibdemo/Chart.yaml
apiVersion: v2
name: usinglibdemo
description: A Helm chart for Kubernetes
dependencies:
- name: libdemo
version: 0.1.0
repository: file://../libdemo
type: application
version: 0.1.0
appVersion: "1.16.0"
# update dependencies in usinglibdemo
helm dependency update usinglibdemo
Saving 1 charts
Deleting outdated charts
Now if you do a dry run, you’ll see your configmap rendered as part of the manifest and it includes the default value we defined in our libraryl.
helm install usinglibdemo usinglibdemo/ --debug --dry-run
install.go:173: [debug] Original chart version: ""
install.go:190: [debug] CHART PATH: /home/user/usinglibdemo
NAME: usinglibdemo
LAST DEPLOYED: Sun Jun 20 14:51:02 2021
NAMESPACE: default
STATUS: pending-install
REVISION: 1
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
affinity: {}
autoscaling:
enabled: false
maxReplicas: 100
minReplicas: 1
targetCPUUtilizationPercentage: 80
fullnameOverride: ""
image:
pullPolicy: IfNotPresent
repository: nginx
tag: ""
imagePullSecrets: []
ingress:
annotations: {}
enabled: false
hosts:
- host: chart-example.local
paths: []
tls: []
libdemo:
global: {}
nameOverride: ""
nodeSelector: {}
podAnnotations: {}
podSecurityContext: {}
replicaCount: 1
resources: {}
securityContext: {}
service:
port: 80
type: ClusterIP
serviceAccount:
annotations: {}
create: true
name: ""
tolerations: []
HOOKS:
---
# Source: usinglibdemo/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "usinglibdemo-test-connection"
labels:
helm.sh/chart: usinglibdemo-0.1.0
app.kubernetes.io/name: usinglibdemo
app.kubernetes.io/instance: usinglibdemo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['usinglibdemo:80']
restartPolicy: Never
MANIFEST:
---
# Source: usinglibdemo/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: usinglibdemo
labels:
helm.sh/chart: usinglibdemo-0.1.0
app.kubernetes.io/name: usinglibdemo
app.kubernetes.io/instance: usinglibdemo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
---
# Source: usinglibdemo/templates/configmap.yaml
apiVersion: v1
data:
defaultvalue: defaultvalue
myvalue: Hello World
kind: ConfigMap
metadata:
name: usinglibdemo-usinglibdemo
---
# Source: usinglibdemo/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: usinglibdemo
labels:
helm.sh/chart: usinglibdemo-0.1.0
app.kubernetes.io/name: usinglibdemo
app.kubernetes.io/instance: usinglibdemo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: usinglibdemo
app.kubernetes.io/instance: usinglibdemo
---
# Source: usinglibdemo/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: usinglibdemo
labels:
helm.sh/chart: usinglibdemo-0.1.0
app.kubernetes.io/name: usinglibdemo
app.kubernetes.io/instance: usinglibdemo
app.kubernetes.io/version: "1.16.0"
app.kubernetes.io/managed-by: Helm
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: usinglibdemo
app.kubernetes.io/instance: usinglibdemo
template:
metadata:
labels:
app.kubernetes.io/name: usinglibdemo
app.kubernetes.io/instance: usinglibdemo
spec:
serviceAccountName: usinglibdemo
securityContext:
{}
containers:
- name: usinglibdemo
securityContext:
{}
image: "nginx:1.16.0"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{}
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=usinglibdemo,app.kubernetes.io/instance=usinglibdemo" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
Packaging and Validating Charts
When we package a chart, we can sign it using a key by which helm will generate a signature of the chart – this will produce a chart in .tgz format. It also generates a provenance file which can be used to confirm that the chart has not been altered after it was signed. Here’s an example of how we can create a gpg key and sign a package.
# Create a gpg key
gpg --full-generate-key
# Follow the prompts in the commandline. I've named the key as testkey
gpg --list-keys
/home/user/.gnupg/pubring.kbx
----------------------------------------------
pub rsa3072 2021-06-20 [SC]
9DC3BC5B27D3BC09DE6DE58474C435882DDA8D6F
uid [ultimate] testkey (nocomments) <testkey@email.email>
sub rsa3072 2021-06-20 [E]
# Helm doesn't work with the latest .kbx file from gpg. So we need to export the private key in secring.gpg file.
gpg --export-secret-keys >~/.gnupg/secring.gpg
# Now we can use the testkey to sign our chart.
helm package --sign --key testkey --keyring ~/.gnupg/secring.gpg usinglibdemo
Successfully packaged chart and saved it to: /home/user/usinglibdemo-0.1.0.tgz
# Note that this will give us two files. The packaged file usinglibdemo-0.1.0.tgz and a provenance file usinglibdemo-0.1.0.tgz.prov where the signature is stored.
# now you can verify your packaged chart with helm verify usinglibdemo-0.1.0.tgz command