Helm应用包管理器初步认识

Helm 介绍

每个成功的软件平台都有一个优秀的打包系统,比如 Debian、Ubuntu 的 apt,Redhat、Centos 的 yum。而 Helm 则是 Kubernetes 上的包管理器,可以很方便的将之前打包好的yaml文件部署到kubernetes上。

本章我们将讨论为什么需要 Helm,它的架构和组件,以及如何使用 Helm。

Helm有两个重要概念:

  • helm:一个命令行客户端工具,主要用于Kubernetes应用chart的创建、打包、发布和管理。

  • Chart:应用描述,一系列用于描述 k8s 资源相关文件的集合。

  • Release:基于Chart的部署实体,一个 chart 被 Helm 运行后将会生成对应的一个 release;将在k8s中创建出真实运行的资源对象。

此外Helm还经常与CI\CD配置使用,在这个过程中用于维护应用程序的安装、升级、回滚等操作。

Helm解决那些问题

Helm 到底解决了什么问题?为什么 Kubernetes 需要 Helm?

答案是:Kubernetes 能够很好地组织和编排容器,但它缺少一个更高层次的应用打包工具,而 Helm 就是来干这件事的。

K8S上的应用对象,都是由特定的资源描述组成,包括deployment、service等。都保存各自文件中或者集中写到一个配置文件。然后kubectl apply –f 部署。

如果应用只由一个或几个这样的服务组成,上面部署方式足够了。

而对于一个复杂的应用,会有很多类似上面的资源描述文件,例如微服务架构应用,组成应用的服务可能多达十个,几十个。如果有更新或回滚应用的需求,可能要修改和维护所涉及的大量资源文件,而这种组织和管理应用的方式就显得力不从心了。

且由于缺少对发布过的应用版本管理和控制,使Kubernetes上的应用维护和更新等面临诸多的挑战,主要面临以下问题:

  1. 如何将这些服务作为一个整体管理
  2. 这些资源文件如何高效复用
  3. 不支持应用级别的版本管理

Helm v3 变化

2019年11月13日, Helm团队发布 Helm v3 的第一个稳定版本。

该版本主要变化如下:

  1. 架构变化

    最明显的变化是 Tiller 的删除

  2. Release名称可以在不同命名空间重用

  3. 支持将 Chart 推送至 Docker 镜像仓库中

  4. 使用JSONSchema验证chart values

  5. 其他

    5.1. 为了更好地协调其他包管理者的措辞 Helm CLI个别更名

    1
    2
    3
    helm delete  更名为 helm uninstall
    helm inspect 更名为 helm show
    helm fetch 更名为 helm pull

    但以上旧的命令当前仍能使用。
    5.2. 移除了用于本地临时搭建 Chart Repositoryhelm serve 命令。
    5.3. 自动创建名称空间,在不存在的命名空间中创建发行版时,Helm 2创建了命名空间。Helm 3遵循其他Kubernetes对象的行为,如果命名空间不存在则返回错误。
    5.4. 不再需要requirements.yaml, 依赖关系是直接在chart.yaml中定义。

Helm客户端

部署Helm客户端

Helm客户端下载地址:https://github.com/helm/helm/releases

解压移动到/usr/bin/目录即可。

1
2
3
wget https://get.helm.sh/helm-v3.8.2-linux-amd64.tar.gz
tar zxvf helm-v3.8.2-linux-amd64.tar.gz
mv linux-amd64/helm /usr/bin/

Helm常用命令

命令描述
create创建一个chart并指定名字
dependency管理chart依赖
get下载一个release。可用子命令:all、hooks、manifest、notes、values
history获取release历史
install安装一个chart
list列出release
package将chart目录打包到chart存档文件中
pull从远程仓库中下载chart并解压到本地 # helm pull stable/mysql –untar
repo添加,列出,移除,更新和索引chart仓库。可用子命令:add、index、list、remove、update
rollback从之前版本回滚
search根据关键字搜索chart。可用子命令:hub、repo
show查看chart详细信息。可用子命令:all、chart、readme、values
status显示已命名版本的状态
template本地呈现模板
uninstall卸载一个release
upgrade更新一个release
version查看helm客户端版本

配置国内Chart仓库

添加存储库:

1
2
3
helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo update

查看配置的存储库:

1
2
helm repo list
helm search repo stable

一直在stable存储库中安装charts,你可以配置其他存储库。

删除存储库:

