I. Basic concepts

Helm is similar to Linux package managers such as Yum/APT, which can quickly and easily deploy previously packaged YAML files into Kubernetes for easy management and maintenance.

  • Helm: a command line client tool used to create/package/publish kubernetes application Chart.
  • Tiller: The server for Helm, which is deployed in Kubernetes. Tiller accepts helm’s request and generates a Kubernetes deployment file (called release by helm) according to the chart. Tiller then submits it to Kubernetes to create the application. Tiller also provides Release upgrade, delete, rollback and a series of functions.
  • Chart: Helm’s package in a tar format that contains all the image/dependency/resource definitions needed to run an application, possibly including service definitions in the Kubernetes cluster
  • Release: A Chart instance running in a cluster in Kubernetes. A Chart can be installed multiple times on the same cluster, with each installation producing a new Release.
  • Repository: the Repository where Chart is published and stored

In short:

  • What helm does: manages software packages just like the yum commands in centos7, except helm manages the various containers installed on k8s.
  • What tiller does: it is like a repository for centos7, which is simply similar to xxx.repo in /etc/yum.repos.d.

Two component architecture

Iii Working Principle

Chart 3.1 install

  • Helm parses the chart structure information from the formulation directory or tar file
  • Helm passes the chart structure and value information formulated to Tiller through gRPC protocol
  • Tiller generates a release based on chart and values
  • Tiller sends the release to Kubernetes through JSON to generate the release

Chart 3.2 update

  • Helm parses the chart structure information from a specified directory or tar file
  • Helm sends the chart structure and value information formulated to Tiller through gRPC protocol
  • Tiller generates the release and updates the history of the named release
  • Tiller sends the release information to Kubernetes to update the release

Chart 3.3 the Rollback

  • Helm passes the name of the release to be rolled to Tiller
  • Tiller looks up history based on the release name
  • Tiller gets the last release from history
  • Tiller sends the previous release to Kubernetes to replace the current release

3.4 Chart handles dependencies

When processing Chart, Tiller directly merged Chart and all Charts it depended on into one Release and passed it to Kubernetes. So Tiller is not responsible for managing the startup order between dependencies. Applications in Chart need to be able to handle dependencies themselves.

4 Installation and Deployment

4.1 Installing V2

4.4.1 installation helm

#On the helm client host, it is usually the master hostWget https://get.helm.sh/helm-v2.14.2-linux-amd64.tar.gz tar xf helm-v2.14.2-linux-amd64.tar.gz mv helm /usr/local/bin/ helm versionCopy the code

4.1.2 Initializing tiller

  • The initialization of tiller automatically reads the ~/. Kube directory, so you need to make sure that the config file exists and is authenticated successfully

  • Tiller Configure RBAC, create rabc-config.yaml, and apply it

#Rbac can be found at: https://github.com/helm/helm/blob/master/docs/rbac.md - config. Yaml

cat > rbac-config.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: tiller
    namespace: kube-system
EOF

kubectl apply -f rbac-config.yaml
Copy the code
  • Develop the mirror
Docker pull jessestuart/tiller: v2.14.2 yum install socat
# yum install socatDocker tag jessestuart/tiller: v2.14.2 GCR. IO/kubernetes - helm/tiller: v2.14.2 helm init - I GCR. IO/kubernetes - helm/tiller: v2.9.0
#You need to pay attention to the parameters-- client-only: Do not install the server application. This may be required in CI&CD because you usually have the application installed in the K8S cluster and you only need to initialize the helm client. -- history-max: The maximum history. When you install an application using the helm, the helm creates a record of the installation in the namespace in which it is installed. - Tiller-namespace: the default namespace is kube-system. You can set it to any other namespace.Copy the code
  • Modify the mirror
#Due to the GFW, https://hub.docker.com/r/jessestuart/tiller/tags can make use of this imageKubectl edit deployment - n kube - system tiller - deploy image: jessestuart/tiller: v2.14.0Copy the code
  • Exception handling
Error: Looks like "https://kubernetes-charts.storage.googleapis.com" is not a valid chart repository or cannot be reached: Get https://kubernetes-charts.storage.googleapis.com/index.yaml: read TCP 10.2.8.44:49020 - > 216.58.220.208:443: read: Connection Reset by peer Solution: Helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts and then at the helm init note: Tiller might be running on a node. Download the tiller image to the node and change the tagCopy the code
  • Check the version
[root@master ~]# helm version Client: & version. Version {SemVer: "v2.14.2 GitCommit:" a8b13cc5ab6a7dbef0a58f5061bcc7c0c61598e7 GitTreeState: "clean"} Server: & version. Version {SemVer: "v2.14.2 + unreleased," GitCommit: "d953c6875cfd4b351a1e8205081ea8aabad7e7d4." GitTreeState:"dirty"}Copy the code

4.2 HelM3 Installation and Deployment

Because many foreign mirror websites, such as gcr. IO, cannot be accessed in China, it is recommended to use Ali Source, developer.aliyun.com/hub.

AppHub is a Helm Hub “China station” hosted on the domestic public cloud and fully public welfare. The back-end of AppHub was developed by three engineers from The Alibaba Cloud Server platform team in 20% of the time.

One of the important responsibilities of this site is to automatically synchronize all applications hosted by Helm’s official Hub to the country. Meanwhile, all urls with network access problems such as gcr. IO in the Charts file are automatically replaced with stable domestic mirrored urls.

Helm3 is no longer dependent on tiller, and Release names can be reused across NS.

2 install the helm

Helm3 does not require tiller installation. Download the Helm binary file and unzip it to $PATH.

CD/opt && wget tar XVF - https://cloudnativeapphub.oss-cn-hangzhou.aliyuncs.com/helm-v3.0.0-alpha.1-linux-amd64.tar.gz Helm-v3.0.0-alpha.1-linux-amd64.tar. gz mv linux-amd64 helm3 mv helm3/helm helm3/helm3 chown root.root helm3-r cat > /etc/profile.d/helm3.sh << EOF export PATH=$PATH:/opt/helm3 EOF source /etc/profile.d/helm3.sh [root@master helm3]# Helm3 version version. BuildInfo {version: "v3.0.0 - alpha. 1," GitCommit: "b9a54967f838723fe241172a6b94d18caf8bcdca." GitTreeState:"clean"}Copy the code

4.2.2 Installing Applications Using helM3

helm repo add apphub https://apphub.aliyuncs.com
helm search guestbook
helm install guestbook apphub/guestbook
Copy the code

Five use

5.1 Basic Commands

http://hub.kubeapps.com/ Completion # Generates an auto-completion script (bash or ZSH) for the specified shell. Create # Creates a new chart delete # with the given name from Kubernetes Fetch # Downloads chart from the repository and (optionally) unloads it into a local directory. Get # Downloads a named release help # to list all help information Init # Initialize Helm inspect on client and server # Check chart details install # Install chart archive Lint # Syntax check chart list # Releases list package # Package chart directory as chart file Plugin # Add list or remove helm plug-in repo # Add list remove updates and indexes Chart repository reset # uninstall Tiller from the cluster rollback # rollback the version to the previous version search # search keywords in chart repository serve # start the local HTTP network server status # display specified Verify that the chart on the given path is a signed and valid version # Print client/server version information dep # Analyze Chart and download dependenciesCopy the code
  • Specify value.yaml to deploy a chart
helm install --name els1 -f values.yaml stable/elasticsearch
Copy the code
  • Upgrade a Chart
helm upgrade --set mysqlRootPassword=passwd db-mysql stable/mysql

helm upgrade go2cloud-api-doc go2cloud-api-doc/ 
Copy the code
  • Roll back a chart
helm rollback db-mysql 1
Copy the code
  • Delete a release
helm delete --purge db-mysql
Copy the code
  • Only render the template and output it, no installation
helm install/upgrade xxx --dry-run --debug
Copy the code

5.2 Chart file organization

Myapp / # Chart / # Chart / # Chart / # Chart Yaml # Describes the Chart, including name, description, version, etc. ├── templates # Directory │ ── deployment. Yaml # Deployment controller Go Helpers.tpl # Files that start with _ will not be deployed on k8s, │ ├── Notes.txt # Chart for a cluster. For example: │ ├── service.yaml # service │ ├── tests │ ├── test.connection │ ├── values. These values are applied to the GO template generation deployment file at installation timeCopy the code

5.3 Create your own Chart

  • Create your own MyChart
[root@master mychart]# helm create mychart Creating mychart [root@master mychart]# ls mychart [root@master mychart]# Tree Mychart/mychart/ ├─ charts Bass exercises ── chart.yaml ├─ templates │ ├── deployment │ ├── Notes.txt # chart │ ├── service.yaml # Service end │ ├── tests │ ├── test-connection. Yaml 3 Directories,  8 filesCopy the code
  • Delete all files under template and create configMap
rm -rf mychart/templates/*
#We first create a named mychart/templates/configmap yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "Hello World"
Copy the code
  • Install the test

Since the YAML file created is under template, tiller reads the file and sends it to Kubernetes.

[root@master mychart]# helm install ./mychart/
NAME:   enervated-dolphin
LAST DEPLOYED: Sun Jul 21 09:29:13 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME               DATA  AGE
mychart-configmap  1     0s

[root@master mychart]# kubectl get cm mychart-configmap
NAME                DATA   AGE
mychart-configmap   1      2m6s
[root@master mychart]# kubectl describe cm mychart-configmap
Name:         mychart-configmap
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
myvalue:
----
this is my chart configmap
Events:  <none>


[root@master mychart]# helm get manifest enervated-dolphin

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "this is my chart configmap"
Copy the code

The helm get manifest command gets the release name (enervated-dolphin) and prints out all the Kubernetes resources uploaded to the server. Each file begins with a — as the YAML document, followed by an automatically generated comment line that tells us the YAML document generated by the template file.

From there, we can see that the YAML data is exactly what we designed in our configMap.yaml file.

Now we can delete our release: helm delete enervated-dolphin.

[root@master mychart]# helm delete enervated-dolphin
release "enervated-dolphin" deleted
Copy the code

5.4 Adding a template call

Hard-coding a name: into a resource is generally considered a bad practice. The name should be unique. So we might want to generate a name field by inserting the release name.

Tip: Name: Due to DNS system restrictions, this field is limited to 63 characters. Therefore, release names are limited to 53 characters. Kubernetes 1.3 and earlier is limited to 24 characters (that is, 14 character names).

Change the previous configMap to the following

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
Copy the code

Name: this value has been changed to {{.release. name}} -configMap.

Template directives are contained in {{and}} blocks.

The template directive {{.release.name}} injects the Release Name into the template. The value passed to the template can be considered a namespace object, where dot (.) Separate each namespace element.

The first dot before Release indicates that we start with the namespace at the top of the scope (we’ll talk a little bit about scope). So we can think of.release. Name as: “Start with the top-level namespace, find the Release object, and then look inside for an object named Name”.

This Release object is one of the built-in objects for Helm, and we’ll talk more about it later. But for now, it’s enough to say that this will display the release name Tiller assigned to our release.

Now, when we install our resource, we immediately see the results of using this template directive:

[root@master mychart]# helm install ./mychart/
NAME:   famous-peahen
LAST DEPLOYED: Sun Jul 21 09:42:05 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                    DATA  AGE
famous-peahen-confgmap  1     0s


[root@master mychart]# helm get manifest famous-peahen

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: famous-peahen-confgmap
data:
  myvalue: "this is my chart configmap"
Copy the code

We looked at the basic template: the YAML file embedded with the template directive, passed. In the next section, we’ll delve into templates. But before you continue, here’s a quick trick to make building templates faster: Use helm install –debug –dry-run./mychart when you want to test template rendering but don’t actually install anything. This will send chart to the Tiller server, which will render the template. But instead of installing chart, it will return the render template so you can see the output:

5.5 Built-in Objects

Helm internal variable

Objects are passed from the template engine to the template. Your code can pass objects (we’ll see an example when we illustrate the with and range statements). There are even several ways to create new objects in a template, like the tuple function we’ll look at later.

Objects can be as simple as having a single value. Or they can contain other objects or functions. For example, the Release object contains multiple objects (such as release.name) and the Files object has several functions.

In the previous section, we inserted the Name of the Release into the template using {{.release.name}}. Release is one of the top-level objects that can be accessed in a template.

  • ReleaseThis object describes the release itself. It contains several objects:
  • Release.Name: the name of the release
  • Release.Time: Indicates the release time
  • Release.NamespaceNamespace for: release (if the listing is not overridden)
  • Release.Service: Name of the release service (alwaysTiller).
  • Release.Revision: The revision number of this release. It starts at 1, everyhelm upgradeOne at a time.
  • Release.IsUpgrade: If the current operation is an upgrade or rollback, set it totrue.
  • Release.IsInstall: Set to if the current operation is installationtrue.
  • ValuesFrom:values.yamlFile and user-supplied files pass in the template values. By default, Values are empty.
  • Chart:Chart.yamlThe contents of the file. Any data on chart.yaml will be accessed here. For example {{.chart. Name}}-{{.chart. Version}} will print mychart-0.1.0. Chart in the guideCharts GuideThe available fields are listed
  • Files: This provides access to all non-special files in chart. Although you cannot use it to access templates, you can use it to access other files in Chart. See the “Access files” section.
  • Files.GetIs a function that gets a file by name (.Files.Get config.ini)
  • Files.GetBytesIs a function that retrieves the contents of a file as an array of bytes instead of a string. This is useful for things like pictures.
  • Capabilities: This provides information about the capabilities supported by Kubernetes clusters.
  • Capabilities.APIVersionsIs a set of version information.
  • Capabilities.APIVersions.Has $versionIndicates whether the version is enabled on the cluster (batch/v1).
  • Capabilities.KubeVersionProvides a way to find Kubernetes versions. It has the following values: Major, Minor, GitVersion, GitCommit, GitTreeState, BuildDate, GoVersion, Compiler, and Platform.
  • Capabilities.TillerVersionProvides a way to find Tiller versions. It has the following values: SemVer, GitCommit, and GitTreeState.
  • Template: contains information about the current template being executed
  • Name: Path to the namespace file of the current template, for examplemychart/templates/mytemplate.yaml)
  • BasePath: Namespace path of the current chart template directory (for example, mychart/templates).

These values can be used with any top-level template. As we’ll see later, that doesn’t mean they’ll be everywhere.

Built-in values always begin with an uppercase letter. This conforms to Go’s naming convention. When you create your own name, feel free to use the conventions that suit your team. Some teams, such as the Kubernetes Chart team, choose to use only lowercase initials to distinguish local names from built-in names. In this guide, we follow that convention.

5.6 values file

In the last section, we looked at the built-in objects provided by the Helm template. One of the four built-in objects is Values. This object provides access to the value of chart passed in. Its content comes from four sources:

  • In the chartvalues.yamlfile
  • If this is a child chart, from the parent chartvalues.yamlfile
  • The value file is passed through the -f flag of helm install or helm upgrade (helm install -f myvals.yaml ./mychart)
  • through--set(e.g.helm install --set foo=bar ./mychart)

The above list is arranged in a specific order: values. Yaml By default, the parent chart level can override the default level, which in turn can be overridden by the user-supplied values file, which in turn can be overridden by the –set parameter.

The value files are pure YAML files. We edit mychart/values.yaml and then edit our ConfigMap template.

Delete the default band values. Yaml, we only set one parameter:

#Edit values. Yaml
domain: anchnet.com

#Reference in the template
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  domain: {{.Values.domain}}
Copy the code

Notice that we get the value of domain ‘in the last line {{.values.domain}}.

[root@master mychart]# helm install --dry-run --debug ./mychart
'''
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: exciting-manta-confgmap
data:
  myvalue: "this is my chart configmap"
  domain: anchnet.com

Copy the code
  • Specify it manually using –set

Since domain is set to anchnet.com in the default values.yaml file, this is the value shown in the template. We can easily override this by adding a –set flag to our helm install command:

helm install --dry-run --debug --set domain=51idc.com ./mychart 
'''
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: foppish-mule-confgmap
data:
  myvalue: "this is my chart configmap"
  domain: 51idc.com
Copy the code

Because –set has a higher priority than the default values.yaml file

  • Deleting the default key

If you need to remove a key from the default, you can override the key’s value to null, in which case Helm will remove the key from the override value merge.

Helm install stable/ Drupal --set image= my-Registry/Drupal :0.1.0 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --set livenessProbe.httpGet=nullCopy the code

5.7 Template functions and pipes

  • The template function

So far, we’ve seen how to put information into a template. But this information is put into the template without modification. Sometimes we want to transform this data to make it more useful to us.

Let’s start with a best practice: when. When the Values object injects strings into the template, we reference those strings. We can do this by calling the function in the quote template directive:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{quote .Values.favorite.drink}}
  food: {{quote .Values.favorite.food}}
Copy the code

Template functions follow the syntax functionName arg1 arg2… . In the code snippet above, quote.values.favorite. Drink calls the quote function and passes an argument to it.

Helm has over 60 functions available. Some of these are defined by the Go Template Language itself. Most of the others are part of the Sprig Template Library. And we’ll see a lot of that as we go through the examples.

While we think of the Helm template language as Helm specific, it is really a combination of the Go template language, some extra functions, and various wrappers to expose certain objects to templates. Many of the resources on the Go template can be helpful in understanding templates.

  • The pipe

One of the powerful features of template languages is their concept of pipes. Using a UNIX concept, a pipe is a tool for a series of template commands linked together to express a series of transformations compactly. In other words, pipes are an efficient way to do several things in sequence. Let’s rewrite the above example with pipes.

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | quote}}
  food: {{.Values.favorite.food | quote}}
Copy the code

In this example, instead of calling quote ARGUMENT, we reversed the order. We use a pipe (|) will “parameters” sent to the function:. Values. The favorite. Ultimately responds | quote. Using pipes, we can link several functions together:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | quote}}
  food: {{.Values.favorite.food | upper | quote}}
Copy the code

Reversing order is a common practice in templates. You will see. Val | quote than quote. Val is more common. So is practice.

When evaluated, the template will produce the following results:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: trendsetting-p-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
Copy the code

Please note that our original pizza has now been converted to “pizza”.

When you have a pipe parameter like this, the result of the first evaluation (.values.favorite.drink) is sent as the last parameter of the function. We can modify the drink example above to show a function that takes two arguments repeat COUNT STRING:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | repeat 5 | quote}}
  food: {{.Values.favorite.food | upper | quote}}
Copy the code

The repeat function sends back a given string and a given number of times, so we get this output:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: melting-porcup-configmap
data:
  myvalue: "Hello World"
  drink: "coffeecoffeecoffeecoffeecoffee"
  food: "PIZZA"
Copy the code
  • Use default

One commonly used function is default: default DEFAULT_VALUE GIVEN_VALUE. This feature allows you to specify a default value inside the template in case it is omitted. Let’s use it to modify the drink example above:

drink: {{.Values.favorite.drink | default "tea" | quote}}
Copy the code

If we run as usual, we will get our coffee:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: virtuous-mink-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
Copy the code

Now, we will remove the preferred beverage Settings from the following locations in values. Yaml:

favorite:
  #drink: coffee
  food: pizza
Copy the code

Now rerun helm install –dry-run –debug./mychart to produce this YAML:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: fair-worm-configmap
data:
  myvalue: "Hello World"
  drink: "tea"
  food: "PIZZA"
Copy the code

In the actual chart, all static default values should exist in values. Yaml and should not be repeated using the default command (otherwise they would be redundant). However, the default command is appropriate for computed values, which cannot be declared in values.yaml. Such as:

drink: {{.Values.favorite.drink | default (printf "%s-tea" (include "fullname" .)) }}
Copy the code

In some places, an if condition may be more appropriate than default. We will see these in the next section.

Template functions and pipes are powerful ways to transform information and insert it into YAML. But sometimes you need to add template logic that’s a little more complicated than just inserting strings. In the next section, we’ll look at the control structures provided by the template language.

  • Operator function

For templates, the operators (eq, ne, lt, GT, and, or, and so on) are implemented functions. In pipes, operators can be grouped in parentheses ((and)).

Place the operator at the front of the declaration, followed by its arguments, just as you would with a function. To use multiple operators together, separate each function by parentheses.

{{/ * include the body of this if statement when the variable .Values.fooString xists and is set to "foo" * /}}
{{if and .Values.fooString (eq .Values.fooString "foo") }}
    {{... }}
{{end}}


{{/ * do not include the body of this if statement because unset variables evaluate o false and .Values.setVariable was negated with the not function. * /}}
{{if or .Values.anUnsetVariable (not .Values.aSetVariable) }}
   {{... }}
{{end}}
Copy the code

Now we can move from functions and pipes to flow controls, conditions, loops, and scope modifiers.

5.8 Process Control

5.8.1 Process Control

Control structures (called “actions” in template language) give the template author the ability to control the template generation process. Helm’s template language provides the following control structures:

  • if/elseUsed to create conditional blocks
  • withSpecified range
  • range, which provides a “for each” style loop

In addition, it provides several operations to declare and use named template segments:

  • defineDeclare a new named template in the template
  • templateImport a named template
  • blockA special fillable template area is declared

In this section, we’ll talk about if, with, and range. The rest is covered in the Named Templates section later in this guide.

5.8.2 the if/else

The first control structure we’ll look at is for conditionally including blocks of text in a template. That’s the if/else block.

The basic structure of the condition is as follows:

{{if PIPELINE}}
  # Do something
{{else if OTHER PIPELINE}}
  # Do something else
{{else}}
  # Default case
{{end}}
Copy the code

Note that we are talking about pipes and not values. The reason for this is to make it clear that the control structure can execute the entire pipeline, not just evaluate a value.

The pipeline is evaluated as false if the value is as follows.

  • A Boolean fake
  • A number zero
  • An empty string
  • anil(Null or null)
  • An empty set (map.slice.tuple.dict.array)

In other cases, the condition value is true and the pipe is executed.

Let’s add a simple condition to ConfigMap. If the drink is set to coffee, we will add another setting:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}mug: true{{ end }}
Copy the code

Note that.values.favorite.drink must be defined, otherwise an error will be thrown when comparing it to “coffee.” Since we commented out drink: coffee in the previous example, the output should not contain the mug: true flag. But if we add the line back to the values.yaml file, the output should look like this:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: true
Copy the code

5.8.3 Controlling Spaces

When looking at conditions, we should take a quick look at the space controls in the template. Let’s take a look at the previous example and format it into a more readable format:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{if eq .Values.favorite.drink "coffee"}}
    mug: true
  {{end}}
Copy the code

At first, this looked good. But if we run it through the template engine, we get an incorrect result:

$ helm install --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key
Copy the code

What happened? Because of the whitespace above, we generated incorrect YAML.

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
    mug: true
Copy the code

The mug is indented incorrectly. Let’s simply indent that line and rerun:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{if eq .Values.favorite.drink "coffee"}}
  mug: true
  {{end}}
Copy the code

When we send this information, we get valid YAML, but it still looks interesting:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: telling-chimp-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"

  mug: true
Copy the code

Note that we received some blank lines in YAML. Why is that? When the template engine runs, it removes the white space in {{and}}, but leaves the remaining white space as it is.

Indented whitespace in YAML is strict, so managing whitespace becomes important. Fortunately, Helm Templates have several tools to help us.

First, you can modify the brace syntax of the template declaration with special characters to tell the template engine to fill in the blanks. {{- (with dashes and Spaces added) means that the case should be moved to the left, while -}} means that the right space should be removed. Attention! Newlines are also Spaces!

Make sure there are Spaces between – and other instructions. -3 means “delete the left space and print 3”, while -3 means “print -3”.

Using this syntax, we can modify our template to get rid of these new lines:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{- if eq .Values.favorite.drink "coffee"}}
  mug: true
  {{- end}}
Copy the code

To make this clear, let’s adjust the above to replace the Spaces with *, which will remove every space as per this rule. An * at the end of the line indicates that the newline character will be removed

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}*
**{{- if eq .Values.favorite.drink "coffee"}}
  mug: true*
* * {{- end}}
Copy the code

With this in mind, we can run our template through Helm and see the results:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: clunky-cat-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: true
Copy the code

Be careful with the chomping modifier. This can easily lead to accidents:

  food: {{.Values.favorite.food | upper | quote}}
  {{- if eq .Values.favorite.drink "coffee" -}}
  mug: true
  {{- end -}}
Copy the code

This will produce food: “PIZZA”mug:true because the newline characters for both sides are removed.

For more information on space control in templates, see Official Go Template documentation

Finally, sometimes it’s easier to tell the template system how to indent, rather than trying to master the spacing of the template instructions. Therefore, you may sometimes find it useful to use the indent function ({{indent 2 “mug:true”}}).

5.8.4 Using the with command to modify the range

The next control structure to look at is with. It controls the scope of variables. Recall that.is a reference to the current scope. Therefore,.values tells the template to look for the Values object in the current range.

The syntax with is similar to a simple if statement:

{{with PIPELINE}}
  # restricted scope
{{end}}
Copy the code

The scope can change. With allows you to change the current range (.) Set to a specific object. For example, we’ve been using.values.Favorites. Let’s rewrite our ConfigMap to change the. Scope to point to.values. Favorites:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  {{- end}}
Copy the code

Note that we can now refer to.drink and.food without qualifying them. This is because the with declaration sets. To point to.values. Favorite. After {{end}}. Resets its previous range.

But be warned! Within a restricted scope, no other objects will be accessible from the parent scope at this point. For example, the following error will be reported:

  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  release: {{.Release.Name}}
  {{- end}}
Copy the code

It will produce an error because Release.Name is not there. Within the limits of. However, if we swap the last two lines, everything will work as expected because the range is reset later.

  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  {{- end}}
  release: {{.Release.Name}}
Copy the code

Taking a look at range, let’s look at the template variable, which provides a solution to the scope problem described above.

5.8.5 cyclerangeaction

Many programming languages support looping using for loops, foreach loops, or similar functional mechanisms. In Helm’s templating language, the way to traverse a collection is to use the range operator.

First, let’s add a list of pizza toppings to our values.yaml file:

favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions
Copy the code

Now we have a list (called slice in the template) of pizzaToppings. We can modify our template to print this list to our ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  {{- end}}
  toppings: |-
    {{- range .Values.pizzaToppings}}
    - {{. | title | quote}}
    {{- end}}
Copy the code

Let’s take a closer look at toppings: List. The range function iterates through the list of pizzaToppings. But now something interesting is happening. Just like the range. Withsets, the same is true for the range operator. Each time you go through the loop, the. Is set to the top of the current pizza. That is the first time. Set up. In the second iteration it is set to cheese, and so on.

We can directly send pipeline. The value, so when we do this {{. | in the | quote}}, it sends out. To title (the title case function), and then to quote. If we run this template, the output will be:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-dragonfly-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  toppings: |-
    - "Mushrooms"
    - "Cheese"
    - "Peppers"
    - "Onions"
Copy the code

Now, in this example, we’ve hit something tricky. The toppings: | – line declares a multi-line string. So our Toppings list is not actually a YAML list. This is a big string. Why are we doing this? Because the data in ConfigMaps, data, consists of key/value pairs, where both keys and values are simple strings. To understand this situation, check out the Kubernetes ConfigMap documentation. But to us, that detail doesn’t matter.

| – marks in YAML said a multi-line string. This can be a useful technique for embedding large chunks of data in a manifest, as shown here.

Sometimes it’s useful to quickly create a list in a template and then iterate through the list. The Helm template has a feature that makes this simple: a tuple. In computer science, a tuple is a collection of list classes of fixed size, but with arbitrary data types. This is a rough representation of how a tuple should be used.

  sizes: |-
    {{- range tuple "small" "medium" "large"}}
    - {{...}}
    {{- end}}
  sizes: |-
    - small
    - medium
    - large
Copy the code

In addition to lists and tuples, a range can also be used to traverse collections with keys and values (such as a map or dict). You’ll see how to do this in the next section when we cover template variables.

5.9 variable

Now that we’ve looked at functions, pipes, objects, and control structures, we can find one of the more basic uses in many programming languages: variables. In templates, they are used less frequently. We’ll see how you can use them to simplify your code and make better use of with and range.

In the previous example, we saw that this code would fail:

  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  release: {{.Release.Name}}
  {{- end}}
Copy the code

Release.Name is not in the range restricted in the with block. One way to solve the scope problem is to assign objects to variables that can be accessed without regard to the current scope.

In the Helm template, a variable is a named reference to another object. It follows the form $name. Variables are given a special assignment operator: :=. We can override the release.name above with a variable.

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- $relname := .Release.Name -}}
  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  release: {{$relname}}
  {{- end}}
Copy the code

Notice that before we start the with block, we assign $relname :=.release.name. Now inside the with block, the $relname variable still points to the publication name.

The result would be:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: viable-badger-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  release: viable-badger
Copy the code

Variables are particularly useful in range loops. They can be used on list-like objects to capture both indexes and values:

toppings: |-
    {{- range $index, $topping := .Values.pizzaToppings}}
      {{$index}}: {{ $topping }}
    {{- end}}
Copy the code

Notice that range is first a variable, then an assignment operator, then a list. This will assign the integer index (starting from zero) to $index and the value to $topping. Running it will produce:

  toppings: |-
      0: mushrooms
      1: cheese
      2: peppers
      3: onions
Copy the code

For data structures that have both a key and a value, we can use range to get both. For example, we can loop to.values. Favorite like this:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- range $key, $val : = .Values.favorite}}
  {{$key}}: {{ $val | quote }}
  {{- end}}
Copy the code

Now in the first iteration, $key is drink and $val is coffee. In the second iteration, $key is food and $val is pizza. Running the above code produces the following:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eager-rabbit-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
Copy the code

Variables are usually not “global.” Their scope is the block they’re in. Previously, we assigned $relname to the top level of the template. This variable will work across the scope of the template. But in our last example, $key and $val will only be used in the {{range… }}{{end}} block.

However, there is always one variable that is the global $variable – this variable always points to the root context. This is useful when you loop within a range where you need to know the chart release name.

For example:

{{- 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 }}"
    app.kubernetes.io/instance: "{{$.Release.Name}}"
    app.kubernetes.io/managed-by: "{{$.Release.Service}}"
type: kubernetes.io/tls
data:
  tls.crt: {{.certificate}}
  tls.key: {{.key}}
---
{{- end}}
Copy the code

So far, we’ve only looked at one template declared in a file. But one of the power of the Helm template language is its ability to declare multiple templates and use them together. We will discuss this in the next section.

5.10 Naming templates

Now is the time to start creating more than one template. In this section, we’ll see how to define named templates in one file and then use them elsewhere. A named template (sometimes called a part or subtemplate) is a template that is qualified within a file and given a name. We have two ways to create it, and several different ways to use it.

In the “Flow Control” section, we described three actions for declaring and managing templates: Define, template, and block. In this section, we’ll introduce these three actions and an include function, similar to template.

One important detail to note when naming a template is that the template name is global. If you declare two templates with the same name, the last template loaded is the active template. Because the templates in the subchart are compiled with the top-level templates, be careful to name the templates with the name of the specific chart.

The common naming convention is to add the chart name to each defined template: {{define “mychart.labels”}}. By using a specific chart name as a prefix, we can avoid any conflicts that might occur due to two different charts for a template with the same name.

5.10.1 partials and_file

So far, we’ve used one file, and one file contains one template. But Helm’s template language allows the creation of specified embedded templates, which can be accessed by name.

Before we start writing these templates, a few file naming conventions are worth mentioning:

  • Most filestemplates/Recognized as containing Kubernetes manifests
  • NOTES.txtIs an exception
  • Underline the name (_Files beginning with) are assumed to have no internal manifest. These files do not render Kubernetes object definitions, but are available for call anywhere in the other Chart templates.

These files are used to store partials and helper programs. In fact, when we first created mychart, we saw a file called _helpers.tpl. This file is the default location for template partials.

5.10.2 withdefinetemplateDeclare and use templates

Six practical

6.1 make charts

  • Go2cloud-api-doc made by SLATE will be made into charts by helm to facilitate subsequent deployment
├─ Charts ├─ Chart. Yaml ├─ templates │ ├── Yaml │ ├── _helpers.tpl │ ├── Notes.txt │ ├── service.yaml │ ├── tests │ ├─ test.yaml ├─ ch.htm values.yaml 3 directories, 8 files

#Configure deployment
[root@master go2cloud_api_doc_charts]# egrep "^$|^#" -v go2cloud-api-doc/templates/deployment.yaml  
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "go2cloud-api-doc.fullname" . }}
  labels:
{{ include "go2cloud-api-doc.labels" . | indent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "go2cloud-api-doc.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "go2cloud-api-doc.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
      imagePullSecrets: 
        - name: {{ .Values.imagePullSecrets }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.port }}
              protocol: TCP
          livenessProbe:
            {{- toYaml .Values.livenessProbe | nindent 12  }}
          readinessProbe:
            {{- toYaml .Values.readinessProbe | nindent 12  }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}


#Configure the service
[root@master go2cloud_api_doc_charts]# egrep "^$|^#" -v go2cloud-api-doc/templates/service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: {{ include "go2cloud-api-doc.fullname" . }}
  labels:
{{ include "go2cloud-api-doc.labels" . | indent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
      nodePort: {{ .Values.service.nodePort }}      
  selector:
    app.kubernetes.io/name: {{ include "go2cloud-api-doc.name" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}


#Configuration values[root@master go2cloud_api_doc_charts]# egrep "^$|^#|^[[:space:]]+#" -v go2cloud-api-doc/values.yaml replicaCount: Image 1: repository: 10.234.2.218 / go2cloud/go2cloud - API - doc tag: latest pullPolicy: Always imagePullSecrets: registry-secret nameOverride: "" fullnameOverride: "" service: type: NodePort port: 4567 nodePort: 30567 ingress: enabled: false annotations: {} hosts: - host: chart-example.local paths: [] tls: [] resources: requests: cpu: 1000m memory: 1280Mi limits: cpu: 1000m memory: 1280Mi livenessProbe: tcpSocket: port: 4567 initialDelaySeconds: 10 failureThreshold: 2 timeoutSeconds: 10 readinessProbe: httpGet: path: /#introduction port: http initialDelaySeconds: 5 failureThreshold: 2 timeoutSeconds: 30 nodeSelector: {} tolerations: [] affinity: {} [root@master go2cloud_api_doc_charts]# egrep "^$|^#|^[[:space:]]+#" -v go2cloud-api-doc/Chart.yaml apiVersion: V1 appVersion: "1.0" Description: A Helm chart for Kubernetes Name: GO2Cloud-api-doc version: 0.1.0

#The deployment of[root@master go2cloud_api_doc_charts]# helm install -n go2cloud-api-doc -f go2cloud-api-doc/values.yaml go2cloud-api-doc/ NAME: go2cloud-api-doc LAST DEPLOYED: Wed Jul 31 14:34:21 2019 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1/Deployment NAME READY UP-TO-DATE AVAILABLE AGE go2cloud-api-doc 0/1 1 0 0s ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE go2cloud-api-doc-7cfb7bb795-clrz8 0/1 ContainerCreating 0 0s ==> v1/Service NAME TYPE CLUSTER-IP External-ip PORT(S) AGE go2cloud-api-doc NodePort 10.96.228.251 < None > 4567:30567/TCP 0s NOTES: 1. Get the application URL by running these commands: export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services go2cloud-api-doc) export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT [root@master go2cloud_api_doc_charts]# helm ls go2cloud-api-doc NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE GO2cloud-api-DOC 1 Wed Jul 31 14:34:21 2019 DEPLOYED GO2cloud-api-DOC-0.1.0 1.0 default [root@master go2cloud_api_doc_charts]# kubectl get deployment go2cloud-api-doc NAME READY UP-TO-DATE AVAILABLE AGE go2cloud-api-doc 0/1 1 0 10m [root@master go2cloud_api_doc_charts]# kubectl get pods |grep go2cloud-api-doc go2cloud-api-doc-7cfb7bb795-clrz8 0/1 CrashLoopBackOff 7 10m [root@master go2cloud_api_doc_charts]# kubectl get svc Go2cloud-api-doc NAME TYPE cluster-ip external-ip PORT(S) AGE go2cloud-api-doc NodePort 10.96.228.251 <none> 4567:30567/TCP 10m


#packaging[root@master go2cloud_api_doc_charts]# helm package ./go2cloud-api-doc/ Successfully packaged chart and saved it to: /data/go2cloud_api_doc_charts/go2cloud-api-doc-0.1.0.tgz [root@master go2cloud_api_doc_charts]# Tree ├── Chart. Yaml │ ├── templates │ ├── deployment. Yaml │ ├── helpers Notes.txt │ ├── service.htm │ ├── tests │ ├─ ch.htm │ ├─ ch.htm Go2cloud-api-doc-0.1.0.tgz 4 Directories, 8 files

#Number of upgrade Copies
helm upgrade go2cloud-api-doc --set replicaCount=2 go2cloud-api-doc/
Copy the code

6.2 configuration minior

Store the charts made to Minio and deploy Minior in K8S

  • Create a local chart directory
mkdir minio-chart
Copy the code
  • Package the modified chart file
helm package redis
Copy the code
  • Copy the package to the local chart directory that you created
Cp redis - 8.0.5..tgz/root/minio - chart /Copy the code
  • Update the index index in /root/minio-chart/
Helm repo index minio - chart/url http://10.234.2.204:31311/minio/common-helm-repo/Copy the code

  • Upload the index.yaml and chart packages to minio
Yaml minio/common-helm-repo/ MC cp redis-8.0.5. TGZ minio/common-helm-repo/Copy the code
  • Upload the charts made to Minio
helm repo add monocular https://helm.github.io/monocular helm install -n monocular monocular/monocular mc cp Go2cloud - API - doc - 0.1.0 from. TGZ minio/common - helm - repoCopy the code

You can view ak key information in ${HOME}/.mc/config.json.

  • validation

6.3 Uploading to a public HELM warehouse

Will make good charts packages can be uploaded to the helm warehouse, can be placed in their own self-built private warehouses, inflows: kubeapps/Monocular/minior etc, can take advantage of the helm command a key installation.

Upload to public cloud public warehouse, such as Apphub created by Alibaba in China. In today’s cloud native ecology, many mature open source software have been made into Helm Charts, making it very convenient for users to download and use, such as Nginx. Apache, Elasticsearch, Redis, etc. However, until the launch of the Open Cloud Native Application Center App Hub (Helm Charts China), it has been difficult for Chinese users to download and use these Charts directly. Now, AppHub not only synchronise all applications in the official Helm Hub for domestic users in real time, but also automatically replace all unaccessible mirror urls (such as gcr. IO, Quay. IO, etc.) in these Charts. It has finally made it possible for domestic developers to “install” their apps with one click via Helm Install.

To submit my own charts, please refer to github.com/cloudnative…

SLATE helps you create beautiful, intelligent, responsive API documentation.

Developer.aliyun.com/hub/detail?…

Welcome to like.

Chapter VII Related Links

  • Helm githab address

  • Helm manual

  • Whmzsu. Making. IO/helm – doc – useful…

  • Github.com/helm/monocu…

  • Helm3