一、為什麼需要Helm?
Kubernetes目前已成為容器編排的事實標準,隨著傳統架構向微服務容器化架構的轉變,從一個巨大的單體的應用切分為多個微服務,每個微服務可獨立部署和擴充套件,實現了敏捷開發和快速部署,但是由於從大一個應用變成了多個微服務,導致服務數大幅增加,對於Kubernetes來說,針對每個服務需要部署如deployment、statufulset、service、pod 等資原始檔,而對於一個複雜的應用來說,可能會有很多類似上面的資原始檔,其中還有更新或回滾的需求,若要執行,需要修改和維護相關的大量資原始檔,這時候,如何針對每個服務涉及到資源集合到一個整體來實現部署、升級和回滾是亟待需要解決的難題,Helm就是在這個背景下誕生的。
二、Helm的簡介和使用場景
那麼Helm是如何解決上述難題的呢?它其實是將Kubernetes資源(如deployment、statufulset、service、pod) 打包到一個chart中,而chart被打包並推送儲存到chart倉庫(repo),然後通過chart倉庫用來儲存和分享chart包。Helm可直接拉取並安裝chart包,生成helm維度整體的應用,其應用會包含如deployment、statufulset、service、pod的資源,並支援以一個整體Helm應用的維度來進行版本控制、打包、釋出、刪除、更新等操作。針對使用者來說主要適用如下場景:
1、將常用搭配、標準化的多個資原始檔放在同一個chart包中,方便統一的版本控制、安裝、部署、升級、回滾和刪除
2、可以大大簡化了使用Kubernetes部署的難度,降低了使用者使用門檻,只需配置引數即可一鍵部署
三、Helm實踐
本次演示Helm的版本號如下:
[root@k8s-master my-second-helm]# helm version version.BuildInfo{Version:"v3.3.1", GitCommit:"249e5215cde0c3fa72e27eb7a30e8d55c9696144", GitTreeState:"clean", GoVersion:"go1.14.7"}
1、安裝
1)製作chart包的形式
Helm 應用的安裝既可以從遠端的repo進行拉取chart包進行安裝,repo中提供了很多官方的chart包,若無個性化的需求,可以類似公開映象一樣進行拉取並安裝,若需要單獨調整,可以在已有的chart包進行更新再次打包,然後拉取更新後的chart包進行安裝,接下來先來實踐下後者,我們先新建一個helm的目錄,然後在此目錄下執行如下命令:
[root@k8s-master helm]# helm create my-second-helm
Creating my-second-helm
[root@k8s-master helm]# ls my-first-helm my-second-helm
[root@k8s-master helm]# cd my-second-helm/ [root@k8s-master my-second-helm]# ls charts Chart.yaml templates values.yaml
[root@k8s-master helm]# tree my-second-helm my-second-helm ├── charts ├── Chart.yaml ├── templates │ ├── deployment.yaml │ ├── _helpers.tpl │ ├── hpa.yaml │ ├── ingress.yaml │ ├── NOTES.txt │ ├── serviceaccount.yaml │ ├── service.yaml │ └── tests │ └── test-connection.yaml └── values.yaml 3 directories, 10 files
我們在前面的操作中只是指定了這個資料夾的名字my-second-helm,但是在這個檔案下預設建立了多個目錄和檔案,接下里我們一起看下預設的檔案中都包含哪些內容,各自充當什麼作用,先一起看下Chart.yaml這個檔案
[root@k8s-master my-second-helm]# cat Chart.yaml apiVersion: v2 name: my-second-helm description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. # # Application charts are a collection of templates that can be packaged into versioned archives # to be deployed. # # Library charts provide useful utilities or functions for the chart developer. They're included as # a dependency of application charts to inject those utilities and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. appVersion: 1.16.0
可以看到Chart.yaml這個檔案主要是用來對chart進行的描述,宣告瞭當前 Chart 的名稱、版本等基本資訊,如果我們自己製作的chart有相關的描述都可以寫入到檔案,這些資訊會在該 Chart 被放入倉庫後,供使用者瀏覽檢索,方便使用者瞭解這個chart。
在templates這個目錄下,我們看到了大家比較熟悉的資源,我們看下deployment.yaml的構成
[root@k8s-master templates]# cat deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "my-second-helm.fullname" . }} labels: {{- include "my-second-helm.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: {{- include "my-second-helm.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} annotations: {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "my-second-helm.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "my-second-helm.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 80 protocol: TCP livenessProbe: httpGet: path: / port: http readinessProbe: httpGet: path: / port: http resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }}
細心的讀者可能發現了,這個kind為deployment的資源與我們平常自己寫的有些出入,主要是出現了很多類似{}的內容,如:replicas: {{ .Values.replicaCount }},平常我們是用replicas: 數字的形式,而這裡已經換成了變數,而整個檔案出現了很多類似的變數,想必讀者也猜到了原來Helm是通過設定變數來統一管理很多需要輸入的值,若有多個檔案引用同一個變數,就再也不用同時修改多個檔案了,細心的讀者可能會發現,即便對K8s不太瞭解的使用者,也可以僅設定變數的值也可以輕鬆部署應用,豈不可以大大降低部署的難度,確實如此,這就是helm的一個主要的特點,讓部署變的簡單,特別是常用的標準的配置我們可將其製作成chart一鍵部署,讓我們再回到replicas: {{ .Values.replicaCount }}這個變數上,既然存在變數,那變數的設值在哪裡呢? 我們可以看到變數的組成中存在Values,而和我們templates同級別的目錄選還存在一個很重要的檔案values.yaml,我們看下這個檔案裡面的內容:
[root@k8s-master my-second-helm]# cat values.yaml # Default values for my-second-helm. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: nginx pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: [] nameOverride: "" fullnameOverride: "" serviceAccount: # Specifies whether a service account should be created create: true # Annotations to add to the service account annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 securityContext: {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true # runAsUser: 1000 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 autoscaling: enabled: false minReplicas: 1 maxReplicas: 100 targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 nodeSelector: {} tolerations: [] affinity: {}
可以看到這個檔案包含的是整個chart中各個資源下變數的值,如replicaCount: 1,前面replicas: {{ .Values.replicaCount }},因此上面的deployment的replicas為1,其他變數的值類似,其中{}表示未指定,使用者可根據需要進行自定義。
接下來我們通過vim 修改下value.yaml檔案中replicaCount: 2內容然後進行安裝部署,但是安裝部署之前需要先打包,我們在上面的操作都是針對目錄和檔案的,helm只能針對包進行安裝,因此我們需先打包,打包之前,最好先驗證下我們之前在各個檔案輸入的值是否合法,可以通過操作進行校驗
[root@k8s-master my-second-helm]# helm lint --strict my-second-helm ==> Linting my-second-helm Error unable to check Chart.yaml file in chart: stat my-second-helm/Chart.yaml: no such file or directory Error: 1 chart(s) linted, 1 chart(s) failed [root@k8s-master my-second-helm]# cd .. [root@k8s-master helm]# ls my-first-helm my-second-helm [root@k8s-master helm]# helm lint --strict my-second-helm ==> Linting my-second-helm [INFO] Chart.yaml: icon is recommended 1 chart(s) linted, 0 chart(s) failed
可以看到在進行校驗的時候,需要回到chart的根目錄,0 chart(s) failed顯示這個chart的之前輸入的value值應無問題,接下來可以執行打包操作了,如下所示,紅色字型就是我們打包後的結果
[root@k8s-master helm]# helm package my-second-helm Successfully packaged chart and saved it to: /home/James/zhanglei/helm/my-second-helm-0.1.0.tgz [root@k8s-master helm]# ls my-first-helm my-second-helm my-second-helm-0.1.0.tgz
這個就是我們所說的chart包,接下來終於可以執行安裝的操作了
[root@k8s-master helm]# helm install my-second-helm-test my-second-helm-0.1.0.tgz NAME: my-second-helm-test LAST DEPLOYED: Sat Sep 12 21:48:18 2020 NAMESPACE: default STATUS: deployed # 狀態 REVISION: 1 NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=my-second-helm,app.kubernetes.io/instance=my-second-helm-test" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace default port-forward $POD_NAME 8080:80 [root@k8s-master helm]# helm list NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION my-second-helm-test default 1 2020-09-12 21:48:18.480694046 +0800 CST deployed my-second-helm-0.1.0 1.16.0
可以看到在install後面指定的就是部署名稱,在helm list中可追溯到時通過哪個版本的chart包進行的安裝,可以看到狀態為deployed,我們之前設定了deployment的副本例項數為2,我們一起來驗證下:
[root@k8s-master helm]# kubectl get pod -o wide |grep my-second-helm
my-second-helm-test-566f5d8757-x27zf 1/1 Running 0 5m25s 10.122.235.244 k8s-master <none> <none>
my-second-helm-test-566f5d8757-zdm47 1/1 Running 0 5m25s 10.122.235.247 k8s-master <none> <none>
可以看到2個Pod已經running了,我們測試這個nginx如下所示為正常了,也就是說我們通過helm install chart包的方式成功安裝了NGINX的應用
[root@k8s-master helm]# curl 10.122.235.244:80 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body>
至此,我們演示了通過在本地目錄裡面新建了一個預設的chart目錄,然後對value中的值進行修改,最後的再次打包,然後安裝的全過程,有讀者不免發問了,這個過程太麻煩了,我只需要型別像公有映象,並不想製作chart,是否可以遠端直接拉取chart包安裝?答案是:可以!接下來我將介紹第二種建立helm 應用的方式。
2)遠端拉取chart包
但是在拉取公有的chart包之前,需要先配置好chart repo,如下所示是我已經配置好的chart repo,裡面提供常用的chart包
[root@k8s-master my-first-helm]# helm repo list NAME URL stable http://mirror.azure.cn/kubernetes/charts incubator http://mirror.azure.cn/kubernetes/charts-incubator svc-cat http://mirror.azure.cn/kubernetes/svc-catalog-charts
我們在正式拉取chart包之前,很多時候想在對應的倉庫中先查詢下有想要的chart包,可以通過如下操作進行檢視下,比如我想檢視下有無nginx的chart
[root@k8s-master my-first-helm]# helm search repo nginx NAME CHART VERSION APP VERSION DESCRIPTION stable/nginx-ingress 1.41.3 v0.34.1 DEPRECATED! An nginx Ingress controller that us... stable/nginx-ldapauth-proxy 0.1.4 1.13.5 nginx proxy with ldapauth stable/nginx-lego 0.3.1 Chart for nginx-ingress-controller and kube-lego stable/gcloud-endpoints 0.1.2 1 DEPRECATED Develop, deploy, protect and monitor...
可以看到,通過search命令,可查詢到倉庫中已有的NGINX的chart包,其中stable是repo的型別,CHART VERSION:chart包的版本,APP VERSION:應用版本,DESCRIPTION:針對此chart包的描述,看到這裡是不是有印象,這正式是我們在上文提到Chart.yaml檔案裡面的資訊,接下來我們嘗試直接從遠端倉庫進行拉取並完成安裝的操作
[root@k8s-master helm]# helm install nginx-test stable/nginx-ingress WARNING: This chart is deprecated NAME: nginx-test LAST DEPLOYED: Sat Sep 12 23:47:59 2020 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: ******************************************************************************************************* * DEPRECATED, please use https://github.com/kubernetes/ingress-nginx/tree/master/charts/ingress-nginx * ******************************************************************************************************* The nginx-ingress controller has been installed. It may take a few minutes for the LoadBalancer IP to be available. You can watch the status by running 'kubectl --namespace default get services -o wide -w nginx-test-nginx-ingress-controller' An example Ingress that makes use of the controller: apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx name: example namespace: foo spec: rules: - host: www.example.com http: paths: - backend: serviceName: exampleService servicePort: 80 path: / # This section is only required if TLS is to be enabled for the Ingress tls: - hosts: - www.example.com secretName: example-tls If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided: apiVersion: v1 kind: Secret metadata: name: example-tls namespace: foo data: tls.crt: <base64 encoded cert> tls.key: <base64 encoded key> type: kubernetes.io/tls
[root@k8s-master helm]# helm list NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION my-second-helm-test default 1 2020-09-12 21:48:18.480694046 +0800 CST deployed my-second-helm-0.1.0 1.16.0 nginx-test default 1 2020-09-12 23:47:59.084999904 +0800 CST deployed nginx-ingress-1.41.3 v0.34.1
可以看到helm應用已經部署成功其應用名稱為nginx-test。
2、升級
1)通過修改檔案內容升級
可以針對chart目錄的檔案內容更新後再進行升級,如vim values.yaml檔案,我們將之前replicaCount的值設定為4
[root@k8s-master my-second-helm]# vim values.yaml # Default values for my-second-helm. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 4
[root@k8s-master my-second-helm]# helm upgrade -f values.yaml my-second-helm-test ./ Release "my-second-helm-test" has been upgraded. Happy Helming! # 顯示升級成功 NAME: my-second-helm-test LAST DEPLOYED: Sun Sep 13 10:41:27 2020 NAMESPACE: default STATUS: deployed REVISION: 5 # release的版本號為5 NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=my-second-helm,app.kubernetes.io/instance=my-second-helm-test" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace default port-forward $POD_NAME 8080:80
[root@k8s-master my-second-helm]# kubectl get pod |grep my-second my-second-helm-test-566f5d8757-8v8wc 1/1 Running 0 6m32s my-second-helm-test-566f5d8757-kfh5s 1/1 Running 0 11h my-second-helm-test-566f5d8757-s4286 1/1 Running 0 86s my-second-helm-test-566f5d8757-t2hhx 1/1 Running 0 11h
如上看到4個pod例項已經正常Running了,升級成功。
2)通過--set引數升級
其格式為:helm upgrade -f 指定目錄/指定檔案 --set 指定檔案引數=value 部署名稱 chart目錄
[root@k8s-master helm]# helm upgrade -f my-second-helm/values.yaml --set replicaCount=3 my-second-helm-test ./my-second-helm Release "my-second-helm-test" has been upgraded. Happy Helming! NAME: my-second-helm-test LAST DEPLOYED: Sun Sep 13 11:08:30 2020 NAMESPACE: default STATUS: deployed REVISION: 6 # 版本6 NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=my-second-helm,app.kubernetes.io/instance=my-second-helm-test" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace default port-forward $POD_NAME 8080:80 [root@k8s-master helm]# kubectl get pod | grep my-se my-second-helm-test-566f5d8757-8v8wc 1/1 Running 0 32m my-second-helm-test-566f5d8757-kfh5s 1/1 Running 0 12h my-second-helm-test-566f5d8757-t2hhx 1/1 Running 0 12h
在上個版本5中我們在values.yaml修改replicaCount的值為4,版本6中通過--set的方式我們修改了replicaCount的值為3,這個是否會同步到values.yaml中呢?
[root@k8s-master my-second-helm]# cat values.yaml # Default values for my-second-helm. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 4
通過檢視,replicaCount的值任然是版本5的值,並未發生改變,也就是說通過--set的值並不會影響到chart目錄下原始檔的值,另外從這種方式的升級來看,我們得知helm應用的升級是針對已經部署後的應用進行升級,並非針對chart版本的版本進行升級,這些地方都需要注意下!
3、回滾
升級操作成功後,每升級一次版本都會加1,在上面的例子裡版本號已經6了,若我們想回到版本5該如何進行操作呢?我們先看下整個release的部署歷史:
[root@k8s-master helm]# helm history my-second-helm-test REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION 1 Sat Sep 12 21:48:18 2020 superseded my-second-helm-0.1.0 1.16.0 Install complete 2 Sun Sep 13 00:07:17 2020 superseded my-second-helm-0.1.0 1.16.0 Upgrade complete 3 Sun Sep 13 10:30:36 2020 superseded my-second-helm-0.1.0 1.16.0 Upgrade complete 4 Sun Sep 13 10:36:21 2020 superseded my-second-helm-0.1.0 1.16.0 Upgrade complete 5 Sun Sep 13 10:41:27 2020 superseded my-second-helm-0.1.0 1.16.0 Upgrade complete 6 Sun Sep 13 11:08:30 2020 deployed my-second-helm-0.1.0 1.16.0 Upgrade complete
可以看到針對同一個release來說,最終生效的只有一個版本,其狀態為:deployed,其他版本為superseded的狀態,接下來我們將版本6回滾到版本5
[root@k8s-master my-second-helm]# helm rollback my-second-helm-test 5 Rollback was a success! Happy Helming!
[root@k8s-master my-second-helm]# kubectl get pod |grep my-second-test [root@k8s-master my-second-helm]# kubectl get pod |grep my-second my-second-helm-test-566f5d8757-8v8wc 1/1 Running 0 51m my-second-helm-test-566f5d8757-kfh5s 1/1 Running 0 12h my-second-helm-test-566f5d8757-njqbn 1/1 Running 0 61s my-second-helm-test-566f5d8757-t2hhx 1/1 Running 0 12h
版本5:4個Pod,版本6:3個Pod,可以看到命令執行完成之後立馬就回滾且生效了,而回滾的操作命令也很簡單,其格式為:helm rollback 部署名稱 版本號
4、刪除
若想刪除已經部署成功的helm應用該如何操作呢?helm應用的刪除其chart下的資源如Pod是否會保留?我們帶著這些問題實踐下:
[root@k8s-master my-second-helm]# helm delete my-second-helm-test release "my-second-helm-test" uninstalled [root@k8s-master my-second-helm]# kubectl get all |grep my-second-helm [root@k8s-master my-second-helm]#
刪除的格式為:helm delete 部署名稱, 經驗證,一旦刪除helm的應用,其關聯生成的所有資源會被全部刪除掉,也就是說,helm提供給使用者是一個集合了所需資源的整體應用,其新建和刪除也都是以整體應用為維度。
四、總結
本文介紹了什麼是Helm,在哪些場景下適用Helm,然後介紹了Helm應用的安裝、部署、升級、回滾和刪除等操作,Helm應用是一個資源的集合,以整體為使用者提供服務,通過這個服務,可以大大簡化使用者部署的難度,另外還針對應用的版本進行了歷史管理,升級和回滾的操作減少了使用者自己重新部署和維護版本的成本,在產品設計中,我們可將整個部署helm的流程體現在UI化達到進一步降低部署應用的門檻,校驗chart》安裝chart-》部署-》升級-》回滾-》刪除,設計的重點保證這個業務主流程資訊可以清晰的傳遞給使用者,針對重點的操作,需要在頁面中重點凸顯出來,且要方便使用者實際進行操作。
作者簡介:雲端計算容器\Docker\K8s\Serverless方向產品經理,學點技術,為更好地設計產品。