首頁>技術>

在我們學習 kubernetes 的過程中,用的最多的是 kubectl 命令列工具,使用 kubectl 工具需要我們編寫好各種部署檔案,這在生產中是非常不方便的,因此 Helm 這個 kubernetes 包管理工具就應運而生了。

Helm 包管理工具不僅可以為我們安裝網上已經成熟的部署庫檔案,而且可以生產本地部署模板,我們只需要簡單改一改,就可以完成一個應用的部署,不需要我們記住那麼多的命令和配置。下面我們就看一下 Helm 的使用。

Helm V2/V3 的對比

元件架構對比:

Helm2:由2個二進位制組成:helm(客戶端cli)、tiller(服務端)Helm3:只有1個二進位制組成:helm、移除了tiller

工作原理對比:

Helm2:helm->tiller->k8s。首先由helm和tiller互動,然後由tiller負責和k8s互動來完成操作。Helm3:helm->k8s。由helm命令去呼叫/root/.kube/config獲取k8s許可權,然後直接與apiserver互動操作。

Helm 安裝的 Chart 的元資訊記錄到k8s的secret裡面,secret名字叫sh.helm.release.v1.${NAME}.v1,另外還有一個serviceaccount,名字叫${NAME}-${CHART名}。helm list命令讀取secret才知道安裝了哪些包。

Helm 主要模組chart:包含應用程式所需要的所有 k8s 資源定義。repository:存放chart的倉庫,類似於docker的映象倉庫。release:chart的例項化,將chart安裝到k8s上,就叫做生成一個release。安裝 Helm

因為 helm3 只有一個二進位制檔案,因此安裝非常簡單

# 二進位制下載地址頁面: https://github.com/helm/helm/releaseswget https://get.helm.sh/helm-v3.4.1-linux-amd64.tar.gztar zxf helm-v3.4.1-linux-amd64.tar.gzcp linux-amd64/helm /usr/local/bin/helm
  source <(helm completion bash)
新增倉庫

命令自動補全,在 /etc/profile 裡面增加如下內容:

helm 和 docker 一樣有預設的官方倉庫,也可以新增第三方倉庫和本地倉庫

helm repo add bitnami https://charts.bitnami.com/bitnamihelm repo add stable https://mirror.azure.cn/kubernetes/chartshelm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo list

更新倉庫快取(helm repo add的時候會獲取一次chart列表並快取,接下來的helm search都是讀取本地快取列表,因此我們需要經常更新快取)

檢視添加了哪些倉庫

helm repo update

移除倉庫

helm repo remove $倉庫名
安裝應用

以安裝 nginx 為例

# 從官方hub搜尋helm search hub nginx# 從所有新增的第三方倉庫中搜索,支援模糊匹配helm search repo nginx# 搜尋指定倉庫的應用(並沒有原生支援,是因為格式都是"倉庫名/chart名",所以可以透過"倉庫名/"來匹配)helm search repo bitnami/ | grep nginx

我們安裝bitnami倉庫的nginx,並指定release名字為myweb

helm install myweb bitnami/nginx

如果要指定名稱空間,則加上-n引數

helm install myweb bitnami/nginx -n kube-system

檢視安裝了哪些庫

helm list --all-namespaces
自定義引數

helm支援兩種自定義引數的方式

values.yaml檔案命令列引數--set

如果兩種同時使用的話,--set的優先順序要高於values.yaml,我們可以透過以下命令來檢視一個chart支援哪些配置引數

helm show values 倉庫名/應用名helm show values bitnami/nginx
使用values.yaml自定義引數來安裝
cat > values.yaml <<EOFresources:  limits:    cpu: 500m    memory: 256Mi  requests:    cpu: 500m    memory: 256MiEOFhelm install -f values.yaml myweb1 bitnami/nginx
使用--set自定義引數來安裝
# 多個引數用英文逗號,隔開helm install --set 'resources.limits.cpu=500m,resources.limits.memory=256Mi,resources.requests.cpu=500m,resources.requests.memory=256Mi' myweb2 bitnami/nginx

檢視我們在某個chart中自定義了哪些引數

helm get values myweb
升級應用

假設要擴容cpu、memory,則修改values.yaml內容,改為

resources:  limits:    cpu: 1000m    memory: 512Mi  requests:    cpu: 1000m    memory: 512Mi

升級命令

helm upgrade -f values.yaml myweb bitnami/nginx
應用回滾

回滾前先檢視歷史版本

helm history myweb

