溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

k8s之helm

發(fā)布時(shí)間:2020-06-25 01:37:58 來(lái)源:網(wǎng)絡(luò) 閱讀:840 作者:wuseeger 欄目:云計(jì)算
Kubernetes 的包管理器。

每個(gè)成功的軟件平臺(tái)都有一個(gè)優(yōu)秀的打包系統(tǒng),比如 Debian、Ubuntu 的 apt,Redhat、Centos 的 yum。而 Helm 則是 Kubernetes 上的包管理器。

Helm,架構(gòu)和組件,以及如何使用 Helm。

Why Helm

Helm 到底解決了什么問題?為什么 Kubernetes 需要 Helm?

答案是:Kubernetes 能夠很好地組織和編排容器,但它缺少一個(gè)更高層次的應(yīng)用打包工具,而 Helm 就是來(lái)干這件事的。

先來(lái)看個(gè)例子。
比如對(duì)于一個(gè) MySQL 服務(wù), Kubernetes 需要部署下面這些對(duì)象:

Service,讓外界能夠訪問到 MySQL。

apiVersion: v1
kind: Service
metadata:
  name: my-mysql
  labels:
    app: my-mysql
spec:
  ports:
  - name: mysql
    port: 3306
    targetPort: mysql
  selector:
    app: my-mysql

Secret,定義 MySQL 的密碼。

apiVersion: v1
kind: Secret
metadata:
  name: my-mysql
  labels:
    app: my-mysql
type: Opaque
data:
  mysql-root-password: "M0MzREhRQWRjeQ=="
  mysql-password: "eGNXZkpMNmlkSw=="

PersistentVolumeClaim,為 MySQL 申請(qǐng)持久化存儲(chǔ)空間。

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: my-mysql
  labels:
    app: my-mysql
spec:
  accessModes:
    - "ReadWriteOnce"
  resources:
    requests:
      storage: 8Gi

Deployment,部署 MySQL Pod,并使用上面的這些支持對(duì)象。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-mysql
  labels:
    app: my-mysql
spec:
  template:
    metadata:
      labels:
        app: my-mysql
    spec:
      containers:
      - name: my-mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: my-mysql
              key: mysql-password
        - name: MYSQL_USER
          value: ""
        - name: MYSQL_DATABASE
          value: ""
        ports:
        - name: mysql
          containerPort: 3306
        volumMounts:
        - name: data
          mountPath: /var/lib/mysql
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: my-mysql

我們可以將上面這些配置保存到對(duì)象各自的文件中,或者集中寫進(jìn)一個(gè)配置文件,然后通過 kubectl apply -f 部署。

到目前為止,Kubernetes 對(duì)服務(wù)的部署支持得都挺好,如果應(yīng)用只由一個(gè)或幾個(gè)這樣的服務(wù)組成,上面的部署方式完全足夠了。

但是,如果我們開發(fā)的是微服務(wù)架構(gòu)的應(yīng)用,組成應(yīng)用的服務(wù)可能多達(dá)十個(gè)甚至幾十上百個(gè),這種組織和管理應(yīng)用的方式:

  1. 很難管理、編輯和維護(hù)如此多的服務(wù)。每個(gè)服務(wù)都有若干配置,缺乏一個(gè)更高層次的工具將這些配置組織起來(lái)。
  2. 不容易將這些服務(wù)作為一個(gè)整體統(tǒng)一發(fā)布。部署人員需要首先理解應(yīng)用都包含哪些服務(wù),然后按照邏輯順序依次執(zhí)行 kubectl apply。即缺少一種工具來(lái)定義應(yīng)用與服務(wù),以及服務(wù)與服務(wù)之間的依賴關(guān)系。
  3. 不能高效地共享和重用服務(wù)。比如兩個(gè)應(yīng)用都要用到 MySQL 服務(wù),但配置的參數(shù)不一樣,這兩個(gè)應(yīng)用只能分別拷貝一套標(biāo)準(zhǔn)的 MySQL 配置文件,修改后通過 kubectl apply 部署。也就是說不支持參數(shù)化配置和多環(huán)境部署。
  4. 不支持應(yīng)用級(jí)別的版本管理。雖然可以通過 kubectl rollout undo 進(jìn)行回滾,但這只能針對(duì)單個(gè) Deployment,不支持整個(gè)應(yīng)用的回滾。
  5. 不支持對(duì)部署的應(yīng)用狀態(tài)進(jìn)行驗(yàn)證。比如是否能通過預(yù)定義的賬號(hào)訪問 MySQL。雖然 Kubernetes 有健康檢查,但那是針對(duì)單個(gè)容器,我們需要應(yīng)用(服務(wù))級(jí)別的健康檢查。

Helm 能夠解決上面這些問題,Helm 幫助 Kubernetes 成為微服務(wù)架構(gòu)應(yīng)用理想的部署平臺(tái)。

Helm 的架構(gòu)。

Helm 的架構(gòu)。

Helm 有兩個(gè)重要的概念:chart 和 release。

chart 是創(chuàng)建一個(gè)應(yīng)用的信息集合,包括各種 Kubernetes 對(duì)象的配置模板、參數(shù)定義、依賴關(guān)系、文檔說明等。chart 是應(yīng)用部署的自包含邏輯單元。可以將 chart 想象成 apt、yum 中的軟件安裝包。

release 是 chart 的運(yùn)行實(shí)例,代表了一個(gè)正在運(yùn)行的應(yīng)用。當(dāng) chart 被安裝到 Kubernetes 集群,就生成一個(gè) release。chart 能夠多次安裝到同一個(gè)集群,每次安裝都是一個(gè) release。

Helm 是包管理工具,這里的包就是指的 chart。Helm 能夠:

  1. 從零創(chuàng)建新 chart。
  2. 與存儲(chǔ) chart 的倉(cāng)庫(kù)交互,拉取、保存和更新 chart。
  3. 在 Kubernetes 集群中安裝和卸載 release。
  4. 更新、回滾和測(cè)試 release。

Helm 包含兩個(gè)組件:Helm 客戶端 和 Tiller 服務(wù)器。

Helm 客戶端是終端用戶使用的命令行工具,用戶可以:

  1. 在本地開發(fā) chart。
  2. 管理 chart 倉(cāng)庫(kù)。
  3. 與 Tiller 服務(wù)器交互。
  4. 在遠(yuǎn)程 Kubernetes 集群上安裝 chart。
  5. 查看 release 信息。
  6. 升級(jí)或卸載已有的 release。

Tiller 服務(wù)器運(yùn)行在 Kubernetes 集群中,它會(huì)處理 Helm 客戶端的請(qǐng)求,與 Kubernetes API Server 交互。Tiller 服務(wù)器負(fù)責(zé):

  1. 監(jiān)聽來(lái)自 Helm 客戶端的請(qǐng)求。
  2. 通過 chart 構(gòu)建 release。
  3. 在 Kubernetes 中安裝 chart,并跟蹤 release 的狀態(tài)。
  4. 通過 API Server 升級(jí)或卸載已有的 release。

