Cloud native in-depth analysis of Kubernetes application package manager Helm's nanny-level tutorial and actual combat

1. Introduction to Helm

① What is Helm?

  • We can think of Helm as apt-get/yum under Kubernetes. Helm is the package manager of kubernetes. There are only configuration list files in the Helm warehouse, but no mirror image. The mirror image is still provided by the mirror warehouse, such as hub.docker.com, Private warehouse.
  • For more information about Helm, please refer to: Official Documentation .

② Helm architecture

insert image description here

③ Helm installation

# 下载包
$  wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz
# 解压压缩包
$ tar -xf helm-v3.9.4-linux-amd64.tar.gz
# 制作软连接
$ ln -s /opt/helm/linux-amd64/helm /usr/local/bin/helm
# 验证
$ helm version
$ helm help

④ Helm components and related terms

  • Helm——Helm is a client tool under the command line, which is mainly used for the creation, packaging and publishing of the Kubernetes application Chart, as well as the creation and management of local and remote Chart warehouses;
  • Chart—Chart represents the Helm package, which contains all the resource definitions needed to run applications, tools or services inside the Kubernetes cluster, and can be regarded as the equivalent of Homebrew formula, Apt dpkg or Yum RPM in Kubernetes;
  • Release——Release is an instance of a chart running in a Kubernetes cluster. A chart can usually be installed multiple times in the same cluster, and each installation will create a new release;
  • Repoistory - A Repository is a place to store and share charts, like Perl's CPAN archive network or Fedora's package repository, except it's for Kubernetes packages.

2. Helm Chart

① Chart directory structure

  • You can create a new chart package with the helm create command:
helm create nginx
tree nginx

insert image description here

  • The Chart directory structure is as follows:
nginx/
├── charts  #依赖其他包的charts文件
├── Chart.yaml # 该chart的描述文件,包括ico地址,版本信息等
├── templates  # #存放k8s模板文件目录
│   ├── deployment.yaml # 创建k8s资源的yaml 模板
│   ├── _helpers.tpl # 下划线开头的文件,可以被其他模板引用
│   ├── hpa.yaml # 弹性扩缩容,配置服务资源CPU 内存
│   ├── ingress.yaml # ingress 配合service域名访问的配置
│   ├── NOTES.txt # 说明文件,helm install之后展示给用户看的内容
│   ├── serviceaccount.yaml # 服务账号配置
│   ├── service.yaml # kubernetes Serivce yaml 模板
│   └── tests # 测试模块
│       └── test-connection.yaml
└── values.yaml # 给模板文件使用的变量
  • There may be writing packages and the following directories:
wordpress/
...
  LICENSE             # 可选: 包含chart许可证的纯文本文件
  README.md           # 可选: 可读的README文件
  values.schema.json  # 可选: 一个使用JSON结构的values.yaml文件
  charts/             # 包含chart依赖的其他chart
  crds/               # 自定义资源的定义
...

② Chart.yaml file

apiVersion: chart API 版本 (必需)
name: chart名称 (必需)
version: chart 版本,语义化2 版本(必需)
kubeVersion: 兼容Kubernetes版本的语义化版本(可选)
description: 一句话对这个项目的描述(可选)
type: chart类型 (可选)
keywords:
  - 关于项目的一组关键字(可选)
home: 项目home页面的URL (可选)
sources:
  - 项目源码的URL列表(可选)
dependencies: # chart 必要条件列表 (可选)
  - name: chart名称 (nginx)
    version: chart版本 ("1.2.3")
    repository: (可选)仓库URL ("https://example.com/charts") 或别名 ("@repo-name")
    condition: (可选) 解析为布尔值的yaml路径,用于启用/禁用chart (e.g. subchart1.enabled)
    tags: # (可选)
      - 用于一次启用/禁用 一组chart的tag
    import-values: # (可选)
      - ImportValue 保存源值到导入父键的映射。每项可以是字符串或者一对子/父列表项
    alias: (可选) chart中使用的别名。当你要多次添加相同的chart时会很有用
maintainers: # (可选)
  - name: 维护者名字 (每个维护者都需要)
    email: 维护者邮箱 (每个维护者可选)
    url: 维护者URL (每个维护者可选)
icon: 用做icon的SVGPNG图片URL (可选)
appVersion: 包含的应用版本(可选),不需要是语义化,建议使用引号
deprecated: 不被推荐的chart (可选,布尔值)
annotations:
  example: 按名称输入的批注列表 (可选)
    • From v3.3.2, extra fields are no longer allowed, the recommended way is to add custom metadata in annotations. Each chart must have a version number (version), and the version must follow the "Semantic Version 2" standard. Unlike classic Helm, Helm v2 and subsequent versions will use the version number as a release marker. Packages in the warehouse are added by name and version ID number. For example, the version field version: 1.2.3 of nginx chart is set to:
nginx-1.2.3.tgz
  • Note: The appVersion field is not related to the version field, which is a way of specifying the app version. For example, this drupal chart might have an appVersion: "8.2.1", indicating that the version of Drupal contained in the chart (by default) is 8.2.1.

③ Chart dependency management (dependencies)

  • Other charts that the current chart depends on will be defined as a list in the dependencies field:
dependencies:
  - name: apache
    version: 1.2.3
    repository: https://example.com/charts
  - name: mysql
    version: 3.2.1
    repository: https://another.example.com/charts
  • illustrate:
    • The name field is the name of the required chart;
    • The version field is the required version of the chart;
    • The repository field is the full URL of the chart warehouse, which must be added locally using helm repo add;
    • The name of the repository can be used instead of the URL.
  • Example demo:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm pull bitnami/wordpress