1
helm repo remove aliyun

Helm基本使用

使用chart部署一个应用

查找chart

1
2
# helm search repo xxxxx
# helm search repo mysql

部署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
# 查看chart信息:
helm show chart stable/mysql

# helm部署到k8s上MySQL服务
helm install db stable/mysql

# 查看发布提示信息:
helm status db

# 查看pods会发现处于Pending。这时需要创建pv。
cat > pv-config.yml <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 8Gi
nfs:
path: /data/nfs-volume/mysql
server: 10.1.1.150
EOF

# 应用pv资源
kubectl apply -f pv-config.yml

如何自定义chart配置安装部署

这是因为并不是所有的chart都能按照默认配置运行成功,可能会需要一些环境依赖,例如PV。

所以我们需要自定义chart配置选项,安装过程中有两种方法可以传递配置数据:

  • –values(或-f):指定带有覆盖的YAML文件。这可以多次指定,最右边的文件优先

  • –set:在命令行上指定替代。如果两者都用,–set优先级高

  • –values使用,先将修改的变量写到一个文件中

使用storageClass资源创建MySQL

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
cat > rbac.yml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get","create","list", "watch","update"]
- apiGroups: ["extensions"]
resources: ["podsecuritypolicies"]
resourceNames: ["nfs-provisioner"]
verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
EOF
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
cat >dp.yml <<EOF
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccount: nfs-provisioner
containers:
- name: nfs-client-provisioner
image: wangxiansen/nfs-client-provisioner:v3.1.0
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME # 供应者的名字
value: data-storage # 名字虽然可以随便起,以后引用要一致
- name: NFS_SERVER
value: 10.1.1.150
- name: NFS_PATH
value: /data/nfs-volume/mysql
volumes:
- name: nfs-client-root
nfs:
server: 10.1.1.150
path: /data/nfs-volume/mysql
EOF
1
2
3
4
5
6
7
8
9
10
11
cat storage.yml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: mysql-nfs-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: data-storage
reclaimPolicy: Delete
allowVolumeExpansion: True
EOF

以下是将创建具有名称的默认MySQL用户k8s,默认root密码为wangxiansen,并授予此用户访问新创建的k8s数据库的权限,但将接受该图表的所有其余默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# helm show values stable/mysql
cat > values.yaml <<EOF
mysqlRootPassword: "wangxiansen"
persistence:
enabled: true
storageClass: "mysql-nfs-storage"
accessMode: ReadWriteOnce
size: 1Gi
mysqlUser: "k8s"
mysqlPassword: "123456"
mysqlDatabase: "k8s"
EOF
# helm创建db2
helm install db2 -f values.yaml stable/mysql
# 查看pods
kubectl get pods
NAME READY STATUS RESTARTS AGE
db2-mysql-f7fbfdd68-7f7q8 1/1 Running 0 2d21h

命令行替代变量:

1
# helm install db3 --set persistence.storageClass="mysql-nfs-storage" --set mysqlRootPassword="testing" stable/mysql

也可以把chart包下载下来查看详情:

1
# helm pull stable/mysql --untar

如果同时使用这两个参数,--values(-f) 将被合并到具有更高优先级的 --set,使用 --set 指定的值将持久化在 ConfigMap 中,对于给定的 release,可以使用 helm get values <release-name> 来查看已经设置的值,已设置的值也通过允许 helm upgrade 并指定 --reset 值来清除。

--set 选项接收零个或多个 name/value 对,最简单的用法就是 --set name=value,相当于 YAML 文件中的:

1
name: value

多个值之间用字符串“,”隔开,用法就是 --set a=b,c=d,相当于 YAML 文件中的:

1
2
a: b
c: d

也支持更加复杂的表达式,例如 --set outer.inner=value,对应 YAML:

1
2
outer:
inner: value

对于列表数组可以用 {} 来包裹,比如 --set name={a, b, c},对应 YAML:

1
2
3
4
name:
- a
- b
- c

从 Helm 2.5.0 开始,就可以使用数组索引语法来访问列表中某个项,比如 --set servers[0].port=80,对应的 YAML 为:

1
2
servers:
- port: 80

也可以这样设置多个值,比如 --set servers[0].port=80,servers[0].host=example,对应的 YAML 为:

1
2
3
servers
- port: 80
host: example

有时候你可能需要在 --set 选项中使用特殊的字符,这个时候可以使用反斜杠来转义字符,比如 --set name=value1\,value2,对应的 YAML 为:

