Difference between revisions of "Kubernetes/Helm"

From Ever changing code
Jump to navigation Jump to search
Line 323: Line 323:


= [https://stackoverflow.com/questions/42823623/can-kubernetes-helm-templates-use-variables Variables and Spring functions] =
= [https://stackoverflow.com/questions/42823623/can-kubernetes-helm-templates-use-variables Variables and Spring functions] =
Templates in helm cannot access variables. However, the context handed to the template is a dictionary. The sprig library is accessible from within the Go templates and can be used.
Variables are normally not "global". They are scoped to the block in which they are declared.
<source>
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
</source>
variables <code>$key</code> and <code>$val</code> will only be in scope inside of the <code>range</code> block.
 
 
<code>$</code> - variable that is always global, points to the root context. It's useful eg. when you are looping in a range and you need to know the chart's release name.
<source>
{{- range .Values.tlsSecrets }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ .name }}
  labels:
    # Many helm templates would use `.` below, but that will not work,
    # however `$` will work here
    app.kubernetes.io/name: {{ template "fullname" $ }}
    # I cannot reference .Chart.Name, but I can do $.Chart.Name
    helm.sh/chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
    ...
{{- end }}
</source>
 
 
Templates in helm cannot access variables. However, the context handed to the template is a dictionary. The spring library is accessible from within the Go templates and can be used.





Revision as of 23:46, 14 September 2020

Note: This content refers to helm3 as authored in April, 2020.

Install

Note: Helm2 development is still active so, the latest tag may point actually to helm2 instead to helm3

# latest version of helm2
LATEST=$(curl --silent "https://api.github.com/repos/helm/helm/releases/latest" | jq -r .tag_name); echo $LATEST

# latest version of helm3
LATEST=$(curl --silent "https://api.github.com/repos/helm/helm/releases" | jq -r .[].tag_name | sort -V | grep -v rc | tail -n -1); echo $LATEST
curl -LO https://get.helm.sh/helm-${LATEST}-linux-amd64.tar.gz
tar xzvf helm-${LATEST}-linux-amd64.tar.gz
sudo install linux-amd64/helm /usr/local/bin/helm

Charts repository

Example below refers how to install metrics-server

# Search repo
$ helm search hub metrics-server # searches 'hub' https://hub.helm.sh/
URL                                               	CHART VERSION	APP VERSION	DESCRIPTION                                       
https://hub.helm.sh/charts/bitnami/metrics-server 	4.2.0        	0.3.7      	Metrics Server...
https://hub.helm.sh/charts/stable/metrics-server  	2.11.1       	0.3.6      	Metrics Server...

# Add repo
$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm repo list 
NAME            	URL                                                   
gitlab          	https://charts.gitlab.io                              
bitnami         	https://charts.bitnami.com/bitnami 
$ helm search repo bitnami/metrics
NAME                  	CHART VERSION	APP VERSION	DESCRIPTION                                       
bitnami/metrics-server	4.2.0        	0.3.7      	Metrics Server is ...

# Install
helm install metrics-server bitnami/metrics-server -n metrics-server --dry-run # my-release is 'metrics-server'

# install 'metrics.k8s.io/v1beta1'
k get apiservices.apiregistration.k8s.io | grep metrics # if return nothing do below
helm upgrade --install metrics-server bitnami/metrics-server -n metrics-server --set apiService.create=true

# Test
k get apiservices.apiregistration.k8s.io | grep metrics
NAME                                   SERVICE                         AVAILABLE   AGE
v1beta1.metrics.k8s.io                 metrics-server/metrics-server   True        32s
kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
k top nodes

Diff plugin

# Install
helm plugin install https://github.com/databus23/helm-diff --version master

# Usage
$> helm diff [command]

Available Commands:
  release     Shows diff between release manifests
  revision    Shows diff between revision manifests
  rollback    Show a diff explaining what a helm rollback could perform
  upgrade     Show a diff explaining what a helm upgrade would change.
  version     Show version of the helm diff plugin

# Examples
$> helm diff revision [flags] RELEASE REVISION1 REVISION2 --context 5
    # --context output NUM lines of context around changes (default -1)
$> helm diff upgrade RELEASE . -C 5
$> helm diff rollback RELEASE REVISION1 -C 5

# Compare the manifests details of a different releases created from the same chart
$> helm diff release RELEASE1 RELEASE2 -C 5

Operations

Helm command

helm version --short # --> v3.1.3+g0a9a9a8
helm [command] -h    # get help create|env|get|history|install|lint|list|plugin|rollback|show|status|template|uninstall|upgrade
  # Flags:
  # --kube-context string -name of the kubeconfig context to use
  # --kubeconfig string -path to the kubeconfig file
  # -n, --namespace string


Operations

# Add repo
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
helm repo update                           # Make sure we get the latest list of charts

helm search repo stable/jenkins

# Set kubectl context
# Helm3 bug:does may not respect '--namespace' flag, so set default namespace
kubectl create ns jenkins
kubectl config set-context $(kubectl config current-context) --namespace=jenkins

# Install release
helm install [NAME]     [CHART]        [flags]
helm install jenkins-ci stable/jenkins                                    # release name: jenkins-ci
helm install jenkins-ci ./jenkins-2.192.1.tgz --set service.type=NodePort # install from a tarball package
helm install stable/mysql   --generate-name # release name will be generated

# Get information about releases
helm ls -A                                  # show a list of all deployed releases -A --all-namespaces
NAME       NAMESPACE REVISION UPDATED          STATUS   CHART          APP VERSION
jenkins-ci jenkins   1        2020-04-25 15:.. deployed jenkins-1.17.2 lts 

helm get all -n jenkins jenkins-ci # all|hooks|manifest|notes|values(user defined values)

# Uninstall release
helm uninstall jenkins-ci --namespace jenkins


Rollback
$> helm rollback <RELEASE> [REVISION] [flags]
$> helm rollback diy       2          --dry-run # <RELEASE> = diy
$> time helm rollback diy 2 --wait
    # --wait until all Pods, PVCs... are in a ready state before marking the release as successful. 
    # It will wait for as long as --timeout

# Note: Each rollback increments REVISION with +1
$> helm history diy
REVISION  UPDATED                   STATUS      CHART     APP VERSION DESCRIPTION
1         Mon Apr 27 10:39:38 2020  superseded  diy-0.1.0 6.4.2       Install complete
2         Mon Apr 27 12:24:54 2020  superseded  diy-0.1.0 6.4.2       Upgrade complete
3         Mon Apr 27 16:22:34 2020  superseded  diy-0.1.0 6.4.2       Upgrade complete
4         Mon Apr 27 16:35:31 2020  deployed    diy-0.1.0 6.4.2       Rollback to 2


Jenkins chart post install instructions or get notes commands
helm get notes -n jenkins jenkins-ci
NAME: jenkins-ci
LAST DEPLOYED: Sat Apr 25 15:33:36 2020
NAMESPACE: jenkins
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:
  printf $(kubectl get secret --namespace jenkins jenkins-ci -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
2. Get the Jenkins URL to visit by running these commands in the same shell:
  export POD_NAME=$(kubectl get pods --namespace jenkins -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=jenkins-ci" -o jsonpath="{.items[0].metadata.name}")
  echo http://127.0.0.1:8080
  kubectl --namespace jenkins port-forward $POD_NAME 8080:8080

3. Login with the password from step 1 and the username: admin


For more information on running Jenkins on Kubernetes, visit:
https://cloud.google.com/solutions/jenkins-on-container-engine

Subcharts

Subcharts and overriding variables

Subchart is a chart that lives in charts/ directory. It gets templated each time with the parent chart but can also be tested and deployed separately.

  • A subchart is considered "stand-alone", which means a subchart can never explicitly depend on its parent chart.
  • For that reason, a subchart cannot access the values of its parent.
  • A parent chart can override values for subcharts.
  • Helm has a concept of global values that can be accessed by all charts.


Overrides

If subchart has a variable variable1 this can be overriden from parent chart like below

chartname:
  variable1: foo

Dependencies

Dependencies in Helm3 are driven by dependencies: block in Chart.yaml file.

dependencies:
- name: nfs-server
  version: "0.1.0"
  condition: deployNfsServer.enabled  # (optional) A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
  repository: "file://../nfs-server"  # if 'repository' is unspecified assumes it exists in the charts directory
  enabled: true


At first we need to build the dependency package snapshot that will be used to build application. The snapshot will be packaged and placed in charts/ directory. Each time we do helm dependency update package get updated with any changes done to the referenced chart.

# Update and build dependencies
helm dependency update # creates Chart.lock file
helm dependency build  # builds a package and places in charts/ directory eg. nfs-server-0.1.0.tgz

# --- step 1 - Chart.lock
cat Chart.lock
dependencies:
- name: nfs-server
  repository: file://../nfs-server
  version: 0.1.0
digest: sha256:e04f412832d56d97cfd153d2b2dd9eb5d6dad2c0fef65d3f92293672a2a6a043
generated: "2020-05-20T17:01:48.039203425Z"

# --- step 2 - package the dependency chart
#  | $ tree mft/charts/
#  | mft/charts/
#  | └── nfs-server-0.1.0.tgz

Now on each install the dependent chart will be installed along with the main chart. All manifests are applied in order:

  • aggregrated into a single set; then
  • sorted by type followed by name; and then
  • created/updated in that order.
helm upgrade --install mychart ./mychart --dry-run

Create a chart

Note: Check The chart file structure as the helm create NAME does not include all optional files you may use

helm version --short # -> v3.1.3+g0a9a9a8

helm create diy # diy = [NAME] # this creates standard file tree not all optional files are included
$> tree diy
diy
├── charts    # any charts upon which this chart depends
├── Chart.yaml
├── templates # no rigid naming pattern, recommended .yaml for YAML files and .tpl for helper
|   |         # all files here will be sent through the template engine
│   ├── deployment.yaml
│   ├── _helpers.tpl       # template helpers that you can re-use throughout the chart
│   ├── ingress.yaml
│   ├── NOTES.txt          # Optional. “help text” for your chart, displayed after installation
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml  # default configuration values for this chart

helm template diy | bat -l yaml --style=plain # display rendered yaml manifests

kubectl create ns diy
helm install ./diy --generate-name -n diy   # Generated name
  #                                       ____NAME______
  # podName       :                   pod/diy-1587926286-866d97bb75-vskns
  # serviceName   :               service/diy-1587926286
  # deploymentName: deployment.extensions/diy-1587926286
  # NAME                                          TYPE
  # secret/sh.helm.release.v1.diy-1587926286.v1   helm.sh/release.v1

helm install foo ./diy -n diy               # Custom name
  #                                       _NAME__
  # podName       :                   pod/foo-diy-6fd79d9bbd-nh5hl
  # serviceName   :               service/foo-diy
  # deploymentName: deployment.extensions/foo-diy
  # NAME                                          TYPE
  # secret/sh.helm.release.v1.foo.v1              helm.sh/release.v1

helm list -n diy
NAME           NAMESPACE REVISION UPDATED             STATUS   CHART     APP VERSION
diy-1587926286 diy       1        2020-04-26 19:38... deployed diy-0.1.0 1.16.0     
foo            diy       1        2020-04-26 20:02... deployed diy-0.1.0 1.16.0

Template

Everything in templates/ goes through templating engine before being rendered. Basic template might look like below:

#            TEMPLATE                    --->            RENDER              --->            DEPLOYED
#                                                                             # helm install mychart ./mychart -n mychart
# cat templates/configmap.yaml             # helm template mychart/           # kubectl -n mychart get cm mychart-configmap -oyaml
| apiVersion: v1                           | apiVersion: v1                   | apiVersion: v1
| kind: ConfigMap                          | kind: ConfigMap                  | kind: ConfigMap
| metadata:                             -> | metadata:                    --> | metadata:
|   name: {{ .Release.Name }}-configmap    |   name: RELEASE-NAME-configmap   |   name: mychart-configmap
| data:                                    | data:                            | data:
|   myvalue: "Hello World Piotrek"         |   myvalue: "Hello World Piotrek" |   myvalue: Hello World Piotrek

Note: In kubectl get cm above creationTimestamp: and selfLink: data yaml keys have been not included for clarity of side-by-side comparison.


References

Text and spaces

Note: Details about whitespace control you can find in Flow Control in Controlling Whitespace section.


If an action's left delimiter (by default "{{") is followed immediately by a minus sign and ASCII space character ("{{- "), all trailing white space is trimmed from the immediately preceding text. Similarly, if the right delimiter ("}}") is preceded by a space and minus sign (" -}}"), all leading white space is trimmed from the immediately following text. In these trim markers, the ASCII space must be present; "{{-3}}" parses as an action containing the number -3.

For instance, when executing the template whose source is

"{{23 -}} < {{- 45}}"
// the generated output would be

"23<45"

For this trimming, the definition of white space characters is the same as in Go: space, horizontal tab, carriage return, and newline.

Package and push charts to OCI registry

Known workarounds

Invalid spec selector after upgrading helm template

This is caused by dynamic labels in selector as of Kubernetes version X field is immutable.


Steps to before upgrade without need of uninstalling charts:

$ kubectl delete statefulsets.apps --cascade=false my-release

# Edit the deployment:
kubectl patch deployments my-release --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]'

Variables and Spring functions

Variables are normally not "global". They are scoped to the block in which they are declared.

{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}

variables $key and $val will only be in scope inside of the range block.


$ - variable that is always global, points to the root context. It's useful eg. when you are looping in a range and you need to know the chart's release name.

{{- range .Values.tlsSecrets }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ .name }}
  labels:
    # Many helm templates would use `.` below, but that will not work,
    # however `$` will work here
    app.kubernetes.io/name: {{ template "fullname" $ }}
    # I cannot reference .Chart.Name, but I can do $.Chart.Name
    helm.sh/chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
    ...
{{- end }}


Templates in helm cannot access variables. However, the context handed to the template is a dictionary. The spring library is accessible from within the Go templates and can be used.


Use function "set" to add a new key/value pair to a dictionary.

$_ := set $myDict "name4" "value4"

Note that set returns the dictionary (a requirement of Go template functions), so you may need to trap the value as done above with the $_ assignment.

Resources