前言
Helm 作为 Kubernetes 的包管理工具和 CNCF 毕业项目,在业界被广泛使用。但在实际使用场景中的一些需求 helm 并不能很好的满足,需要进行一些修改和适配,如同时部署多个 chart、不同部署环境的区分以及 chart 的版本控制。Helmfile 就是一个能够很好解决这些问题的小工具。
基础介绍
Helmfile 通过 helmfile.yaml 文件帮助用户管理和维护众多 helm chart,其最主要作用是:
- 集成在 CI/CD 系统中,提高部署的可观测性和可重复性,区分环境,免去各种
--set 造成的困扰。 - 方便对 helm chart 进行版本控制,如指定版本范围、锁定版本等。
- 定期同步,避免环境中出现不符合预期的配置。
安装
helmfile 提供了多种安装方式,除了直接在 release 页面下载,还可以通过如下方式安装:
- macOS (使用 homebrew):
brew install helmfile - Windows (使用 scoop):
scoop install helmfile - Archlinux:
pacman -S helmfile - openSUSE:
zypper in helmfile
同时还支持作为容器运行,可以非常方便的集成到 CI/CD 系统中:
1
2
3
4
5
| # helm 2
$ docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.helm:/root/.helm" -v "${PWD}:/wd" --workdir /wd quay.io/roboll/helmfile:v0.135.0 helmfile sync
# helm 3
$ docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.config/helm:/root/.config/helm" -v "${PWD}:/wd" --workdir /wd quay.io/roboll/helmfile:helm3-v0.135.0 helmfile sync
|
其他依赖
除了安装 helmfile 以外,还需要安装 helm、kubectl 以及 helm 插件 helm-diff。
helm-diff 安装方式:
1
| $ helm plugin install https://github.com/databus23/helm-diff
|
helmfile.yaml
helmfile.yaml 是 helmfile 的核心文件,其用来声明所有的配置。下面会简要介绍一下,详细内容见官方文档。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
| # 声明 repo 配置
repositories:
- name: <repo-name>
# url: repo url
# 可以设置基础配置 或 tls 认证
# certFile: certificate 文件
# keyFile: key 文件
# username: 用户名
# password: 密码
# helm 二进制文件的路径
helmBinary: path/to/helm3
# helm 的一些默认设置,这些配置与 `helm SUBCOMMAND` 相同,可以通过这个配置声明一些,默认的配置
helmDefaults:
tillerNamespace: tiller-namespace #dedicated default key for tiller-namespace
tillerless: false #dedicated default key for tillerless
kubeContext: kube-context #dedicated default key for kube-context (--kube-context)
cleanupOnFail: false #dedicated default key for helm flag --cleanup-on-fail
# additional and global args passed to helm (default "")
args:
- "--set k=v"
# verify the chart before upgrading (only works with packaged charts not directories) (default false)
verify: true
# wait for k8s resources via --wait. (default false)
wait: true
# time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks, and waits on pod/pvc/svc/deployment readiness) (default 300)
timeout: 600
# performs pods restart for the resource if applicable (default false)
recreatePods: true
# forces resource update through delete/recreate if needed (default false)
force: false
# when using helm 3.2+, automatically create release namespaces if they do not exist (default true)
createNamespace: true
...
# 为 helmfile 中所有的 release 设置相同的 label,可用于为所有 release 标记相同的版本
commonLabels:
hello: world
# 设置 release 配置(支持多 release)
releases:
# 远程 chart 示例(chart 已经上传到 remote 仓库)
- name: vault # name of this release
namespace: vault # target namespace
createNamespace: true # helm 3.2+ automatically create release namespace (default true)
labels: # Arbitrary key value pairs for filtering releases
foo: bar
chart: roboll/vault-secret-manager # the chart being installed to create this release, referenced by `repository/chart` syntax
version: ~1.24.1 # the semver of the chart. range constraint is supported
condition: vault.enabled # The values lookup key for filtering releases. Corresponds to the boolean value of `vault.enabled`, where `vault` is an arbitrary value
missingFileHandler: Warn # set to either "Error" or "Warn". "Error" instructs helmfile to fail when unable to find a values or secrets file. When "Warn", it prints the file and continues.
# Values files used for rendering the chart
values:
# Value files passed via --values
- vault.yaml
# Inline values, passed via a temporary values file and --values, so that it doesn't suffer from type issues like --set
- address: https://vault.example.com
# Go template available in inline values and values files.
- image:
# The end result is more or less YAML. So do `quote` to prevent number-like strings from accidentally parsed into numbers!
# See https://github.com/roboll/helmfile/issues/608
tag: {{ requiredEnv "IMAGE_TAG" | quote }}
# Otherwise:
# tag: "{{ requiredEnv "IMAGE_TAG" }}"
# tag: !!string {{ requiredEnv "IMAGE_TAG" }}
db:
username: {{ requiredEnv "DB_USERNAME" }}
# value taken from environment variable. Quotes are necessary. Will throw an error if the environment variable is not set. $DB_PASSWORD needs to be set in the calling environment ex: export DB_PASSWORD='password1'
password: {{ requiredEnv "DB_PASSWORD" }}
proxy:
# Interpolate environment variable with a fixed string
domain: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
scheme: {{ env "SCHEME" | default "https" }}
# Use `values` whenever possible!
# `set` translates to helm's `--set key=val`, that is known to suffer from type issues like https://github.com/roboll/helmfile/issues/608
set:
# single value loaded from a local file, translates to --set-file foo.config=path/to/file
- name: foo.config
file: path/to/file
# set a single array value in an array, translates to --set bar[0]={1,2}
- name: bar[0]
values:
- 1
- 2
# set a templated value
- name: namespace
value: {{ .Namespace }}
# will attempt to decrypt it using helm-secrets plugin
# 本地 chart 示例(chart 保存在本地)
- name: grafana # name of this release
namespace: another # target namespace
chart: ../my-charts/grafana # the chart being installed to create this release, referenced by relative path to local helmfile
values:
- "../../my-values/grafana/values.yaml" # Values file (relative path to manifest)
- ./values/{{ requiredEnv "PLATFORM_ENV" }}/config.yaml # Values file taken from path with environment variable. $PLATFORM_ENV must be set in the calling environment.
wait: true
# 可以嵌套其他的 helmfiles,支持从本地和远程拉取 helmfile
helmfiles:
- path: path/to/subhelmfile.yaml
# label 选择器可以过滤需要覆盖的 release
selectors:
- name=prometheus
# 覆盖 value
values:
# 使用文件覆盖
- additional.values.yaml
# 覆盖单独的 key
- key1: val1
- # 远程拉取配置
path: git::https://github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=0.40.0
# 如果指向不存在路径,则打印告警错误
missingFileHandler: Error
# 多环境管理
environments:
# 当没有设置 `--environment NAME` 时,使用 default
default:
values:
# 内容可以是文件路径或者 key:value
- environments/default/values.yaml
- myChartVer: 1.0.0-dev
# "production" 环境,当设置了 `helmfile --environment production sync` 时
production:
values:
- environment/production/values.yaml
- myChartVer: 1.0.0
# disable vault release processing
- vault:
enabled: false
## `secrets.yaml` is decrypted by `helm-secrets` and available via `{{ .Environment.Values.KEY }}`
secrets:
- environment/production/secrets.yaml
# 当占不到 `environments.NAME.values` 时,可以设置为 "Error", "Warn", "Info", "Debug",默认是 "Error"
missingFileHandler: Error
# 分层管理,可以将所有文件合并,顺序为:environments.yaml < - defaults.yaml < - templates.yaml < - helmfile.yaml
bases:
- environments.yaml
- defaults.yaml
- templates.yaml
# API 功能
apiVersions:
- example/v1
|
Apply
helmfile apply 是 helmfile 中最常用命令,体验与 kubectl apply 类似,根据 helmfile.yaml 中声明的配置可以一键执行相应的动作,如:添加 repo、安装或更新 release 等。
helmfile.yaml 如下:
1
2
3
4
5
6
7
8
9
10
| repositories:
- name: stable https://charts.helm.sh/stable
releases:
- name: prom-norbac-ubuntu
namespace: prometheus
chart: stable/prometheus
set:
- name: rbac.create
value: false
|
执行 helmfile apply 之后,helmfile 会进行如下操作:
- 添加
repositories 中声明的 repo - 运行
helm diff 进行对比 - 根据
release中声明的配置,安装或更新 chart
效果如下(由于输出内容过多,这里只节选了部分输出):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| Adding repo stable https://charts.helm.sh/stable
"stable" has been added to your repositories
Comparing release=prom-norbac-ubuntu, chart=stable/prometheus
...
prometheus, prom-norbac-ubuntu-prometheus-server, ServiceAccount (v1) has been added:
-
+ # Source: prometheus/templates/rbac/server-serviceaccount.yaml
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ labels:
+ component: "server"
+ app: prometheus
+ release: prom-norbac-ubuntu
+ chart: prometheus-11.12.1
+ heritage: Helm
+ name: prom-norbac-ubuntu-prometheus-server
+ namespace: prometheus
+ annotations:
+ {}
Upgrading release=prom-norbac-ubuntu, chart=stable/prometheus
Release "prom-norbac-ubuntu" does not exist. Installing it now.
NAME: prom-norbac-ubuntu
LAST DEPLOYED: Wed Dec 23 11:23:31 2020
NAMESPACE: prometheus
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
...
Listing releases matching ^prom-norbac-ubuntu$
prom-norbac-ubuntu prometheus 1 2020-12-23 11:23:31.779328 +0800 CST deployed prometheus-11.12.1 2.20.1
UPDATED RELEASES:
NAME CHART VERSION
prom-norbac-ubuntu stable/prometheus 11.12.1
|
模板化
helmfile 和 helm templete 一样可以使用 Go templates,同时还有一个特殊的功能 requiredEnv,该函数允许声明模板渲染所需的特定环境变量,如果环境变量未设置或为空,则渲染失败返回错误信息。
使用环境变量
可以在 helmfile 中直接使用环境变量,使用方式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| repositories:
- name: your-private-git-repo-hosted-charts https://{{ requiredEnv "GITHUB_TOKEN"}}@raw.githubusercontent.com/kmzfs/helm-repo-in-github/master/
releases:
- name: {{ requiredEnv "NAME" }}-vault
namespace: {{ requiredEnv "NAME" }}
chart: roboll/vault-secret-manager
values:
- db:
username: {{ requiredEnv "DB_USERNAME" }}
password: {{ requiredEnv "DB_PASSWORD" }}
set:
- name: proxy.domain
value: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
- name: proxy.scheme
value: {{ env "SCHEME" | default "https" }}
|
进阶实践
helm 还有一些进阶使用方式,如:版本控制、环境区分、hook、交互式操作、集成 kustomize 等。这里简单介绍几种,更多功能请看官方文档。
版本控制
helmfile 支持 Semver 2.0 的版本号,可以锁定主版本,防止误升级导致的错误。
1
2
3
4
| releases:
- name: vault
namespace: vault
version: ~1.24.1 # 限制版本 >=1.24.1 && < 1.25.0
|
同时还能通过 helmfile deps 命令生成 lock 文件,在 CD 时,除非修改 lock 文件,否无法发布新版本。helmfile.lock 内容如下:
1
2
3
4
5
6
7
| version: v0.135.0
dependencies:
- name: prometheus
repository: https://charts.helm.sh/stable
version: 11.12.1
digest: sha256:a5158f1361f2bbc4e73a80a22dd92b44538bdebeb2419658c36e31aa603b05fd
generated: "2020-12-23T16:26:57.42503+08:00"
|
当需要更新时,再次执行 helmfile deps 即可。
区分环境
这也是个使用率较高的功能,使用 environments 配置·。如果不指定 --environment NAME 参数,默认使用 default 配置。
这里假设有三个文件,helmfile.yaml、production.yaml 和 default.yaml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # helmfile.yaml
environments:
default:
values:
- default.yaml
production:
values:
- production.yaml
releases:
- name: myapp-{{ .Values.releaseName }} # 根据环境名,可能是 `dev` 或 `prod`
values:
- url: { .Values.domain }} # 根据环境名,可能是 `dev.example.com` 或 `prod.example.com`
{{ if eq .Environment.Name "production" }} # 使用 Go template 的
- values/production-specified-values.yaml
{{ end }}
|
1
2
3
| # production.yaml
domain: prod.example.com
releaseName: prod
|
1
2
3
| # default.yaml
domain: dev.example.com
releaseName: dev
|
在执行 helmfile 时,只需使用 --environment 指定需要安装的环境:
1
| $ helmfile --environment production apply
|
Hook
Helmfile hook 是一个每次发布的扩展点,它由以下部分组成:
eventscommandargsshowlogs
helmfile 在运行时,会触发各种事件,一旦事件触发,相关的 hook 就会被执行,目前支持的如下事件:
preparepresyncpreuninstallpostuninstallpostsynccleanup
下面这个示例,会打印事件触发时的的上下文信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| environments:
default:
prod:
releases:
- name: myapp
chart: mychart
# *snip*
hooks:
- events: ["prepare", "cleanup"]
showlogs: true
command: "echo"
args: ["{{`{{.Environment.Name}}`}}", "{{`{{.Release.Name}}`}}", "{{`{{.HelmfileCommand}}`}}\
"]
|
执行命令,可以看到 command 执行成功:
1
2
3
4
| $ helmfile -e prod sync
helmfile.yaml: basePath=.
hook[prepare] logs | prod myapp sync
|
这也是个十分好用的功能,可以为不同的事件配置不同的 hook,这样在 CD 出现问题时,通过 hook 可以第一时间收到通知,并快速定位问题。
结语
Helmfile 是一个很不错 Helm 生态工具,很大程度上弥补了 Helm 的不足。提高部署的可观测性和可重复性,提高了效率,最终实现 Release AS Code。