Myblog

the amazing thing of think

helmfile 最佳实践指南(机翻)

本指南介绍Helmfile 用于编写高级helmfile文件所考虑的模式。它侧重于如何构建和执行helmfile文件.

缺少键和默认值

Helmfile 尽力通知用户注意潜在的错误。
helmfile如何实现的一个例子是,当你试图访问环境值中缺少的键时, helmfile会失败。
也就是说,下面的例子让 helmfile 在环境值中没有定义 eventApi.replicas 时失败。

.Values.eventApi.replicas | default 1

万一不是错误,而你又确实想允许缺失键,请使用 get 模板函数。

.Values | get “eventApi.replicas” nil

这将导致在你的模板中打印 <no value, 这可能不会导致失败。
如果你想要一个默认值,当一个缺失的键被引用时,使用默认值,如

.Values | get “eventApi.replicas” 1

现在,当环境值中没有定义eventApi.replicas时,你会得到1。

Release Template/常规目录结构

在一个涉及几十个版本的大型项目中引入 helmfile,往往会导致 files. helmfile.yaml 中出现很多重复的内容
下面的例子显示了 “namespace”、”chart”、”values “和 “secrets “中的重复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
releases:
# *snip*
- name: heapster
namespace: kube-system
chart: stable/heapster
version: 0.3.2
values:
- "./config/heapster/values.yaml"
- "./config/heapster/{{ .Environment.Name }}.yaml"
secrets:
- "./config/heapster/secrets.yaml"
- "./config/heapster/{{ .Environment.Name }}-secrets.yaml"
- name: kubernetes-dashboard
namespace: kube-system
chart: stable/kubernetes-dashboard
version: 0.10.0
values:
- "./config/kubernetes-dashboard/values.yaml"
- "./config/kubernetes-dashboard/{{ .Environment.Name }}.yaml"
secrets:
- "./config/kubernetes-dashboard/secrets.yaml"
- "./config/kubernetes-dashboard/{{ .Environment.Name }}-secrets.yaml"

这时Helmfile的高级功能Release Template就派上用场了。

它允许你把发布中的重复抽象掉,变成一个模板,然后通过使用YAML锚/alias来包含和执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
templates:
default: &default
chart: stable/{{`{{ .Release.Name }}`}}
namespace: kube-system
# This prevents helmfile exiting when it encounters a missing file
# Valid values are "Error", "Warn", "Info", "Debug". The default is "Error"
# Use "Debug" to make missing files errors invisible at the default log level(--log-level=INFO)
missingFileHandler: Warn
values:
- config/{{`{{ .Release.Name }}`}}/values.yaml
- config/{{`{{ .Release.Name }}`}}/{{`{{ .Environment.Name }}`}}.yaml
secrets:
- config/{{`{{ .Release.Name }}`}}/secrets.yaml
- config/{{`{{ .Release.Name }}`}}/{{`{{ .Environment.Name }}`}}-secrets.yaml
releases:
- name: heapster
<<: *default
- name: kubernetes-dashboard
<<: *default

Release Templating支持以下部分的发布定义。

  • 基本字段。namenamespacechartversion
  • 布尔字段。installed, wait, tillerless, verify等字段,并附加文字。

仅为模板设计的字段:installedTemplate, waitTemplate, tillerlessTemplate, verifyTemplate

1
2
3
4
# ...
installedTemplate: '{{`{{ eq .Release.Namespace "kube-system" }}`}}'
waitTemplate: '{{`{{ eq .Release.Labels.tag "safe" | not }}`}}'
# ...
  • set块值

    1
    2
    3
    4
    5
    # ...
    setTemplate:
    - name: '{{`{{ .Release.Name }}`}}'
    values: '{{`{{ .Release.Namespace }}`}}'
    # ...
  • “值”和”机密”文件路径

    1
    2
    3
    4
    5
    6
    # ...
    valuesTemplate:
    - config/{{`{{ .Release.Name }}`}}/values.yaml
    secrets:
    - config/{{`{{ .Release.Name }}`}}/secrets.yaml
    # ...
  • 内联”value”映射:

    1
    2
    3
    4
    5
    # ...
    valuesTemplate:
    - image:
    tag: `{{ .Release.Labels.tag }}`
    # ...

    查看 issue 428 了解更多关于这应该如何工作的背景。

分层发布值

请注意,不可能对 “values “部分进行分层。如果 “values “是在发布版和发布版模板中定义的,则只考虑发布版中定义的 “values”。这同样适用于 “secrets “和 “set”。

状态文件分层

如果你要分层模板,请看分层状态模板文件