簡(jiǎn)單的講:Helm 客戶端負(fù)責(zé)管理 chart;Tiller 服務(wù)器負(fù)責(zé)管理 release。

部署 Helm
安裝和部署 Helm 客戶端和 Tiller 服務(wù)器。 Helm 客戶端

通常,我們將 Helm 客戶端安裝在能夠執(zhí)行 kubectl 命令的節(jié)點(diǎn)上,只需要下面一條命令:

curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash

執(zhí)行 helm version 驗(yàn)證。

[root@k8s-master ~]# helm version
Client: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"}
Error: could not find tiller

目前只能查看到客戶端的版本,服務(wù)器還沒有安裝。

helm 有很多子命令和參數(shù),為了提高使用命令行的效率,通常建議安裝 helm 的 bash 命令補(bǔ)全腳本,方法如下:

# source <(helm completion bash)
# echo "source <(helm completion bash)" >> ~/.bashrc

重新登錄后就可以通過 Tab 鍵補(bǔ)全 helm 子命令和參數(shù)了。

[root@k8s-master ~]# helm 
completion  history     list        search      verify
create      home        package     serve       version
delete      init        plugin      status      
dependency  inspect     repo        template    
fetch       install     reset       test        
get         lint        rollback    upgrade     
[root@k8s-master ~]# helm install --
--atomic                      --render-subchart-notes
--ca-file=                    --replace
--cert-file=                  --repo=
--debug                       --set=
--dep-up                      --set-file=
--description=                --set-string=
--devel                       --tiller-connection-timeout=
--dry-run                     --tiller-namespace=
--home=                       --timeout=
--host=                       --tls
--key-file=                   --tls-ca-cert=
--keyring=                    --tls-cert=
--kubeconfig=                 --tls-hostname=
--kube-context=               --tls-key=
--name=                       --tls-verify
--namespace=                  --username=
--name-template=              --values=
--no-crd-hook                 --verify
--no-hooks                    --version=
--password=                   --wait
[root@k8s-master ~]# helm install --
Tiller 服務(wù)器

Tiller 服務(wù)器安裝非常簡(jiǎn)單,只需要執(zhí)行 helm init:

[root@k8s-master ~]# helm init
Creating /root/.helm 
Creating /root/.helm/repository 
Creating /root/.helm/repository/cache 
Creating /root/.helm/repository/local 
Creating /root/.helm/plugins 
Creating /root/.helm/starters 
Creating /root/.helm/cache/archive 
Creating /root/.helm/repository/repositories.yaml 
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com 
Adding local repo with URL: http://127.0.0.1:8879/charts 
$HELM_HOME has been configured at /root/.helm.

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation

Tiller 本身也是作為容器化應(yīng)用運(yùn)行在 Kubernetes Cluster 中的:

[root@k8s-master ~]# kubectl get --namespace=kube-system svc tiller-deploy 
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
tiller-deploy   ClusterIP   10.104.165.164   <none>        44134/TCP   30s
# docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.14.3

# docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.14.3 gcr.io/kubernetes-helm/tiller:v2.14.3

可以看到 Tiller 的 Service、Deployment 和 Pod。

[root@k8s-master ~]# kubectl get --namespace=kube-system svc tiller-deploy 
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
tiller-deploy   ClusterIP   10.104.165.164   <none>        44134/TCP   19m
[root@k8s-master ~]# kubectl get -n kube-system deployments. tiller-deploy 
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
tiller-deploy   1/1     1            1           16m
[root@k8s-master ~]# kubectl get -n kube-system pod tiller-deploy-75f6c87b87-qlw4h 
NAME                             READY   STATUS    RESTARTS   AGE
tiller-deploy-75f6c87b87-qlw4h   1/1     Running   0          17m

現(xiàn)在, helm version 已經(jīng)能夠查看到服務(wù)器的版本信息了。

[root@k8s-master ~]# helm version 
Client: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"}
使用 Helm

Helm 安裝成功后,可執(zhí)行 helm search 查看當(dāng)前可安裝的 chart。

# helm search 

Helm 可以像 apt 和 yum 管理軟件包一樣管理 chart。apt 和 yum 的軟件包存放在倉(cāng)庫(kù)中,同樣的,Helm 也有倉(cāng)庫(kù)。

[root@k8s-master ~]# helm repo list
NAME    URL                                             
stable  https://kubernetes-charts.storage.googleapis.com
local   http://127.0.0.1:8879/charts                    
[root@k8s-master ~]# 

Helm 安裝時(shí)已經(jīng)默認(rèn)配置好了兩個(gè)倉(cāng)庫(kù):stable 和 local。stable 是官方倉(cāng)庫(kù),local 是用戶存放自己開發(fā)的 chart 的本地倉(cāng)庫(kù)。

helm search 會(huì)顯示 chart 位于哪個(gè)倉(cāng)庫(kù),比如 local/cool-chart 和 stable/acs-engine-autoscaler。

用戶可以通過 helm repo add 添加更多的倉(cāng)庫(kù),比如企業(yè)的私有倉(cāng)庫(kù),倉(cāng)庫(kù)的管理和維護(hù)方法請(qǐng)參考官網(wǎng)文檔 https://docs.helm.sh

與 apt 和 yum 一樣,helm 也支持關(guān)鍵字搜索:

[root@k8s-master ~]# helm search mysql
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION                                                 
stable/mysql                            1.4.0           5.7.27          Fast, reliable, scalable, and easy to use open-source rel...
stable/mysqldump                        2.6.0           2.4.1           A Helm chart to help backup MySQL databases using mysqldump 
stable/prometheus-mysql-exporter        0.5.1           v0.11.0         A Helm chart for prometheus mysql exporter with cloudsqlp...
stable/percona                          1.2.0           5.7.17          free, fully compatible, enhanced, open source drop-in rep...
stable/percona-xtradb-cluster           1.0.2           5.7.19          free, fully compatible, enhanced, open source drop-in rep...
stable/phpmyadmin                       3.0.7           4.9.1           phpMyAdmin is an mysql administration frontend              
stable/gcloud-sqlproxy                  0.6.1           1.11            DEPRECATED Google Cloud SQL Proxy                           
stable/mariadb                          6.11.1          10.3.18         Fast, reliable, scalable, and easy to use open-source rel...

包括 DESCRIPTION 在內(nèi)的所有信息,只要跟關(guān)鍵字匹配,都會(huì)顯示在結(jié)果列表中。

安裝 chart 也很簡(jiǎn)單,執(zhí)行如下命令可以安裝 MySQL。

helm install stable/mysql
如果看到如下報(bào)錯(cuò),通常是因?yàn)?Tiller 服務(wù)器的權(quán)限不足。

