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技術來處理此複雜應用。