1. 开始
  1. Charts
  2. 简单示例
  3. 第一个模板
  4. 赋值示例
  1. 内置对象
  2. Values Files
  1. 删除默认Key
  1. Template Functions And Pipelines
  1. Pipelines
  2. 使用默认方法
  3. OPERATORS ARE FUNCTIONS
  1. 控制流
  1. IF/ELSE
  2. 空白管理
  3. 修改作用域
  4. range操作
  1. 变量
  2. Named Templates
  1. 特殊文件
  2. 使用 define 和 template
  3. SETTING THE SCOPE OF A TEMPLATE
  4. include
  1. 模板中访问文件
  1. BASIC EXAMPLE
  2. PATH HELPERS
  3. GLOB PATTERNS
  4. CONFIGMAP AND SECRETS UTILITY FUNCTIONS
  5. ENCODING
  6. LINES
  1. 创建 NOTES.Txt 文件
  2. Subcharts And Global Values
  1. Creating A Subchart
  2. ADDING VALUES AND A TEMPLATE TO THE SUBCHART
  3. OVERRIDING VALUES OF A CHILD CHART
  4. GLOBAL CHART VALUES
  1. Debugging Templates
  2. Next Steps
  3. 附录: YAML 技巧
  1. Scalars and Collections
  2. Yaml 中的类型
  1. 附录: Go 语言数据类型和模板

开始

1.1 Charts

Charts 的结构如下:

mychart/
  Chart.yaml
  values.yaml
  charts/
  templates/
  ...
  • templates/:这个目录下装的是 k8s 的资源模板文件。
  • values.yaml:这个文件里的是这个 chart 的默认值。
  • Chart.yaml:这个文件里是对这个 chart 的描述。
1.2 简单示例

下面,创建一个叫 mychart 的 chart。

$ helm create mychart
Creating mychart

helm 客户端会自动的为我们创建些文件:

mychart
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── ingress.yaml
│   └── service.yaml
└── values.yaml
  • NOTES.txt
  • deployment.yaml
  • service.yaml
  • _helpers.tpl
3. 第一个模板
rm -rf mychart/templates/*.*

删除掉 Helm 生成的模板文件,我们自己来实现一个模板。

我们的第一个模板是创建一个 ConfigMap。创建一个 mychart/templates/configmap.yaml文件,并写入如下内容:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "Hello World"

注意:Template 目录下的文件没有严格的命名规范。但我们推荐用 .yaml 的后缀表示 YAML 文件,用 .tpl 表示帮助文件。

现在我们来部署它:

$ helm install ./mychart
NAME: full-coral
LAST DEPLOYED: Tue Nov  1 17:36:01 2016
NAMESPACE: default
STATUS: DEPLOYED

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

我们可以来查看下部署信息

$ helm get manifest full-coral

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

helm get manifest 这条命令可以通过 release 名来打印其相关信息。

现在,删除掉刚刚发布的 release:helm delete full-coral

赋值示例

内置对象

  • Release:用来描述 release 本身
  • Release.NAME
  • Release.Time
  • Release.Namespace
  • Release.Service:值总是 Tiller
  • Release.Revision:release 版本号。从 1 开始,每次执行 helm upgrade ,数加 1
  • Release.IsUpgrade:本次操作是否为升级
  • Release.IsInstall:本次操作是否为安装
  • Values:
  • Chart:Chart.yaml里的内容
  • Files:
  • File.Get 通过名字获取文件(.Files.Get config.ini
  • File.getBytes 以字节流的方式获取,在获取类型图片时比较有用
  • Capabilities:
  • Template:

Values Files

前面讲了内置对像 Values,它的值有四个来源:

  • values.yaml 文件
  • 如果这是个子 chart,其父 chart 的 Values.yaml 文件
  • helm installhelm upgrade 时,通过 -f 指定的文件
  • 通过 --set 指定的参数( 例:helm install --set foo=bar ./mychart )

优先级从上到下依次增加,即 --set 最高。

现在让我们来编辑 mychart/values.yaml,删除默认值,只写一个参数:

favoriteDrink:coffee

在模板中使用刚刚写的参数:

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

注意最后一行,我们通过访问 Values 属性的方式 {{ .Values.favoriteDrink}} 来获取 favoriteDrink 值。

来看下渲染的结果:

$ helm install --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
NAME:   geared-marsupi
TARGET NAMESPACE:   default
CHART:  mychart 0.1.0
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: geared-marsupi-configmap
data:
  myvalue: "Hello World"
  drink: coffee

还可以通过 --set 覆盖掉这个值:

helm install --dry-run --debug --set favoriteDrink=slurm ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
NAME:   solid-vulture
TARGET NAMESPACE:   default
CHART:  mychart 0.1.0
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: solid-vulture-configmap
data:
  myvalue: "Hello World"
  drink: slurm

由于 --set 的优先级高于 values.yaml,所有我们的模板最终输出为 drink: slurm

Values 文件还可以包含结构内容。

favorite:
  drink: coffee
  food: pizza

现在我们需要修改下模板:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink }}
  food: {{ .Values.favorite.food }}
1. 删除默认Key

如果你想从默认值中删除一个key,你可以通过传 null 值,这样 Helm 在合并时就会删除这个 key。

举个例子, 名为 Drupal 的 chart 中配置的有存活检测。下面是它的默认设置:

livenessProbe:
  httpGet:
    path: /user/login
    port: http
  initialDelaySeconds: 120

如果你要使用 exec 替换 httpGet,可以通过 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt],Helm 会合并默认值和传进去的值,结果如下:

livenessProbe:
  httpGet:
    path: /user/login
    port: http
  exec:
    command:
    - cat
    - docroot/CHANGELOG.txt
  initialDelaySeconds: 120

这时,k8s 就会出错,因为你定义了两个 liveness handler。要解决这个问题,你可以通过给 livenessProbe.httpGet传个 null 值来删除它:

helm install stable/drupal --set image=my-registry/drupal:0.1.0 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --set livenessProbe.httpGet=null

模板函数和Pipelines

让我们从一个练习开始:当我们往 .Values 里注入一个字符串时,应当用引号将它们括起来,在模板中可以直接使用 quote 函数来实现:

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

模板函数的语法如下:functionName arg1 arg2...。在上面的小例子中, quote .Values.favorite.drink, 使用了 quote 函数并传递了一个参数。

1. Pipelines

通过管道可以在一行里干多件事,我们使用 pipeline 重写上面的例子:

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

通过 pipeline,我们可以链式的调用多个函数:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
2. 使用 Default 函数

这个函数允许你指定一个默认值:

drink: {{ .Values.favorite.drink | default "tea" | quote }}
3. OPERATORS ARE FUNCTIONS

操作符是按照函数的方式实现的,返回一个布尔值。使用 eq, ne, lt, gt, and, or, not时,要将它们放到句子的最前面,后面跟上对应的参数。多个操作符一起使用时,可以用小括号包起来。

{{/* include the body of this if statement when the variable .Values.fooString exists 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 to false and .Values.setVariable was negated with the not function. */}}
{{ if or .Values.anUnsetVariable (not .Values.aSetVariable) }}
   {{ ... }}
{{ end }}

控制流

Helm 的模板语言提供下面几种控制结构:

  • if / else
  • with
  • range 提供类型 for each 的循环

另外,还提供了几种方式来声明和使用命名模板:

  • define
  • template
  • block

这节只讨论 if, with, 和 range。其它的会在之后的 “Named Templates” 那节介绍。

1. IF / ELSE

基本结构如下:

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

注意这里我们用 pipelines 面不是 values,是为了表明这个控制结构是可以运行完全的 pipeline 的,而仅仅只能放个值。