[root@k8s-master ~]# helm install stable/mysql
Error: failed to download "stable/mysql" (hint: running `helm repo update` may help)

執(zhí)行如下命名添加權(quán)限:

kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
[root@k8s-master ~]# kubectl create serviceaccount -n kube-system tiller
serviceaccount/tiller created
[root@k8s-master ~]# kubectl create clusterrolebinding tille[root@k8s-master ~]# kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
clusterrolebinding.rbac.authorization.k8s.io/tiller-cluster-rule created
[root@k8s-master ~]# kubectl patch deploy -n kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
deployment.extensions/tiller-deploy patched

然后再次執(zhí)行

[root@k8s-master ~]# helm install stable/mysql
NAME:   olfactory-bird
LAST DEPLOYED: Tue Oct 15 17:36:02 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                       DATA  AGE
olfactory-bird-mysql-test  1     0s

==> v1/Deployment
NAME                  READY  UP-TO-DATE  AVAILABLE  AGE
olfactory-bird-mysql  0/1    1           0          0s

==> v1/PersistentVolumeClaim
NAME                  STATUS   VOLUME  CAPACITY  ACCESS MODES  STORAGECLASS  AGE
olfactory-bird-mysql  Pending  0s

==> v1/Pod(related)
NAME                                  READY  STATUS   RESTARTS  AGE
olfactory-bird-mysql-5cd5bc6b7-qmbj6  0/1    Pending  0         0s

==> v1/Secret
NAME                  TYPE    DATA  AGE
olfactory-bird-mysql  Opaque  2     0s

==> v1/Service
NAME                  TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)   AGE
olfactory-bird-mysql  ClusterIP  10.102.142.220  <none>       3306/TCP  0s

NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
olfactory-bird-mysql.default.svc.cluster.local

To get your root password run:

    MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default olfactory-bird-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)

To connect to your database:

1. Run an Ubuntu pod that you can use as a client:

    kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

2. Install the mysql client:

    $ apt-get update && apt-get install mysql-client -y

3. Connect using the mysql cli, then provide your password:
    $ mysql -h olfactory-bird-mysql -p

To connect to your database directly from outside the K8s cluster:
    MYSQL_HOST=127.0.0.1
    MYSQL_PORT=3306

    # Execute the following command to route the connection:
    kubectl port-forward svc/olfactory-bird-mysql 3306

    mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}

輸出分為三部分:

① chart 本次部署的描述信息:

NAME 是 release 的名字,因?yàn)槲覀儧]用 -n 參數(shù)指定,Helm 隨機(jī)生成了一個(gè),這里是 fun-zorse。

NAMESPACE 是 release 部署的 namespace,默認(rèn)是 default,也可以通過 --namespace 指定。

STATUS 為 DEPLOYED,表示已經(jīng)將 chart 部署到集群。

② 當(dāng)前 release 包含的資源:Service、Deployment、Secret 和 PersistentVolumeClaim,其名字都是 fun-zorse-mysql,命名的格式為 ReleasName-ChartName。

③ NOTES 部分顯示的是 release 的使用方法。比如如何訪問 Service,如何獲取數(shù)據(jù)庫(kù)密碼,以及如何連接數(shù)據(jù)庫(kù)等。

通過 kubectl get 可以查看組成 release 的各個(gè)對(duì)象:

[root@k8s-master ~]# kubectl get service olfactory-bird-mysql 
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
olfactory-bird-mysql   ClusterIP   10.102.142.220   <none>        3306/TCP   4m14s
[root@k8s-master ~]# kubectl get deployments. 
nginx-configmap       olfactory-bird-mysql  
[root@k8s-master ~]# kubectl get deployments. olfactory-bird-mysql 
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
olfactory-bird-mysql   0/1     1            0           4m29s
[root@k8s-master ~]# kubectl get pod olfactory-bird-mysql-5cd5bc6b7-qmbj6 
NAME                                   READY   STATUS    RESTARTS   AGE
olfactory-bird-mysql-5cd5bc6b7-qmbj6   0/1     Pending   0          4m40s
[root@k8s-master ~]# kubectl get pvc olfactory-bird-mysql 
NAME                   STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
olfactory-bird-mysql   Pending                                                     4m56s

因?yàn)槲覀冞€沒有準(zhǔn)備 PersistentVolume,當(dāng)前 release 還不可用。

helm list 顯示已經(jīng)部署的 release,helm delete 可以刪除 release。

[root@k8s-master ~]# helm list
NAME            REVISION        UPDATED                         STATUS        CHART           APP VERSION     NAMESPACE
olfactory-bird  1               Tue Oct 15 17:36:02 2019        DEPLOYED      mysql-1.4.0     5.7.27          default  
[root@k8s-master ~]# 

Helm 的使用方法像極了 apt 和 yum,用 Helm 來(lái)管理 Kubernetes 應(yīng)用非常方便。

chart 目錄結(jié)構(gòu)

chart 是 Helm 的應(yīng)用打包格式。chart 由一系列文件組成,這些文件描述了 Kubernetes 部署應(yīng)用時(shí)所需要的資源,比如 Service、Deployment、PersistentVolumeClaim、Secret、ConfigMap 等。

單個(gè)的 chart 可以非常簡(jiǎn)單,只用于部署一個(gè)服務(wù),比如 Memcached;chart 也可以很復(fù)雜,部署整個(gè)應(yīng)用,比如包含 HTTP Servers、 Database、消息中間件、cache 等。

chart 將這些文件放置在預(yù)定義的目錄結(jié)構(gòu)中,通常整個(gè) chart 被打成 tar 包,而且標(biāo)注上版本信息,便于 Helm 部署。

下面我們將詳細(xì)討論 chart 的目錄結(jié)構(gòu)以及包含的各類文件。

chart 目錄結(jié)構(gòu)

以前面 MySQL chart 為例。一旦安裝了某個(gè) chart,我們就可以在 ~/.helm/cache/archive 中找到 chart 的 tar 包。

# ls ~/.helm/cache/archive/
mysql-1.4.0.tgz 

解壓后,MySQL chart 目錄結(jié)構(gòu)如下:

[root@k8s-master ~]# tree mysql
mysql
├── Chart.yaml
├── README.md
├── templates
│?? ├── configurationFiles-configmap.yaml
│?? ├── deployment.yaml
│?? ├── _helpers.tpl
│?? ├── initializationFiles-configmap.yaml
│?? ├── NOTES.txt
│?? ├── pvc.yaml
│?? ├── secrets.yaml
│?? ├── servicemonitor.yaml
│?? ├── svc.yaml
│?? └── tests
│??     ├── test-configmap.yaml
│??     └── test.yaml
└── values.yaml

2 directories, 14 files

目錄名就是 chart 的名字(不帶版本信息),這里是 mysql,包含如下內(nèi)容:

Chart.yaml

YAML 文件,描述 chart 的概要信息。