獲得REVISION號後就可以進行回滾操作

helm rollback myweb 2
解除安裝應用

解除安裝並保留歷史記錄,加上--keep-history(不影響解除安裝應用,但會保留記錄)

helm uninstall myweb1 --keep-history

檢視解除安裝過哪些應用(只有保留記錄的才能看到)

helm list --uninstalled --all-namespaces
Chart 目錄結構

如果我們不能聯網,那麼也可以透過helm建立一個本地chart

helm create foo

foo目錄結構如下:

├── .helmignore   # 編譯包的時候忽略哪些檔案├── Chart.yaml    # chart資訊,包括chart版本、app版本、描述等├── charts        # 放置依賴和子chart├── values.yaml   # 模版的預設引數└── templates     # 存放模版    ├── deployment.yaml      # k8s deployment模版    ├── _helpers.tpl         # 定義命名模版(即變數),可在模版裡引用,類似程式設計時候定義一個變數,模版裡就可以引用這個變數    ├── hpa.yaml             # k8s HPA模版    ├── ingress.yaml         # k8s ingress模版    ├── NOTES.txt            # 展示在helm install安裝完成後看到的NOTES部分,同樣支援模版語法    ├── serviceaccount.yaml   # k8s serviceaccount模版    ├── service.yaml         # k8s service模版    └── tests               # 用於測試release是否執行成功        └── test-connection.yaml    # k8s物件,預設是busybox Pod,透過wget測試nginx

templates目錄裡下劃線開頭的不會被渲染,tests用於安裝完成後,執行檢測

測試用例

helm也支援測試用例,例如對於web應用,可以測試http介面是否為200。

那麼如何讓helm認為是測試使用:helm會讀取templates下所有yaml,當metadata帶有如下註解時,helm則認為這是測試專用,而不是普通物件。注意,helm不會去認名字為tests的目錄,只會認註解,因此如果tests下放置沒有註解的k8s物件時會被當作普通物件對待

annotations:  "helm.sh/hook": test

helm install安裝後不會自動進行測試,需要手工執行helm test命令,此時會建立k8s物件(如pod)進行測試,若pod執行完畢(狀態為Completed,即容器裡的程式退出碼為0則表示成功)

helm install myfoo foohelm test myfoo

如果Phase顯示Successed表示成功,檢測失敗則為Failed

Chart 的依賴和父子關係簡介

什麼是chart依賴:如果將chart比作rpm包,則chart依賴也相當於rpm包的依賴,當yum install時候會自動安裝依賴包。對於helm來說,chart依賴就是在Chart.yaml裡設定依賴dependencies哪些倉庫的哪些chart,在helm intall時候就會自動把依賴的chart下載到charts目錄。也可以手工把依賴包下載下來放到charts目錄裡。

什麼是父子chart:下載、手工放到charts目錄裡的chart就是子chart,根目錄的chart就是父chart。無論是否在Values.yaml裡設定了dependencies。

簡單來講:

子Chart只要是已經放到會helm install會自動下載到charts目錄裡的chart,都是子chartChart依賴Chart.yaml裡配置了dependencies就是chart依賴

處理chart依賴的2種方式

嵌入式手工建立,可以透過helm package打成tgz包依賴匯入式透過倉庫獲取,helm dependency update會自動從指定倉庫下載指定版本的chartChart依賴

我們來測試一下Chart的依賴,首先準備測試環境

mkdir chartcd charthelm create webhelm create backendcd web

編輯Chart.yaml,增加如下內容

dependencies:- name: backend  repository: file://../backend  version: 0.1.0- name: mariadb  repository: https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts/  version: 2.1.6

file://是使用本地目錄,但是隻能用於外部目錄,不能用於當前目錄下的自定義子目錄,比如

file://foo 或 file://charts/foo# 都會提示錯誤

處理依賴

helm dependency listhelm dependency update

檢視charts目錄,應當有2個tgz

另外,除了update,還有build命令

helm dependency build
build和update的區別是update:重新讀取Chart.yaml來獲取依賴包build:要先執行過update才能執行build,否則會提示Chart.lock和Chart.yaml不同步,請先update父子Chart

怎麼確定Chart的父子關係,我們看以下的例子:

helm create foocd foocd chartshelm create bar
如果我們把子Chart放到外部目錄,然後透過file://../bar或者其他方式引用,那麼就成了依賴,而不是父子子Chart如果沒寫到Chart.yaml裡面,在檢視依賴時候會提示WARNING: "charts/foo" is not in Chart.yaml.,但不影響使用

