Exploring files that constitute a Chart

I prepared a Chart that defines the go-demo-3 application. We'll use it to get familiar with writing Charts. Even if we choose to use Helm only for third-party applications, familiarity with Chart files is a must since we might have to look at them to better understand the application we want to install.

The files are located in helm/go-demo-3 directory inside the repository. Let's take a look at what we have.

 1  ls -1 helm/go-demo-3

The output is as follows.

Chart.yaml
LICENSE
README.md
templates
values.yaml

A Chart is organized as a collection of files inside a directory. The directory name is the name of the Chart (without versioning information). So, a Chart that describes go-demo-3 is stored in the directory with the same name.

The first file we'll explore is Chart.yml. It is a mandatory file with a combination of compulsory and optional fields.

Let's take a closer look.

 1  cat helm/go-demo-3/Chart.yaml

The output is as follows.

name: go-demo-3
version: 0.0.1
apiVersion: v1
description: A silly demo based on API written in Go and MongoDB
keywords:
- api
- backend
- go
- database
- mongodb
home: http://www.devopstoolkitseries.com/
sources:
- https://github.com/vfarcic/go-demo-3 
maintainers:
- name: Viktor Farcic email: [email protected]

The name, version, and apiVersion are mandatory fields. All the others are optional.

Even though most of the fields should be self-explanatory, we'll go through each of them just in case.