apiVersion: v1
appVersion: 5.7.27
description: Fast, reliable, scalable, and easy to use open-source relational database
  system.
engine: gotpl
home: https://www.mysql.com/
icon: https://cache.yisu.com/upload/information/20200309/33/54090.jpg
keywords:
- mysql
- database
- sql
maintainers:
- email: o.with@sportradar.com
  name: olemarkus
- email: viglesias@google.com
  name: viglesiasce
name: mysql
sources:
- https://github.com/kubernetes/charts
- https://github.com/docker-library/mysql
version: 1.4.0

name 和 version 是必填項(xiàng),其他都是可選。

README.md

Markdown 格式的 README 文件,相當(dāng)于 chart 的使用文檔,此文件為可選。

LICENSE

文本文件,描述 chart 的許可信息,此文件為可選。

requirements.yaml

chart 可能依賴其他的 chart,這些依賴關(guān)系可通過 requirements.yaml 指定,比如:

在安裝過程中,依賴的 chart 也會(huì)被一起安裝。

values.yaml
chart 支持在安裝的時(shí)根據(jù)參數(shù)進(jìn)行定制化配置,而 values.yaml 則提供了這些配置參數(shù)的默認(rèn)值。

templates 目錄
各類 Kubernetes 資源的配置模板都放置在這里。Helm 會(huì)將 values.yaml 中的參數(shù)值注入到模板中生成標(biāo)準(zhǔn)的 YAML 配置文件。

模板是 chart 最重要的部分,也是 Helm 最強(qiáng)大的地方。模板增加了應(yīng)用部署的靈活性,能夠適用不同的環(huán)境,我們后面會(huì)詳細(xì)討論。

templates/NOTES.txt
chart 的簡(jiǎn)易使用文檔,chart 安裝成功后會(huì)顯示此文檔內(nèi)容。

與模板一樣,可以在 NOTE.txt 中插入配置參數(shù),Helm 會(huì)動(dòng)態(tài)注入?yún)?shù)值。

chart 模板

Helm 通過模板創(chuàng)建 Kubernetes 能夠理解的 YAML 格式的資源配置文件,我們將通過例子來(lái)學(xué)習(xí)如何使用模板。

以 templates/secrets.yaml 為例:

{{- if not .Values.existingSecret }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ template "mysql.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ template "mysql.fullname" . }}
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
type: Opaque
data:
  {{ if .Values.mysqlRootPassword }}
  mysql-root-password:  {{ .Values.mysqlRootPassword | b64enc | quote }}
  {{ else }}
  mysql-root-password: {{ randAlphaNum 10 | b64enc | quote }}
  {{ end }}
  {{ if .Values.mysqlPassword }}
  mysql-password:  {{ .Values.mysqlPassword | b64enc | quote }}
  {{ else }}
  mysql-password: {{ randAlphaNum 10 | b64enc | quote }}
  {{ end }}
{{- if .Values.ssl.enabled }}
{{ if .Values.ssl.certificates }}
{{- range .Values.ssl.certificates }}
---
apiVersion: v1
kind: Secret
metadata:
  name: {{ .name }}
  labels:
    app: {{ template "mysql.fullname" $ }}
    chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
    release: "{{ $.Release.Name }}"
    heritage: "{{ $.Release.Service }}"
type: Opaque
data:
  ca.pem: {{ .ca | b64enc }}
  server-cert.pem: {{ .cert | b64enc }}
  server-key.pem: {{ .key | b64enc }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

從結(jié)構(gòu)看,文件的內(nèi)容非常像 Secret 配置,只是大部分屬性值變成了{(lán){ xxx }}。這些 {{ xxx }} 實(shí)際上是模板的語(yǔ)法。Helm 采用了 Go 語(yǔ)言的模板來(lái)編寫 chart。Go 模板非常強(qiáng)大,支持變量、對(duì)象、函數(shù)、流控制等功能。下面我們通過解析 templates/secrets.yaml 快速學(xué)習(xí)模板。

① {{ template "mysql.fullname" . }} 定義 Secret 的 name。
關(guān)鍵字 template 的作用是引用一個(gè)子模板 mysql.fullname。這個(gè)子模板是在 templates/_helpers.tpl 文件中定義的。

這個(gè)定義還是很復(fù)雜的,因?yàn)樗玫搅四0逭Z(yǔ)言中的對(duì)象、函數(shù)、流控制等概念。現(xiàn)在看不懂沒關(guān)系,這里我們學(xué)習(xí)的重點(diǎn)是:如果存在一些信息多個(gè)模板都會(huì)用到,則可在 templates/_helpers.tpl 中將其定義為子模板,然后通過 templates 函數(shù)引用。

這里 mysql.fullname 是由 release 與 chart 二者名字拼接組成。

根據(jù) chart 的最佳實(shí)踐,所有資源的名稱都應(yīng)該保持一致,對(duì)于我們這個(gè) chart,無(wú)論 Secret 還是 Deployment、PersistentVolumeClaim、Service,它們的名字都是子模板 mysql.fullname 的值。

② Chart 和 Release 是 Helm 預(yù)定義的對(duì)象,每個(gè)對(duì)象都有自己的屬性,可以在模板中使用。如果使用下面命令安裝 chart:

helm install stable/mysql -n my
那么:
{{ .Chart.Name }} 的值為 mysql。
{{ .Chart.Version }} 的值為 0.3.0。
{{ .Release.Name }} 的值為 my。
{{ .Release.Service }} 始終取值為 Tiller。
{{ template "mysql.fullname" . }} 計(jì)算結(jié)果為 my-mysql。

③ 這里指定 mysql-root-password 的值,不過使用了 if-else 的流控制,其邏輯為:
如果 .Values.mysqlRootPassword 有值,則對(duì)其進(jìn)行 base64 編碼;否則隨機(jī)生成一個(gè) 10 位的字符串并編碼。

Values 也是預(yù)定義的對(duì)象,代表的是 values.yaml 文件。而 .Values.mysqlRootPassword 則是 values.yaml 中定義的 mysqlRootPassword 參數(shù):

因?yàn)?mysqlRootPassword 被注釋掉了,沒有賦值,所以邏輯判斷會(huì)走 else,即隨機(jī)生成密碼。

randAlphaNum、b64enc、quote 都是 Go 模板語(yǔ)言支持的函數(shù),函數(shù)之間可以通過管道 | 連接。{{ randAlphaNum 10 | b64enc | quote }} 的作用是首先隨機(jī)產(chǎn)生一個(gè)長(zhǎng)度為 10 的字符串,然后將其 base64 編碼,最后兩邊加上雙引號(hào)。

templates/secrets.yaml 這個(gè)例子展示了 chart 模板主要的功能,我們最大的收獲應(yīng)該是:模板將 chart 參數(shù)化了,通過 values.yaml 可以靈活定制應(yīng)用。