在Chart包foo的子目錄charts下建立新的Chart包bar,foo就是父,bar就是子

Helm模板詳解模板基本使用

使用模板的含義就是要建立一個通用的Chart。如果不用模版,就需要為每個應用建立獨立的deployment、service等資源物件,那麼此時用helm的意義就不大,可以看下面的例子:

helm create mychartcd mychartrm -rf templates/*cd templates
apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: mydep  name: mydepspec:  replicas: 2  selector:    matchLabels:      app: mydep  template:    metadata:      labels:        app: mydep    spec:      containers:      - image: nginx        imagePullPolicy: IfNotPresent        name: nginx
apiVersion: v1kind: Servicemetadata:  labels:    app: mydep  name: mysvcspec:  ports:  - port: 80    protocol: TCP    targetPort: 80  selector:    app: mydep  type: NodePort

測試

kubectl apply -f .  # 此時與helm無關或helm install myapp ../   # 此時雖然放在模版目錄裡,但實際上不是模版,因為沒用到任何模版標籤。

Helm模版,支援很多特性,使用起來非常靈活,本質是基於golang內建的text/template模組。如果想在Chart安裝前檢視模板渲染後的內容,有2種辦法

helm template release名 chart名/目錄helm install --dry-run release名 chart名/目錄
第一種是很純粹的看模板渲染效果第二種還有附帶install的其他資訊

上面自定義引數提到的2種方法,也可以用在helm template

helm template -f values.yaml foohelm template --set 'aa=bb' foohelm template foo

上面命令的前提是當前目錄下有一個foo目錄,如果想看倉庫裡的,帶上倉庫名即可

helm template bitnami/nginx

helm template檢視的是尚未安裝成release的yaml,而不是已經安裝成為release的yaml。如果安裝後檢視yaml的命令是

helm get manifest RELEASE名
模板標籤

模板標籤:{{}},例如

{{ .Release.Name }}

測試

helm create foorm -rf foo/templates/*cat > foo/templates/a.yaml <<EOF#{{ .Release.Name }}EOFhelm template myfoo foo

加上 # 的原因是此時的a.yaml不是合法的k8s資源物件格式

可以看出.Release.Name渲染成了release名,如果想要檢視所有的變數/常量

#{{ . }}
模板註釋

模板中的註釋有2種

yaml的註釋模板的註釋

yaml註釋

# This is a commenttype: sprocket

模板註釋

單行{{/* a comment */}}多行{{/*This is a comment*/}}或{{- /*This is a comment*/}}
內建物件

常用的模版內建物件可檢視官方文件

{{ quote .Release.Name }}{{ .Release.Name | quote }}{{ quote .Release.Name | b64enc }}

quote:雙引號

helm內建很多模版函式,這裡無法一一列舉,因此選取幾個常用來說明

{{ quote .Release.Name }}

default:預設值

# 當找不到.Values.hello時預設為world{{ default "world" .Values.hello }}

indent:縮排

{{ indent 4 .Values.hello }}# 注意,hello必須是string,如果是數字,則報錯,此時可以結合quote{{ quote .Values.hello | indent 4 }}

nindent:先換行,再縮排,並且只能處理字串,而且不能用#

ReleaseName: {{ indent 2 .Release.Name }}ReleaseNamespace: {{ nindent 2 .Release.Namespace }}replicaCount: {{ quote .Values.replicaCount | nindent 2 }}

title:首字母大寫

{{ title .Release.Name }}

upper:全部大寫

{{ upper .Release.Name }}

b64enc:base64編碼

{{ b64enc .Release.Name }}

b64dec:base64解碼

{{ b64dec .Release.Name }}
條件判斷if

if/else

{{ if PIPELINE }}  # Do something{{ else if OTHER PIPELINE }}  # Do something else{{ else }}  # Default case{{ end }}

if如何判斷條件是否為假

布林值false數字0空字串""不存在的變數/常量,如.Values.xxx(假設xxx不存在)空集合(map, slice, tuple, dict, array),如values.yaml裡有imagePullSecrets: []

除此之外其他條件都為真

示例如下:

{{ if false }}{{ if 0 }}{{ if "" }}{{ if .Values.xxx }}{{ if .Values.imagePullSecrets }}

字串/數字大小 比較

eq:字串相同/數字相同ne:字串不同/數字不同lt:字串(根據編碼)前者小於後者/數字前者小於後者le:字串(根據編碼)前者小於等於後者/數字前者小於等於後者gt:字串(根據編碼)前者大於後者/數字前者大於後者ge:字串(根據編碼)前者大於等於後者/數字前者大於等於後者

數字比較,要用float64格式,即帶小數點的

示例如下:

{{ if eq 5 5 }}{{ if eq "5" "5" }}{{ if gt "a" "b" }}{{ if gt .Values.replicaCount 2.0 }}

空白符

foo: 123{{ if eq 2 2 }}aa: 11{{ else }}aa: 22{{ end }}bar: 456
遍歷with/range

with:用於map結構

假設values.yaml裡有這麼一段

aa:  xx: 123  yy: 456

用with省去了.Values

{{- with .Values.aa }}aa:  xx: {{ .xx }}  yy: {{ .yy }}{{- end }}

可以用toYaml函式進一步簡寫

{{- with .Values.aa }}aa:  {{- toYaml . | nindent 2 }}{{- end }}

range

和with很像,更適合陣列而不是map(即hash),因為range會自動將map結構中的value獲取出來(忽略key)

data:{{- range .Values.aa }}-  {{ . }}{{- end }}

渲染為如下,可以看到少了key

data:  123  456

因此range更適合陣列,假設values.yaml有一段

bb:- 123- 456

示例如下:

data:{{- range .Values.bb }}- {{ . }}{{- end }}

with裡不能用變數

假設values.yaml

aa:  xx: 123  yy: 456

模板

{{- with .Values.aa }}aa:  xx: {{ .Release.Name }}  yy: {{ .yy }}{{- end }}

會報錯,因為用了with後,.Release.Name變成是從with .Values.aa裡找,當然找不到。有2個解決辦法:

變數賦值

{{- $releaseName := .Release.Name }}{{- with .Values.aa }}aa:  xx: {{ $releaseName }}  yy: {{ .yy }}{{- end }}

用$符號:$.Release.Name

{{- with .Values.aa }}aa:  xx: {{ $.Release.Name }}  yy: {{ .yy }}{{- end }}

range獲取鍵值

data:{{- range $k, $v := .Values.aa }}- name: {{ $k }}  value: {{ $v | quote }}{{- end }}
命名模板

使用define函式定義命名模板,使用template函式引用命名模板

# 定義{{- define "aa" }}...{{- end }}# 引用{{ template "aa" }}

可以寫在templates的yaml裡也可以寫在_helper.tpl(templates目錄裡)作為全域性使用

直接寫在templates的yaml裡
{{- define "foo" }}- name: abc  value: 123- name: def  value: 456{{- end }}kind: ConfigMapdata:{{- template "foo" }}
寫在_helper.tpl裡
# 以下內容放到templates/_helper.tpl{{- define "foo" }}- name: abc  value: 123- name: def  value: 456{{- end }}# 以下內容放到templates/a.yamlkind: ConfigMapdata:{{- template "foo" }}

上述2種方法輸出結果都是

kind: ConfigMapdata:- name: abc  value: 123- name: def  value: 456

template函式不支援管道

{{- define "aaa" -}}bbb{{- end }}xx: {{ template "aaa" | quote }}

會報錯,解決辦法:改用include函式

{{- define "aaa" -}}bbb{{- end }}xx: {{ include "aaa" . | quote }}
變數/常量/作用域

變數

_helpers.tpl裡的{{- define ... -}}

常量

yaml語法自帶的節點定位(Node Anchor)常量:values.yaml裡定義的replicaCount: 1

共享常量

子chart和父chart之間的常量不能共享,如果需要共享,就需要共享常量,這定義在父chart中

父chart的values.yaml里加入

子chart名:  replicaCount: 2

注意必須是子chart名,即charts目錄裡存在該名字

接下來,子chart的模板裡就可以引用

{{ .Values.replicaCount }}

當helm發現節點名是子chart名時,它會自動複製這個常量到子chart的values.yaml中

全域性常量

共享常量只能把常量共享給一個子chart,如果需要多個子chart之間共享,需要全域性常量。在父chart的values.yaml裡用global標識

父chart的values.yaml里加入

global:  hello: world

父和子chart的模板裡都可以引用

{{ .Values.global.hello }}
搭建私有倉庫安裝push外掛
yum install githelm plugin install https://github.com/chartmuseum/helm-push  # 比較慢,耐心等# 如果上面覺得比較慢,可以下載離線寶# tar zxf helm-push-v0.9.0-allinone.tgz# helm plugin install helm-pushhelm push --help  # 新增的push子命令
ChartMuseum

官方提供的私有倉庫,功能較少,適合學習測試使用,類似docker的registry

# https://chartmuseum.com/docs/docker run --rm -tid -p 8080:8080 -v $(pwd)/charts:/charts -e DEBUG=true -e STORAGE=local -e STORAGE_LOCAL_ROOTDIR=/charts chartmuseum/chartmuseum:latestchmod 777 charts

上傳chart方法1:chartmuseum專屬方法,不通用

curl --data-binary "@mychart-0.1.0.tgz" http://localhost:8080/api/charts

上傳chart方法2:使用push外掛(要先新增倉庫)

helm repo add chartmuseum http://localhost:8080helm push foo chartmuseum

查詢有哪些chart

curl http://localhost:8080/api/charts  # 僅chartmuseum支援curl http://localhost:8080/index.yaml  # 通用,helm repo add讀取的是這個檔案,helm search repo 是讀取快取到本地的index.yaml

下載chart

helm pull chartmuseum/mychart
Harbor

harbor內建集成了chartmuseum

wget https://github.com/goharbor/harbor/releases/download/v2.1.0/harbor-offline-installer-v2.1.0.tgztar zxf harbor-offline-installer-v2.1.0.tgzcd harborcp harbor.yml.tmpl harbor.yml編輯harbor.yml  1. 將hostname的值改為伺服器IP,例如192.168.36.128,如果改成域名,要能解析  2. 註釋https部分  # 要在/etc/docker/daemon.json裡增加insecure-registries  3. data_volume改為/data/harbormkdir -p /data/harbor./install.sh --with-chartmuseum

在harbor上配置

建立使用者,假設為user1建立私有專案,假設為helm將user1新增到helm專案裡

新增倉庫

helm repo add myharbor http://192.168.36.128/chartrepo/helm --username user1

上傳chart並檢視

輸入密碼後,成功的話會提示"myharbor" has been added to your repositories

helm push mychart-0.1.0.tgz myharbor# 此時harbor頁面上可以看到新增了mycharthelm repo updatehelm search repo myharbor/

下載chart

helm pull myharbor/mycharthelm pull myharbor/mychart --version 0.1.0
生產環境最佳實踐

建議1:引數名要統一,尤其在一個團隊裡。例如表示deployment副本數,不要有的用replicas,有的用replicaCount

建議2:給引數加註釋。因為chart寫好後很少需要改動,時間長了容易忘。

建議3:針對不同環境(開發/測試/生產),建立不同的values.yaml,如values-dev.yaml、values-production.yaml

建議4:建立values.schema.json來校驗values

其中第4點例子如下,假設values.yaml裡有如下內容

image:  repository: my-docker-image  pullPolicy: IfNotPresent
要求image引數必須存在,要求image必須存在repository和pullPolicyrepository型別為字串,值的格式為小寫字母a-z、數字0-9、橫線-、下劃線_pullPolicy型別為字串,值只能有3種:Always、Never、IfNotPresent

在同級目錄下建立values.schema.json,內容如下

{  "$schema": "http://json-schema.org/schema#",  "type": "object",  "required": [    "image"  ],  "properties": {    "image": {      "type": "object",      "required": [        "repository",        "pullPolicy"      ],      "properties": {        "repository": {          "type": "string",          "pattern": "^[a-z0-9-_]+$"        },        "pullPolicy": {          "type": "string",          "pattern": "^(Always|Never|IfNotPresent)$"        }      }    }  }}

執行helm install、helm upgrade、helm lint、helm template測試,若不滿足如上3點要求,則應當報錯。

建議5:在chart目錄下放置README.md,將chart的設計思想、注意事項寫清楚,推薦格式如下

# chart名## 一鍵安裝命令(helm repo add、helm intall)## 介紹## 環境要求## 安裝chart## 解除安裝chart## 引數### global引數### common引數### 父chart引數### 子chart引數### 注意事項## 參考文件## CHANGELOG

建議6:templates裡的NOTES.txt是部署完成後看到的內容,針對不同條件設定輸出

建議7:helm預設讀取/root/.kube/config,但這樣許可權過大,很不安全,而且helm通常是由開發或CD系統來使用,因此需要提前在K8S的RBAC裡建立分配好許可權,只允許在指定的namespace裡進行部署

建議8:不要把各種微服務集中於一個chart,可以採用父子chart方式,讓每個chart更輕量

22
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 「Rust語言」用Rust構建微服務