在Kubernetes环境中使用Helm来管理应用颇为方便,在教程的这部分,我们一起来开发一个Chart(简单说就是一个软件包),这样的实战可加深对Chart的理解,今后在使用第三方的Chart时,不论学习还是修改都会更加得心应手。
- 在k8s中,一般是通过yaml文件把要创建的service、pod等定义好,很可能会产生多个yaml文件,将这些文件放在一个目录中,就变成一个chart了。
环境的版本信息如下,请确保以下软件都已运行正常:
- 操作系统 :Ubuntu 20.04.3 LTS (查看命令:
lsb_release -a
) - Kubernetes:v1.23.5(查看命令:
kubectl version
) - Helm:v3.8.1(查看命令:
helm version
)
1、创建并发布一个简单的Helm Chart
注意:yaml格式文件修改需要注意的几个地方:
- 使用2个空格作缩进;
- 确认数字为字符类型时,使用双引号引起来;
- 为了迎合helm3的规范,空定义最好将相关符号补上:
string: ""
,list: []
,map: {}
。
1.1 构建一个Helm Chart模板
执行
helm create mychart
命令,会得到一个 helm 自动生成的空 chart。这个 chart 里的名称是 mychart。 使用tree mychart
命令可以查看mychart的目录结构。
- 注意: Chart.yaml 里面的 name 名称需要和生成的 Chart 文件夹名称一致(本例是mychart)。如果修改 mychart,则需要做一致的修改。
mychart/
Chart.yaml # 用于描述 chart 信息的 yaml 文件
LICENSE # 可选:用于存储关于 chart 的 LICENSE 文件
README.md # 可选:README 文件
values.yaml # 用于存储 chart 所需要的默认配置
values.schema.json # 可选: 一个使用JSON结构的 values.yaml 文件
charts/ # 包含 chart 所依赖的其他 chart
crds/ # 自定义资源的定义
templates/ # chart 模板文件,引入变量值后可以生成用于 Kubernetes 的 manifest 文件
templates/NOTES.txt # 可选: 包含简短使用说明的纯文本文件,会在用户运行 `helm install` 时显示给用户。
templates/_helpers.tpl # 放置可以通过chart复用的模板辅助对象(模板助手),所有模板都可以在这里定义,然后在任何yaml文件当中都可以调用这个文件下的模板。
templates/deployment.yaml #kubernetes Deployment object,创建Kubernetes 工作负载的基本清单。
templates/service.yaml #kubernetes Serivce,为你的工作负载创建一个service终端基本清单。
从这里开始,我们将在 mychart 目录中工作。由于创建的chart已经是一个nginx示例,所以它是可以运行的。至此,我们的创建的第一个chart就完成了,是不是很简单。
1.2 修改Chart.yaml
在根目录下的 Chart.yaml 文件内,声明了当前 Chart 的名称、版本等基本信息,这些信息会在该 Chart 被放入仓库后,供用户浏览检索。
- apiVersion k8s api 版本
- name chart名,英文开头,支持小写字母,数字,减号(-)
- version chart 版本,也就是我们应用包的发布版本,版本必须遵循 SemVer 2 标准
- appVersion 内部程序版本即代码版本(内部实际使用的应用版本)
- description 描述
- keywords 分类,关键词
- home 应用程序官网
- icon 图标,支持 url 和 base64
- maintainers 作者
- sources 应用程序来源
使用命令 vim mychart/Chart.yaml
,编写mychart的应用描述信息。修改后的内容如下:
- 如有必要可以修改app版本和包版本,一般情况不做修改
- keywords、home、icon、maintainers、sources是加上去的属性(非必填),自动生成的脚本中没有。
root@xnzysq20210118001:~# vim mychart/Chart.yaml
apiVersion: v2 # 当前helm api版本,不需要修改
name: mychart # 模板名,对应目录名 [*]
description: helm nginx demo # 介绍此chart是干嘛的,按需求修改
type: application
version: 0.1.0 # 此chart版本号 [*]
appVersion: 1.16.0 # 此处为你应用程序的版本号 [*]
keywords: # 关键字列表,可以有0到多个 [*]
- Nginx
- http
- web
- www
home: http://www.nginx.org # 应用程序官网 [*]
icon: http://nginx.org/nginx.png # 应用程序logo地址 [*]
maintainers: # 维护人员列表 [*]
- name: kongzi
email: kongzi@demo.com
sources: # 应用程序来源 [-]
- https://github.com/bitnami/bitnami-docker-nginx
1.3 校验打包
可以使用
Helm lint
来粗略地检查一下制作的 Chart 有没有什么语法上的错误。如果没有问题的话,就可以使用helm package
命令对我们的 Chart 文件夹进行打包,打包后我们可以得到一个 mychart-0.1.0.tgz 的应用包。这个便是我们完成的应用了。
- 打包好的 chart 可以上传到 chart 仓库中。
# 检查依赖和模板配置是否正确
helm lint mychart/
# 打包
helm package mychart/
1.4 安装/发布Chart
可以使用
helm install
命令尝试安装一下刚刚做好的应用包,使用helm list
或helm ls
命令查看 release(会列出所有被发布的Chart)。
- 可以使用
helm uninstall
命令卸载 release 。
# 安装
helm install demo mychart-0.1.0.tgz
# 列出 release(所有被发布的Chart), 还可以用 helm ls
helm list
1.5 映射宿主机端口到pod端口
用
kubectl get pods
命令查看一下运行 pod 的状态,通过kubectl port-forward
命令将该 pod 的端口映射到宿主机端口上,这个时候就可以通过访问 localhost 来访问部署好的应用了。
- kubectl port-forward 通过端口转发映射宿主机端口到指定的应用端口,从而访问集群中的应用程序(Pod).
# 查看运行 pod 的状态, 找到pod的name
kubectl get pods
# 把pod或者其他资源的端口映射到宿主机端口,宿主机端口在前,81是宿主机端口,80是pod容器端口
# demo-mychart-6df56fb8df-w8x6l 为 pod 的 name
# 下面的命令只能使用localhost或127.0.0.1访问
kubectl port-forward demo-mychart-6df56fb8df-w8x6l 81:80
# kubectl port-forward 设置参数--address 0.0.0.0, K8S会监听任何地址,可以使用K8S节点的IP地址去访问
kubectl port-forward --address 0.0.0.0 demo-mychart-6df56fb8df-w8x6l 81:80
解决办法:
kubectl run nginx --image nginx:latest
从上图我们可以发现,端口转发报错unable to do port forwarding: socat not found
,解决办法是在 k8s 所有node节点上安装socat,安装命令:apt-get install socat
。
- socat是一个多功能的网络工具,名字来由是” Socket CAT”,可以看作是netcat的N倍加强版,socat的官方网站:http://www.dest-unreach.org/socat/ 。
安装socat完成后,再次执行 kubectl port-forward
命令。
使用命令 curl 127.0.0.1:81
访问,成功了,但是从别的主机使用浏览器无法访问!!!
kubectl port-forward 设置参数 --address 0.0.0.0
, K8S会监听任何地址,可以使用K8S节点的IP地址去访问
运行命令:kubectl port-forward --address 0.0.0.0 demo-mychart-6df56fb8df-w8x6l 81:80
。
- 该端口仅在kubectl进程运行时转发,因此您可以杀死转发端口的kubectl进程。在大多数情况下,这只是意味着在运行port-forward命令的终端中按CTRL + C.
- 也可以使该命令在后台运行,即在末尾加
&
, 完整命令kubectl port-forward --address 0.0.0.0 demo-mychart-6df56fb8df-w8x6l 81:80 &
,如果需要结束转发,使用ps -ef|grep port-forward
命令查看进程,再使用kill -9 进程号
杀掉进程即可。
从别的主机使用浏览器访问:
1.6 卸载release
如果不需要的release,我们可以通过 helm uninstall RELEASE_NAME
命令卸载。可以通过 helm status
命令查看release状态。
# helm list 或 helm ls 命令会列出所有release
helm ls
# 查看名称为demo的release状态
helm status demo
# 卸载名称为demo的release
helm uninstall demo
# 查看名称为demo的release状态
helm status demo
2、编写Helm Chart模板
所有模板文件都存储在 chart 的 templates/ 文件夹中。当 Helm 渲染 charts 时,会通过模板引擎将所有文件发送到 templates/ 目录中,然后渲染模板并发送给Kubernetes。模板的值有两种提供方法:
- chart 开发人员可能会在 chart 内部提供一个 values.yaml 文件。该文件可以包含默认值。
- chart 用户可能会提供一个包含值的 YAML 文件。这可以通过命令行提供 helm install -f。
上例中的模板templates是自动生成的示例,看一下 mychart/templates/ 目录,发现如下几个文件:
- NOTES.txt:chart 的 “帮助文件”。这会在用户运行
helm install
时显示给用户。 - deployment.yaml:创建Kubernetes 工作负载的基本清单。
- service.yaml:为你的工作负载创建一个service终端基本清单。
- _helpers.tpl:放置可以通过chart复用的模板辅助对象(模板助手),所有模板都可以再这里定义,然后再任何yaml文件当中都可以调用这个文件下的模板。
为了方便我们从头开始学习,现在要做的就是… 全部删除它们!我们开始时会创建自己的 NOTES.txt 和_helpers.tpl。
- 在编写生产级 chart 时,使用这些 chart 的基本版本可能非常有用。所以在你的日常 chart 制作中,可以不删除它们。
rm -rf mychart/templates/*
2.1 写一个chart模板
我们要创建的第一个模板将是一个 ConfigMap配置集。在 Kubernetes 中,ConfigMap 只是存储配置数据的地方。其他的东西,比如 Pod,可以访问 ConfigMap 中的数据。
由于 ConfigMaps 很简单,所以让我们入门简单很多。
1)创建一个名为 mychart/templates/configmap.yaml的文件
- 提示: 模板名称不遵循严格的命名模式。但是建议: .yaml 为 YAML 文件后缀,.tpl 为模板助手后缀。
# 创建编辑文件
vim mychart/templates/configmap.yaml
# 查看文件内容
cat mychart/templates/configmap.yaml
configmap.yaml文件内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
- 上面的 YAML 文件是一个简单的 ConfigMap,具有最少的必要字段。由于该文件位于 mychart/templates/ 目录中,因此将通过模板引擎发送。
- 像这样将一个普通YAML文件放在mychart/templates/目录中是没问题的。当Helm读取这个模板时会按照原样传递给Kubernetes。
2)安装 chart
有了这个简单的模板,我们就有了一个可安装的 chart,chart安装好后,我们称其为一个release:
# release名称为hello
root@xnzysq20210118001:~# helm install hello mychart
# 以下为命令执行后终端的输出
NAME: hello
LAST DEPLOYED: Sat Apr 16 12:12:19 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
在上面的输出中,可以看到我们的 ConfigMap 已经创建。 注意hello这个名字是我们指定的,如果想要随机生成 name
需要用参数 --generate-name
。
3)查看release
使用 helm ls
命令查看release信息,可以看到 release name
为 hello
。
root@xnzysq20210118001:~# helm ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
hello default 1 2022-04-16 16:17:51.061166221 +0800 CST deployed mychart-0.1.0 1.16.0
4)查看template
使用 helm get manifest hello
检索版本并查看 helm chart 实际加载的模板(hello为release的名称)。
root@xnzysq20210118001:~# helm get manifest hello
# 以下是输入指令后终端返回的结果
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
该 helm get manifest + release 名称(hello)
命令打印出了所有已经上传到服务器的Kubernetes 资源。
- 每个文件以—开头,表示YAML文件的开头,然后是自动生成的注释行,表示哪个模板文件生成了这个YAML文档。
- 从apiVerison那行开始,我们看到的YAML数据确实是configmap.yaml文件中的内容。
5)卸载release
现在卸载release: helm uninstall hello
,命令中的hello为release的名称。
2.2 通过模板命令调用变量
通过上面的理解后,我们开始进一步定义一些变量,然后给变量赋值。
1)修改 configmap.yaml 文件
看一下下面的yaml文件。
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
这个文件硬编码 name字段值为
mychart-configmap
,这里的硬编码肯定是不好的做法。名称应该是唯一的一个版本。所以我们希望通过插入 release 名称来生成一个名称字段。
- 提示: 由于 DNS 系统的限制,该name字段长度限制为 63 个字符。因此,release 名称限制为 53 个字符。Kubernetes 1.3 及更早版本仅限于 24 个字符(名称长度 14 个字符名称)。
让我们改一下 configmap.yaml。
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
name值发生了变化 ,改成了 {{ .Release.Name }}-configmap
。
- 模板命令要括在 {{ 和 }} 之间。
- 模板命令 {{ .Release.Name }} 是将 release 名称注入模板。传递给模板的值可以认为是 namespace 对象,其中 dot(.)分隔每个 namespace 元素。
- Release 前面的前一个小圆点表示作用域最顶层的命名空间开始。这样
.Release.Name
"从顶层命名空间开始,找到 Release 对象,然后在里面查找名为 Name 的对象"。 - 该 Release 对象是 Helm 的内置对象之一。但就目前而言,这足以说明这会显示 Tiller 分配给我们发布的 release 名称。
2)安装 chart
现在,当我们安装我们的资源时,我们会立即看到使用这个模板指令的结果:
root@xnzysq20210118001:~# helm install hello mychart
NAME: hello
LAST DEPLOYED: Sat Apr 16 16:34:41 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
3)查看template
使用 helm get manifest hello
检索版本并查看生成的完整的YAML(hello为release的名称)。
root@xnzysq20210118001:~# helm get manifest hello
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-configmap
data:
myvalue: "Hello World"
从终端返回的结果可以看到,kubernetes内的配置映射名称是 hello-configmap
,而不是之前的 mychart-configmap
。
- 由此我们已经看到了最基本的模板:YAML文件有嵌入在{{ 和 }}之间的模板命令。
2.3 helm模板测试
写好了helm怎么做测试呢?有一个快捷的技巧可以加快模板的构建速度:当你想测试模板渲染的内容但又不想安装任何实际应用时,可以使用 helm install --debug --dry-run goodly-guppy mychart
。这样就不会安装应用(chart)到你的kubenetes集群中,只会渲染模板内容到控制台(用于测试)。渲染后的模板如下:
- 也可以使用命令
helm install solid-vulture ./mychart --dry-run --debug
root@xnzysq20210118001:~# helm install --debug --dry-run goodly-guppy ./mychart
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /root/mychart
NAME: goodly-guppy
LAST DEPLOYED: Sat Apr 16 16:45:31 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
affinity: {}
autoscaling:
enabled: false
maxReplicas: 100
minReplicas: 1
targetCPUUtilizationPercentage: 80
fullnameOverride: ""
image:
pullPolicy: IfNotPresent
repository: nginx
tag: ""
imagePullSecrets: []
ingress:
annotations: {}
className: ""
enabled: false
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
nameOverride: ""
nodeSelector: {}
podAnnotations: {}
podSecurityContext: {}
replicaCount: 1
resources: {}
securityContext: {}
service:
port: 80
type: ClusterIP
serviceAccount:
annotations: {}
create: true
name: ""
tolerations: []
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: goodly-guppy-configmap
data:
myvalue: "Hello World"
- 使用 --dry-run 可以更容易地测试代码,但不能确保 Kubernetes 本身会接受生成的模板。最好不要假定你的 chart 只要 --dry-run 成功而被安装。
评论区