無(wú)論多復(fù)雜的應(yīng)用,用戶都可以用 Go 模板語(yǔ)言編寫出 chart。無(wú)非是使用到更多的函數(shù)、對(duì)象和流控制。對(duì)于初學(xué)者,我的建議是盡量參考官方的 chart。根據(jù)二八定律,這些 chart 已經(jīng)覆蓋了絕大部分情況,而且采用了最佳實(shí)踐。如何遇到不懂的函數(shù)、對(duì)象和其他語(yǔ)法,可參考官網(wǎng)文檔 https://docs.helm.sh

實(shí)踐 MySQL chart
chart 安裝前的準(zhǔn)備

作為準(zhǔn)備工作,安裝之前需要先清楚 chart 的使用方法。這些信息通常記錄在 values.yaml 和 README.md 中。除了下載源文件查看,執(zhí)行 helm inspect values 可能是更方便的方法。

[root@k8s-master ~]# helm inspect values stable/mysql
## mysql image version
## ref: https://hub.docker.com/r/library/mysql/tags/
##
image: "mysql"
imageTag: "5.7.14"

busybox:
  image: "busybox"
  tag: "1.29.3"

testFramework:
  enabled: true
  image: "dduportal/bats"
  tag: "0.4.0"

## Specify password for root user
##
## Default: random 10 character string
# mysqlRootPassword: testing

## Create a database user
##
# mysqlUser:
## Default: random 10 character string
# mysqlPassword:

## Allow unauthenticated access, uncomment to enable
##
# mysqlAllowEmptyPassword: true

## Create a database
##
# mysqlDatabase:

## Specify an imagePullPolicy (Required)
## It's recommended to change this to 'Always' if the image tag is 'latest'
## ref: http://kubernetes.io/docs/user-guide/images/#updating-images
##
imagePullPolicy: IfNotPresent

## Additionnal arguments that are passed to the MySQL container.
## For example use --default-authentication-plugin=mysql_native_password if older clients need to
## connect to a MySQL 8 instance.
args: []

extraVolumes: |
  # - name: extras
  #   emptyDir: {}

extraVolumeMounts: |
  # - name: extras
  #   mountPath: /usr/share/extras
  #   readOnly: true

extraInitContainers: |
  # - name: do-something
  #   image: busybox
  #   command: ['do', 'something']

# Optionally specify an array of imagePullSecrets.
# Secrets must be manually created in the namespace.
# ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod
# imagePullSecrets:
  # - name: myRegistryKeySecretName

## Node selector
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
nodeSelector: {}

## Tolerations for pod assignment
## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
##
tolerations: []

livenessProbe:
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  successThreshold: 1
  failureThreshold: 3

readinessProbe:
  initialDelaySeconds: 5
  periodSeconds: 10
  timeoutSeconds: 1
  successThreshold: 1
  failureThreshold: 3

## Persist data to a persistent volume
persistence:
  enabled: true
  ## database data Persistent Volume Storage Class
  ## If defined, storageClassName: <storageClass>
  ## If set to "-", storageClassName: "", which disables dynamic provisioning
  ## If undefined (the default) or set to null, no storageClassName spec is
  ##   set, choosing the default provisioner.  (gp2 on AWS, standard on
  ##   GKE, AWS & OpenStack)
  ##
  # storageClass: "-"
  accessMode: ReadWriteOnce
  size: 8Gi
  annotations: {}

## Use an alternate scheduler, e.g. "stork".
## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
##
# schedulerName:

## Security context
securityContext:
  enabled: false
  runAsUser: 999
  fsGroup: 999

## Configure resource requests and limits
## ref: http://kubernetes.io/docs/user-guide/compute-resources/
##
resources:
  requests:
    memory: 256Mi
    cpu: 100m

# Custom mysql configuration files path
configurationFilesPath: /etc/mysql/conf.d/

# Custom mysql configuration files used to override default mysql settings
configurationFiles: {}
#  mysql.cnf: |-
#    [mysqld]
#    skip-name-resolve
#    ssl-ca=/ssl/ca.pem
#    ssl-cert=/ssl/server-cert.pem
#    ssl-key=/ssl/server-key.pem

# Custom mysql init SQL files used to initialize the database
initializationFiles: {}
#  first-db.sql: |-
#    CREATE DATABASE IF NOT EXISTS first DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
#  second-db.sql: |-
#    CREATE DATABASE IF NOT EXISTS second DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;

metrics:
  enabled: false
  image: prom/mysqld-exporter
  imageTag: v0.10.0
  imagePullPolicy: IfNotPresent
  resources: {}
  annotations: {}
    # prometheus.io/scrape: "true"
    # prometheus.io/port: "9104"
  livenessProbe:
    initialDelaySeconds: 15
    timeoutSeconds: 5
  readinessProbe:
    initialDelaySeconds: 5
    timeoutSeconds: 1
  flags: []
  serviceMonitor:
    enabled: false
    additionalLabels: {}

## Configure the service
## ref: http://kubernetes.io/docs/user-guide/services/
service:
  annotations: {}
  ## Specify a service type
  ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types
  type: ClusterIP
  port: 3306
  # nodePort: 32000
  # loadBalancerIP:

ssl:
  enabled: false
  secret: mysql-ssl-certs
  certificates:
#  - name: mysql-ssl-certs
#    ca: |-
#      -----BEGIN CERTIFICATE-----
#      ...
#      -----END CERTIFICATE-----
#    cert: |-
#      -----BEGIN CERTIFICATE-----
#      ...
#      -----END CERTIFICATE-----
#    key: |-
#      -----BEGIN RSA PRIVATE KEY-----
#      ...
#      -----END RSA PRIVATE KEY-----

## Populates the 'TZ' system timezone environment variable
## ref: https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html
##
## Default: nil (mysql will use image's default timezone, normally UTC)
## Example: 'Australia/Sydney'
# timezone:

# Deployment Annotations
deploymentAnnotations: {}

# To be added to the database server pod(s)
podAnnotations: {}
podLabels: {}

## Set pod priorityClassName
# priorityClassName: {}

## Init container resources defaults
initContainer:
  resources:
    requests:
      memory: 10Mi
      cpu: 10m

輸出的實(shí)際上是 values.yaml 的內(nèi)容。閱讀注釋就可以知道 MySQL chart 支持哪些參數(shù),安裝之前需要做哪些準(zhǔn)備。其中有一部分是關(guān)于存儲(chǔ)的:

## Persist data to a persistent volume
persistence:
  enabled: true
  ## database data Persistent Volume Storage Class
  ## If defined, storageClassName: <storageClass>
  ## If set to "-", storageClassName: "", which disables dynamic provisioning
  ## If undefined (the default) or set to null, no storageClassName spec is
  ##   set, choosing the default provisioner.  (gp2 on AWS, standard on
  ##   GKE, AWS & OpenStack)
  ##
  # storageClass: "-"
  accessMode: ReadWriteOnce
  size: 8Gi
  annotations: {}