tar -xf wordpress
cat wordpress/Chart.yaml

insert image description here

  • Once the dependencies are defined, running helm dependency update will use the dependency files to download all specified charts to the charts/ directory:
helm dependency update ./wordpress
  • When helm dependency update pulls a chart, a chart package is formed in the charts/ directory, so for the above example, you would expect to see the following files in the chart directory:
wordpress/charts/
├── common
├── common-2.0.1.tgz
├── mariadb
├── mariadb-11.2.2.tgz
├── memcached
└── memcached-6.2.3.tgz

insert image description here

  • Tag and condition fields in dependencies: In addition to the other fields above, each requirement item can contain optional fields tags and condition, and all charts will be loaded by default. If tags or condition fields are present, they will be evaluated and used to control the loading of the chart they apply to.
    • Condition: The condition field field contains one or more YAML paths (separated by commas). If this path already exists in the upper-level values ​​and resolves to a Boolean value, the chart will enable or disable the chart based on the Boolean value, and only the ones found in the list will be used. The first valid path, if the path is not found then the condition is invalid;
    • Tags: The tag field is a list of tags in YAML format associated with the chart. In the top-level value, you can enable or disable all tagged charts by specifying tag and Boolean value.
# parentchart/Chart.yaml

dependencies:
  - name: subchart1
    repository: http://localhost:10191
    version: 0.1.0
    condition: subchart1.enabled, global.subchart1.enabled
    tags:
      - front-end
      - subchart1
  - name: subchart2
    repository: http://localhost:10191
    version: 0.1.0
    condition: subchart2.enabled,global.subchart2.enabled
    tags:
      - back-end
      - subchart2
# parentchart/values.yaml

subchart1:
  enabled: true
tags:
  front-end: false
  back-end: true
  • In the above example, all charts with front-end tag will be disabled, but as long as the subchart1.enabled path in the value of the upper layer is set to 'true', the condition will override the front-end tag and subchart1 will be enabled.
  • Once subchart2 uses the back-end tag and is set to true, subchart2 will be enabled. Also note that although subchart2 specifies a condition field, the upper-level value does not have a corresponding path and value, so this condition will not take effect.
  • The --set parameter can be used to set labels and conditional values:
helm install --set tags.front-end=true --set subchart2.enabled=false
  • Parsing of tags and conditions:
    • The condition (when set in value) will always override the label. If the conditional path of the first chart exists, the following path will be ignored;
    • The label is defined as 'If any of the chart labels is true, the chart can be enabled;
    • Labels and conditional values ​​must be set in the top-level value;
    • The tags: key in value must be a top-level key.

④ Import child Value through dependency

  • In some cases it is worthwhile to allow child chart values ​​to be passed as public defaults into the parent chart, with the added benefit of using the exports format so that future tools can introspect user-settable values.
  • The imported key containing values ​​can be specified in the import-values ​​field in the dependencies of the parent chart in the form of a YAML list, and each item in the list is a key imported from the exports field in the child chart.
  • Import values ​​not contained in the exports key, using the child-parent format.
  • Use the export format: If the values.yaml file of the child chart contains the exports field in the root node, its content can be directly imported into the value of the parent chart by specifying, as follows:
# parent's Chart.yaml file

dependencies:
  - name: subchart
    repository: http://localhost:10191
    version: 0.1.0
    import-values:
      - data
# child's values.yaml file

exports:
  data:
    myint: 99
  • As long as the key data is specified in the import list, Helm will look for the data key in the subchart's exports field and import its contents.
  • The final parent value will contain our exported fields. The parent key data is not included in the parent's final value. If you want to specify this parent key, use the 'child-parent' format:
# parent's values

myint: 99
  • The import-values ​​in the example shown below instructs Helm to take any values ​​it can find in the child: path and copy them to the specified path in parent::
# parent's Chart.yaml file

dependencies:
  - name: subchart1
    repository: http://localhost:10191
    version: 0.1.0
    ...
    import-values:
      - child: default.data
        parent: myimports
  • The value of default.data found in subchart1 will be imported into the myimports key of the parent chart, the details are as follows:
# parent's values.yaml file

myimports:
  myint: 0
  mybool: false
  mystring: "helm rocks!"
# subchart1's values.yaml file

default:
  data:
    myint: 999
    mybool: true

三、Templates and Values

① Introduction to Templates and Values

  • The Helm Chart template is written in the Go templating language, adding about 50 additional template functions from the Sprig library and some other specified functions.
  • All template files are stored in the chart's templates/ folder. When Helm renders a chart, it traverses each file in the directory through the template engine.
  • The Value of the template is provided in two ways:
    • Chart developers can provide a file named values.yaml in the chart, which contains default values;
    • Chart users can provide a YAML file containing value, and can specify the value file by -f when using the helm install command on the command line.
  • Template example:
apiVersion: v1
kind: ReplicationController
metadata:
  name: deis-database
  namespace: deis
  labels:
    app.kubernetes.io/managed-by: deis
