Helm是K8S集群下面的一个包管理器,经过其工程师可将应用打包成一个总体,而用户可以使用helm安装打包后的应用,其功能相似于apt-get
之于ubuntu系统、yum/dnf
之于redhat
系统。本文做者将讲述如何经过helm打包应用,以及如何使用其部署应用,但读者须先了解K8S基础知识,如Deployment、Satefulset、Service、Configmap、Secret、PV/PVC等,不然可先移步“迈入Docker、Kubernetes容器世界的大门”这里。linux
本节使用清单文件部署一测试用例,其含两个服务hello、greeter,hello依赖于greeter,调用关系以下所示:git
<http get> --------> (hello) --------> (greeter)
执行以下命令clone仓库1,然后将示例部署于demo命名空间中。github
git clone https://github.com/zylpsrs/helm-example.git cd helm-example kubectl create namespace demo kubectl -n demo apply -f k8s
为简单考虑,本文示例仅使用deployment、ingress、service这些技术部署且内容很简单,k8s目录以下所示:shell
$ tree k8s/ k8s/ ├── deploy-greeter.yaml # 部署greeter ├── deploy-hello.yaml # 部署hello ├── ing-hello.yaml # 为hello服务建立ingress,故集群需部署有ingress控制器 ├── svc-greeter.yaml # 为greeter建立服务 └── svc-hello.yaml # 为hello建立服务
以下所示,本节在demo命名空间中部署有以下对象:json
$ kubectl -n demo get deploy,svc,ingress,pod NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/greeter 1/1 1 1 20h deployment.apps/hello 1/1 1 1 20h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/greeter ClusterIP 10.100.44.79 <none> 9080/TCP 20h service/hello ClusterIP 10.106.116.73 <none> 9080/TCP 20h NAME CLASS HOSTS ADDRESS PORTS AGE ingress.extensions/hello <none> hello.app.zyl.io 192.168.120.6 80 20h NAME READY STATUS RESTARTS AGE pod/greeter-7cbd47b5bd-6vtxk 1/1 Running 1 20h pod/hello-6f8f75f799-z8kgj 1/1 Running 1 20h
若集群安装有ingress controller,则可经过ingress地址访问hello服务,不然在计算节点上经过其service地址访问,以下所示,咱们调用hello服务时传递helloTo=<value>
参数,其返回Hello, <value>!字符串。ubuntu
$ curl http://hello.app.zyl.io/?helloTo=Ingress Hello, Ingress! $ curl http://10.106.116.73:9080/?helloTo=Service Hello, Service!
上面经过清单文件部署了测试用例,了解了此测试用例功能,那么,本文接着将使用helm打包两应用,并使用其部署到集群中。为了后续helm部署测试,执行以下命令删除部署的应用。segmentfault
kubectl delete -f k8s
首先,执行以下命令安装helm
,这里选择v3版本:api
wget -O helm.tgz https://get.helm.sh/helm-v3.2.3-linux-amd64.tar.gz tar pxz helm.tgz -C /usr/local/bin/ --strip-components=1 chmod 755 /usr/local/bin/helm
执行以下命令分别将hello、greeter打包2,其将生成两个目录。app
helm create hello helm create greeter
注意:helm
将以模板填充目录,故可参照模板按需调整。咱们在values.yaml文件中定义变量,于模板文件中经过{{ <var> }}引用变量,而_helpers.tpl文件可定义一些模板,其支持条件判断等语法。curl
$ tree hello hello ├── charts # 此chart依赖于哪些chart,则至于此目录下 ├── Chart.yaml # 此文件含Chart元数据信息,如名称、版本,若无特别须要可保持默认 ├── templates # 模板,此目录含部署清单文件,按需增减清单文件 │ ├── deployment.yaml │ ├── _helpers.tpl # 此文件中可定义一些模板,可经过include或template引用 │ ├── hpa.yaml │ ├── ingress.yaml │ ├── NOTES.txt │ ├── serviceaccount.yaml │ ├── service.yaml │ └── tests │ └── test-connection.yaml └── values.yaml # 变量配置文件,此文件定义变量以.Values开始
可选。以greeter为例配置图表的Chart.yaml文件,于其中添加元数据信息,其可经过.Chart.<var>被引用。
# greeter dir: $ cat > Chart.yaml <<'EOF' # 这几个字段模板中默认包含 apiVersion: v2 name: greeter description: Receive messages from http and return welcome message type: application version: 0.1.0 appVersion: 1.0.0 # 可添加一些维护者等信息 maintainers: - email: zylpsrs@sina.cn name: Yanlin. Zhou sources: - https://github.com/zylpsrs/helm-example.git keywords: - helm - deployment #home: https://github.com/zylpsrs/helm-example #icon: EOF
配置values.yaml文件于其中添加自定义变量信息,对于模板生成的默认信息咱们按需增减。注意:为尽可能少调整模板配置,对于咱们不使用的配置最好不要删除。
# greeter dir: $ vi values.yaml replicaCount: 1 image: repository: registry.cn-hangzhou.aliyuncs.com/zylpsrs/example/greeter pullPolicy: IfNotPresent tag: latest service: type: ClusterIP port: 9080 EOF
# hello dir: $ vi values.yaml replicaCount: 1 image: repository: registry.cn-hangzhou.aliyuncs.com/zylpsrs/example/hello pullPolicy: IfNotPresent tag: latest service: type: ClusterIP port: 9080 ingress: enabled: false hosts: - host: hello.app.zyl.io paths: [/] greeter: enabled: true replicaCount: 2 service: port: 9080
templates/service.yaml为service配置文件,对于两应用咱们无需修改保持默认便可,其配置经过引用values.yaml的变量与_helpers.tpl中的模板来完善,以下所示:
# templates/service.yaml apiVersion: v1 kind: Service metadata: name: {{ include "greeter.fullname" . }} # 引用自_helpers.tpl文件 labels: {{- include "greeter.labels" . | nindent 4 }} # 引用自_helpers.tpl文件 spec: type: {{ .Values.service.type }} # 此处引用values.yaml中得service.type ports: - port: {{ .Values.service.port }} # 此处引用values.yaml中得service.port targetPort: http protocol: TCP name: http selector: {{- include "greeter.selectorLabels" . | nindent 4 }} # 引用自_helpers.tpl文件
service配置虽无需修改,但其名称引用自_helpers.tpl中的模板名则需知晓,对于默认生成的greeter.fullname模板其定义有些复杂,但其值通常等于.Release.Name-.Chart.Name,如若安装chart时指定其Release名为test,则greeter.fullname为test-greeter。
# templates/_helpers.tpl ... {{- define "greeter.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} ...
参考k8s/deploy-hello.yaml部署清单文件,其经过环境变量GREETER=http://greeter:9080
设置greeter地址,如若经过helm安装greeter,其服务名称引用模板greeter.fullname的值为.Release.Name-greeter,那么于hello图表中,咱们为greeter服务在_helpers.tpl中定一个模板名称,其值等于.Release.Name-greeter。
# templates/_helpers.tpl {{- define "hello.greeter.fullname" -}} {{- $name := default "greeter" .Values.greeter.nameOverride -}} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} {{- end -}}
两应用采用deployment无状态部署,此处咱们调整templates/deployment.yaml文件:
# 调整以下配置的容器端口: ports: - name: http containerPort: 80 protocol: TCP # 容器端口调整为9080: ports: - name: http containerPort: 9080 protocol: TCP
# 删除以下项: livenessProbe: httpGet: path: / port: http readinessProbe: httpGet: path: / port: http
# 添加环境变量信息: env: - name: GREETER {{- if .Values.greeter.enabled }} value: 'http://{{ template "hello.greeter.fullname" . }}:9080' {{- else }} value: 'http://greeter:9080' {{- end }}
templates/ingress.yaml为ingress配置文件,在图表中可设置ingress.enabled=true
启用ingress,对于此文件保持默认无需修改,而若图表不想提供ingress配置功能,可将此文件删除。
当经过helm部署图表时,其渲染templates/NOTES.txt文件以打印备注信息,通常咱们无需修改此文件,但对于本例,咱们将容器端口80调整为9080。
最后,将以下依赖关系添加hello图表的requirements.yaml或Chart.yaml(推荐)文件中,这样当经过chart安装hello时,其将自动安装greeter应用,同时,咱们还须要将greeter目录拷贝到hello/charts目录。
# hello dir: $ cat >> Chart.yaml <<EOF # 依赖关系 dependencies: - name: greeter # 版本,以下匹配0.开头的全部版本 version: 0.x.x # helm仓库地址,如同image镜像,helm也可推送到仓库,此仓库咱们后续再配置 repository: http://chartmuseum.app.zyl.io # 经过此条件判断是否依赖于此应用 condition: greerer.enabled # 可为此应用添加一些标签 tags: - http EOF $ cp -a ../greeter charts/ $ helm dep list # 检查依赖关系,显示正常 NAME VERSION REPOSITORY STATUS greeter 0.x.x http://chartmuseum.app.zyl.io unpacked
至此,咱们已为hello、greeter分别部署了图表,而hello依赖于greeter,故在Chart.yaml文件中为其配置了依赖关系,其中指定的仓库名称后续咱们再配置。
咱们使用helm
部署hello图表,其将自动部署其依赖的greeter应用,在hello图表目录下,执行以下命令建立一个名为test的实例:
$ helm -n demo install test . NAME: test LAST DEPLOYED: Thu Jun 18 15:51:13 2020 NAMESPACE: demo STATUS: deployed REVISION: 1 NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods --namespace demo -l "app.kubernetes.io/name=hello,app.kubernetes.io/instance=test" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace demo port-forward $POD_NAME 8080:9080
咱们可执行helm list
查看当前命名空间中经过helm
部署的实例:
$ helm list -n demo NAME NAMESPACE ... STATUS CHART APP VERSION test demo ... deployed hello-0.1.0 1.0.0
以下所示,安装hello图表时也安装了greeter应用,由于在hello图表的values.yaml文件中配置greeter.replicaCount=2
,故此处greeter部署了2个pod。
$ kubectl -n demo get svc,ingress,deploy,pod NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/test-greeter ClusterIP 10.99.226.226 <none> 9080/TCP 11m service/test-hello ClusterIP 10.105.187.216 <none> 9080/TCP 11m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/test-greeter 2/2 2 2 11m deployment.apps/test-hello 1/1 1 1 11m NAME READY STATUS RESTARTS AGE pod/test-greeter-695ffd859d-pwc7s 1/1 Running 0 11m pod/test-greeter-695ffd859d-r2gcw 1/1 Running 0 11m pod/test-hello-779565bb5d-rdwbh 1/1 Running 0 11m $ curl http://10.105.187.216:9080/?helloTo=Helm Hello, Helm!
如同image镜像,咱们也可将helm图表推送到仓库中以利于分发与部署,下面咱们则搭建一个私有helm仓库并将chart推送到仓库中,然后使用此仓库获取chart并部署应用,但在此前,执行以下命令将当前部署的实例删除。
$ helm -n demo uninstall test release "test" uninstalled
参考Helm官方文档The Chart Repository Guide可知有不少方式来搭建helm仓库,本文安装chartmuseum做为helm仓库,可是,如若已部署有harbor镜像仓库,则其已支持helm仓库,故无需从新为helm单独部署仓库了。
下面使用helm将chartmuseum安装于独立的命令空间中,首先添加仓库3:
$ helm repo add stable https://kubernetes-charts.storage.googleapis.com
为保证推送到仓库的charts持久化,咱们需为chartmuseum提供一个持久化存储卷,如若读者参考此文章”使用kubeadm搭建一单节点k8s测试集群“部署测试k8s集群,则咱们集群上将有一个nfs类型的默认持久化存储。
$ oc get storageclass NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ... nfs (default) cluster.local/nfs-server-provisioner Delete Immediate ...
注意:若容器若使用nfs卷,则集群节点需安装nfs客户端,不然将没法挂载nfs。
$ yum -y install nfs-utils
执行以下命令将chartmuseum部署到单独的命名空间helm-repo中,为简单考虑,这里不为仓库配置任何验证:任何人都可获取与上传chart包,若集群配置有ingress controler,则可为仓库配置ingress,此处主机名称配置为chartmuseum.app.zyl.io。
$ kubectl create namespace helm-repo $ cat > /tmp/values.yaml <<'EOF' env: open: STORAGE: local # 默认禁用API操做,此处必须关闭,不然咱们无法经过API管理chart DISABLE_API: false # 容许上传相同版本的chart ALLOW_OVERWRITE: true persistence: enabled: true accessMode: ReadWriteOnce size: 8Gi ingress: enabled: true hosts: - name: chartmuseum.app.zyl.io path: / tls: false EOF $ helm -n helm-repo install -f values.yaml mychart stable/chartmuseum
待pod启动成功后,参考文档咱们以其API接口上传一个测试chart到仓库,以下所示:
$ helm create mychart $ helm package mychart Successfully packaged chart and saved it to: /tmp/mychart-0.1.0.tgz # upload: $ curl --data-binary "@mychart-0.1.0.tgz" http://chartmuseum.app.zyl.io/api/charts {"saved":true} # list all charts: $ curl http://chartmuseum.app.zyl.io/api/charts {"mychart":[{"name":"mychart","version":"0.1.0",...]}
使用curl
工具显示经过API管理仓库不友好,咱们可安装[helm-push]()插件,然后执行helm push
将上文建立的图表上传到仓库,以下所示:
$ helm plugin install https://github.com/chartmuseum/helm-push.git $ helm repo add mychart http://chartmuseum.app.zyl.io $ cd hello && helm push . mychart $ cd .. && helm push greeter mychart # or $ helm push greeter http://chartmuseum.app.zyl.io # list all charts: $ helm repo update # 先同步仓库信息 $ helm search repo mychart NAME CHART VERSION APP VERSION DESCRIPTION mychart/mychart 0.1.0 1.16.0 A Helm chart for Kubernetes mychart/greeter 0.1.0 1.0.0 Receive messages from http and return ... mychart/hello 0.1.0 1.0.0 Receive messages from http and return ...
如今咱们部署了helm仓库,已将chart上传到仓库中,而且经过helm repo add
将仓库添加到了本地helm仓库中,那么本节咱们就用此仓库安装图表。以下所示,咱们但愿为hello应用部署ingress,故经过参数--set key=value
或-f file
覆盖默认配置。
$ cat > /tmp/values.yaml <<EOF ingress: enabled: true hosts: - host: hello.app.zyl.io paths: [/] EOF $ helm -n demo install -f /tmp/values.yaml test mychart/hello
以下所示,此时demo命名空间中部署有ingress对象,咱们经过其访问应用:
$ oc get ing,pod NAME CLASS HOSTS ADDRESS PORTS AGE ingress.extensions/test-hello <none> hello.app.zyl.io 192.168.120.6 80 78s NAME READY STATUS RESTARTS AGE pod/test-greeter-695ffd859d-5mkvk 1/1 Running 0 78s pod/test-greeter-695ffd859d-hqqtc 1/1 Running 0 78s pod/test-hello-779565bb5d-2z9jv 1/1 Running 0 78s $ curl http://hello.app.zyl.io/?helloTo=helmRepo Hello, helmRepo!
经过本文咱们学习了如何经过helm打包复杂的应用,虽然示例并不复杂,但其具有此能力,然而helm适合无状态应用,其具有第一天安装应用的能力,但次日应用维护操做(如升级/备份)比较无能为力,鉴于此,做者将在后面讲解operator技术来处理此复杂应用。