chart 定義了一個(gè) PersistentVolumeClaim,申請(qǐng) 8G 的 PersistentVolume。由于我們的實(shí)驗(yàn)環(huán)境不支持動(dòng)態(tài)供給,所以得預(yù)先創(chuàng)建好相應(yīng)的 PV,其配置文件 mysql-pv.yml 內(nèi)容為:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 8Gi
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /nfsdata/mysql-pv
    server: 192.168.77.10

創(chuàng)建 PV mysql-pv:

[root@k8s-master ~]# kubectl apply -f mysql-pv.yml
persistentvolume/mysql-pv created
[root@k8s-master ~]# kubectl get pv
NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mysql-pv   8Gi        RWO            Retain           Available                                   5s

接下來(lái)就可以安裝 chart 了。

定制化安裝 chart

除了接受 values.yaml 的默認(rèn)值,我們還可以定制化 chart,比如設(shè)置 mysqlRootPassword。

Helm 有兩種方式傳遞配置參數(shù):

  1. 指定自己的 values 文件。
    通常的做法是首先通過 helm inspect values mysql > myvalues.yaml生成 values 文件,然后設(shè)置 mysqlRootPassword,之后執(zhí)行 helm install --values=myvalues.yaml mysql。
  2. 通過 --set 直接傳入?yún)?shù)值,比如:
[root@k8s-master ~]# helm install stable/mysql --set mysqlRootPassword=abc123 -n my
NAME:   my
LAST DEPLOYED: Tue Oct 15 21:52:35 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME           DATA  AGE
my-mysql-test  1     0s

==> v1/Deployment
NAME      READY  UP-TO-DATE  AVAILABLE  AGE
my-mysql  0/1    1           0          0s

==> v1/PersistentVolumeClaim
NAME      STATUS   VOLUME  CAPACITY  ACCESS MODES  STORAGECLASS  AGE
my-mysql  Pending  0s

==> v1/Pod(related)
NAME                       READY  STATUS   RESTARTS  AGE
my-mysql-6dcc9b7d67-qh7m4  0/1    Pending  0         0s

==> v1/Secret
NAME      TYPE    DATA  AGE
my-mysql  Opaque  2     0s

==> v1/Service
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP  PORT(S)   AGE
my-mysql  ClusterIP  10.105.14.41  <none>       3306/TCP  0s

NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
my-mysql.default.svc.cluster.local

To get your root password run:

    MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default my-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)

To connect to your database:

1. Run an Ubuntu pod that you can use as a client:

    kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

2. Install the mysql client:

    $ apt-get update && apt-get install mysql-client -y

3. Connect using the mysql cli, then provide your password:
    $ mysql -h my-mysql -p

To connect to your database directly from outside the K8s cluster:
    MYSQL_HOST=127.0.0.1
    MYSQL_PORT=3306

    # Execute the following command to route the connection:
    kubectl port-forward svc/my-mysql 3306

    mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}

mysqlRootPassword 設(shè)置為 abc123。另外,-n 設(shè)置 release 為 my,各類資源的名稱即為my-mysql。

通過 helm list 和 helm status 可以查看 chart 的最新狀態(tài)。

[root@k8s-master ~]# helm list
NAME                    REVISION        UPDATED                         STATUS     CHART           APP VERSION     NAMESPACE
my                      1               Tue Oct 15 21:52:35 2019        DEPLOYED   mysql-1.4.0     5.7.27          default  

[root@k8s-master ~]# helm status my
LAST DEPLOYED: Tue Oct 15 21:52:35 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME           DATA  AGE
my-mysql-test  1     10m

==> v1/Deployment
NAME      READY  UP-TO-DATE  AVAILABLE  AGE
my-mysql  1/1    1           1          10m

==> v1/PersistentVolumeClaim
NAME      STATUS  VOLUME    CAPACITY  ACCESS MODES  STORAGECLASS  AGE
my-mysql  Bound   mysql-pv  8Gi       RWO           10m

==> v1/Pod(related)
NAME                       READY  STATUS   RESTARTS  AGE
my-mysql-6dcc9b7d67-qh7m4  1/1    Running  0         10m

==> v1/Secret
NAME      TYPE    DATA  AGE
my-mysql  Opaque  2     10m

==> v1/Service
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP  PORT(S)   AGE
my-mysql  ClusterIP  10.105.14.41  <none>       3306/TCP  10m

NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
my-mysql.default.svc.cluster.local

To get your root password run:

    MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default my-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)

To connect to your database:

1. Run an Ubuntu pod that you can use as a client:

    kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

2. Install the mysql client:

    $ apt-get update && apt-get install mysql-client -y

3. Connect using the mysql cli, then provide your password:
    $ mysql -h my-mysql -p

To connect to your database directly from outside the K8s cluster:
    MYSQL_HOST=127.0.0.1
    MYSQL_PORT=3306

    # Execute the following command to route the connection:
    kubectl port-forward svc/my-mysql 3306

    mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}

PVC 已經(jīng) Bound,Deployment 也 AVAILABLE。

升級(jí)和回滾 release

release 發(fā)布后可以執(zhí)行 helm upgrade 對(duì)其升級(jí),通過 --values 或 --set應(yīng)用新的配置。比如將當(dāng)前的 MySQL 版本升級(jí)到 5.7.15:

[root@k8s-master ~]# helm upgrade --set imageTag=5.7.15 my stable/mysql
Release "my" has been upgraded.
LAST DEPLOYED: Tue Oct 15 22:13:15 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME           DATA  AGE
my-mysql-test  1     20m

==> v1/Deployment
NAME      READY  UP-TO-DATE  AVAILABLE  AGE
my-mysql  1/1    1           1          20m

==> v1/PersistentVolumeClaim
NAME      STATUS  VOLUME    CAPACITY  ACCESS MODES  STORAGECLASS  AGE
my-mysql  Bound   mysql-pv  8Gi       RWO           20m

==> v1/Pod(related)
NAME                       READY  STATUS    RESTARTS  AGE
my-mysql-67f47db69b-tx2kt  0/1    Init:0/1  0         0s
my-mysql-6dcc9b7d67-qh7m4  1/1    Running   0         20m

==> v1/Secret
NAME      TYPE    DATA  AGE
my-mysql  Opaque  2     20m

==> v1/Service
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP  PORT(S)   AGE
my-mysql  ClusterIP  10.105.14.41  <none>       3306/TCP  20m

NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
my-mysql.default.svc.cluster.local

To get your root password run:

    MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default my-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)

To connect to your database:

1. Run an Ubuntu pod that you can use as a client:

    kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

2. Install the mysql client:

    $ apt-get update && apt-get install mysql-client -y

3. Connect using the mysql cli, then provide your password:
    $ mysql -h my-mysql -p