下面情况其值会被认为是 false:

  1. bool 型的 false
  2. 数字 0
  3. 空字符串
  4. nil
  5. 空集合(map, slice, tuple, dict, array)

让我们来修改下 ConfigMap。当 drinkcoffee时,增加一个设置:

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

注意, .Values.favorite.drink 必须被定义,否则当它和 coffee作比较时会报错。最后的输出就变成:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: true
2. 空白管理

空白的使用在模板中是受限制的。下面把之前的代码格式修改下,使它更易阅读:

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

这就是因为空格导致的:

# 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 中有行是空的。

{{ 表示去掉左边的空格,}}表示去掉右边的空格。

3. 修改作用域

通过 . 可以引用当前的作用域。 .Values 告诉模板在当前作用域上查找 Values

可以通过 with 来调整作用域

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

with 允许你把当前作用域(.)指到一个特定的对象上。举个例子,将 . 指到 .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 }}

警告,在这个特定的作用域中,你不能够访问其父领域的对象。下面的例子,访问会出错:

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

Helm 可以通过 range 操作符来迭代集合。

values.yaml 中增加列表:

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

现在修改下 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 }}

with一样,range也可以设置作用域,所以在这里,. 表示的是 pizzaToppings这个作用域。我们能把 . 直接传递给管道使用 {{ . | 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: |- 表示这是一个多行的字符串。

变量

在模板里,变量较少被使用。但通过使用变量,可以使 withrange 得到更好的使用。

这是之前的一个例子,会报错:

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

在 Helm 模板里,一个变量是对一个对象的引用。格式是 $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 仍然指向 .Release.name

Named Templates

本节我们学习如何定义一个命名模板,并在别处使用它。

你要注意的是:模板的名字是全局性的。当你定义了两个相同名字的模板时,最后加载的那个将被使用。

推荐的做法是在定义模板名字时,加上 chart:{{define "mychart.labels"}}

1. 特殊文件

在开始具体写模板前,有些命令惯例值得提醒下:

  • template/中的大部分文件可以认为是 k8s 的资源
  • NOTES.txt文件除外
  • 有下划线的除外
2. 使用 definetemplate

define 允许我们在模板文件中创建一个命名模板:

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

举个例子

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

现在我们把它嵌套到之前的 ConfigMap 上,然后通过 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 }}

最终,经过渲染,文件会变成:

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

习惯上 Helm 把这些模板放在一个特殊的文件里,通常是 _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 }}

再次提醒下,命名模板是全局性的。如果定义了两个相同名字的模板,则后面定义的那个生效。

3. 给模板设定作用域
4. include 函数

如下,我们定义了个模板:

{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}"
{{- end -}}

现在,我们想把它同时入到 labelsdata

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

但是结果不是我们预期的:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: measly-whippet-configmap
  labels:
    app_name: mychart
app_version: "0.1.0+1478129847"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
  app_name: mychart
app_version: "0.1.0+1478129847"

可以看到 app_version 的缩进是不对的。

通过使用 nindent 来实现正确的缩进:

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

模板中访问文件

在之前的章节中我们学会了创建和访问命令模板,让我们可以方便的在一个模板中导入另一个模板。但是有些时我们要导入一个文件而不需要经过模板的渲染。

Helm 通过 .Files 对象提供了访问文件的功能。在开始例子之前,有些点需要我们注意:

  • 在 Helm 中可以增加额处文件,它们也会被发给 Tiller。但要注意,由于 k8s 存储大小的限制, Charts 的大小不能超过 1M。
  • 因为安全的原因,有些文件是不能被 .Files 访问到的
  • templates/ 中的文件不能被访问
  • .helmignore 中的文件不能被访问
1. Basic Example

我们直接在 mychar/ 下创建三个文件
config1.toml

message = Hello from config 1

config2.toml

message = Hello from config 2

config3.toml

message = Hello from config 3

我们知道它位的名字,所以我们可以使用 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 }}

通过运行它,我们能够得到:

# 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
2. Path Helpers
3. Glob Patterns
5. Encoding

使用base-64加密:

apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-secret
type: Opaque
data:
  token: |-
    {{ .Files.Get "config1.toml" | b64enc }}
# Source: mychart/templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: lucky-turkey-secret
type: Opaque
data:
  token: |-
    bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
6. Lines

创建 NOTES.Txt 文件

这节,来介绍下如何给你的 chart 增加说明。在 helm install 或者 helm upgrade 的最后,会打印一些对用户有用的信息,而这些信息是可以高度定制化的。

创建 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 {{ .Release.Name }}

现在运行 helm install 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 rude-cardinal

NOTES.txt 不是必须的,但强烈推荐你创建一个。

Subcharts and Global Values

之前我们一直在使用一个 chart,但 chart 之间是可以存在依赖关系的,称之为 subchart,它们可以有自己的值和模板。这节我们将会创建一个 subchart,来看看它是如何获取值的。

在开始前,有几个关于 subchart 的知识点需要大家知道:

  1. subchart 是可以独立部署的,故而它不能明确的依赖 parent chart。
  2. subchart 不能获取 parent chart 的值。
  3. parent chart 可以覆盖 subchart 的值。
  4. Helm 有一个 global values 的概念,可以被所有 chart 获取。
1. Creating A Subchart
$ cd mychart/charts
$ helm create mysubchart
Creating mysubchart
$ rm -rf mysubchart/templates/*.*
2. ADDING VALUES AND A TEMPLATE TO THE SUBCHART

Debugging Templates

几种 debug 的方式:

  • helm lint:检查你的 chart 是否有可以优化的地方
  • helm install --dry-run --debug:让 Tiller 渲染模板,并返回其生成的 yaml 文件
  • helm get manifest:查看 k8s 部署的是什么样的模板

附录:YAML 技巧

前面全部关注在模板的书写上,现在让我们来看看 YAML 的格式。

1. Scalars and Collections

两种类型的集合,map和队列

map:
  one: 1
  two: 2
  three: 3

sequence:
  - one
  - two
  - three
2. Yaml 中的类型

数字类型

count: 1
size: 2.34

如果它们被引号引起来,就变成了字符串

count: "1" # <-- string, not int
size: '2.34' # <-- string, not float

布尔类型也是如此:

isGood: true   # bool
answer: "true" # string

空值由 null 表示,而不是 nil

需要注意的是,port: "80" 是符合 YAML 语法的,当通过模板引擎传递给 k8s 时,如果 k8s 要求此字段是 int 类型,那么会报错。

可以通过 Yaml 的标记,对类型进行强制的转换:

coffee: "yes, please"
age: !!str 21
port: !!int "80"

上面的代码,!!str 告诉分析器,age 是的 String 类型, port 是个 int 类型。

3. Yaml 中的字符串
5. Yaml 是 Json 的超集

因为 Yaml 是 Json 的超集,所以任何符合 JSON 格式的文档都符合 YAML 的规范。

{
  "coffee": "yes, please",
  "coffees": [
    "Latte", "Cappuccino", "Espresso"
  ]
}

上面内容的另一种表示方式:

coffee: yes, please
coffees:
- Latte
- Cappuccino
- Espresso

两者还能混合书写:

coffee: "yes, please"
coffees: [ "Latte", "Cappuccino", "Espresso"]

上面三种方式表示的内容是一致的。

这表示 values.yaml 里可以包含 JSON 格式的数据。但 Helm 不允许文件后缀名为 .json

Yaml Anchor

附录: Go 语言数据类型和模板

因为 Helm 的模板编辑是基于 Go 语言的,而 Go 本身就是一种强类型的语言,所以模板中的变量是有类型的。

  • string
  • bool
  • int
  • float64
  • a byte slice
  • struct
  • a slice
  • a string-kyed map

获取变量类型最简单的方式是 printf "%t"。也可以使用 typeofkindf函数来获取。