helm 包管理器

 

 

前言:

heml 是k8s中的一个包管理器

官方文档:https://helm.sh/zh/docs/chart_template_guide/getting_started/

 

 Helm chart的结构如下:

mychart/
  Chart.yaml
  values.yaml
  charts/
  templates/
  ...

templates/     目录包括了模板文件。当Helm评估chart时,会通过模板渲染引擎将所有文件发送到templates/目录中。 然后收集模板的结果并发送给Kubernetes。

values.yaml 文件也导入到了模板。这个文件包含了chart的 默认值 。这些值会在用户执行helm install 或 helm upgrade时被覆盖。

Chart.yaml   文件包含了该chart的描述。你可以从模板中访问它。

charts/目录 可以 包含其他的chart(称之为 子chart)。 指南稍后我们会看到当涉及模板渲染时这些是如何工作的。

 

看看 mychart/templates/ 目录

  • NOTES.txt: chart的"帮助文本"。这会在你的用户执行helm install时展示给他们。
  • deployment.yaml: 创建Kubernetes 工作负载的基本清单
  • service.yaml: 为你的工作负载创建一个 service终端基本清单。
  • _helpers.tpl: 放置可以通过chart复用的模板辅助对象

然后我们要做的是... 把它们全部删掉 这样我们就可以从头开始学习我们的教程。我们在开始时会创造自己的NOTES.txt_helpers.tpl

 

 

基本操作

//创建一个chart包模板
helm create xxx(chart包名)

//安装
helm install xxx -n namespace

//卸载
helm uninstall xxx -n namespace

//预填充变量/参数
helm install xxx -n namespace --dry-run

 

查看安装

//安装资源, 可以立即看到模板命令的结果:
helm install clunky-serval ./mychart
NAME: full-coral
LAST DEPLOYED: Tue Nov  1 17:36:01 2016
NAMESPACE: default
STATUS: DEPLOYED
REVISION: 1
TEST SUITE: None
//查看生成的完整的YAML
helm get manifest clunky-serval
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "Hello World"

当你想测试模板渲染的内容但又不想安装任何实际应用时,可以使用helm install --debug --dry-run goodly-guppy ./mychart。这样不会安装应用(chart)到你的kubenetes集群中,只会渲染模板内容到控制台(用于测试)。渲染后的模板如下:

$ helm install --debug --dry-run goodly-guppy ./mychart
install.go:149: [debug] Original chart version: ""
install.go:166: [debug] CHART PATH: /Users/ninja/mychart

NAME: goodly-guppy
LAST DEPLOYED: Thu Dec 26 17:24:13 2019
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
fullnameOverride: ""
image:
  pullPolicy: IfNotPresent
  repository: nginx
imagePullSecrets: []
ingress:
  annotations: {}
  enabled: false
  hosts:
  - host: chart-example.local
    paths: []
  tls: []
nameOverride: ""
nodeSelector: {}
podSecurityContext: {}
replicaCount: 1
resources: {}
securityContext: {}
service:
  port: 80
  type: ClusterIP
serviceAccount:
  create: true
  name: null
tolerations: []

HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: goodly-guppy-configmap
data:
  myvalue: "Hello World"

使用--dry-run会让你变得更容易测试,但不能保证Kubernetes会接受你生成的模板。 最好不要仅仅因为--dry-run可以正常运行就觉得chart可以安装。

 

内置对象

Release: Release对象描述了版本发布本身。包含了以下对象:

Values: Values对象是从values.yaml文件和用户提供的文件传进模板的。默认为空

Chart: Chart.yaml文件内容。 Chart.yaml里的所有数据在这里都可以可访问的。比如 {{ .Chart.Name }}-{{ .Chart.Version }} 会打印出 mychart-0.1.0

Files: 在chart中提供访问所有的非特殊文件的对象。你不能使用它访问Template对象,只能访问其他文件。 请查看这个 文件访问 部分了解更多信息

Capabilities: 提供关于Kubernetes集群支持功能的信息

Template: 包含当前被执行的当前模板信息

内置的值都是以大写字母开始。 这是符合Go的命名惯例。当你创建自己的名称时,可以按照团队约定自由设置。 就像很多你在 Artifact Hub 中看到的chart,其团队选择使用首字母小写将本地名称与内置对象区分开,本指南中我们也遵循该惯例。

 

详情请前往官网内置对象 

 

Values

在chart中引用方式: {{ .Values.my_property }}  (通过 . [点]来表达引用内置对象)

内容来自于多个位置:

  • chart中的values.yaml文件
  • 如果是子chart,就是父chart中的values.yaml文件
  • 使用-f参数(helm install -f myvals.yaml ./mychart)传递到 helm install 或 helm upgrade的values文件
  • 使用--set (比如helm install --set foo=bar ./mychart)传递的单个参数

 

以上列表有明确顺序:

默认使用values.yaml,可以被父chart的values.yaml覆盖,继而被用户提供values文件覆盖, 最后会被--set参数覆盖,优先级为values.yaml最低,--set参数最高。

命令行多次使用 -f 指定文件,从左至右,优先级依次提高, 优先使用最后一个-f 指定文件

官网使用示例: https://helm.sh/zh/docs/chart_template_guide/values_files/

 

values.yaml:

favorite:
  drink: coffee
  food: pizza

quote函数: 把字符串用双引号括起来

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

执行后结果:

apiVersion: v1
kind: ConfigMap
metadata:
  name: trendsetting-p-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

 

Helm 有超过60个可用函数。其中有些通过 Go模板语言本身定义。其他大部分都是 Sprig 模板库。我们可以在示例看到其中很多函数。

 

管道

模板语言其中一个强大功能是 管道 概念。借鉴UNIX中的概念,管道符是将一系列的模板语言紧凑地将多个流式处理结果合并的工具。管道符是按顺序完成一系列任务的方式(白话文: 和linux 中的 管道 |  用法和作用都一样)

示例:

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

倒置命令是模板中的常见做法。可以经常看到 .val | quote 而不是 quote .val。实际上两种操作都是可以的。

结果:

apiVersion: v1
kind: ConfigMap
metadata:
  name: trendsetting-p-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"

 

default

模板中频繁使用的一个函数是default: default DEFAULT_VALUE GIVEN_VALUE。 这个函数允许你在模板中指定一个默认值,以防这个值被忽略

示例:

values.yaml中移除设置:

favorite:
  #drink: coffee
  food: pizza

重写chart:

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

重新运行 helm install --dry-run --debug fair-worm ./mychart 会生成如下内容:

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

在实际的chart中,所有的静态默认值应该设置在 values.yaml 文件中,且不应该重复使用 default 命令 (否则会出现冗余)。然而这个default 命令很适合计算值,其不能声明在values.yaml文件中,比如:

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

 

注意: {{ default x y }}  与 {{  y | default x }} 是相同的 

 

lookup 函数

lookup 函数可以用于在运行的集群中 查找 资源。lookup函数简述为查找 apiVersion, kind, namespace,name

资源或者资源列表:

 

 name 和 namespace 都是选填的,且可以传空字符串("")作为空。

以下是可能的参数组合:

 

 

lookup返回一个对象,它会返回一个字典。这个字典可以进一步被引导以获取特定值。

下面的例子将返回mynamespace对象的annotations属性:

(lookup "v1" "Namespace" "" "mynamespace").metadata.annotations

lookup返回一个对象列表时,可以通过items字段访问对象列表:

{{ range $index, $service := (lookup "v1" "Service" "mynamespace" "").items }}
    {{/* do something with each service */}}
{{ end }}

当对象未找到时,会返回空值。可以用来检测对象是否存在。

lookup函数使用Helm已有的Kubernetes连接配置查询Kubernetes。当与调用API服务交互时返回了错误 (比如缺少资源访问的权限),helm 的模板操作会失败。

请记住,

Helm在helm template或者helm install|upgrade|delete|rollback --dry-run时, 不应该请求Kubernetes API服务。由此,lookup函数在该案例中会返回空列表(即字典)。

 

运算符也是函数

对于模板来说,运算符(eqneltgtandor等等) 都是作为函数来实现的。 在管道符中,操作可以按照圆括号分组。

现在我们可以从函数和管道符返回到条件控制流,循环和范围修饰符。

 

模板函数

Helm 包含了很多可以在模板中利用的模板函数:

官网: https://helm.sh/zh/docs/chart_template_guide/function_list/

 

一功能分类目录:

 

 

tpl 救援功能

Helm 提供了一种简单的模板语言, 允许我们轻松引用在“values.yaml” 中定义的配置值。

 

引用值字符串类型

使用示例: 这是一个提供 API 端点 URL 列表的示例 “values.yaml” 文件

apiOneUrl:http://example.com/apiOne/v0
apiTwoUrl:http://example.com/apiTwo/v3
apiThreeUrl:http://example.com/apiThree/v7

 

您可能会发现所有配置字段都包含字符串“http://example.com/”。如果我们能提供如下的“值文件”就好了:

baseUrl: http://example.com

apiOneUrl: "{{ .Values.baseUrl }}/apiOne/v0"
apiTwoUrl: "{{ .Values.baseUrl }}/apiTwo/v3"
apiThreeUrl: "{{ .Values.baseUrl }}/apiThree/v7

 

它不仅会减少冗余并节省大量打字,而且还会使将来更新配置值变得更加容易。但是,

由于 Helm 的模板引擎不会处理 “values.yaml”, 因此如果您尝试在模板中引用 .Values.apiOneUrl, 您将只会得到原始字符串

{{ .Values.baseUrl }}/apiOne/v0

 

此时, 就需要我们 tpl 函数上场了该函数需要 2 个参数:

  • 第一个参数是要处理的模板字符串。

  • 最后一个参数是模板字符串处理时要使用的上下文数据。我们通常可以传递当前上下文数据 .(点) 以使所有配置值在模板处理期间可用。

 

在模板中尝试以下操作:

MyApiOneUrl: {{ tpl .Values.apiOneUrl . }}

 

.Values.apiOneUrl 的值 "{{ .Values.baseUrl }}/apiOne/v0"将被转换为"http://example.com//apiOne/v0”并且上面的模板将被渲染为:

MyApiOneUrl:http://example.com/apiOne/v0

 

非字符串类型

values.yaml 中如下:

apiUrls:
apiOneUrl: "{{ .Values.baseUrl }}/apiOne/v0"
apiTwoUrl: "{{ .Values.baseUrl }}/apiTwo/v3"
apiThreeUrl: "{{ .Values.baseUrl }}/apiThree/v7"

 

此时,按照上方操作, 你会发现会报错: 错误的值类型;预期的字符串........

由于tpl函数需要一个模板字符串作为第一个参数,因此错误消息确实有意义。

 

解决:  在传递给tpl函数之前,使用toYaml函数将非字符串类型值(在本例中为“map”)转换为“YAML”字符串:

{{- tpl (.Values.apiUrls | toYaml) .context }}

结果:

apiOneUrl**:http://example.com/apiOne/v0
apiTwoUrl:http://example.com/apiTwo/v3
apiThreeUrl:http://example.com/apiThree/v7

 

完美的解决方案:

{{/*
Renders a value that contains template.
Usage:
{{ include "helm.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }}
*/}}
{{- define "helm.tplvalues.render" -}}
    {{- if typeIs "string" .value }}
        {{- tpl .value .context }}
    {{- else }}
        {{- tpl (.value | toYaml) .context }}
    {{- end }}
{{- end -}}

 

在模板中使用,您可以使用include函数调用“命名模板”:

MyApiOneUrl: {{ include "helm.tplvalues.render" ( dict "value" .Values.apiOneUrl "context" .) }}

 

你想省去定义自己的 “命名模板” 的工作量, 还可以使用 Bitnami 的 “通用”库图表 common 。要使用它,只需将以下依赖项添加到您的Chart.yaml

逻辑调用包含的 “命名模板”:

MyApiOneUrl: {{ include "common.tplvalues.render" ( dict "value" .Values.apiOneUrl "context" .) }}

 

流程控制(if/else)

控制结构(在模板语言中称为"actions")提供给你和模板作者控制模板迭代流的能力。 Helm的模板语言提供了以下控制结构:

  • if/else, 用来创建条件语句
  • with, 用来指定范围
  • range, 提供"for each"类型的循环

 

if/else

基本的条件结构看起来像这样:

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

注意我们讨论的是 管道 (判断条件)而不是值。这样做的原因是要清楚地说明控制结构可以执行整个管道,而不仅仅是计算一个值。

默认false的值:

如果是以下值时,管道会被设置为 false

  • 布尔false
  • 数字0
  • 空字符串
  • nil (空或null)
  • 空集合(mapslicetupledictarray)

在所有其他条件下,条件都为true。

示例:

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 }}

由于我们在最后一个例子中注释了drink: coffee,输出中就不会包含mug: "true"标识。 但如果将这行添加到values.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"

 

控制空格

格式化之前的例子,使其更易于阅读:

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 }}

看起来没问题。但是如果通过模板引擎运行时,我们将得到一个不幸的结果:

$ helm install --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/helm.sh/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

发生了啥?因为空格导致生成了错误的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"

mug的缩进是不对的。取消缩进重新执行一下:

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 }}

这个就得到了合法的YAML,但是看起来还是有点滑稽:

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

  mug: "true"

注意在YAML中有一个空行,为什么?当模板引擎运行时,它 移除了 {{ 和 }} 里面的内容,但是留下的空白完全保持原样。

 

YAML认为空白是有意义的,因此管理空白变得很重要。幸运的是,Helm模板有些工具可以处理此类问题。

首先,模板声明的大括号语法可以通过特殊的字符修改,并通知模板引擎取消空白。{{- (包括添加的横杠和空格)表示向左删除空白, 而 -}}表示右边的空格应该被去掉。 一定注意空格就是换行

要确保-和其他命令之间有一个空格。 {{- 3 }} 表示“删除左边空格并打印3”,而{{-3 }}表示“打印-3”。

使用这个语法,我们就可修改我们的模板,去掉新加的空白行:

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 }}

记住这一点,我们可以通过Helm运行模板并查看结果:

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

 

常出现的错误

要注意这个删除字符的更改,很容易意外地出现情况:

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

这样会变成food: "PIZZA"mug:"true",因为这把两边的新行都删除了。 

模板中的空白控制,请查看 官方Go模板文档

 

示例:

  aa: "{{ 23 }} < {{ 45 }}"
  aa: "{{23 }} < {{45 }}"
  aa: "{{23}} < {{45}}"
  aa: "{{23 }} < {{ 45}}"
  aa: "{{23 -}} < {{- 45 }}"

 

执行后结果:

  aa: "23 < 45"
  aa: "23 < 45"
  aa: "23 < 45"
  aa: "23 < 45"
  aa: "23<45"

 

最终,有时这更容易告诉模板系统如何缩进,而不是试图控制模板指令间的间距。

因此,您有时会发现使用indent方法 ( {{ indent 2 "mug:true" }} ) 会很有用。

 

with 控制

这个用来控制变量范围。回想一下,.是对 当前作用域 的引用。因此 .Values就是告诉模板在当前作用域查找Values对象。

with的语法与if语句类似,结构:

{{ with PIPELINE }}
  # restricted scope
{{ end }}

 

作用域可以被改变。

with允许你为特定对象设定当前作用域(.)。比如,我们已经在使用.Values.favorite。 修改配置映射中的.的作用域指向.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 }}

注意我们从之前的练习中移除了if条件,因为现在不需要了——with后面的块只有在 PIPELINE 的值不为空时才会执行。

注意现在我们可以引用.drink.food了,而不必限定他们。因为with语句设置了.指向.Values.favorite。 .被重置为{{ end }}之后的上一个作用域。

 

这里有个注意事项,在限定的作用域内,无法使用.访问父作用域的对象。这样会报错因为Release.Name不在.限定的作用域内。

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

但是如果对调最后两行就是正常的, 因为在{{ end }}之后作用域被重置了。

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

或者,我们可以使用 从父作用域中访问Release.Name对象。当模板开始执行后 会被映射到根作用域且执行过程中不会更改。 下面这种方式也可以正常工作:

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

 

range (迭代方式)

很多编程语言支持使用for循环,foreach循环,或者类似的方法机制。在Helm的模板语言中,在一个集合中迭代的方式是使用range操作符。

 

开始之前,我们先在values.yaml文件添加一个披萨的配料列表:

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

现在我们有了一个pizzaToppings列表(模板中称为切片)。修改模板把这个列表打印到配置映射中:

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 }}    

我可以使用$从父作用域访问Values.pizzaToppings列表。当模板开始执行后$会被映射到根作用域, 且执行过程中不会更改。下面这种方式也可以正常工作:

  toppings: |-
    {{- range $.Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}    
  {{- end }}

range方法“涵盖”(迭代)pizzaToppings列表。但现在发生了有意思的事情。 就像with设置了.的作用域,range操作符也做了同样的事。每一次循环,.都会设置为当前的披萨配料。 也就是说,第一次.设置成了mushrooms,第二次迭代设置成了cheese,等等。

 

我们可以直接发送.的值给管道,因此当我们执行{{ . | title | quote }}时,它会发送.title然后发送到quote。 如果执行这个模板,输出是这样的:

# 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"    

toppings: |-行是声明的多行字符串。 所以这个配料列表实际上不是YAML列表, 是个大字符串。为什么要这样做?因为在配置映射data中的数据是由键值对组成,key和value都是简单的字符串。

要理解这个示例,请查看 Kubernetes ConfigMap 文档

 

tuple :

Helm模板的tuple可以很容易实现该功能。在计算机科学中, 元组表示一个有固定大小的类似列表的集合,但可以是任意数据类型。这大致表达了tuple的用法

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

上述模板会生成以下内容:

  sizes: |-
    - small
    - medium
    - large    

除了列表和元组,range可被用于迭代有键值对的集合(像mapdict)。我们会在下一部分介绍模板变量是看到它是如何应用的。

 

变量

函数、管道符、对象和控制结构都可以控制,我们转向很多编程语言中更基本的思想之一:变量。 在模板中,很少被使用。但是我们可以使用变量简化代码,并更好地使用withrange

 

在之前的例子中,我们看到下面的代码会失败:

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

Release.Name 不在with块的限制范围内。解决作用域问题的一种方法是将对象分配给可以不考虑当前作用域而访问的变量。

 

Helm模板中,变量是对另一个对象的命名引用。遵循$name变量的格式且指定了一个特殊的赋值运算符::=。 我们可以使用针对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 }}

注意在with块开始之前,赋值$relname := .Release.Name。 现在在with块中,$relname变量仍会执行版本名称。

 

运行之后会生成以下内容:

# 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

 

变量range循环中特别有用。可以用于类似列表的对象,以捕获索引和值:

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

 

注意先是range,然后是变量,然后是赋值运算符,然后是列表。会将整型索引(从0开始)赋值给$index并将值赋值给$topping。 执行会生成:

 

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

 

对于数据结构有key和value,可以使用range获取key和value。比如,可以通过.Values.favorite进行循环:

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

 

第一次迭代,$key会是drink$val会是coffee,第二次迭代$key会是food$val会是pizza。 运行之后会生成:

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

 

变量一般不是"全局的"。作用域是其声明所在的块。上面我们在模板的顶层赋值了$relname。变量的作用域会是整个模板。 但在最后一个例子中$key$val作用域会在{{ range... }}{{ end }}块内。

 

但有个变量一直是全局的 - $ - 这个变量一直是指向根的上下文。当在一个范围内循环时会很有用,同时你要知道chart的版本名称。

示例:

{{- 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 }}"
    # Value from appVersion in Chart.yaml
    app.kubernetes.io/version: "{{ $.Chart.AppVersion }}"
    app.kubernetes.io/managed-by: "{{ $.Release.Service }}"
type: kubernetes.io/tls
data:
  tls.crt: {{ .certificate }}
  tls.key: {{ .key }}
---
{{- end }}

 

命名模板

三种声明和管理模板的方法:definetemplate,和block。在这部分,我们将使用这三种操作并介绍一种特殊用途的 include方法,类似于template操作。

命名模板时要记住一个重要细节:模板名称是全局的。如果您想声明两个相同名称的模板,哪个最后加载就使用哪个。 因为在子chart中的模板和顶层模板一起编译,命名时要注意 chart特定名称

一个常见的命名惯例是用chart名称作为模板前缀:{{ define "mychart.labels" }}。使用特定chart名称作为前缀可以避免可能因为 两个不同chart使用了相同名称的模板而引起的冲突。

 

局部和 _ 文件

目前为止,我们已经使用了单个文件,且单个文件中包含了单个模板。但Helm的模板语言允许你创建命名的嵌入式模板, 这样就可以在其他位置按名称访问。

 

在编写模板细节之前,文件的命名惯例需要注意:

  • templates/中的大多数文件被视为包含Kubernetes清单
  • NOTES.txt是个例外
  • 命名以下划线(_)开始的文件则假定 没有 包含清单内容。这些文件不会渲染为Kubernetes对象定义,但在其他chart模板中都可用。

 

这些文件用来存储局部和辅助对象,实际上当我们第一次创建mychart时,会看到一个名为_helpers.tpl的文件,这个文件是模板局部的默认位置。

 

用 define 和 template 声明和使用模板

define操作允许我们在模板文件中创建一个命名模板,语法如下:

{{- define "MY.NAME" }}
  # body of template here
{{- end }}

示例:

{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

 

将模板嵌入到了已有的配置映射中,然后使用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 }}

 

当模板引擎读取该文件时,它会存储mychart.labels的引用直到template "mychart.labels"被调用。 然后会按行渲染模板,因此结果类似这样:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: running-panda-configmap
  labels:
    generator: helm
    date: 2016-11-02
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

 

注意:define不会有输出,除非像本示例一样用模板调用它。

 

按照惯例,Helm chart将这些模板放置在局部文件中,一般是_helpers.tpl。把这个方法移到那里:

{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

 

按照惯例define方法会有个简单的文档块({{/* ... */}})来描述要做的事。

尽管这个定义是在_helpers.tpl中,但它仍能访问configmap.yaml

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 }}

 

如上所述,模板名称是全局的

因此,如果两个模板使用相同名字声明,会使用最后出现的那个。由于子chart中的模板和顶层模板一起编译, 最好用 chart特定名称 命名你的模板。

常用的命名规则是用chart的名字作为模板的前缀: {{ define "mychart.labels" }}

 

设置模板范围(传参)

在上面定义的模板中,我们没有使用任何对象,仅仅使用了方法。修改定义好的模板让其包含chart名称和版本号(包含对象调用):
{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
    chart: {{ .Chart.Name }}
    version: {{ .Chart.Version }}
{{- end }}

 

如果渲染这个,会得到以下错误:

$ helm install --dry-run moldy-jaguar ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [unknown object type "nil" in ConfigMap.metadata.labels.chart, unknown object type "nil" in ConfigMap.metadata.labels.version]

要查看渲染了什么,可以用--disable-openapi-validation参数。

重新执行: helm install --dry-run --disable-openapi-validation moldy-jaguar ./mychart。 结果并不是我们想要的:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: moldy-jaguar-configmap
  labels:
    generator: helm
    date: 2021-03-06
    chart:
    version:

 

名字和版本号怎么了?没有出现在我们定义的模板中。当一个(使用define创建的)命名模板被渲染时,会接收被template调用传入的内容。 在我们的示例中,包含模板如下

{{- template "mychart.labels" }}

 

没有内容传入,所以模板中无法用.访问任何内容。但这个很容易解决,只需要传递一个范围给模板:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" . }}

 

注意这个在template调用末尾传入的.,我们可以简单传入.Values.Values.favorite或其他需要的范围。但一定要是顶层范围。

 

现在我们可以用helm install --dry-run --debug plinking-anaco ./mychart执行模板,然后得到:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: plinking-anaco-configmap
  labels:
    generator: helm
    date: 2021-03-06
    chart: mychart
    version: 0.1.0

 

现在{{ .Chart.Name }}解析为mychart{{ .Chart.Version }}解析为0.1.0

 

include 方法

相较于使用template,在helm中使用include被认为是更好的方式 只是为了更好地处理YAML文档的输出格式

 

假设定义了一个简单模板如下:

{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}

 

现在假设我想把这个插入到模板的labels:部分和data:部分:

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" . }}

 

如果渲染这个,会得到以下错误:

$ 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]

 

要查看渲染了什么,可以用--disable-openapi-validation参数

重新执行: helm install --dry-run --disable-openapi-validation measly-whippet ./mychart。 输入不是我们想要的:

# 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"

 

注意两处的app_version缩进都不对,为啥?因为被替换的模板中文本是左对齐的。由于template是一个行为,不是方法,无法将 template调用的输出传给其他方法,数据只是简单地按行插入。

为了处理这个问题,Helm提供了一个template的可选项,可以将模板内容导入当前管道,然后传递给管道中的其他方法。

 

下面这个示例,使用indent正确地缩进了mychart.app模板:

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 }}

 

现在生成的YAML每一部分都可以正确缩进了:

# 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"

 

在模板内访问外部文件

有时想导入的是不是模板的文件并注入其内容,而无需通过模板渲染发送内容。

Helm 提供了通过.Files对象访问文件的方法。

 

不过,在我们使用模板示例之前,有些事情需要注意:

  • 可以添加额外的文件到chart中。虽然这些文件会被绑定。但是要小心,由于Kubernetes对象的限制,Chart必须小于1M。
  • 通常处于安全考虑,一些文件无法通过.Files对象访问:
    • 无法访问templates/中的文件
    • 无法访问使用.helmignore排除的文件
    • helm应用 subchart之外的文件,包括父级中的,不能被访问的
  • Chart不能保留UNIX模式信息,因此当文件涉及到.Files对象时,文件级权限不会影响文件的可用性。

 

基本示例

我们来写一个读取三个文件到配置映射ConfigMap的模板。开始之前,我们会在chart中添加三个文件, 直接放到mychart/目录中。

config1.toml:

message = Hello from config 1

 

config2.toml:

message = This is config 2

 

config3.toml:

message = Goo

 

每个都是简单的TOML文件(类似于windows老式的INI文件)。我们知道这些文件的名称,因此我们使用range功能遍历它们并将它们的内容注入到我们的ConfigMap中。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  {{- $files := .Files }}
  {{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
  {{ . }}: |-
        {{ $files.Get . }}
  {{- end }}

 

我们创建了一个$files变量来引用.Files对象。

我们也使用了tuple方法创建了一个可遍历的文件列表。

然后我们打印每个文件的名字({{ . }}: |-),然后通过{{ $files.Get . }}打印文件内容。

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: quieting-giraf-configmap
data:
  config1.toml: |-
        message = Hello from config 1

  config2.toml: |-
        message = This is config 2

  config3.toml: |-
        message = Goodbye from config 3

 

更多详情请前往官网: Helm Files 对象

 

子 chart 和全局值

chart可以使用依赖,称为 子chart,且有自己的值和模板。

在深入研究代码之前,需要了解一些应用的子chart的重要细节:

  1. 子chart被认为是“独立的”,意味着子chart从来不会显示依赖它的父chart。
  2. 因此,子chart无法访问父chart的值。
  3. 父chart可以覆盖子chart的值。
  4. Helm有一个 全局值 的概念,所有的chart都可以访问。

这些限制不一定都适用于提供标准化辅助功能的:library charts

其中有两个 libraty chart 的示例:

官网: common        bitnami : common 

 

 

创建子 chart

为了做这些练习,我们可以从本指南开始时创建的mychart/开始,并在其中添加一个新的chart。

$ cd mychart/charts
$ helm create mysubchart
Creating mysubchart
$ rm -rf mysubchart/templates/*

 

注意,和以前一样,我们删除了所有的基本模板,然后从头开始,在这个指南中,我们聚焦于模板如何工作,而不是管理依赖。 但 Chart指南提供了更多子chart运行的信息。

 

下一步,为mysubchart创建一个简单的模板和values文件。mychart/charts/mysubchart应该已经有一个values.yaml。 设置如下:

dessert: cake

 

下一步,在mychart/charts/mysubchart/templates/configmap.yaml中创建一个新的配置映射模板:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-cfgmap2
data:
  dessert: {{ .Values.dessert }}

 

因为每个子chart都是 独立的chart,可以单独测试mysubchart

$ helm install --generate-name --dry-run --debug mychart/charts/mysubchart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/helm.sh/helm/_scratch/mychart/charts/mysubchart
NAME:   newbie-elk
TARGET NAMESPACE:   default
CHART:  mysubchart 0.1.0
MANIFEST:
---
# Source: mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: newbie-elk-cfgmap2
data:
  dessert: cake

 

用父 chart 的值来覆盖

原始chart,mychart现在是mysubchart的 。这种关系是基于mysubchartmychart/charts中这一事实。

因为mychart是父级,可以在mychart指定配置并将配置推送到mysubchart。比如可以修改mychart/values.yaml如下:

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

mysubchart:
  dessert: ice cream

 

注意最后两行,在mysubchart中的所有指令会被发送到mysubchartchart中。因此如果运行helm install --dry-run --debug mychart,会看到一项mysubchart的配置:

# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: unhinged-bee-cfgmap2
data:
  dessert: ice cream

 

现在,子chart的值已经被顶层的值覆盖了。

 

这里需要注意个重要细节。我们不会改变mychart/charts/mysubchart/templates/configmap.yaml模板到 .Values.mysubchart.dessert的指向。

从模板的角度来看,值依然是在.Values.dessert。当模板引擎传递值时,会设置范围。 因此对于mysubchart模板,.Values中只提供专门用于mysubchart的值。

 

但是有时确实希望某些值对所有模板都可用。这是使用全局chart值完成的。

 

全局值 chart

全局值是使用完全一样的名字在所有的chart及子chart中都能访问的值。全局变量需要显示声明。不能将现有的非全局值作为全局值使用。

这些值数据类型有个保留部分叫Values.global,可以用来设置全局值。在mychart/values.yaml文件中设置一个值如下:

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

mysubchart:
  dessert: ice cream

global:
  salad: caesar

 

因为全局的工作方式,mychart/templates/configmap.yamlmysubchart/templates/configmap.yaml 应该都能以{{ .Values.global.salad }}进行访问。

 

mychart/templates/configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  salad: {{ .Values.global.salad }}

 

mysubchart/templates/configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-cfgmap2
data:
  dessert: {{ .Values.dessert }}
  salad: {{ .Values.global.salad }}

 

现在如果预安装,两个输出会看到相同的值:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: silly-snake-configmap
data:
  salad: caesar

---
# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: silly-snake-cfgmap2
data:
  dessert: ice cream
  salad: caesar

 

全局值在类似这样传递信息时很有用,不过要确保使用全局值配置正确的模板,确实需要一些计划。

 

避免用 块(block) 

Go 模板语言提供了一个 block 关键字允许开发者提供一个稍后会被重写的默认实现。在Helm chart中, 块并不是用于覆盖的最好工具,因为如果提供了同一个块的多个实现,无法预测哪个会被选定。

建议改为使用include

 

NOTES.txt文件

NOTES.txt 为chart用户提供说明的Helm工具。在helm install 或 helm upgrade命令的最后,Helm会打印出对用户有用的信息。 使用模板可以高度自定义这部分信息。

要在chart添加安装说明,只需创建templates/NOTES.txt文件即可。该文件是纯文本,但会像模板一样处理, 所有正常的模板函数和对象都是可用的。

 

让我们创建一个简单的NOTES.txt文件:

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 }}

 

现在如果我们执行helm install rude-cardinal ./mychart 会在底部看到:

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

 

使用NOTES.txt这种方式是给用户提供关于如何使用新安装的chart细节信息的好方法。尽管并不是必需的,强烈建议创建一个NOTES.txt文件。

 

.helmignore 文件

.helmignore 文件用来指定你不想包含在你的helm chart中的文件。

如果该文件存在,helm package 命令会在打包应用时忽略所有在.helmignore文件中匹配的文件。

这有助于避免不需要的或敏感文件及目录添加到你的helm chart中。

 

.helmignore 文件支持Unix shell的全局匹配,相对路径匹配,以及反向匹配(以!作为前缀)。每行只考虑一种模式。

这里是一个.helmignore文件示例:

# comment

# Match any file or path named .helmignore
.helmignore

# Match any file or path named .git
.git

# Match any text file
*.txt

# Match only directories named mydir
mydir/

# Match only text files in the top-level directory
/*.txt

# Match only the file foo.txt in the top-level directory
/foo.txt

# Match any file named ab.txt, ac.txt, or ad.txt
a[b-d].txt

# Match any file under subdir matching temp*
*/temp*

*/*/temp*
temp?

 

一些值得注意的和.gitignore不同之处:

  • 不支持'**'语法。
  • globbing库是Go的 'filepath.Match',不是fnmatch(3)
  • 末尾空格总会被忽略(不支持转义序列)
  • 不支持'!'作为特殊的引导序列
  • 默认不会排除自身,需要显式添加 .helmignore

 

调式模板

调试模板可能很棘手,因为渲染后的模板发送给了Kubernetes API server,可能会以格式化以外的原因拒绝YAML文件。

以下命令有助于调试:

  • helm lint 是验证chart是否遵循最佳实践的首选工具。
  • helm template --debug 在本地测试渲染chart模板。
  • helm install --dry-run --debug:我们已经看到过这个技巧了,这是让服务器渲染模板的好方法,然后返回生成的清单文件。
  • helm get manifest: 这是查看安装在服务器上的模板的好方法。

 

当你的YAML文件解析失败,但你想知道生成了什么,检索YAML一个简单的方式是注释掉模板中有问题的部分,

然后重新运行 helm install --dry-run --debug

apiVersion: v2
# some: problem section
# {{ .Values.foo | quote }}

 

以上内容会被渲染同时返回完整的注释:

apiVersion: v2
# some: problem section
#  "bar"

 

这样就提供了一种快速查看没有被YAML错误解析阻塞的生成内容的方式。

 

博主常用:

1、另一种方式是通过 --disable-openapi-validation 参数。 模板渲染失败, 要查看渲染了什么,可以用 --disable-openapi-validation 参数

helm install --dry-run --disable-openapi-validation moldy-jaguar ./mychart

 

2、 本地测试渲染chart模板, 不会向服务器端API发送请求 helm template

helm template -f Helm-test/values-sit.yaml -f Helm-test/values-dev.yaml  my-helm Helm-test/

 

3、渲染单个template文件:

1、cd mychart/(--show-only 的模板参数必须 templates 开头)
2、helm template --show-only templates/test.yaml test2 ../mychart/

 

 常见报错记录

推荐:在执行前先执行 helm lint 验证chart是否合法

1. helm install -f mychart/values-1.yaml --disable-openapi-validation --debug --dry-run mychart/
Error: INSTALLATION FAILED: must either provide a name or specify --generate-name

原因: 执行时 没有为取chart 的名称
解决:

helm install -f mychart/values-1.yaml --disable-openapi-validation --generate-name --debug --dry-run mychart/

helm install -f mychart/values-1.yaml --disable-openapi-validation --debug --dry-run test-chart mychart/

 

2、error converting YAML to JSON: yaml: line 21: did not find expected key

原因 1:这个问题是由于缩进而发生的.
原因 2:引用时名称错误(如:多一个空格...)

 

个人经验:

安装:

helm install xxx ./chart

 

测试模板渲染的内容(不实际安装)

helm install --debug --dry-run xxx ./chart

 

指定values.yaml 文件

helm install -f mychart/values-1.yaml --debug --dry-run xxx ./chart

 

模板渲染失败, 要查看渲染了什么,可以用--disable-openapi-validation参数

helm install --dry-run --disable-openapi-validation moldy-jaguar ./mychart

 

渲染单个template文件:

1、cd mychart/(--show-only 的模板参数必须 templates 开头)
2、helm template --show-only templates/test.yaml test2 ../mychart/

 

断言:

required 方法 的例子声明了一个.Values.who需要的条目,并且当这个条目不存在时会打印错误信息:

value: {{ required "A valid .Values.who entry required!" .Values.who }}

 

NOTES.txt 文件在template 下:

NOTES.txt这种方式是给用户提供关于如何使用新安装的chart细节信息的好方法。不是必需的,建议创建一个NOTES.txt文件。

$ : 模板开始执行后$会被映射到根作用域,且执行过程中不会更改

|- : 标识是指多行字符串。(k8s 中的 结构类型:lists)

--- :是分隔符,是可选的,在单一文件中,可用连续三个连字号---区分多个文件。

yaml 中 data: 对应的value 必须是string 类型

 

强制推断特定类型:

age: !!str 21
port: !!int "80"

 

!!str 告诉解释器 age 是一个字符串,即使它看起来像是整型。即使 port 被引号括起来,也会被视为 int。

 

posted @ 2023-02-08 17:33  萤huo虫  阅读(405)  评论(0编辑  收藏  举报