To connect to your database directly from outside the K8s cluster:
    MYSQL_HOST=127.0.0.1
    MYSQL_PORT=3306

    # Execute the following command to route the connection:
    kubectl port-forward svc/my-mysql 3306

    mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}

等待一些時(shí)間,升級(jí)成功。

[root@k8s-master ~]# kubectl get deployments. my-mysql -o wide
NAME       READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
my-mysql   1/1     1            1           21m   my-mysql     mysql:5.7.15   app=my-mysql,release=my

helm history 可以查看 release 所有的版本。通過 helm rollback 可以回滾到任何版本。

[root@k8s-master ~]# helm history my
REVISION        UPDATED                         STATUS          CHART           DESCRIPTION     
1               Tue Oct 15 21:52:35 2019        SUPERSEDED      mysql-1.4.0     Install complete
2               Tue Oct 15 22:13:15 2019        DEPLOYED        mysql-1.4.0     Upgrade complete

[root@k8s-master ~]# helm rollback my 1
Rollback was a success.

回滾成功,MySQL 恢復(fù)到 5.7.14。

[root@k8s-master ~]# helm history my
REVISION        UPDATED                         STATUS          CHART           DESCRIPTION     
1               Tue Oct 15 21:52:35 2019        SUPERSEDED      mysql-1.4.0     Install complete
2               Tue Oct 15 22:13:15 2019        SUPERSEDED      mysql-1.4.0     Upgrade complete
3               Tue Oct 15 22:16:21 2019        DEPLOYED        mysql-1.4.0     Rollback to 1  

[root@k8s-master ~]# kubectl get deployments. my-mysql -o wide
NAME       READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
my-mysql   1/1     1            1           24m   my-mysql     mysql:5.7.14   app=my-mysql,release=my
開發(fā)自己的 chart

Kubernetes 給我們提供了大量官方 chart,不過要部署微服務(wù)應(yīng)用,還是需要開發(fā)自己的 chart。

創(chuàng)建 chart

執(zhí)行 helm create mychart 的命令創(chuàng)建 chart mychart:

[root@k8s-master ~]# helm create mychart
Creating mychart
[root@k8s-master ~]# tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templates
│?? ├── deployment.yaml
│?? ├── _helpers.tpl
│?? ├── ingress.yaml
│?? ├── NOTES.txt
│?? ├── service.yaml
│?? └── tests
│??     └── test-connection.yaml
└── values.yaml

3 directories, 8 files

Helm 會(huì)幫我們創(chuàng)建目錄 mychart,并生成了各類 chart 文件。這樣我們就可以在此基礎(chǔ)上開發(fā)自己的 chart 了。

新建的 chart 默認(rèn)包含一個(gè) nginx 應(yīng)用示例,values.yaml 內(nèi)容如下:

# Default values for mychart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent

imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: chart-example.local
      paths: []

  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

nodeSelector: {}

tolerations: []

affinity: {}

開發(fā)時(shí)建議大家參考官方 chart 中的模板、values.yaml、Chart.yaml,里面包含了大量最佳實(shí)踐和最常用的函數(shù)、流控制。

調(diào)試 chart

只要是程序就會(huì)有 bug,chart 也不例外。Helm 提供了 debug 的工具:helm lint 和 helm install --dry-run --debug。

helm lint 會(huì)檢測(cè) chart 的語(yǔ)法,報(bào)告錯(cuò)誤以及給出建議。

比如我們故意在 values.yaml漏掉了一個(gè) :,

helm lint mychart 會(huì)指出這個(gè)語(yǔ)法錯(cuò)誤。

[root@k8s-master ~]# helm lint mychart
==> Linting mychart
[INFO] Chart.yaml: icon is recommended
[ERROR] values.yaml: unable to parse YAML
        error converting YAML to JSON: yaml: line 12: could not find expected ':'

Error: 1 chart(s) linted, 1 chart(s) failed

mychart 目錄被作為參數(shù)傳遞給 helm lint。錯(cuò)誤修復(fù)后則能通過檢測(cè)。

helm install --dry-run --debug 會(huì)模擬安裝 chart,并輸出每個(gè)模板生成的 YAML 內(nèi)容。

[root@k8s-master ~]# helm install --dry-run mychart --debug
[debug] Created tunnel using local port: '37754'

[debug] SERVER: "127.0.0.1:37754"

[debug] Original chart version: ""
[debug] CHART PATH: /root/mychart

NAME:   quieting-quetzal
REVISION: 1
RELEASED: Wed Oct 16 23:12:34 2019
CHART: mychart-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
fullnameOverride: ""
image:
  pullPolicy: IfNotPresent
  repository: nginx
  tag: stable
imagePullSecrets: []
ingress:
  annotations: {}
  enabled: false
  hosts:
  - host: chart-example.local
    paths: []
  tls: []
nameOverride: ""
nodeSelector: {}
replicaCount: 1
resources: {}
service:
  externalPort: 80
  internalPort: 80
  type: ClusterIP
tolerations: []

HOOKS:
---
# quieting-quetzal-mychart-test-connection
apiVersion: v1
kind: Pod
metadata:
  name: "quieting-quetzal-mychart-test-connection"
  labels:
    app.kubernetes.io/name: mychart
    helm.sh/chart: mychart-0.1.0
    app.kubernetes.io/instance: quieting-quetzal
    app.kubernetes.io/version: "1.0"
    app.kubernetes.io/managed-by: Tiller
  annotations:
    "helm.sh/hook": test-success
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args:  ['quieting-quetzal-mychart:']
  restartPolicy: Never
MANIFEST:

---
# Source: mychart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: quieting-quetzal-mychart
  labels:
    app.kubernetes.io/name: mychart
    helm.sh/chart: mychart-0.1.0
    app.kubernetes.io/instance: quieting-quetzal
    app.kubernetes.io/version: "1.0"
    app.kubernetes.io/managed-by: Tiller
spec:
  type: ClusterIP
  ports:
    - port: 
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: mychart
    app.kubernetes.io/instance: quieting-quetzal
---
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: quieting-quetzal-mychart
  labels:
    app.kubernetes.io/name: mychart
    helm.sh/chart: mychart-0.1.0
    app.kubernetes.io/instance: quieting-quetzal
    app.kubernetes.io/version: "1.0"
    app.kubernetes.io/managed-by: Tiller
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: mychart
      app.kubernetes.io/instance: quieting-quetzal
  template:
    metadata:
      labels:
        app.kubernetes.io/name: mychart
        app.kubernetes.io/instance: quieting-quetzal
    spec:
      containers:
        - name: mychart
          image: "nginx:stable"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}

我們可以檢視這些輸出,判斷是否與預(yù)期相符。

同樣,mychart 目錄作為參數(shù)傳遞給 helm install --dry-run --debug。

管理和安裝 chart