1
name: "value1,value2"

类似的,你还可以转义.,当 chart 模板中使用 toYaml 函数来解析 annotations、labels 以及 node selectors 之类的时候,这非常有用,比如 --set nodeSelector."kubernetes\.io/role"=master,对应的 YAML 文件:

1
2
nodeSelector:
kubernetes.io/role: master

深度嵌套的数据结构可能很难使用 --set 来表示,所以一般推荐还是使用 YAML 文件来进行覆盖,当然在设计 chart 模板的时候也可以结合考虑到 --set 这种用法,尽可能的提供更好的支持。

更多安装方式

helm install 命令可以从多个源进行安装:

升级和回滚

当新版本的 chart 包发布的时候,或者当你要更改 release 的配置的时候,你可以使用 helm upgrade 命令来操作。升级需要一个现有的 release,并根据提供的信息对其进行升级。因为 Kubernetes charts 可能很大而且很复杂,Helm 会尝试以最小的侵入性进行升级,它只会更新自上一版本以来发生的变化:

1
2
3
4
5
6
7
8
 helm upgrade -f config.yaml mysql stable/mysql
Release "mysql" has been upgraded. Happy Helming!
NAME: mysql
LAST DEPLOYED: Fri Dec 6 21:06:11 2022
NAMESPACE: default
STATUS: deployed
REVISION: 2
...

我们这里 mysql 这个 release 用相同的 chart 包进行升级,但是新增了一个配置项:

1
mysqlRootPassword: passw0rd

我们可以使用 helm get values 来查看新设置是否生效:

1
2
3
4
5
6
7
8
➜ helm get values mysql
USER-SUPPLIED VALUES:
mysqlDatabase: user0db
mysqlPassword: user0pwd
mysqlRootPassword: passw0rd
mysqlUser: user0
persistence:
enabled: false

helm get 命令是查看集群中 release 的非常有用的命令,正如我们在上面看到的,它显示了 panda.yaml 中的新配置值被部署到了集群中,现在如果某个版本在发布期间没有按计划进行,那么可以使用 helm rollback [RELEASE] [REVISION] 命令很容易回滚到之前的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜ helm ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
mysql default 2 2022-12-06 21:06:11.36358 +0800 CST deployed mysql-1.5.0 5.7.27
➜ helm history mysql
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Fri Dec 6 17:53:03 2022 superseded mysql-1.5.0 5.7.27 Install complete
2 Fri Dec 6 21:06:11 2022 deployed mysql-1.5.0 5.7.27 Upgrade complete
➜ helm rollback mysql 1
Rollback was a success! Happy Helming!
➜ kubectl get pods -l release=mysql
NAME READY STATUS RESTARTS AGE
mysql-ddd798f48-gnrzd 1/1 Running 0 3h25m
➜ helm get values mysql
USER-SUPPLIED VALUES:
mysqlDatabase: user0db
mysqlPassword: user0pwd
mysqlUser: user0
persistence:
enabled: false

可以看到 values 配置已经回滚到之前的版本了。上面的命令回滚到了 release 的第一个版本,每次进行安装、升级或回滚时,修订号都会加 1,第一个修订号始终为1,我们可以使用 helm history [RELEASE] 来查看某个版本的修订号。

除此之外我们还可以指定一些有用的选项来定制 install/upgrade/rollback 的一些行为,要查看完整的参数标志,我们可以运行 helm <command> --help 来查看,这里我们介绍几个有用的参数:

  • --timeout: 等待 Kubernetes 命令完成的时间,默认是 300(5分钟)
  • --wait: 等待直到所有 Pods 都处于就绪状态、PVCs 已经绑定、Deployments 具有处于就绪状态的最小 Pods 数量(期望值减去 maxUnavailable)以及 Service 有一个 IP 地址,然后才标记 release 为成功状态。它将等待与 --timeout 值一样长的时间,如果达到超时,则 release 将标记为失败。注意:在 Deployment 将副本设置为 1 并且作为滚动更新策略的一部分,maxUnavailable 未设置为0的情况下,--wait 将返回就绪状态,因为它已满足就绪状态下的最小 Pod 数量
  • --no-hooks: 将会跳过命令的运行 hooks
  • --recreate-pods: 仅适用于 upgrade 和 rollback,这个标志将导致重新创建所有的 Pods。(Helm3 中启用了)