使用helm將複雜應用打包並部署到k8s集羣中

前言

HelmK8S集羣下面的一個包管理器,經過其工程師可將應用打包成一個總體,而用戶可以使用helm安裝打包後的應用,其功能相似於apt-get之於ubuntu系統、yum/dnf之於redhat系統。本文做者將講述如何經過helm打包應用,以及如何使用其部署應用,但讀者須先了解K8S基礎知識,如DeploymentSatefulsetServiceConfigmapSecretPV/PVC等,不然可先移步「邁入Docker、Kubernetes容器世界的大門」這裏。linux

常規部署應用

本節使用清單文件部署一測試用例,其含兩個服務hellogreeterhello依賴於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

爲簡單考慮,本文示例僅使用deploymentingressservice這些技術部署且內容很簡單,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打包

首先,執行以下命令安裝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

執行以下命令分別將hellogreeter打包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配置以下,部署一個replicat,鏡像名爲greeter:latestservice端口爲9080,其使用ClusterIP方式:
# 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配置與greeter相似,咱們調整了默認ingress配置(雖然未啓用),因其依賴於greeter,咱們於其中添加greeter信息,這裏咱們設置了一些變量以覆蓋greeter默認的配置,如設置replicas2
# 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.yamlservice配置文件,對於兩應用咱們無需修改保持默認便可,其配置經過引用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.fullnametest-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文件:

  • 兩應用均監聽在9080端口,故調整容器端口爲9080
# 調整以下配置的容器端口:
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
# 容器端口調整爲9080:
          ports:
            - name: http
              containerPort: 9080
              protocol: TCP
  • 爲求簡單,咱們不配置存活與就緒探針,刪除livenessProbereadinessProbe項。
# 刪除以下項:
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
  • hello依賴於greeter,在hello部署清單中設置環境變量GREETER:若hello部署清單中啓用了greeter服務,則服務名稱引用至模板名hello.greeter.fullname,不然爲greeter
# 添加環境變量信息:
          env:
            - name: GREETER
            {{- if .Values.greeter.enabled }}
              value: 'http://{{ template "hello.greeter.fullname" . }}:9080'
            {{- else }}
              value: 'http://greeter:9080'
            {{- end }}

templates/ingress.yamlingress配置文件,在圖表中可設置ingress.enabled=true啓用ingress,對於此文件保持默認無需修改,而若圖表不想提供ingress配置功能,可將此文件刪除。

當經過helm部署圖表時,其渲染templates/NOTES.txt文件以打印備註信息,通常咱們無需修改此文件,但對於本例,咱們將容器端口80調整爲9080

最後,將以下依賴關係添加hello圖表的requirements.yamlChart.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

至此,咱們已爲hellogreeter分別部署了圖表,而hello依賴於greeter,故在Chart.yaml文件中爲其配置了依賴關係,其中指定的倉庫名稱後續咱們再配置。

使用Helm部署

咱們使用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部署了2pod

$ 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倉庫

參考Helm官方文檔The Chart Repository Guide可知有不少方式來搭建helm倉庫,本文安裝chartmuseum做爲helm倉庫,可是,如若已部署有harbor鏡像倉庫,則其已支持helm倉庫,故無需從新爲helm單獨部署倉庫了。

下面使用helmchartmuseum安裝於獨立的命令空間中,首先添加倉庫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技術來處理此複雜應用。


  1. repo: 讀者可今後倉庫上獲取本文全部代碼
  2. chart: 包在helm中表示爲chart
  3. crepo: helm項目地址在https://github.com/helm/chart...
相關文章
相關標籤/搜索