spec:
  replicas: 1
  selector:
    app.kubernetes.io/name: deis-database
  template:
    metadata:
      labels:
        app.kubernetes.io/name: deis-database
    spec:
      serviceAccount: deis-database
      containers:
        - name: deis-database
          image: {
    
    {
    
     .Values.imageRegistry }}/postgres:{
    
    {
    
     .Values.dockerTag }}
          imagePullPolicy: {
    
    {
    
     .Values.pullPolicy }}
          ports:
            - containerPort: 5432
          env:
            - name: DATABASE_STORAGE
              value: {
    
    {
    
     default "minio" .Values.storage }}
  • charts is a template for a Kubernetes replica controller, which can use the following four template values ​​(generally defined in the values.yaml file):
    • imageRegistry: the source registry of the Docker image;
    • dockerTag: the tag of the Docker image;
    • pullPolicy: Pull policy of Kubernetes;
    • storage: background storage, default is "minio".

② Predefined Values

  • Values ​​are provided through the values.yaml file accessible by the .Values ​​object in the template (or via the --set parameter), but other predefined data fragments are accessible in the template. The following values ​​are predefined, valid for each template, and can be overridden, and like all values, the names are case-sensitive:
    • Release.Name: version name (non-chart);
    • Release.Namespace: the namespace of the released chart version;
    • Release.Service: Organization version service;
    • Release.IsUpgrade: Set to true if the current operation is an upgrade or rollback;
    • Release.IsInstall: Set to true if the current operation is installation;
    • Chart: the content of Chart.yaml, so the version of chart can be obtained from Chart.Version, and the maintainer is in Chart.Maintainers;
    • Files: A diagram-like object in a chart that contains non-special files, this will not allow you to access templates, but will allow access to other existing files (unless excluded by .helmignore), use { { index .Files " file.name ” }} You can access the file or use the { {.Files.Get name }} function, or you can use { { .Files.GetBytes }} as []byte to access the file content;
    • Capabilities: A class diagram object containing Kubernetes version information. ({ { .Capabilities.KubeVersion }}) and supported Kubernetes API versions ({ { .Capabilities.APIVersions.Has “batch/v1” }});
  • Considering the template from the previous section, the necessary values ​​provided by the values.yaml file are as follows:
imageRegistry: "quay.io/deis"
dockerTag: "latest"
pullPolicy: "Always"
storage: "s3"
  • The values ​​file is defined in YAML format. The chart will contain a default values.yaml file. The Helm installation command allows the user to override this value with additional YAML values:
helm install --generate-name --values=myvals.yaml wordpress

③ Scope, dependencies and values

  • A Values ​​file can declare values ​​for the top-level chart, as well as any other charts contained in the charts/ directory. Or to put it another way, the values ​​file can provide values ​​for the chart and any of its dependencies. For example, the WordPress chart demonstrated above has both mysql and apache as dependencies, and the values ​​file can provide dependencies for all of the following components:
title: "My WordPress Site" # Sent to the WordPress template

mysql:
  max_connections: 100 # Sent to MySQL
  password: "secret"

apache:
  port: 8080 # Passed to Apache
  • Higher-level charts have access to all variables defined below, so WordPress charts can access MySQL passwords with Values.mysql.password. But lower-level charts cannot access parent charts, so MySQL cannot access the title attribute, nor can it access apache.port.

④ Global Values

  • Since 2.0.0-Alpha.2, Helm supports special global values, imagine a modified version of the previous example:
title: "My WordPress Site" # Sent to the WordPress template

global:
  app: MyWordPress

mysql:
  max_connections: 100 # Sent to MySQL
  password: "secret"

apache:
  port: 8080 # Passed to Apache
  • Added a global section and a value app: MyWordPress, which is available in all charts as .Values.global.app. For example, the mysql template can access the app as { {.Values.global.app}}, and the apache chart can also access it. In fact the above values ​​file will be regenerated like this:
title: "My WordPress Site" # Sent to the WordPress template

global:
  app: MyWordPress

mysql:
  global:
    app: MyWordPress
  max_connections: 100 # Sent to MySQL
  password: "secret"

apache:
  global:
    app: MyWordPress
  port: 8080 # Passed to Apache

4. Helm resource installation order

  • Namespace
  • NetworkPolicy
  • ResourceQuota
  • LimitRange
  • SubSecurityPolicy
  • PodDisruptionBudget
  • ServiceAccount
  • Secret
  • SecretList
  • ConfigMap
  • StorageClass
  • PersistentVolume
  • PersistentVolumeClaim
  • CustomResourceDefinition
  • ClusterRole
  • ClusterRoleList
  • ClusterRoleBinding
  • ClusterRoleBindingList
  • Role
  • RoleList
  • RoleBinding
  • RoleBindingList
  • Service
  • DaemonSet
  • Pod
  • ReplicationController
  • ReplicaSet
  • Deployment
  • HorizontalPodAutoscaler
  • StatefulSet
  • Job
  • CronJob
  • Ingress
  • APIService

5. Three ways for Helm to install the Chart package

  • Helm comes with a powerful search command that can be used to search from two sources:
    • helm search hub finds and lists helm charts from Artifact Hub https://artifacthub.io/, where a large number of different warehouses are stored;
    • helm search repo Searches from the repositories added (using helm repo add) to the local helm client. This command searches based on local data and does not require an Internet connection.
# 添加bitnami仓库源
helm repo add bitnami https://charts.bitnami.com/bitnami
# 从bitnami源查找所有chart包,不指定具体源的话,会查找本地添加的所有源地址的所有chart包
helm search repo bitnami

① values ​​parameter passing

  • There are two ways to pass configuration data during installation:
    • –values ​​(or -f): Use YAML files to override the configuration, you can specify multiple times, and use the rightmost file first;
    • –set: Overwrite the specified item through the command line.
  • If both methods are used at the same time, the value in --set will be merged into --values, but the value in --set has higher priority. The content overwritten in –set will be saved in ConfigMap. You can use helm get values ​​to view the value set by --set in the specified release, or you can run helm upgrade and specify the --reset-values ​​field to clear -- The value set in set. Examples are as follows:
echo '{
    
    mariadb.auth.database: user0db, mariadb.auth.username: user0}' > values.yaml
helm install -f values.yaml bitnami/wordpress --generate-name
  • Direct online installation does not need to download the package to the local first:
helm install mysql bitnami/mysql
helm list
  • Offline installation is installed directly through the installation package:
# 先删除
helm uninstall mysql
# 拉包到本地
helm pull bitnami/mysql
# 不解压直接安装
helm install mysql ./mysql-9.3.1.tgz
helm list
  • Offline installation Unzip the package and install again:
# 拉包到本地
helm pull bitnami/mysql
# 解压安装
tar -xf mysql-9.3.1.tgz

# 开始安装
helm install mysql ./mysql \
--namespace=mysql \
--create-namespace \
--set image.registry=myharbor.com \
--set image.repository=bigdata/mysql \
--set image.tag=8.0.30 \
--set primary.service.type=NodePort \
--set service.nodePorts.mysql=30306

# 查看在运行的Release
helm list

# 卸载
helm uninstall mysql -n mysql

6. Helm Basic Grammar

① variable

  • The variables in the template (templates/) are placed in { {}}, for example: { { .Values.images }} means the images field under the Values ​​object. Values ​​come from the values.yaml file or the yaml file specified by -f, or the variables set by --set.
  • Use - to delete spaces and line breaks. To delete other spaces and line breaks in that line, you can use { { - or -}}, one is to delete spaces and line breaks on the left, and the other is to delete spaces and line breaks on the right .

② Built-in objects

  • Release: The Release object describes the release itself, including the following objects:
    • Release.Name: release name;
    • Release.Namespace: the namespace included in the release (if not covered by the manifest);
    • Release.IsUpgrade: If the current operation is an upgrade or rollback, this value will be set to true
    • Release.IsInstall: If the current operation is to install, the value will be set to true
    • Release.Revision: The version number of this revision, which is 1 when installed, will increase automatically every time you upgrade or roll back;
    • Release.Service: This service is used to render the current template, and Helm is always in Helm.
  • Values: The Values ​​object is passed into the template from the values.yaml file and the file provided by the user, and is empty by default;
  • Chart: the content of the Chart.yaml file, all data in Chart.yaml can be accessed here, for example { { .Chart.Name }}-{ { .Chart.Version }} will print out mychart-0.1.0;
  • Template: Contains the current template information currently being executed:
    • Template.Name: namespace file path of the current template (eg mychart/templates/mytemplate.yaml);
    • Template.BasePath: The path of the current chart template directory (eg mychart/templates).

③ Commonly used built-in functions

  • quote and squote
    • This function converts the value into a string enclosed in double quotes (quote) or **single quote (squote)**. Examples are as follows:
apiVersion: v1
kind: ConfigMap
metadata:
  name: {
    
    {
    
     .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {
    
    {
    
     .Values.favorite.drink | quote }}
  food: {
    
    {
    
     .Values.favorite.food | upper | quote }}
    • Inverting commands is a common practice in templates, you can often see .val | quote instead of quote .val, in fact both operations are possible.
  • default
    • This function allows specifying a default value in the template, in case the value is ignored:
# 如果.Values.favorite.drink是非空值,则使用它,否则会返回tea。
drink: {
    
    {
    
     .Values.favorite.drink | default "tea" | quote }}

# 还可以这样写,如果.Bar是非空值,则使用它,否则会返回foo。
default "foo" .Bar
    • The "empty" definition depends on the following types:
整型: 0
字符串: ""
列表: []
字典: {
    
    }
布尔: false
以及所有的nil (或 null)
  • print
    • Returns a string composed of parts, and non-string types will be converted to strings:
print "Matt has " .Dogs " dogs"
    • When two adjacent parameters are not strings, a space will be added between them.
  • println : Same as print, but adds a new line at the end.
  • printf
    • Returns the formatted string passed by the parameters in order:
printf "%s has %d dogs." .Name .NumberDogs
{
    
    {
    
    - printf "%d" (.Values.externalCache.port | int ) -}}
{
    
    {
    
    - printf "%s" .Values.existingSecret -}}

{
    
    {
    
    - printf "%v" .context.Values.redis.enabled -}}

# %s 字符串占位符,未解析的二进制字符串或切片
# %d 数字占位符,十进制
# %v 默认格式的值,当打印字典时,加号参数(%+v)可以添加字段名称
  • trim : trim lines remove spaces on both sides of the string:
trim "   hello    "
  • trimAll
    • Remove given characters from a string:
trimAll "$" "$5.00"
    • The result of the above is: 5.00 (as a string).
  • lower
    • Convert the entire string to lowercase:
lower "HELLO"
    • The above result is: hello
  • upper
    • Convert the entire string to uppercase:
upper "hello"
    • The above result is: HELLO.
  • title
    • Convert the first letter to uppercase:
title "hello world"
    • The above result is: Hello World.
  • substr
    • Get the substring of the string, there are three parameters:
start (int)
end (int)
string (string)
substr 0 5 "hello world"
    • The above result is: hello.
  • abbrev :
    • Truncating a string with an ellipsis (...):
abbrev 5 "hello world"
# 第一个参数:最大长度
# 第二个参数:字符串
    • The result of the above is: he..., because the ellipsis is counted into the length.
  • contains : Tests if a string is contained within another string:
contains "cat" "catch"
  • cat
    • The cat function combines multiple strings into one, separated by spaces:
cat "hello" "beautiful" "world"
    • The above result is: hello beautiful world.
  • indent
    • indent indents the line of the given string by the specified length, useful when aligning multi-line strings:
indent 4 $lots_of_text
    • The result of the above would be to indent each line by 4 spaces.
  • nindent :
    • The nindent function is the same as the indent function, but can add a newline at the beginning of the string:
nindent 4 $lots_of_text
    • The above result will indent the line containing the string by 4 characters and add a new line at the beginning.
  • replace
    • Perform a simple string replacement:
# 下面两行等价
replace " " "-" "I Am Henry VIII"
"I Am Henry VIII" | replace " " "-"

# 参数1:待替换字符串
# 参数2:要替换字符串
# 参数3:源字符串
    • The above results are: I-Am-Henry-VIII.
  • date
    • The date function formats a date, and the date is formatted as YEAR-MONTH-DAY:
now | date "2006-01-02"

④ Type conversion function

  • Helm provides the following type conversion functions:
    • atoi: convert string to integer;
    • float64: convert to float64;
    • int: convert to int according to the system integer width;
    • int64: convert to int64;
    • toDecimal: Convert unix octal to int64;
    • toString: converted to a string;
    • toStrings: Convert a list, slice or array into a list of strings;
    • toJson (mustToJson): Convert a list, slice, array, dictionary or object to JSON;
    • toPrettyJson (mustToPrettyJson): Convert a list, slice, array, dictionary or object into formatted JSON;
    • toRawJson (mustToRawJson): Convert a list, slice, array, dictionary or object to JSON with HTML characters unescaped.

⑤ Regular Expressions

  • Helm includes the following regular expression functions:
    • regexFind(blackRegexFind)
  • -regexFindAll(mustRegexFindAll)
  • -regexMatch (mustRegexMatch)
  • -regexReplaceAll (mustRegexReplaceAll)
  • -regexReplaceAllLiteral(mustRegexReplaceAllLiteral)
  • -regexSplit (blackRegexSplit)

⑥ Encoding and decoding functions

  • Helm has the following encode and decode functions:
  • -b64enc/b64dec: Encode or decode Base64
  • -b32enc/b32dec: Encode or decode Base32

⑦ Dictionaries and Dict Functions

  • Helm provides a key/value storage type called dict (short for "dictionary", also available in Python), dict is an unordered type, and the key of the dictionary must be a string. But the value can be of any type, even another dict or list.
  • Create a dictionary (dict) , as follows to create a dictionary of three key-value pairs:
$myDict := dict "name1" "value1" "name2" "value2" "name3" "value 3"
  • Get the value (get) :
    • Given a map and a key, get the value from the map, resulting in "value1":
get $myDict "name1"
    • Note that if not found, it will simply return "" and no error will be generated.
  • Add key-value pairs (set) :
    • Use set to add a key-value pair to the dictionary:
$_ := set $myDict "name4" "value4"
    • Note that set returns a dictionary (a requirement of Go template functions), so it may be necessary to use $_ assignment as above to get the value.
  • delete (unset) :
    • Given a map and a key, remove the key from the map:
$_ := unset $myDict "name4"
    • Like set, a dictionary needs to be returned.
  • Judgment key (hasKey) :
    • The hasKey function will return true if the given key is contained in the given dictionary:
hasKey $myDict "name1"
    • If the key is not found, false will be returned.
  • pluck
    • The pluck function is given a key and multiple mappings and gets a list of all matches:
pluck "name1" $myDict $myOtherDict
    • The above will return a list containing each found value ([value1 otherValue1]).
  • Merge dict(merge, mustMerge) : Merge two or more dictionaries into one, target dictionary first:
$newdict := merge $dest $source1 $source2
  • Get all keys :
    • The keys function will return a list of all keys in one or more dict types. Since dictionaries are unordered, the keys will not have a predictable order, and can be stored using sortAlpha:
keys $myDict | sortAlpha
    • When multiple dictionaries are provided, the keys are concatenated, using the uniq function and sortAlpha to obtain a uniquely ordered list of keys:
keys $myDict $myOtherDict | uniq | sortAlpha
  • Get all values :
    • The values ​​function is similar to keys, returning a new list containing all the values ​​in the source dictionary (only one dictionary is supported).
$vals := values $myDict
    • The result of the above is: list["value1", "value2", "value 3"]. Note that values ​​does not guarantee the order of the results; if order is desired, sortAlpha can be used.

⑧ Lists and List Functions

  • Helm provides a simple list type that holds lists in any order. Similar to arrays or slices, but lists are designed for immutable data types.
  • Create list :
$myList := list 1 2 3 4 5
    • The above would generate a list [1 2 3 4 5].
  • Get the first item of the list (first, mustFirst) :
    • To get the first item in the list, use first.
first $myList
# 返回 1
    • An error will occur if there is a problem with first, and an error will be returned to the template engine if there is a problem with mustFirst.
  • Get the tail content of the list (rest, mustRest) :
    • To get the tail content of the list (everything but the first item), use rest:
rest $myList
# 返回 [2 3 4 5]
    • An error will occur when there is a problem with rest, and an error will be returned to the template engine when there is a problem with mustRest.
  • Get the last item of a list (last, mustLast) :
    • Use last to get the last item of a list:
last $myList
# 返回 5。这大致类似于反转列表然后调用first。
  • Get all contents of list (initial, mustInitial) :
    • By returning all elements except the last one:
 initial $myList
 # 返回 [1 2 3 4]
    • An error will occur if there is a problem with initial, but an error will be returned to the template engine if there is a problem with mustInitial.
  • Add elements at the end (append, mustAppend) :
    • Append an item to an existing list, creating a new list:
$new = append $myList 6
    • The above statement will be set to [123456], myList will remain unchanged,
    • There is an error when append fails, but mustAppend returns an error to the template engine when there is a problem.
  • Add elements in front (prepend, mustPrepend) :
    • Add elements to the front of a list, producing a new list:
prepend $myList 0
    • The above statement will generate [0 1 2 3 4 5] and $myList will remain unchanged.
    • prepend will throw an error, but mustPrepend will return an error to the template engine if there is a problem.
  • Multiple list concatenation (concat) :
    • Concatenate any number of lists into one:
concat $myList ( list 6 7 ) ( list 8 )
    • The above statement would generate [1 2 3 4 5 6 7 8] and $myList would remain unchanged.
  • reverse(reverse, mustReverse) :
    • Reverse the given list to generate a new list:
reverse $myList
    • The above statement produces a list: [5 4 3 2 1].
    • reverse will throw an error, but mustReverse will return an error to the template engine.
  • deduplication(uniq, mustUniq) :
    • Generate a list with duplicates removed:
list 1 1 1 2 | uniq
    • The above statement produces [1 2].
    • uniq will throw an error when there is a problem, but mustUniq will return an error to the template engine if there is a problem.
  • filter(without, mustWithout) :
    • The without function filters content from a list:
without $myList 3
# 上述语句会生成 [1 2 4 5]
    • A filter can filter multiple elements:
without $myList 1 3 5
# 这样会得到: [2 4]
    • Without will throw an error, but mustWithout will return an error to the template engine.
  • Determine whether an element exists (has, mustHas) :
    • Verify that a list has a specific element:
has 4 $myList
    • The above statement will return true, but has “hello” $myList will return false.
    • Has will throw an error, but mustHas will return an error to the template engine.
  • Remove empty items (compact, mustCompact) :
    • Take a list and remove null items:
$list := list 1 "a" "foo" ""
$copy := compact $list
    • compact returns a new list with null values ​​(eg, "") removed.
    • compact will throw an error, but mustCompact will return an error to the template engine.
  • index
    • Use index list[n] to get the nth element of a list, and index list[n][m] ... to get multibit list elements:
index $myList 0 返回 1,同 myList[0]
index $myList 0 1 同 myList[0][1]
  • Get some elements (slice, mustSlice) : Get some elements from the list, use slice list [n] [m], which is equivalent to list[n:m]:
    • slice $myList returns [1 2 3 4 5], equivalent to myList[:];
    • slice $myList 3 returns [4 5] equivalent to myList[3:];
    • slice $myList 1 3 returns [2 3] equivalent to myList[1:3];
    • slice $myList 0 3 returns [1 2 3] which is equivalent to myList[:3].
    • slice will throw an error, but mustSlice will return an error to the template engine if there is a problem.
  • Build a list of integers (until) :
    • The until function builds a range of integers:
until 5
    • The above statement produces a list: [0, 1, 2, 3, 4].
    • Useful for loops: range e := until 5.
  • seq
seq 5       => 1 2 3 4 5
seq -3      => 1 0 -1 -2 -3
seq 0 2     => 0 1 2
seq 2 -2    => 2 1 0 -1 -2
seq 0 2 10  => 0 2 4 6 8 10
seq 0 -2 -5 => 0 -2 -4

⑨ Math Functions

  • Sum (add): Use add to sum, accepting two or more inputs.
  • Self-increment 1 (add1): self-increment by 1, use add1.
  • Subtraction (sub): use sub for subtraction.
  • Divide (div): use div for integer division.
  • Modulo (mod): modulo uses mod.
  • Multiply (mul): Multiplication uses mul, which accepts two or more inputs.
  • Get Maximum (max): Returns the largest integer in a set of integers.
  • Get the minimum value (min): returns the smallest number in a set of numbers.
  • GetLength(len): Returns the length of the argument as an integer.
  • Network Functions: Helm provides several network functions:
    • getHostByName receives a domain name and returns an IP address;
    • getHostByName "www.google.com" will return the corresponding address of www.google.com.

⑩ conditional statement

  • operator:
eq: 等于(equal to)
ne: 不等于(not equal to)
lt: 小于(less than)
le: 小于等于(less than or equal to)
gt: 大于(greater than)
ge: 大于等于(greater than or equal to)
  • if/else usage:
{
    
    {
    
    if 命令}}
{
    
    {
    
    else if 命令}}
{
    
    {
    
    else}}
{
    
    {
    
    end}}
  • Pipeline will be set to false if:
布尔false
数字0
空字符串
nil (空或null)
空集合(map, slice, tuple, dict, array)
  • Example:
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 }}

⑪ Change scope with

  • The next control structure is the with operator, this is used to control variable scope. Recall that . is a reference to the current scope, so .Values ​​is telling the template to look for the Values ​​object in the current scope.
  • The syntax of with is similar to that of an if statement:
{
    
    {
    
     with PIPELINE }}
  # restricted scope
{
    
    {
    
     end }}
  • The scope can be changed, with allows specific objects to set the current scope (.). For example, if .Values.favorite is already in use, modify the scope of . in the configuration map to point to .Values.favorite:
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 }}
  • But there is a caveat here. Within a limited scope, you cannot use . to access objects in the parent scope. Examples of errors are as follows:
{
    
    {
    
    - with .Values.favorite }}
drink: {
    
    {
    
     .drink | default "tea" | quote }}
food: {
    
    {
    
     .food | upper | quote }}
release: {
    
    {
    
     .Release.Name }}
{
    
    {
    
    - end }}
  • This will report an error because Release.Name is not within the scope of the . limit. But it's normal if you reverse the last two lines, because the scope is reset after { { end }}:
{
    
    {
    
    - with .Values.favorite }}
drink: {
    
    {
    
     .drink | default "tea" | quote }}
food: {
    
    {
    
     .food | upper | quote }}
{
    
    {
    
    - end }}
release: {
    
    {
    
     .Release.Name }}
  • Or you can access the Release.Name object from the parent scope, access the Release.Name object from the parent scope when the template starts executing, and when the template starts executingThe Re l e a se . Name object is accessed from the parent scope . When the template starts executing, it will be mapped to the root scope and will not change during execution . The following also works fine:
{
    
    {
    
    - with .Values.favorite }}
drink: {
    
    {
    
     .drink | default "tea" | quote }}
food: {
    
    {
    
     .food | upper | quote }}
release: {
    
    {
    
     $.Release.Name }}
{
    
    {
    
    - end }}
  • Variables can also be defined outside, following the format of the $name variable and specifying a special assignment operator: :=, which can be rewritten using a variable for Release.Name:
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 }}
  • Note that before the with block begins, the assignment relname := .Release .Name , now in the with block, relname := .Release.Name , now in the with block,relname:=.R e l e a se .N ame , now in the with block , the relname variable still executes the version name .

⑫ rang loop statement

  • Many programming languages ​​support the use of for loops, foreach loops, or similar method mechanisms. In Helm's templating language, the way to iterate over a collection is to use the range operator.
  • Define values:
favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions
  • Now that we have a list of pizzaToppings (called slices in the template), modify the template to print this list to the configuration map:
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 }}
  • Sometimes it is useful to be able to quickly create a list in a template and then iterate over it, and Helm template tuples make this easy. In computer science, a tuple represents a list-like collection of fixed size, but can be of any data type, which roughly expresses the usage of tuple:
  sizes: |-
    {
     
     {
     
     - range tuple "small" "medium" "large" }}
    - {
     
     {
     
      . }}
    {
     
     {
     
     - end }}
上述模板会生成以下内容:

  sizes: |-
    - small
    - medium
    - large

⑬ Naming templates

  • At this point, you need to skip the template and start creating other content. This section can see how to define a named template in a file and use it elsewhere. A named template (sometimes called a "section" or a "subtemplate") is simply a template defined inside a file with a name.
  • Three methods of declaring and managing templates: define, template, and block. In this section, these three operations will be used and a special-purpose include method will be introduced, similar to the template operation.
  • An important detail to remember when naming templates: template names are global, if you want to declare two templates with the same name, whichever is loaded last will be used, because templates in subcharts are compiled together with top-level templates, pay attention when naming chart specific name.
  • A common naming convention is to prefix the template with the chart name: { { define “mychart.labels” }}, using a specific chart name as a prefix can avoid possible conflicts caused by two different charts using a template with the same name.
  • Before writing the template details, the naming convention of the file needs attention:
    • Most files in templates/ are considered to contain Kubernetes manifests;
    • NOTES.txt is an exception;
    • Files whose names begin with an underscore (_) are assumed to contain no manifest content. These files are not rendered as Kubernetes object definitions, but are available in other chart templates.
  • These files are used to store local and helper objects. In fact, when we first create mychart, we will see a file called _helpers.tpl, which is the default location for template locals.
  • Declare and use templates with define and template:
    • The define operation allows us to create a named template in the template file, the syntax is as follows:
{
    
    {
    
    - define "MY.NAME" }}
  # body of template here
{
    
    {
    
    - end }}
    • A template can be defined to encapsulate Kubernetes tags:
{
    
    {
    
    - define "mychart.labels" }}
  labels:
    generator: helm
    date: {
    
    {
    
     now | htmlDate }}
{
    
    {
    
    - end }}
    • Now embed the template into the existing configuration map and include it using template:
{
    
    {
    
    - define "mychart.labels" }}
  labels:
    generator: helm
    date: {
    
    {
    
     now | htmlDate }}
{
    
    {
    
    - end }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {
    
    {
    
     .Release.Name }}-configmap
  {
    
    {
    
    - template "mychart.labels" }}
data:
  myvalue: "Hello World"
  {
    
    {
    
    - range $key, $val := .Values.favorite }}
  {
    
    {
    
     $key }}: {
    
    {
    
     $val | quote }}
  {
    
    {
    
    - end }}
    • When the template engine reads the file, it stores the reference to mychart.labels until template "mychart.labels" is called, and then renders the template line by line, so the result looks like this:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: running-panda-configmap
  labels:
    generator: helm
    date: 2022-09-04
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
    • By convention, Helm charts place these templates in a partial file, typically _helpers.tpl, and move this method there:
{
    
    {
    
    /* Generate basic labels */}}
{
    
    {
    
    - define "mychart.labels" }}
  labels:
    generator: helm
    date: {
    
    {
    
     now | htmlDate }}
{
    
    {
    
    - end }}
  • Set template scope:
    • In the template defined above, no objects are used, only methods are used. Modify the defined template to include the chart name and version number:
{
    
    {
    
    /* Generate basic labels */}}
{
    
    {
    
    - define "mychart.labels" }}
  labels:
    generator: helm
    date: {
    
    {
    
     now | htmlDate }}
    chart: {
    
    {
    
     .Chart.Name }}
    version: {
    
    {
    
     .Chart.Version }}
{
    
    {
    
    - end }}
  • include method:
    • Suppose a simple template is defined as follows:
{
    
    {
    
    - define "mychart.app" -}}
app_name: {
    
    {
    
     .Chart.Name }}
app_version: "{
    
    { .Chart.Version }}"
{
    
    {
    
    - end -}}
    • Now suppose you want to insert this into the labels: and data: sections of your template:
apiVersion: v1
kind: ConfigMap
metadata:
  name: {
    
    {
    
     .Release.Name }}-configmap
  labels:
    {
    
    {
    
     template "mychart.app" . }}
data:
  myvalue: "Hello World"
  {
    
    {
    
    - range $key, $val := .Values.favorite }}
  {
    
    {
    
     $key }}: {
    
    {
    
     $val | quote }}
  {
    
    {
    
    - end }}
{
    
    {
    
     template "mychart.app" . }}
    • If I render this, I get the following error:
$ helm install --dry-run measly-whippet ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [ValidationError(ConfigMap): unknown field "app_name" in io.k8s.api.core.v1.ConfigMap, ValidationError(ConfigMap): unknown field "app_version" in io.k8s.api.core.v1.ConfigMap]
    • To see what is rendered, re-execute with the --disable-openapi-validation parameter: helm install --dry-run --disable-openapi-validation measly-whippet ./mychart. Input is not what you want:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: measly-whippet-configmap
  labels:
    app_name: mychart
app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
app_name: mychart
app_version: "0.1.0"
    • Note that the indentation of app_version in both places is wrong, why is this? Because the text in the replaced template is left-aligned, and since template is a behavior, not a method, the output of the template call cannot be passed to other methods, and the data is simply inserted line by line. To deal with this problem, Helm provides an include, which can import the template content into the current pipeline, and then pass it to other methods in the pipeline. The mychart.app template is properly indented using indent as follows:
apiVersion: v1
kind: ConfigMap
metadata:
  name: {
    
    {
    
     .Release.Name }}-configmap
  labels:
{
    
    {
    
     include "mychart.app" . | indent 4 }}
data:
  myvalue: "Hello World"
  {
    
    {
    
    - range $key, $val := .Values.favorite }}
  {
    
    {
    
     $key }}: {
    
    {
    
     $val | quote }}
  {
    
    {
    
    - end }}
{
    
    {
    
     include "mychart.app" . | indent 2 }}
    • Every part of the generated YAML is now properly indented:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-mole-configmap
  labels:
    app_name: mychart
    app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
  app_name: mychart
  app_version: "0.1.0"
    • include Compared to using template, using include in helm is considered a better way just to better handle the output format of YAML documents.

⑭ NOTES.txt file

  • At the end of the helm install or helm upgrade command, Helm will print useful information to the user, which can be highly customized using templates. To add installation notes to a chart, simply create a templates/NOTES.txt file, which is plain text but is treated like a template, and all normal template functions and objects are available.
  • Create a simple NOTES.txt file:
Thank you for installing {
    
    {
    
     .Chart.Name }}.

Your release is named {
    
    {
    
     .Release.Name }}.

To learn more about the release, try:

  $ helm status {
    
    {
    
     .Release.Name }}
  $ helm get all {
    
    {
    
     .Release.Name }}
  • If you execute helm install rude-cardinal ./mychart you will see at the bottom:
RESOURCES:
==> v1/Secret
NAME                   TYPE      DATA      AGE
rude-cardinal-secret   Opaque    1         0s

==> v1/ConfigMap
NAME                      DATA      AGE
rude-cardinal-configmap   3         0s


NOTES:
Thank you for installing mychart.

Your release is named rude-cardinal.

To learn more about the release, try:

  $ helm status rude-cardinal
  $ helm get all rude-cardinal
  • Using NOTES.txt this way is a good way to provide users with detailed information on how to use a newly installed chart. Although not required, it is strongly recommended to create a NOTES.txt file.

⑮ Template debugging

  • Debugging templates can be tricky, as rendered templates are sent to the Kubernetes API server, which may reject the YAML file for reasons other than formatting. The following commands are helpful for debugging:
    • helm lint is the tool of choice for verifying that charts follow best practices;
    • helm install --dry-run --debug or helm template --debug: This is a great way to have the server render a template and return the resulting manifest file;
    • helm get manifest: This is a great way to view the templates installed on the server.
  • When parsing a YAML file fails, but you want to know what was generated, an easy way to retrieve the YAML is to comment out the offending part of the template, then re-run helm install --dry-run --debug:
apiVersion: v2
# some: problem section
# {
    
    {
    
     .Values.foo | quote }}
  • The above will be rendered and return the full comment:
apiVersion: v2
# some: problem section
#  "bar"

Guess you like

Origin blog.csdn.net/Forever_wj/article/details/131324055