The name is the name of the Chart, and the version is the version. That's obvious, isn't it? The critical thing to note is that versions must follow SemVer 2 (https://semver.org/) standard. The full identification of a Chart package in a repository is always a combination of a name and a version. If we package this Chart, its name would be go-demo-3-0.0.1.tgz. The apiVersion is the version of the Helm API and, at this moment, the only supported value is v1.

The rest of the fields are mostly informational. You should be able to understand their meaning, so I won't bother you with lengthy explanations.

The next in line is the LICENSE file.

 1  cat helm/go-demo-3/LICENSE

The first few lines of the output are as follows.

The MIT License (MIT)
    
Copyright (c) 2018 Viktor Farcic
    
Permission is hereby granted, free ...

The go-demo-3 application is licensed as MIT. It's up to you to decide which license you'll use, if any.

README.md is used to describe the application.

 1  cat helm/go-demo-3/README.md

The output is as follows.

This is just a silly demo.

I was too lazy to write a proper description. You shouldn't be. As a rule of thumb, README.md should contain a description of the application, a list of the pre-requisites and the requirements, a description of the options available through values.yaml, and anything else you might deem important. As the extension suggests, it should be written in Markdown format.

Now we are getting to the critical part.

The values that can be used to customize the installation are defined in values.yaml.

 1  cat helm/go-demo-3/values.yaml

The output is as follows.

replicaCount: 3
dbReplicaCount: 3
image:
  tag: latest
  dbTag: 3.3
ingress:
  enabled: true
  host: acme.com
service:
  # Change to NodePort if ingress.enable=false
  type: ClusterIP
rbac:
  enabled: true
resources:
  limits:
   cpu: 0.2
   memory: 20Mi
  requests:
   cpu: 0.1
   memory: 10Mi
dbResources:
  limits:
   memory: "200Mi"
   cpu: 0.2
  requests:
   memory: "100Mi"
   cpu: 0.1
dbPersistence:
  ## If defined, storageClassName: <storageClass>
  ## If set to "-", storageClassName: "", which disables dynamic provisioning
  ## If undefined (the default) or set to null, no storageClassName spec is
  ##   set, choosing the default provisioner.  (gp2 on AWS, standard on
  ##   GKE, AWS & OpenStack)
  ##
  # storageClass: "-"
  accessMode: ReadWriteOnce
  size: 2Gi

As you can see, all the things that may vary from one go-demo-3 installation to another are defined here. We can set how many replicas should be deployed for both the API and the DB. Tags of both can be changed as well. We can disable Ingress and change the host. We can change the type of the Service or disable RBAC. The resources are split into two groups, so that the API and the DB can be controlled separately. Finally, we can change database persistence by specifying the storageClass, the accessMode, or the size.

I should have described those values in more detail in README.md, but, as I already admitted, I was too lazy to do that. The alternative explanation of the lack of proper README is that we'll go through the YAML files where those values are used, and everything will become much more apparent.

The important thing to note is that the values defined in that file are defaults that are used only if we do not overwrite them during the installation through --set or --values arguments.

The files that define all the resources are in the templates directory.

 1  ls -1 helm/go-demo-3/templates/

The output is as follows.

NOTES.txt
_helpers.tpl
deployment.yaml
ing.yaml
rbac.yaml
sts.yaml
svc.yaml

The templates are written in Go template language (https://golang.org/pkg/text/template/) extended with add-on functions from Sprig library (https://github.com/Masterminds/sprig) and a few others specific to Helm. Don't worry if you are new to Go. You will not need to learn it. For most use-cases, a few templating rules are more than enough for most of the use-cases. With time, you might decide to "go crazy" and learn everything templating offers. That time is not today.

When Helm renders the chart, it'll pass all the files in the templates directory through its templating engine.

Let's take a look at the NOTES.txt file.

 1  cat helm/go-demo-3/templates/NOTES.txt

The output is as follows.

1. Wait until the applicaiton is rolled out:
   kubectl -n {{ .Release.Namespace }} rollout status deployment {{ template "helm.fullname" . }}
    
2. Test the application by running these commands:
{{- if .Values.ingress.enabled }}
     curl http://{{ .Values.ingress.host }}/demo/hello
{{- else if contains "NodePort" .Values.service.type }}
    export PORT=$(kubectl -n {{ .Release.Namespace }} get svc {{ template "helm.fullname" . }} -o 
jsonpath="{.spec.ports[0].nodePort}")
# If you are running Docker for Mac/Windows export ADDR=localhost # If you are running minikube export ADDR=$(minikube ip) # If you are running anything else export ADDR=$(kubectl -n {{ .Release.Namespace }} get nodes -o jsonpath="{.items[0].status.addresses[0].address}") curl http://$NODE_IP:$PORT/demo/hello {{- else }} If the application is running in OpenShift, please create a Route to enable access. For everyone else, you set ingress.enabled=false and service.type is not set to NodePort. The application cannot be accessed from outside the cluster. {{- end }}

The content of the NOTES.txt file will be printed after the installation or upgrade. You already saw a similar one in action when we installed Jenkins. The instructions we received how to open it and how to retrieve the password came from the NOTES.txt file stored in Jenkins Chart.

That file is our first direct contact with Helm templating. You'll notice that parts of it are inside if/else blocks. If we take a look at the second bullet, we can deduce that one set of instructions will be printed if ingress is enabled, another if the type of the Service is NodePort, and yet another if neither of the first two conditions is met.

Template snippets are always inside double curly braces (for example, {{ and }}). Inside them can be (often simple) logic like an if statement, as well as predefined and custom-made function. An example of a custom-made function is {{ template "helm.fullname" . }}. It is defined in _helpers.tpl file which we'll explore soon.

Variables always start with a dot (.). Those coming from the values.yaml file are always prefixed with .Values. An example is .Values.ingress.host that defines the host that will be configured in our Ingress resource.

Helm also provides a set of pre-defined variables prefixed with .Release, .Chart, .Files, and .Capabilities. As an example, near the top of the NOTES.txt file is {{ .Release.Namespace }} snippet that will get converted to the Namespace into which we decided to install our Chart.

The full list of the pre-defined values is as follows (a copy of the official documentation).

  • Release.Name: The name of the release (not the Chart).
  • Release.Time: The time the Chart release was last updated. This will match the last released time on a release object.
  • Release.Namespace: The Namespace the Chart was released to.
  • Release.Service: The service that conducted the release. Usually this is Tiller.
  • Release.IsUpgrade: This is set to true if the current operation is an upgrade or rollback.
  • Release.IsInstall: This is set to true if the current operation is an install.
  • Release.Revision: The revision number. It begins at 1 and increments with each helm upgrade.
  • Chart: The contents of the Chart.yaml. Thus, the Chart version is obtainable as Chart.Version and the maintainers are in Chart.Maintainers.
  • Files: A map-like object containing all non-special files in the Chart. This will not give you access to templates, but will give you access to additional files that are present (unless they are excluded using .helmignore). Files can be accessed using {{index .Files "file.name"}} or using the {{.Files.Get name}} or {{.Files.GetString name}} functions. You can also access the contents of the file as []byte using {{.Files.GetBytes}}.
  • Capabilities: A map-like object that contains information about the versions of Kubernetes ({{.Capabilities.KubeVersion}}, Tiller ({{.Capabilities.TillerVersion}}, and the supported Kubernetes API versions ({{.Capabilities.APIVersions.Has "batch/v1"}}).

You'll also notice that our if, else if, else, and end statements start with a dash (-). That's the Go template way of specifying that we want all empty space before the statement (when - is on the left) or after the statement (when - is on the right) to be removed.

There's much more to Go templating that what we just explored. I'll comment on other use-cases as they come. For now, this should be enough to get you going. You are free to consult template package documentation (https://golang.org/pkg/text/template/) for more info. For now, the critical thing to note is that we have the NOTES.txt file that will provide useful post-installation information to those who will use our Chart.

I mentioned _helpers.tpl as the source of custom functions and variables. Let's take a look at it.

 1  cat helm/go-demo-3/templates/_helpers.tpl

The output is as follows.

{{/* vim: set filetype=mustache: */}}
{{/*
  Expand the name of the chart.
*/}}
{{- define "helm.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix
"-" -}}
{{- end -}} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are
limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "helm.fullname" -}} {{- $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 -}}

That file is the exact copy of the _helpers.tpl file that was created with the helm create command that generated a sample Chart. You can extend it with your own functions. I didn't. I kept it as-is. It consists of two functions with comments that describe them. The first (helm.name) returns the name of the Chart trimmed to 63 characters which is the limitation for the size of some of the Kubernetes fields. The second function (helm.fullname) returns fully qualified name of the application. If you go back to the NOTES.txt file, you'll notice that we are using helm.fullname in a few occasions. Later on, you'll see that we'll use it in quite a few other places.

Now that NOTES.txt and _helpers.tpl are out of the way, we can take a look at the first template that defines one of the Kubernetes resources.

 1  cat helm/go-demo-3/templates/deployment.yaml

The output is as follows.

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: {{ template "helm.fullname" . }}
  labels:
    app: {{ template "helm.name" . }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ template "helm.name" . }}
      release: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ template "helm.name" . }}
        release: {{ .Release.Name }}
    spec:
      containers:
      - name: api
        image: "vfarcic/go-demo-3:{{ .Values.image.tag }}"
        env:
        - name: DB
          value: {{ template "helm.fullname" . }}-db
        readinessProbe:
          httpGet:
            path: /demo/hello
            port: 8080
          periodSeconds: 1
        livenessProbe:
          httpGet:
            path: /demo/hello
            port: 8080
          resources:
{{ toYaml .Values.resources | indent 10 }}

That file defines the deployment of the go-demo-3 API. The first thing I did was to copy the definition from the YAML file we used in the previous chapters. Afterwards, I replaced parts of it with functions and variables. The name, for example, is now {{ template "helm.fullname" . }}, which guarantees that this deployment will have a unique name. The rest of the file follows the same logic. Some things are using pre-defined values like {{ .Chart.Name }} and {{ .Release.Name }}, while others are using those from the values.yaml. An example of the latter is {{ .Values.replicaCount }}.

The last line contains a syntax we haven't seen before. {{ toYaml .Values.resources | indent 10 }} will take all the entries from the resources field in the values.yaml, and convert them to YAML format. Since the final YAML needs to be correctly indented, we piped the output to indent 10. Since the resources: section of deployment.yaml is indented by eight spaces, indenting the entries from resources in values.yaml by ten will put them just two spaces inside it.

Let's take a look at one more template.

 1  cat helm/go-demo-3/templates/ing.yaml

The output is as follows.

{{- if .Values.ingress.enabled -}}
{{- $serviceName := include "helm.fullname" . -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: {{ template "helm.fullname" . }}
  labels:
    app: {{ template "helm.name" . }}
    chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
  annotations:
    ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
  spec:
    rules:
    - http:
      paths:
      - backend:
          serviceName: {{ $serviceName }}
          servicePort: 8080
    host: {{ .Values.ingress.host }}
{{- end -}}

That YAML defines the Ingress resource that makes the API deployment accessible through its Service. Most of the values are the same as in the deployment. There's only one difference worthwhile commenting.

The whole YAML is enveloped in the {{- if .Values.ingress.enabled -}} statement. The resource will be installed only if ingress.enabled value is set to true. Since that is already the default value in values.yaml, we'll have to explicitly disable it if we do not want Ingress.

Feel free to explore the rest of the templates. They are following the same logic as the two we just described.

There's one potentially significant file we did not define. We have not created requirements.yaml for go-demo-3. We did not need any. We will use it though in one of the next chapters, so I'll save the explanation for later.

Now that we went through the files that constitute the go-demo-3 Chart, we should lint it to confirm that the format does not contain any apparent issues.

 1  helm lint helm/go-demo-3

The output is as follows.

==> Linting helm/go-demo-3
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, no failures

If we ignore the complaint that the icon is not defined, our Chart seems to be defined correctly, and we can create a package.

 1  helm package helm/go-demo-3 -d helm

The output is as follows.

Successfully packaged chart and saved it to: helm/go-demo-3-0.0.1.tgz

The -d argument is new. It specified that we want to create a package in helm directory.

We will not use the package just yet. For now, I wanted to make sure that you remember that we can create it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.20.231