安裝 chart
當(dāng)我們覺得準(zhǔn)備就緒,就可以安裝 chart,Helm 支持四種安裝方法:

安裝倉(cāng)庫(kù)中的 chart,例如:helm install stable/nginx

通過 tar 包安裝,例如:helm install ./nginx-1.2.3.tgz

通過 chart 本地目錄安裝,例如:helm install ./nginx

通過 URL 安裝,例如:helm install https://example.com/charts/nginx-1.2.3.tgz

這里我們使用本地目錄安裝:

[root@k8s-master ~]# helm install mychart
NAME:   exasperated-olm
LAST DEPLOYED: Wed Oct 16 23:49:18 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Deployment
NAME                     READY  UP-TO-DATE  AVAILABLE  AGE
exasperated-olm-mychart  0/1    1           0          0s

==> v1/Pod(related)
NAME                                      READY  STATUS             RESTARTS  AGE
exasperated-olm-mychart-6845d8bb6c-mflfj  0/1    ContainerCreating  0         0s

==> v1/Service
NAME                     TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE
exasperated-olm-mychart  ClusterIP  10.109.135.88  <none>       80/TCP   0s

NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=exasperated-olm" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:80

當(dāng) chart 部署到 Kubernetes 集群,便可以對(duì)其進(jìn)行更為全面的測(cè)試。

將 chart 添加到倉(cāng)庫(kù)

chart 通過測(cè)試后可以將其添加到倉(cāng)庫(kù),團(tuán)隊(duì)其他成員就能夠使用。任何 HTTP Server 都可以用作 chart 倉(cāng)庫(kù),下面在 k8s-node1上搭建倉(cāng)庫(kù)。

在 k8s-node1 上啟動(dòng)一個(gè) httpd 容器。

[root@k8s-node1 ~]# mkdir /var/www
[root@k8s-node1 ~]# docker run -d -p 8080:80 -v /var/www:/usr/local/apache2/htdocs/ httpd 
f571b574a59017de31b615402ae7d6886cde18907bb14c22fe82b8a68757e859

通過 helm package 將 mychart 打包。

[root@k8s-master ~]# helm package mychart
Successfully packaged chart and saved it to: /root/mychart-0.1.0.tgz

執(zhí)行 helm repo index 生成倉(cāng)庫(kù)的 index 文件。

[root@k8s-master ~]# mkdir myrepo
[root@k8s-master ~]# mv mychart-0.1.0.tgz myrepo
[root@k8s-master ~]# helm repo index myrepo/ --url http://192.168.77.20:8080/charts
[root@k8s-master ~]# ls myrepo/
index.yaml  mychart-0.1.0.tgz
[root@k8s-master ~]# 

Helm 會(huì)掃描 myrepo 目錄中的所有 tgz 包并生成 index.yaml。--url指定的是新倉(cāng)庫(kù)的訪問路徑。新生成的 index.yaml 記錄了當(dāng)前倉(cāng)庫(kù)中所有 chart 的信息:

apiVersion: v1
entries:
  mychart:
  - apiVersion: v1
    appVersion: "1.0"
    created: "2019-10-16T23:58:54.835722169+08:00"
    description: A Helm chart for Kubernetes
    digest: 31c8cc4336c1afd09be0094a6bbb5d4c37abb20bbffbcc0c3e72101f6f0635b6
    name: mychart
    urls:
    - http://192.168.77.20:8080/charts/mychart-0.1.0.tgz
    version: 0.1.0
generated: "2019-10-16T23:58:54.834212265+08:00"

當(dāng)前只有 mychart 這一個(gè) chart。

將 mychart-0.1.0.tgz 和 index.yaml 上傳到 k8s-node1 的 /var/www/charts 目錄。

通過 helm repo add 將新倉(cāng)庫(kù)添加到 Helm。

[root@k8s-master ~]# helm repo add newrepo http://192.168.77.20:8080/charts
"newrepo" has been added to your repositories
[root@k8s-master ~]# 
[root@k8s-master ~]# helm repo list
NAME    URL                                             
stable  https://kubernetes-charts.storage.googleapis.com
local   http://127.0.0.1:8879/charts                    
newrepo http://192.168.77.20:8080/charts

倉(cāng)庫(kù)命名為 newrepo,Helm 會(huì)從倉(cāng)庫(kù)下載 index.yaml。

現(xiàn)在已經(jīng)可以 repo search 到 mychart 了。

[root@k8s-master ~]# helm search mychart
NAME            CHART VERSION   APP VERSION     DESCRIPTION                
local/mychart   0.1.0           1.0             A Helm chart for Kubernetes
newrepo/mychart 0.1.0           1.0             A Helm chart for Kubernetes

除了 newrepo/mychart,這里還有一個(gè) local/mychart。這是因?yàn)樵趫?zhí)行第 2 步打包操作的同時(shí),mychart 也被同步到了 local 的倉(cāng)庫(kù)。

已經(jīng)可以直接從新倉(cāng)庫(kù)安裝 mychart 了。

[root@k8s-master ~]# helm install newrepo/mychart
NAME:   ardent-pug
LAST DEPLOYED: Thu Oct 17 00:06:23 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Deployment
NAME                READY  UP-TO-DATE  AVAILABLE  AGE
ardent-pug-mychart  0/1    1           0          0s

==> v1/Pod(related)
NAME                               READY  STATUS             RESTARTS  AGE
ardent-pug-mychart-7858fd5f-j8mpb  0/1    ContainerCreating  0         0s

==> v1/Service
NAME                TYPE       CLUSTER-IP   EXTERNAL-IP  PORT(S)  AGE
ardent-pug-mychart  ClusterIP  10.98.167.6  <none>       80/TCP   0s

NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=ardent-pug" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:80

如果以后倉(cāng)庫(kù)添加了新的 chart,需要用 helm repo update 更新本地的 index。

[root@k8s-master ~]# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "newrepo" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete.

這個(gè)操作相當(dāng)于CentOS的yum update。

小結(jié)
Helm 讓我們能夠像 apt 管理 deb 包那樣安裝、部署、升級(jí)和刪除容器化應(yīng)用。
Helm 由客戶端和 Tiller 服務(wù)器組成??蛻舳素?fù)責(zé)管理 chart,服務(wù)器負(fù)責(zé)管理 release。

chart 是 Helm 的應(yīng)用打包格式,它由一組文件和目錄構(gòu)成。其中最重要的是模板,模板中定義了 Kubernetes 各類資源的配置信息,Helm 在部署時(shí)通過 values.yaml 實(shí)例化模板。

Helm 允許用戶開發(fā)自己的 chart,并為用戶提供了調(diào)試工具。用戶可以搭建自己的 chart 倉(cāng)庫(kù),在團(tuán)隊(duì)中共享 chart。

Helm 幫助用戶在 Kubernetes 上高效地運(yùn)行和管理微服務(wù)架構(gòu)應(yīng)用,Helm 非常重要。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI