基于Helm的Kubernetes资源管理
Helm是Kubernetes的包管理器,由客户端组件helm和服务端组件Tiller组成。Helm能够将一组K8S资源打包统一管理。
到官网下载Helm后解压,可以得到helm客户端二进制文件。然后执行下面的命令,在集群中安装Tiller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 自动使用kubectl config current-context这个上下文对应的K8S集群 # 安装或者重新安装Tiller helm init # 国内访问,指定仓库镜像 helm init --stable-repo-url http://mirror.azure.cn/kubernetes/charts # 微软也提供了孵化器仓库的镜像 https://mirror.azure.cn/kubernetes/charts-incubator # 如果向安装到其它K8S集群 helm init --kube-context ctx-name # 如果想升级Tiller helm init --upgrade helm init --node-selectors "beta.kubernetes.io/os"="linux" # 指定在何种Node上运行Tiller --service-account tiller # 指定使用的SA,需要cluster-admin权限 # 为Helm使用的SA添加权限 kubectl create clusterrolebinding kube-system-tiller-crb --clusterrole=cluster-admin --serviceaccount=kube-system:tiller |
Helm会在K8S集群中创建一个Deployment,其Pod使用镜像gcr.io/kubernetes-helm/tiller:v2.8.2
1 2 |
# 添加下面这行,启用helm的命令行自动完成功能 source <(helm completion bash) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 安装 helm install stable/mysql --namespace dang # 列出部署的Release helm ls # NAME REVISION UPDATED STATUS CHART NAMESPACE # hazy-eagle 1 Fri Apr 20 00:14:33 2018 DEPLOYED mysql-0.3.7 jx # womping-lamb 1 Fri Apr 20 00:16:33 2018 DEPLOYED mysql-0.3.7 dang # 删除已经部署的Release helm delete hazy-eagle # 即使删除后,历史轨迹还是保留的 helm status hazy-eagle |
Helm的打包格式叫做Chart,所谓Chart就是一系列文件,它描述了一组相关的K8S集群资源。Chart中的文件安装特定的目录结构组织。
Chart的压缩包命名为chartname-version,例如nginx-1.2.3.tgz。
包含Chart的基本信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
name: "Chart的名称,必须" version: "Chart的版本,必须" kubeVersion: "兼容的K8S版本范围" description: "简短描述" keywords: - "此Chart的搜索关键字" home: "项目的主页" sources: - "项目源代码URL" # 模板引擎 engine: gotpl icon: "一个SVG或者PNG的URL" appVersion: "应用程序版本" deprecated: "是否被废弃,取值false/true" tillerVersion: "此Chart需求的Tiller版本" |
此文件中包含了必要的值定义(默认值):
1 2 3 4 |
imageRegistry: "quay.io/deis" dockerTag: "latest" pullPolicy: "Always" storage: "s3" |
如果需要删除某个默认值,你可以为其提供 null值
描述Chart的依赖关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
dependencies: - name: apache version: 1.2.3 repository: http://example.com/charts - name: mysql version: 3.2.1 repository: http://another.example.com/charts # 依赖的别名 alias: another-name-of-chart # 条件性依赖,指定YAML路径,如果这些路径在顶级父Chart上可以解析为布尔值,则 # 此依赖被启用/禁用。如果指定多个路径,仅仅第一个可以解析的路径生效,并决定此Chart是否被依赖 condition: subchart1.enabled, global.subchart1.enabled # 条件性依赖,根据顶级父Chart的Value来决定,例如tags.front-end=true则此Chart被依赖 tags: - front-end # 从子代Chart导入值,子代values.yaml必须包含exports: data: ... import-values: - data |
此目录存放子Chart(Subchart)的定义,每个目录对应一个Subchart。
Subchart也就是当前Chart依赖的Chart,在requirements.yaml中声明。
此目录包括一系列的模板:
- _helpers.tpl(或者任何 _开头的文件),通常在此文件中定义一些可重用的模板片断,此文件中的定义在任何资源定义模板中可用
- NOTES.txt 说明文档,供人类阅读
- 其它文件为K8S资源定义模板,通常不同的K8S资源放在不同文件中,并以资源类型为文件名
Tiller在部署Chart时,会使用你提供的Value来渲染templates目录中的所有第3类文件,并把结果YAML交付K8S,创建各种资源。
由Helm管理的资源,应该添加以下标签:
标签 | 说明 |
heritage | 必须添加此标签,并且值必须为 {{ .Release.Service }},用于确定一个资源是否由Tiller来管理 |
release | 应当设置为 {{ .Release.Name }} |
chart | 应当设置为 {{ .Chart.Name }}-{{ .Chart.Version}} |
Helm目标遵从Go Template标准,其特点是双花括号形式的占位符,例如 {{.Values.imageRegistry}}。Go Template相关的知识可以参考我的文章:Go语言中的模板引擎
在目标中你可以使用 .Values前缀来访问值,--set选项或values.yaml中包含的值包含在此对象中。
值 | 说明 | ||
Release.Name | 当前Release的名称 | ||
Release.Time | 当前Release最后被更新的时间 | ||
Release.Namespace | Chart被release到的名字空间 | ||
Release.Service | 管理此Release的服务,通常是Tiller | ||
Release.IsUpgrade | 当前操作是升级还是回滚 | ||
Release.IsInstall | 当前操作是否为安装 | ||
Release.Revision | 当前Release的修订版,从1开始,每次helm upgrade后增加1 | ||
Chart | 对应Chart.yaml中的数据 | ||
Values | 经过合并处理后的指定义 | ||
Files |
用于读取文件的内容,嵌入到资源定义中,示例:
|
||
Template.Name | 当前模板的名称 | ||
Template.BasePath | 模板所在目录 | ||
Capabilities |
用于访问K8S集群所支持的特性:
|
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 |
// 从顶级命名空间(前导的点号)中寻找Release对象的Name属性 // 签到的点号表示“当前上下文对象” {{ .Release.Name }} /** 定义变量 **/ // 注意变量的前导$,赋值操作符:= {{- $files := .Files }} /** 调用函数 **/ // 语法 func arg1 arg2 ... {{ quote .Values.favorite.drink }} // 加引号 {{indent 2 "mug:true"}} // 缩进 {{ default "tea" .Values.drink}} // 默认值 {{ upper .Values.drink }} // 转为大写 /** 使用管道 **/ // 前面的内容作为后面函数的入参 {{ .Values.favorite.drink | quote }} // 如果值为空,则默认为tea,最后给值添加引号 {{ .Values.favorite.drink | default "tea" | quote }} /** 空白字符处理 **/ {{- /* 删除左侧空白符 删除右侧空白符*/ -}} // 注意留白 {{- 3 }} // 删除左侧空白,打印3 {{-3}} // 打印-3 /** 条件分支 **/ // false、0、''、nil、空集合的值为假 {{ if PIPELINE }} // Do something {{ else if OTHER PIPELINE }} // Do something else {{ else }} // Default case {{ end }} /** with 指定作用域对象 **/ {{- with .Values.favorite }} // .drink实际上访问.Values.favorite.drink drink: {{ .drink | default "tea" | quote }} {{- end }} /** range,执行for-each循环 **/ // 此对象必须是数组 {{- range .Values.pizzaToppings }} // 这里的点号代表数组元素 - {{ . | title | quote }} {{- end }} // 指定需要遍历的元组,tuple函数用于创建列表 {{- range tuple "A" "B" "C" }} {{ . }} {{- end }} /** 模板相关 **/ // 定义一个模板,注意模板名称是全局性的,如果出现冲突,最后加载的那个被使用 // 通常模板名以当前chart的名字为前缀 {{- define "mychart.labels" }} // 模板体 {{- end }} // 渲染模板 // 将当前对象传递给模板,作为其上下文 {{- template "mychart.labels" . }} // template是一个Action而非Function,不能后接管道。因此要实现缩进效果,可以用include // 通常都是使用include,而非template {{ include "mychart.app" . | indent 4 }} /** 读取文件内容 **/ // 访问指定的文件 {{- $files := .Files }} // 赋值为变量 {{- range tuple "config1.toml" "config2.toml" "config3.toml" }} {{ . }}: |- {{ $files.Get . }} // 使用变量来访问.Files,因为当前上下文对象是"config*.toml" {{- end }} // 访问匹配通配符的文件 {{ range $path := .Files.Glob "**.yaml" }} {{ end }} // 逐行处理 {{ range .Files.Lines "foo/bar.txt" }} {{ . }}{{ end }} // 要处理文件路径,可以使用path包的Base Dir Ext IsAbs Clean函数,但是首字母要改为小写 /** ConfigMap处理 **/ kind: ConfigMap data: {{ (.Files.Glob "foo/*").AsConfig | indent 2 }} /** Secret处理 **/ kind: Secret type: Opaque data: {{ (.Files.Glob "bar/*").AsSecrets | indent 2 }} // 转换为Secret data: token: |- {{ .Files.Get "config1.toml" | b64enc }} // 手工进行Base64编码 |
从父Chart的values.yaml中,可以定义传递给Subchart的值:
1 2 3 |
# 必须以Subchart名(对应charts子目录名)为根 subchartname: key: value |
如果希望在任何级别的Subchart中,使用和父Chart一致的值键,可以使用全局值(Global Values):
1 2 3 |
# 在此定义全局值 global: key: name |
在任何一个子Chart中,都可以通过 {{ .Values.global.key }}引用全局值。
父子Chart可以共享模板定义, 任何一个Chart中定义的 {{- define }}都可以被其它Chart使用。
Helm提供了一种钩子机制,允许Chart开发者干预Release声明周期的某些点,使用Hooks你可以:
- 在任何其它Charts加载之前,加载一个ConfigMap或Secret
- 在安装新Chart之前,执行一个Job来备份数据库;在安装Chart之后,执行另一个Job来恢复数据库
- 在删除Release之前,运行Job进行优雅清理
支持的钩子包括:
钩子 | 说明 | ||
pre-install | 模板渲染后,创建任何K8S资源之前 | ||
post-install | 所有资源加载到K8S之后 | ||
crd-install |
在执行任何检查之前,添加CRD资源,只能用于CRD的YAML,示例:
如果不加此钩子,则安装自定义资源时可能遭遇 no matches for kind "xxx" in version "xx.gmem.cc/v1"]错误 |
||
pre-delete | 执行Release删除请求时,从K8S中删除任何资源之前 | ||
post-delete | 执行Release删除请求时,从K8S中删除任何资源之后 | ||
pre-upgrade | 升级Release前后,类似于install | ||
post-upgrade | |||
pre-rollback | 回滚Release前后,类似于install | ||
post-rollback |
钩子可以开发为具有特定注解的Job:
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 |
apiVersion: batch/v1 kind: Job metadata: name: "{{.Release.Name}}" labels: app.kubernetes.io/managed-by: {{.Release.Service | quote}} app.kubernetes.io/instance: {{.Release.Name | quote}} helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" annotations: # 下面的注解声明此Job是一个钩子,而非正在安装的Release的一部分 # 一个Job可以运行在多个阶段:"helm.sh/hook": post-install,post-upgrade "helm.sh/hook": post-install # 权重决定钩子的执行顺序 "helm.sh/hook-weight": "-5" # Hook资源(此Job)的删除策略 # hook-succeeded 执行成功后删除 # hook-failed 如果钩子执行失败,则删除 # before-hook-creation 提示Tiller在创建新的钩子之前,删除之前的版本 "helm.sh/hook-delete-policy": hook-succeeded spec: template: metadata: name: "{{.Release.Name}}" labels: app.kubernetes.io/managed-by: {{.Release.Service | quote}} app.kubernetes.io/instance: {{.Release.Name | quote}} helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" spec: restartPolicy: Never containers: - name: post-install-job image: "alpine:3.3" command: ["/bin/sleep","{{default"10".Values.sleepyTime}}"] |
子命令 | 说明 | ||||
create |
创建指定名称的新Chart,会创建一个新的Chart目录,包含通用的文件和子目录:
|
||||
delete |
删除指定的Release 格式: helm delete [flags] RELEASE_NAME [...] 选项: --dry-run 模拟运行 |
||||
dependency | 管理Chart的依赖 | ||||
dependency build | 构建出charts目录 | ||||
dependency list | 列出依赖 | ||||
dependency update | 根据requirements.yaml来更新charts | ||||
fetch |
从仓库下载Chart,可选的,解压到本地目录 格式: helm fetch [flags] [chart URL | repo/chartname] [...] 选项: --username string 仓库用户名 |
||||
get |
下载指定的Release 格式:helm get [flags] RELEASE_NAME |
||||
get hooks | 下载指定Release的Hooks,Hooks为YAML格式,使用 --- 分隔 | ||||
get manifest | 下载指定Release的Manifest,也就是Helm提交给K8S的资源定义 | ||||
get values | 下载指定Release的Value文件 | ||||
history | 显示指定Release的Revisions列表 | ||||
inspect | 查看Chart的信息 | ||||
inspect chart | 查看Chart的Charts.yaml信息 | ||||
inspect values | 查看Chart的values.yaml信息 | ||||
install |
安装Chart归档,可以从Chart引用、打包Chart路径、解压Chart目录安装 Chart引用(Reference)是引用仓库中Chart的便捷方式。例如stable/mariadb表示stable仓库中的mariadb这个Chart 选项: --name string 安装的Release的名字,如果不指定则自动生成
--timeout int 单个K8S操作的超时时间,默认300s 示例:
|
||||
list |
列出所有Release 选项: -a, --all 列出所有Release,不仅仅是DEPLOYED |
||||
package |
将Chart目录打包为归档文件 格式: helm package [flags] [CHART_PATH] [...] 示例:
|
||||
repo |
列出、添加、移除、升级、索引指定的Chart仓库 示例:
|
||||
reset | 从集群中卸载Tiller | ||||
rollback |
将Release回滚到先前的修订版 格式: helm rollback [flags] [RELEASE] [REVISION] 选项: --force 如果需要,通过delete/recreate来强制更新资源。默认情况下helm使用patch方式来修改资源,但是某些资源的字段是不可变的,只能删除并重新创建整个资源 |
||||
search |
从索引中搜索,注意,远程仓库的索引需要预先更新 格式: helm search [keyword] [flags] 选项: -l, --versions 搜索所有版本 示例:
|
||||
serve | 启动本地HTTP服务器作为Chart仓库,默认端口127.0.0.1:8879 | ||||
status |
显示指定Release的状态 格式: helm status [flags] RELEASE_NAME |
||||
template |
在本地填充模板,不支持远程Chart仓库 选项: -x 仅仅对指定的模板进行填充 示例:
|
||||
upgrade |
升级一个Release到新版本Chart。你需要指定RELEASE和CHART参数。CHART参数可以是引用(reponame/chartname形式)或者是指向Chart所在位置的路径/URL 关于滚动更新: --recreate-pods,则无视deployment的更新策略设置,立即删除所有Pod,会导致downtime 仅仅当Deployment的Pod Spec发生变化,才可能触发滚动更新 格式: helm upgrade [RELEASE] [CHART] [flags] 选项: --dry-run 模拟运行 示例:
|
||||
verify |
校验指定的Chart已经签名且有效 格式: helm verify [flags] PATH |
Helm 3和Helm 2的主要区别:
- 移除了服务器端组件Tiller:由于RBAC从K8S 1.6开始默认启用,Tiller的功能已经没有必要。Helm 2中Release信息从Tiller读取,现在则直接从K8S API Server读取。Chart的渲染功能下沉到客户端进行
- 三方合并:在Helm 2中,升级、回滚时仅仅会对比新的资源清单、旧的资源清单,根据差异来决定需要增删改哪些资源。在Helm3中,资源的当前状态也被考虑。举例来说:
- 用户通过 kubectl scale -replicas=0 将副本置零,资源当前状态变化了。但是helm rollback可能没有任何效果(如果没有upgrade历史)
- 你安装了应用,其状态如下:
123containers:- name: serverimage: my_app:2.0.0然后,你安装了Istio,它会修改资源,添加Sidecar:
12345containers:- name: serverimage: my_app:2.0.0- name: istio-sidecarimage: istio-sidecar-proxy:1.0.0这时你通过Helm升级应用到2.1.0,资源变为:
123containers:- name: serverimage: my_app:2.1.0Istio的Sidecar消失了……在Helm3中不会出现这种问题,因为它会对旧资源清单、新资源清单、当前状态进行三方合并
- 将Secret作为默认存储驱动(Storage Driver):Helm 2 以ConfigMap作为默认存储驱动,Helm 3则以helm.sh/release类型的Secret作为存储驱动
- 默认情况下,必须提供Release名称,除非指定--generate-name
- helm serve功能被移除,它能提供一个本地运行的Chart仓库
- 不再自动创建namespace
- helm upgrade 可以指定--history-max,默认10,历史记录最大数量
- helm init、helm home、helm reset、helm serve废除
- helm delete 重命名为 helm uninstall;helm fetch 重命名为 helm pull;helm inspect 重命名为 helm show
- helm template <chart> -x <path_to_template> 改为 helm template <release_name> <chart> -s <path_to_template>
- XDG风格的客户端数据布局。Helm2将数据存储在$HELM_HOME(默认$HOME/.helm)。Helm3保持了对Helm2的兼容性,此外还支持使用如下布局:
- $HELM_PATH_CACHE,缓存位置,例如 ${HOME}/.cache/helm/
- $HELM_PATH_CONFIG,配置位置,例如 ${HOME}/.config/helm/。仓库定义存放在/home/alex/.config/helm/repositories.yaml
- $HELM_PATH_DATA,数据位置,例如 ${HOME}/.local/share/helm
- 对CRD的支持更好,Helm 2的crd-install钩子工作的不好,在Helm 3被移除。在Helm 3中CRD应当放在Chart的crds目录下,在安装Release时该目录下的CRD会自动安装,升级、删除的时候则不做操作
升级一个Release时,尚不存在的资源会创建,旧配置存在而新配置删除的资源则删除。逻辑复杂的时被修改的资源,Helm需要基于一定的策略,生成一个Patch。
Helm 2的策略比较简单,它只会比较新、旧配置(填充后的模板),其差异就是Patch。
Helm 3的策略较为复杂,Patch通过三方合并获得。三方合并逻辑由k8s.io/apimachinary提供,定义在/pkg/util/strategicpatch/patch.go中:
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 |
// 三方合并会调和(Reconcile)新配置、旧配置,同时保留当前对旧配置的变更、删除 // 也就是说,三方合并保证K8S或人工修改的配置信息不会丢失 // 旧 新 当前(Etcd中的实时状态) func CreateThreeWayMergePatch(original, modified, current []byte, schema LookupPatchMeta, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) { // 将旧配置、新配置、当前(可能被手工修改过的)配置转换为映射 originalMap := map[string]interface{}{} if len(original) > 0 { if err := json.Unmarshal(original, &originalMap); err != nil { return nil, mergepatch.ErrBadJSONDoc } } modifiedMap := map[string]interface{}{} if len(modified) > 0 { if err := json.Unmarshal(modified, &modifiedMap); err != nil { return nil, mergepatch.ErrBadJSONDoc } } currentMap := map[string]interface{}{} if len(current) > 0 { if err := json.Unmarshal(current, ¤tMap); err != nil { return nil, mergepatch.ErrBadJSONDoc } } // 得到新配置新增、修改字段,相当于当前配置引发的增量,行为: // 1. 新配置有的,当前配置没有的字段。这个应该不存在,K8S会把缺失的字段添上,因此当前配置字段最全 // 2. 新配置、当前配置都有的字段,且取值不一样,增量为新配置提供的值 // 3. 当前配置有,新配置没有的字段,不体现为增量(IgnoreDeletions: true) deltaMapDiffOptions := DiffOptions{ IgnoreDeletions: true, SetElementOrder: true, } deltaMap, err := diffMaps(currentMap, modifiedMap, schema, deltaMapDiffOptions) if err != nil { return nil, err } // 得到新配置删除字段,相对于旧配置引发的增量 // 使用$patch: delete这样的键值标记删除操作 deletionsMapDiffOptions := DiffOptions{ SetElementOrder: true, IgnoreChangesAndAdditions: true, } // 合并两个Patch deletionsMap, err := diffMaps(originalMap, modifiedMap, schema, deletionsMapDiffOptions) if err != nil { return nil, err } mergeOptions := MergeOptions{} patchMap, err := mergeMap(deletionsMap, deltaMap, schema, mergeOptions) if err != nil { return nil, err } return json.Marshal(patchMap) } |
以Nginx为例, 修改副本数、禁用持久化支持后。
其Deployment会生成如下Patch:
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 |
--- spec: # 修改 replicas: 3 template: spec: # 排序 "$setElementOrder/containers": - name: nginx "$setElementOrder/volumes": - name: lt-config - name: tz-config containers: - "$setElementOrder/volumeMounts": - mountPath: "/etc/localtime" - mountPath: "/etc/timezone" name: nginx volumeMounts: # 删除 - "$patch": delete mountPath: "/usr/share/nginx/html" volumes: # 删除 - "$patch": delete name: wwwdata-volume |
其Service则会生成如下Patch:
1 2 |
spec: clusterIP: '' |
此Patch尝试修改不可变字段clusterIP,则是无法成功的,将收到错误:spec.clusterIP: Invalid value: "": field is immutable。
这种尝试设置不可变字段为空值的Chart,在Helm 2中可能正常工作,因为Helm 2仅仅比对新旧配置。只要你没有后续设置clusterIP为非空就不会出问题。但是到了Helm 3中则肯定会出现问题,因为三方合并的关系,Helm 3认为需要将clusterIP从一个IP地址设置为空。
指定--force标记时,可以在升级时,进行必要的强制更新:
- 对于Helm 2,当PATCH操作失败时,会删除、再重建目标资源
- 对于Helm 3,会用PUT操作来替换(replace/overwrite)目标资源
如果三方合并出现问题,有可能通过强制更新解决。对于Helm 3来说,更多情况下无济于事,主要是K8S限制某些字段一旦创建即不可变更。
在Helm 3中,即使强制更新,你也可能遇到类似下面的错误:
- ersistentVolumeClaim "ng" is invalid: spec: Forbidden: is immutable after creation except resources.requests for bound claims
- failed to replace object: Service "ng" is invalid: spec.clusterIP: Invalid value: "": field is immutable
PUT操作解决不了不可变字段的问题,然而Helm 2删除后再创建,则规避了不可变字段问题,但会引发其它问题:
- PVC删除,PV级联删除么?数据怎么办
- Service删除,会导致暂时的服务不可用么?
- Chart的apiVersion从v1变更为v2。Helm 3仍然支持v1格式的Chart,但是Helm 2则不支持v2格式的Chart
- 模板变量校验:支持基于JSON Schema对模板变量值进行校验,校验规则存放在values.schema.json中
- 依赖声明方式变更:Helm 2中在requirements.yaml文件中声明依赖。现在直接放到Chart.yaml中
- 工具库Chart(Library charts):通用、助手类Chart用于提供所有Chart都会反复用到的助手函数、模板片段。现在Chart被分为application、library两种类型,前者产生正常的应用程序,后者仅仅用作application的依赖,管理方式和其它依赖一样,但是不会被安装
- 支持中心化的Chart仓库Helm Hub
Helm 3的编程接口更加简单,下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import ( "log" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli" ) func main() { settings = cli.New() // 配置信息 actionConfig := new(action.Configuration) err := actionConfig.Init(settings.RESTClientGetter(), namespace, os.Getenv("HELM_DRIVER"), log.Printf) if err != nil { log.Fatalf("Failed to get Helm action config: %v", err) } // 获取Get客户端 client := action.NewGet(actionConfig) // Get一个Release rel, err := client.Run("mysql") if err != nil { log.Fatalf("Failed to get run command: %v", err) } log.Printf("%+v", rel) } |
Helm 3 使用Go Modules进行依赖管理, 导入路径从k8s.io/helm改为helm.sh/helm。
Helm 3支持基于OCI的包分发,你可以将Chart存储在基于OCI的Registry(例如registry镜像)中。目前这个支持尚属试验特性,需要显式开启:
1 |
export HELM_EXPERIMENTAL_OCI=1 |
使用如下命令:
1 |
helm registry login -u alex docker.gmem.cc |
要登出,可以使用: helm registry logout
下面的命令将目录中的Chart保存到本地缓存:
1 |
helm chart save mychart/ docker.gmem.cc/myrepo/mychart:1.0.0 |
从本地缓存删除Chart:
1 |
helm chart remove localhost:5000/myrepo/mychart:1.0.0 |
下面的命令列出所有存储在OCI注册表中的Chart:
1 |
helm chart list |
从OCI注册表下载Chart到目录:
1 |
helm chart export docker.gmem.cc/myrepo/mychart:1.0.0 |
下面的命令推送本地缓存中的Chart到OCI仓库:
1 |
helm chart push docker.gmem.cc/myrepo/mychart:1.0.0 |
下面的命令将OCI仓库中的Chart下载到本地缓存:
1 |
helm chart pull localhost:5000/myrepo/mychart:1.0.0 |
要从Helm2基于index.yaml的仓库迁移到Helm3,可以使用如下命令序列:
1 2 3 |
helm fetch helm chart save helm chart push |
所有命令都可以通过 -n指定命名空间,主要列出和Helm2差异的地方:
子命令 | 说明 |
uninstall |
helm uninstall RELEASE_NAME [...] [flags] Helm3删除默认不保留历史,没有 --purge标记。如果要保留历史,需要显式指定 --keep-history |
install | helm install [NAME] [CHART] [flags] |
upgrade | helm upgrade [RELEASE] [CHART] [flags] |
rollback | helm rollback <RELEASE> [REVISION] [flags] |
list |
helm list [flags] 和Helm2不同,Helm3默认仅仅列出default命名空间中安装的release,要查看所有命名空间的release,使用标记 --all-namespaces。 此外,某些状态下的Release默认不显示,使用标记 -a, --all可以全部显示 |
这是一个开源的Helm Chart仓库,支持多种云存储作为后端,也可以使用本地存储。
CM提供很简单的RESTful API,你可以通过helm命令调用这些API:
API | 说明 |
GET /index.yaml |
添加仓库时Helm访问此API: helm repo add gmem https://chartmuseum.gmem.cc |
GET /charts/mychart-0.1.0.tgz |
安装一个Chart到K8S时,Helm访问此API: helm install chartmuseum/mychart |
GET /charts/mychart-0.1.0.tgz.prov | 使用--verify选项安装一个Chart到K8S时,Helm访问此API |
POST /api/charts | 上传一个Chart版本 |
POST /api/prov | 上传一个Provenance文件 |
DELETE /api/charts/mychart/0.1.0 | 删除一个Chart版本 |
GET /api/charts/mychart/0.1.0 | 描述一个Chart版本的详细信息 |
GET /api/charts | 列出所有Chart |
GET /api/charts/mychart | 列出Chart的所有版本 |
GET /health | 服务器健康状态探针 |
1 2 3 4 5 6 7 8 9 10 11 12 |
FROM chartmuseum/chartmuseum:latest ADD /etc /etc ENV PORT 443 ENV STORAGE local ENV STORAGE_LOCAL_ROOTDIR /chartstorage ENV BASE_AUTH_USER admin ENV BASE_AUTH_PASS admin ENV AUTH_ANONYMOUS_GET true ENV TLS_CERT /etc/domain.crt ENV TLS_KEY /etc/domain.key |
1 2 3 4 |
#!/bin/bash docker stop chartmuseum && docker rm chartmuseum docker run --name chartmuseum -h chartmuseum --network local --ip 172.21.0.10 --dns 172.21.0.1 \ --restart=always -d docker.gmem.cc/chartmuseum |
Monocular是管理K8S应用程序(仅限于Helm Chart打包格式)的Web GUI工具。
1 2 3 4 5 |
helm repo add monocular https://kubernetes-helm.github.io/monocular helm install monocular/monocular --name monocular --namespace kube-system helm fetch monocular/monocular --untar helm repo remove monocular cd monocular |
我直接修改了Chart源码,推荐使用helm -f values-overrides.yaml进行覆盖:
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 |
api: replicaCount: 1 image: # 镜像拉取策略全部改为: pullPolicy: IfNotPresent config: repos: # 私有仓库 - name: gmem url: https://chartmuseum.gmem.cc source: https://github.com/gmemcc/charts # 测试环境,减少副本 ui: replicaCount: 1 prerender: replicaCount: 1 # 基于HTTPS的入口路由规则 ingress: enabled: true hosts: - monocular.k8s.gmem.cc annotations: # 官网配置的 ingress.kubernetes.io/rewrite-target: / 在我的环境下无效 nginx.ingress.kubernetes.io/rewrite-target: / tls: secretName: gmemk8scert |
1 2 3 4 |
helm package --save monocular curl -X DELETE https://chartmuseum.gmem.cc/api/charts/monocular/0.5.0 curl --data-binary "@monocular-0.5.0.tgz" https://chartmuseum.gmem.cc/api/charts helm search gmem/ |
1 2 3 |
helm delete --purge monocular helm repo update gmem helm install gmem/monocular --name monocular --namespace=kube-system |
Kubeapps是一个用于部署、管理K8S应用程序的Web前端。特性包括:
- 浏览、部署Helm Chart
- 查看、升级、删除基于Helm的应用陈故乡
- 添加自定义的、私有的Chart仓库。支持Chartmuseum、JFrog Artifactory两类仓库
- 浏览并且Provision外部服务(基于K8S的Service Catalog机制)
- 利用Service Catalog Binding将Helm应用连接到外部服务
- 基于RBAC的身份验证和访问控制
Monocular项目的设计目标是,开发一个Helm Chart仓库的搜索、发现网站 —— 类似于Docker Hub那样。0.x版本的Monocular支持简单的应用发布,从1.0开始这些功能被移除了。
1 2 3 4 |
kubectl delete crd apprepositories.kubeapps.com kubectl -n devops delete secrets kubeapps-mongodb # gmem仓库地址:https://chartmuseum.gmem.cc/public helm install --name kubeapps --namespace devops gmem/kubeapps -f kubeapps/overrides/development.yaml |
提供WebUI,可以方便的浏览Chart,部署Release。还提供和Kubernetes Service Catalog的集成。
访问仪表盘时需要提供Kubernetes API Token:
1 2 |
kubectl -n kube-system get secret $(kubectl -n kube-system get serviceaccount admin \ -o jsonpath='{.secrets[].name}') -o jsonpath='{.data.token}' | base64 --decode |
保证和Tiller之间的通信的安全性,Dashboard通过此代理连接到Tiller。
这是一个CRD + 它的控制器。 Apprepository表示被Kubeapps管理的Chart仓库。
一个负责扫描Chart仓库、抓取Chart元数据并且存储到MongoDB中的工具。作为Monocular项目的一部分来维护。
读取上述MongoDB数据库,对外提供Chart元数据查询服务。
尝试helm install时出现此错误,可能原因是,本地仓库的索引太旧了。
删除Release时报此错误,或者卡死,可以使用--no-hooks:
1 |
helm delete yellow-vulture --purge --no-hooks |
注意:已经存在的K8S资源需要手工清理。
去掉参数:--reuse-values即可。可能是Bug。
要禁用,可以添加Tag:
1 |
journal max write bytes: !!string 1073714824 |
使用命令行传递值时,可以用 --set-string代替 --set,也可以规避此问题。
需要在运行Tiller的节点上安装socat:
1 |
yum install -y socat |
相同的命令直接在Agent的终端上执行,没有任何问题,但是在Jenkins流水线中执行,就会报错:
Error: UPGRADE FAILED: cannot re-use a name that is still in use
Error: YAML parse error on error converting YAML to JSON: yaml: line 39: did not find expected key
最终没有找到原因,但是把Value中的双引号去掉就可以了:
1 2 3 |
/helm upgrade kube-system-health-controller health-controller --namespace=kube-system \ # 去掉双引号 --install --force --wait --timeout=1800 --set replicas=2,image.registry="registry.k8s.eb.mid/ao",args.cniCheckPeriod=15,args.dnsCheckPeriod=15,args.dnsCheckDomains="kubernetes.default\,e.session.api\,e.login.api",args.nodeSshPswd=dell1950,args.verboseLevel=1 |
Leave a Reply