你可能偶尔会有很多helmfile,这些helmfile共享一些共同的部分,比如使用哪个仓库,默认要捆绑哪个版本。

使用Layering将共同的部分提取到一个专门的库 helmfile中,这样每个头盔文件就变成了DRY。

假设你的’helmfile.yaml’长这样:

1
2
3
4
5
6
7
bases:
- environments.yaml
releases:
- name: metricbeat
chart: stable/metricbeat
- name: myapp
chart: mychart

environments.yaml则包含了众所周知的环境。

1
2
3
environments:
development:
production:

在运行时,你的 “helmfile.yaml “中的 “bases “将被评估以产生。

1
2
3
4
5
6
7
8
9
10
11
12
---
# environments.yaml
environments:
development:
production:
---
# helmfile.yaml
releases:
- name: myapp
chart: mychart
- name: metricbeat
chart: stable/metricbeat

最后将产生的YAML文档按照出现的顺序进行合并。
这样你的helmfile.yaml就变成了:

1
2
3
4
5
6
7
8
environments:
development:
production:
releases:
- name: metricbeat
chart: stable/metricbeat
- name: myapp
chart: mychart

Great!

现在,对你的每个 “helmfile.yaml “重复以上步骤,这样你所有的helmfiles就会变成DRY。

合并层中数组

Helmfile不会跨层合并数组。也就是说,下面的例子并不能像你想象的那样工作。

1
2
3
4
5
6
7
releases:
- name: metricbeat
chart: stable/metricbeat
---
releases:
- name: myapp
chart: mychart

Helmfile用最新的层覆盖了releases数组,所以产生的状态文件将:

1
2
3
4
releases:
# metricbeat release disappeared! but that's how helmfile works
- name: myapp
chart: mychart

一个变通的办法是将状态文件作为一个go模板,并使用readFile模板函数将你的状态文件的公共部分以纯文本的形式导入。

common.yaml:

1
2
3
4
templates:
metricbeat: &metricbeat
name: metricbeat
chart: stable/metricbeat

helmfile.yaml:

1
2
3
4
5
{{ readFile "common.yaml" }}
releases:
- <<: *metricbeat
- name: myapp
chart: mychart

分层状态模板文件

你需要让你的状态文件更DRY吗?

发现分层状态文件对你来说还不够?

Helmfile支持一个高级功能,可以让你组成状态 “模板 “文件来生成最终要处理的状态。

在下面的例子helmfile.yaml.gotmpl中,文件中每一个----分隔的部分都是一个go模板。

helmfile.yaml.gotmpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Part 1: Reused Enviroment Values
bases:
- myenv.yaml
---
# Part 2: Reused Defaults
bases:
- mydefaults.yaml
---
# Part 3: Dynamic Releases
releases:
- name: test1
chart: mychart-{{ .Values.myname }}
values:
replicaCount: 1
image:
repository: "nginx"
tag: "latest"

假设第一部分加载的myenv.yamltest.env.yaml是这样的。

myenv.yaml:

1
2
3
4
environments:
test:
values:
- test.env.yaml

test.env.yaml:

1
2
3
4
kubeContext: test
wait: false
cvOnly: false
myname: "dog"

其中第二部分加载的gotmpl文件是这样的:

mydefaults.yaml.gotmpl:

1
2
3
4
5
6
7
8
9
10
11
12
helmDefaults:
tillerNamespace: kube-system
kubeContext: {{ .Values.kubeContext }}
verify: false
{{ if .Values.wait }}
wait: true
{{ else }}
wait: false
{{ end }}
timeout: 600
recreatePods: false
force: true

每个go模板都是在.Values从前一部分继承的上下文中呈现的。

所以在mydefaults.yaml.gotmpl中,.Values.kubeContext.Values.wait都是有效的,因为它们确实存在于你的helmfile.yaml.gotmpl的前一部分(=第一部分)继承的环境值中,因此模板被渲染到。

1
2
3
4
5
6
7
8
helmDefaults:
tillerNamespace: kube-system
kubeContext: test
verify: false
wait: false
timeout: 600
recreatePods: false
force: true

同样,顶层helmfile.yaml.gotmpl的第三部分.Values.myname也是有效的,因为它包含在从前面部分继承的环境值中:

1
2
3
4
5
6
7
8
9
# Part 3: Dynamic Releases
releases:
- name: test1
chart: mychart-{{ .Values.myname }}
values:
replicaCount: 1
image:
repository: "nginx"
tag: "latest"

因此呈现:

1
2
3
4
5
6
7
8
9
# Part 3: Dynamic Releases
releases:
- name: test1
chart: mychart-dog
values:
replicaCount: 1
image:
repository: "nginx"
tag: "latest"