溫馨提示×

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

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

玩K8S不得不會(huì)的HELM

發(fā)布時(shí)間:2020-06-13 08:35:05 來源:網(wǎng)絡(luò) 閱讀:1338 作者:KaliArch 欄目:云計(jì)算

一 基本概念

helm 類似于Linux系統(tǒng)下的包管理器,如yum/apt等,可以方便快捷的將之前打包好的yaml文件快速部署進(jìn)kubernetes內(nèi),方便管理維護(hù)。

  • helm:一個(gè)命令行下客戶端工具,主要用于kubernetes應(yīng)用chart的創(chuàng)建/打包/發(fā)布已經(jīng)創(chuàng)建和管理和遠(yuǎn)程Chart倉庫。
  • Tiller:helm的服務(wù)端,部署于kubernetes內(nèi),Tiller接受helm的請(qǐng)求,并根據(jù)chart生成kubernetes部署文件(helm稱為release),然后提交給 Kubernetes 創(chuàng)建應(yīng)用。Tiller 還提供了 Release 的升級(jí)、刪除、回滾等一系列功能。
  • Chart: helm的軟件包,采用tar格式,其中包含運(yùn)行一個(gè)應(yīng)用所需的所有鏡像/依賴/資源定義等,還可能包含kubernetes集群中服務(wù)定義
  • Release:在kubernetes中集群中運(yùn)行的一個(gè)Chart實(shí)例,在同一個(gè)集群上,一個(gè)Chart可以安裝多次,每次安裝均會(huì)生成一個(gè)新的release。
  • Repository:用于發(fā)布和存儲(chǔ)Chart的倉庫

簡(jiǎn)單來說:

  • helm的作用:像centos7中的yum命令一樣,管理軟件包,只不過helm這兒管理的是在k8s上安裝的各種容器。
  • tiller的作用:像centos7的軟件倉庫一樣,簡(jiǎn)單說類似于/etc/yum.repos.d目錄下的xxx.repo。

二 組件架構(gòu)

玩K8S不得不會(huì)的HELMcdn.xitu.io/2019/8/20/16cada288ee25884?w=1778&h=1094&f=png&s=802665">

三 工作原理

3.1 Chart install

  • helm從制定目錄或tar文件解析chart結(jié)構(gòu)信息
  • helm將制定的chart結(jié)構(gòu)和value信息通過gRPC協(xié)議傳遞給tiller
  • tiller根據(jù)chart和values生成一個(gè)release
  • tiller通過json將release發(fā)送給kubernetes,生成release

3.2 Chart update

  • helm從制定的目錄或tar文件解析chart結(jié)構(gòu)信息
  • helm將制定的chart結(jié)構(gòu)和value信息通過gRPC協(xié)議傳給tiller
  • tiller生成release并更新制定名稱的release的history
  • tiller將release信息發(fā)送給kubernetes用于更新release

3.3 Chart Rollback

  • helm將會(huì)滾的release名稱傳遞給tiller
  • tiller根據(jù)release名稱查找history
  • tiller從history中獲取到上一個(gè)release
  • tiller將上一個(gè)release發(fā)送給kubernetes用于替換當(dāng)前release

3.4 Chart處理依賴

Tiller 在處理 Chart 時(shí),直接將 Chart 以及其依賴的所有 Charts 合并為一個(gè) Release,同時(shí)傳遞給 Kubernetes。因此 Tiller 并不負(fù)責(zé)管理依賴之間的啟動(dòng)順序。Chart 中的應(yīng)用需要能夠自行處理依賴關(guān)系。

四 安裝部署

4.1 v2版本安裝

4.1.1 安裝helm

# 在helm客戶端主機(jī)上,一般為master主機(jī)
wget https://get.helm.sh/helm-v2.14.2-linux-amd64.tar.gz
tar xf helm-v2.14.2-linux-amd64.tar.gz
mv helm /usr/local/bin/
helm version

4.1.2 初始化tiller

  • 初始化tiller會(huì)自動(dòng)讀取~/.kube目錄,所以需要確保config文件存在并認(rèn)證成功

  • tiller配置rbac,新建rabc-config.yaml并應(yīng)用
# 在:https://github.com/helm/helm/blob/master/docs/rbac.md 可以找到rbac-config.yaml

cat > rbac-config.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: tiller
    namespace: kube-system
EOF

kubectl apply -f rbac-config.yaml
  • 制定鏡像
docker pull jessestuart/tiller:v2.14.2

yum install socat

# yum install socat

docker tag jessestuart/tiller:v2.14.2 gcr.io/kubernetes-helm/tiller:v2.14.2

helm init -i gcr.io/kubernetes-helm/tiller:v2.9.0

# 需要注意點(diǎn)參數(shù)
–client-only:也就是不安裝服務(wù)端應(yīng)用,這在 CI&CD 中可能需要,因?yàn)橥ǔD阋呀?jīng)在 k8s 集群中安裝好應(yīng)用了,這時(shí)只需初始化 helm 客戶端即可;
–history-max:最大歷史,當(dāng)你用 helm 安裝應(yīng)用的時(shí)候,helm 會(huì)在所在的 namespace 中創(chuàng)建一份安裝記錄,隨著更新次數(shù)增加,這份記錄會(huì)越來越多;
–tiller-namespace:默認(rèn)是 kube-system,你也可以設(shè)置為其它 namespace;
  • 修改鏡像
# 由于gfw原因,可以利用此鏡像https://hub.docker.com/r/jessestuart/tiller/tags
kubectl edit deployment -n kube-system tiller-deploy

image: jessestuart/tiller:v2.14.0
  • 異常處理
Error: Looks like "https://kubernetes-charts.storage.googleapis.com" is not a valid chart repository or cannot be reached: Get https://kubernetes-charts.storage.googleapis.com/index.yaml: read tcp 10.2.8.44:49020->216.58.220.208:443: read: connection reset by peer

解決方案:更換源:helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

然后在helm init

注意:tiller可能運(yùn)行在node節(jié)點(diǎn),將tiller鏡像下載到node節(jié)點(diǎn)并修改tag
  • 查看版本
[root@master ~]# helm version
Client: &version.Version{SemVer:"v2.14.2", GitCommit:"a8b13cc5ab6a7dbef0a58f5061bcc7c0c61598e7", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.2+unreleased", GitCommit:"d953c6875cfd4b351a1e8205081ea8aabad7e7d4", GitTreeState:"dirty"}

4.2 helm3 安裝部署

由于國外很多鏡像網(wǎng)站國內(nèi)無法訪問,例如gcr.io ,建議使用阿里源,https://developer.aliyun.com/hub。

AppHub 是一個(gè)托管在國內(nèi)公有云上、全公益性的 Helm Hub “中國站”,它的后端由阿里云容器平臺(tái)團(tuán)隊(duì)的三位工程師利用 20% 時(shí)間開發(fā)完成。

而這個(gè)站點(diǎn)的一個(gè)重要職責(zé),就是把所有 Helm 官方 Hub 托管的應(yīng)用自動(dòng)同步到國內(nèi);同時(shí),自動(dòng)將 Charts 文件中的 gcr.io 等所有有網(wǎng)絡(luò)訪問問題的 URL 替換成為穩(wěn)定的國內(nèi)鏡像 URL。

目前helm3已經(jīng)不依賴于tiller,Release 名稱可在不同 ns 間重用。

4.2.1 安裝helm

Helm3 不需要安裝tiller,下載到 Helm 二進(jìn)制文件直接解壓到 $PATH 下就可以使用了。

cd /opt && wget https://cloudnativeapphub.oss-cn-hangzhou.aliyuncs.com/helm-v3.0.0-alpha.1-linux-amd64.tar.gz
tar -xvf helm-v3.0.0-alpha.1-linux-amd64.tar.gz
mv linux-amd64 helm3
mv helm3/helm helm3/helm3
chown root.root helm3 -R
cat > /etc/profile.d/helm3.sh << EOF
export PATH=$PATH:/opt/helm3
EOF
source /etc/profile.d/helm3.sh

[root@master helm3]# helm3 version
version.BuildInfo{Version:"v3.0.0-alpha.1", GitCommit:"b9a54967f838723fe241172a6b94d18caf8bcdca", GitTreeState:"clean"}

4.2.2 使用helm3安裝應(yīng)用

helm repo add apphub https://apphub.aliyuncs.com
helm search guestbook
helm install guestbook apphub/guestbook

五 使用

5.1 基礎(chǔ)命令

http://hub.kubeapps.com/

completion  # 為指定的shell生成自動(dòng)完成腳本(bash或zsh)
create      # 創(chuàng)建一個(gè)具有給定名稱的新 chart
delete      # 從 Kubernetes 刪除指定名稱的 release
dependency  # 管理 chart 的依賴關(guān)系
fetch       # 從存儲(chǔ)庫下載 chart 并(可選)將其解壓縮到本地目錄中
get         # 下載一個(gè)命名 release
help        # 列出所有幫助信息
history     # 獲取 release 歷史
home        # 顯示 HELM_HOME 的位置
init        # 在客戶端和服務(wù)器上初始化Helm
inspect     # 檢查 chart 詳細(xì)信息
install     # 安裝 chart 存檔
lint        # 對(duì) chart 進(jìn)行語法檢查
list        # releases 列表
package     # 將 chart 目錄打包成 chart 檔案
plugin      # 添加列表或刪除 helm 插件
repo        # 添加列表刪除更新和索引 chart 存儲(chǔ)庫
reset       # 從集群中卸載 Tiller
rollback    # 將版本回滾到以前的版本
search      # 在 chart 存儲(chǔ)庫中搜索關(guān)鍵字
serve       # 啟動(dòng)本地http網(wǎng)絡(luò)服務(wù)器
status      # 顯示指定 release 的狀態(tài)
template    # 本地渲染模板
test        # 測(cè)試一個(gè) release
upgrade     # 升級(jí)一個(gè) release
verify      # 驗(yàn)證給定路徑上的 chart 是否已簽名且有效
version     # 打印客戶端/服務(wù)器版本信息
dep         # 分析 Chart 并下載依賴
  • 指定value.yaml部署一個(gè)chart
helm install --name els1 -f values.yaml stable/elasticsearch
  • 升級(jí)一個(gè)chart
helm upgrade --set mysqlRootPassword=passwd db-mysql stable/mysql

helm upgrade go2cloud-api-doc go2cloud-api-doc/ 
  • 回滾一個(gè) chart
helm rollback db-mysql 1
  • 刪除一個(gè) release
helm delete --purge db-mysql
  • 只對(duì)模板進(jìn)行渲染然后輸出,不進(jìn)行安裝
helm install/upgrade xxx --dry-run --debug

5.2 Chart文件組織

myapp/                               # Chart 目錄
├── charts                           # 這個(gè) charts 依賴的其他 charts,始終被安裝
├── Chart.yaml                       # 描述這個(gè) Chart 的相關(guān)信息、包括名字、描述信息、版本等
├── templates                        # 模板目錄
│   ├── deployment.yaml              # deployment 控制器的 Go 模板文件
│   ├── _helpers.tpl                 # 以 _ 開頭的文件不會(huì)部署到 k8s 上,可用于定制通用信息
│   ├── ingress.yaml                 # ingress 的模板文件
│   ├── NOTES.txt                    # Chart 部署到集群后的一些信息,例如:如何使用、列出缺省值
│   ├── service.yaml                 # service 的 Go 模板文件
│   └── tests
│       └── test-connection.yaml
└── values.yaml                      # 模板的值文件,這些值會(huì)在安裝時(shí)應(yīng)用到 GO 模板生成部署文件

5.3 新建自己的Chart

  • 創(chuàng)建自己的mychart
[root@master mychart]# helm create mychart
Creating mychart
[root@master mychart]# ls
mychart
[root@master mychart]# tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templates
│?? ├── deployment.yaml                     # 部署相關(guān)資源
│?? ├── _helpers.tpl                            # 模版助手
│?? ├── ingress.yaml                            # ingress資源
│?? ├── NOTES.txt                                   # chart的幫助文本,運(yùn)行helm install展示給用戶
│?? ├── service.yaml                            # service端點(diǎn)
│?? └── tests
│??     └── test-connection.yaml
└── values.yaml

3 directories, 8 files
  • 刪除template下的所有文件,并創(chuàng)建configmap
rm -rf mychart/templates/*
# 我們首先創(chuàng)建一個(gè)名為 mychart/templates/configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "Hello World"
  • 安裝測(cè)試

由于創(chuàng)建的yaml文件在template下,tiller讀取此文件,會(huì)將其發(fā)送給kubernetes。

[root@master mychart]# helm install ./mychart/
NAME:   enervated-dolphin
LAST DEPLOYED: Sun Jul 21 09:29:13 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME               DATA  AGE
mychart-configmap  1     0s

[root@master mychart]# kubectl get cm mychart-configmap
NAME                DATA   AGE
mychart-configmap   1      2m6s
[root@master mychart]# kubectl describe cm mychart-configmap
Name:         mychart-configmap
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
myvalue:
----
this is my chart configmap
Events:  <none>

[root@master mychart]# helm get manifest enervated-dolphin

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "this is my chart configmap"

helm get manifest 命令獲取 release 名稱(enervated-dolphin)并打印出上傳到服務(wù)器的所有 Kubernetes 資源。每個(gè)文件都以 --- 開始作為 YAML 文檔的開始,然后是一個(gè)自動(dòng)生成的注釋行,告訴我們?cè)撃0逦募傻倪@個(gè) YAML 文檔。

從那里開始,我們可以看到 YAML 數(shù)據(jù)正是我們?cè)谖覀兊?configmap.yaml 文件中所設(shè)計(jì)的 。

現(xiàn)在我們可以刪除我們的 release:helm delete enervated-dolphin

[root@master mychart]# helm delete enervated-dolphin
release "enervated-dolphin" deleted

5.4 添加模版調(diào)用

硬編碼 name: 成資源通常被認(rèn)為是不好的做法。名稱應(yīng)該是唯一的一個(gè)版本。所以我們可能希望通過插入 release 名稱來生成一個(gè)名稱字段。

提示: name: 由于 DNS 系統(tǒng)的限制,該字段限制為 63 個(gè)字符。因此,release 名稱限制為 53 個(gè)字符。Kubernetes 1.3 及更早版本僅限于 24 個(gè)字符(即 14 個(gè)字符名稱)。

修改下之前的configmap為如下內(nèi)容

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"

name: 現(xiàn)在這個(gè)值發(fā)生了變化成了 {{.Release.Name}}-configmap。

模板指令包含在 {{}} 塊中。

模板指令 {{.Release.Name}} 將 release 名稱注入模板。傳遞給模板的值可以認(rèn)為是 namespace 對(duì)象,其中 dot(.)分隔每個(gè) namespace 元素。

Release 前面的前一個(gè)小圓點(diǎn)表示我們從這個(gè)范圍的最上面的 namespace 開始(我們將稍微談一下 scope)。所以我們可以這樣理解 .Release.Name:"從頂層命名空間開始,找到 Release 對(duì)象,然后在里面查找名為 Name 的對(duì)象"。

該 Release 對(duì)象是 Helm 的內(nèi)置對(duì)象之一,稍后我們將更深入地介紹它。但就目前而言,這足以說明這會(huì)顯示 Tiller 分配給我們發(fā)布的 release 名稱。

現(xiàn)在,當(dāng)我們安裝我們的資源時(shí),我們會(huì)立即看到使用這個(gè)模板指令的結(jié)果:

[root@master mychart]# helm install ./mychart/
NAME:   famous-peahen
LAST DEPLOYED: Sun Jul 21 09:42:05 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                    DATA  AGE
famous-peahen-confgmap  1     0s

[root@master mychart]# helm get manifest famous-peahen

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: famous-peahen-confgmap
data:
  myvalue: "this is my chart configmap"

我們看過了基礎(chǔ)的模板:YAML 文件嵌入了模板指令,通過 。在下一部分中,我們將深入研究模板。但在繼續(xù)之前,有一個(gè)快速技巧可以使構(gòu)建模板更快:當(dāng)您想測(cè)試模板渲染,但實(shí)際上沒有安裝任何東西時(shí),可以使用 helm install --debug --dry-run ./mychart。這會(huì)將 chart 發(fā)送到 Tiller 服務(wù)器,它將渲染模板。但不是安裝 chart,它會(huì)將渲染模板返回,以便可以看到輸出:

5.5 內(nèi)置對(duì)象

helm內(nèi)部變量

對(duì)象從模板引擎?zhèn)鬟f到模板中。你的代碼可以傳遞對(duì)象(我們將在說明 withrange 語句時(shí)看到示例)。甚至有幾種方法在模板中創(chuàng)建新對(duì)象,就像我們稍后會(huì)看的 tuple 函數(shù)一樣。

對(duì)象可以很簡(jiǎn)單,只有一個(gè)值?;蛘咚麄兛梢园渌麑?duì)象或函數(shù)。例如,Release 對(duì)象包含多個(gè)對(duì)象(如 Release.Name)并且 Files 對(duì)象具有一些函數(shù)。

在上一節(jié)中,我們使用 {{.Release.Name}} 將 release 的名稱插入到模板中。Release 是可以在模板中訪問的頂級(jí)對(duì)象之一。

  • Release:這個(gè)對(duì)象描述了 release 本身。它里面有幾個(gè)對(duì)象:
  • Release.Name:release 名稱
  • Release.Time:release 的時(shí)間
  • Release.Namespace:release 的 namespace(如果清單未覆蓋)
  • Release.Service:release 服務(wù)的名稱(始終是 Tiller)。
  • Release.Revision:此 release 的修訂版本號(hào)。它從 1 開始,每 helm upgrade 一次增加一個(gè)。
  • Release.IsUpgrade:如果當(dāng)前操作是升級(jí)或回滾,則將其設(shè)置為 true。
  • Release.IsInstall:如果當(dāng)前操作是安裝,則設(shè)置為 true。
  • Values:從 values.yaml 文件和用戶提供的文件傳入模板的值。默認(rèn)情況下,Values 是空的。
  • ChartChart.yaml 文件的內(nèi)容。任何數(shù)據(jù) Chart.yaml 將在這里訪問。例如 {{.Chart.Name}}-{{.Chart.Version}} 將打印出來 mychart-0.1.0。chart 指南中 Charts Guide 列出了可用字段
  • Files:這提供對(duì) chart 中所有非特殊文件的訪問。雖然無法使用它來訪問模板,但可以使用它來訪問 chart 中的其他文件。請(qǐng)參閱 "訪問文件" 部分。
  • Files.Get 是一個(gè)按名稱獲取文件的函數(shù)(.Files.Get config.ini
  • Files.GetBytes 是將文件內(nèi)容作為字節(jié)數(shù)組而不是字符串獲取的函數(shù)。這對(duì)于像圖片這樣的東西很有用。
  • Capabilities:這提供了關(guān)于 Kubernetes 集群支持的功能的信息。
  • Capabilities.APIVersions 是一組版本信息。
  • Capabilities.APIVersions.Has $version 指示是否在群集上啟用版本(batch/v1)。
  • Capabilities.KubeVersion 提供了查找 Kubernetes 版本的方法。它具有以下值:Major,Minor,GitVersion,GitCommit,GitTreeState,BuildDate,GoVersion,Compiler,和 Platform。
  • Capabilities.TillerVersion 提供了查找 Tiller 版本的方法。它具有以下值:SemVer,GitCommit,和 GitTreeState。
  • Template:包含有關(guān)正在執(zhí)行的當(dāng)前模板的信息
  • Name:到當(dāng)前模板的 namespace 文件路徑(例如 mychart/templates/mytemplate.yaml
  • BasePath:當(dāng)前 chart 模板目錄的 namespace 路徑(例如 mychart/templates)。

這些值可用于任何頂級(jí)模板。我們稍后會(huì)看到,這并不意味著它們將在任何地方都要有。

內(nèi)置值始終以大寫字母開頭。這符合Go的命名約定。當(dāng)你創(chuàng)建自己的名字時(shí),你可以自由地使用適合你的團(tuán)隊(duì)的慣例。一些團(tuán)隊(duì),如Kubernetes chart團(tuán)隊(duì),選擇僅使用首字母小寫字母來區(qū)分本地名稱與內(nèi)置名稱。在本指南中,我們遵循該約定。

5.6 values文件

在上一節(jié)中,我們看了 Helm 模板提供的內(nèi)置對(duì)象。四個(gè)內(nèi)置對(duì)象之一是 Values。該對(duì)象提供對(duì)傳入 chart 的值的訪問。其內(nèi)容來自四個(gè)來源:

  • chart 中的 values.yaml 文件
  • 如果這是一個(gè)子 chart,來自父 chart 的 values.yaml 文件
  • value 文件通過 helm install 或 helm upgrade 的 - f 標(biāo)志傳入文件(helm install -f myvals.yaml ./mychart
  • 通過 --set(例如 helm install --set foo=bar ./mychart

上面的列表按照特定的順序排列:values.yaml 在默認(rèn)情況下,父級(jí) chart 的可以覆蓋該默認(rèn)級(jí)別,而該 chart values.yaml 又可以被用戶提供的 values 文件覆蓋,而該文件又可以被 --set 參數(shù)覆蓋。

值文件是純 YAML 文件。我們編輯 mychart/values.yaml,然后來編輯我們的 ConfigMap 模板。

刪除默認(rèn)帶的 values.yaml,我們只設(shè)置一個(gè)參數(shù):

# 編輯values.yaml
domain: anchnet.com

# 在模版中引用
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  domain: {{.Values.domain}}

注意我們?cè)谧詈笠恍?{{ .Values.domain}} 獲取 domain` 的值。

[root@master mychart]# helm install --dry-run --debug ./mychart
'''
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: exciting-manta-confgmap
data:
  myvalue: "this is my chart configmap"
  domain: anchnet.com
  • 手動(dòng)利用--set指定

由于 domain 在默認(rèn) values.yaml 文件中設(shè)置為 anchnet.com,這就是模板中顯示的值。我們可以輕松地在我們的 helm install 命令中通過加一個(gè) --set 添標(biāo)志來覆蓋:

helm install --dry-run --debug --set domain=51idc.com ./mychart 
'''
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: foppish-mule-confgmap
data:
  myvalue: "this is my chart configmap"
  domain: 51idc.com

由于 --set 比默認(rèn) values.yaml 文件具有更高的優(yōu)先級(jí)

  • 刪除默認(rèn) key

如果您需要從默認(rèn)值中刪除一個(gè)鍵,可以覆蓋該鍵的值為 null,在這種情況下,Helm 將從覆蓋值合并中刪除該鍵。

helm install stable/drupal --set image=my-registry/drupal:0.1.0 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --set livenessProbe.httpGet=null

5.7 模版函數(shù)和管道

  • 模版函數(shù)

目前為止,我們已經(jīng)知道如何將信息放入模板中。但是這些信息未經(jīng)修改就被放入模板中。有時(shí)我們想要轉(zhuǎn)換這些數(shù)據(jù),使得他們對(duì)我們來說更有用。

讓我們從一個(gè)最佳實(shí)踐開始:當(dāng)從. Values 對(duì)象注入字符串到模板中時(shí),我們引用這些字符串。我們可以通過調(diào)用 quote 模板指令中的函數(shù)來實(shí)現(xiàn):

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{quote .Values.favorite.drink}}
  food: {{quote .Values.favorite.food}}

模板函數(shù)遵循語法 functionName arg1 arg2...。在上面的代碼片段中,quote .Values.favorite.drink 調(diào)用 quote 函數(shù)并將一個(gè)參數(shù)傳遞給它。

Helm 擁有超過 60 種可用函數(shù)。其中一些是由 Go 模板語言 Go template language 本身定義的。其他大多數(shù)都是 Sprig 模板庫 Sprig template library 的一部分。在我們講解例子進(jìn)行的過程中,我們會(huì)看到很多。

雖然我們將 Helm 模板語言視為 Helm 特有的,但它實(shí)際上是 Go 模板語言,一些額外函數(shù)和各種包裝器的組合,以將某些對(duì)象暴露給模板。Go 模板上的許多資源在了解模板時(shí)可能會(huì)有所幫助。

  • 管道

模板語言的強(qiáng)大功能之一是其管道概念。利用 UNIX 的一個(gè)概念,管道是一個(gè)鏈接在一起的一系列模板命令的工具,以緊湊地表達(dá)一系列轉(zhuǎn)換。換句話說,管道是按順序完成幾件事情的有效方式。我們用管道重寫上面的例子。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | quote}}
  food: {{.Values.favorite.food | quote}}

在這個(gè)例子中,沒有調(diào)用 quote ARGUMENT,我們調(diào)換了順序。我們使用管道(|)將 “參數(shù)” 發(fā)送給函數(shù):.Values.favorite.drink | quote。使用管道,我們可以將幾個(gè)功能鏈接在一起:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | quote}}
  food: {{.Values.favorite.food | upper | quote}}

反轉(zhuǎn)順序是模板中的常見做法。你會(huì)看到.val | quotequote .val 更常見。練習(xí)也是。

當(dāng)評(píng)估時(shí),該模板將產(chǎn)生如下結(jié)果:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: trendsetting-p-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"

請(qǐng)注意,我們的原來 pizza 現(xiàn)在已經(jīng)轉(zhuǎn)換為 "PIZZA"。

當(dāng)有像這樣管道參數(shù)時(shí),第一個(gè)評(píng)估(.Values.favorite.drink)的結(jié)果將作為函數(shù)的最后一個(gè)參數(shù)發(fā)送。我們可以修改上面的飲料示例來說明一個(gè)帶有兩個(gè)參數(shù)的函數(shù) repeat COUNT STRING

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | repeat 5 | quote}}
  food: {{.Values.favorite.food | upper | quote}}

該 repeat 函數(shù)將回送給定的字符串和給定的次數(shù),所以我們將得到這個(gè)輸出:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: melting-porcup-configmap
data:
  myvalue: "Hello World"
  drink: "coffeecoffeecoffeecoffeecoffee"
  food: "PIZZA"
  • 使用 default 函數(shù)

經(jīng)常使用的一個(gè)函數(shù)是 defaultdefault DEFAULT_VALUE GIVEN_VALUE。該功能允許在模板內(nèi)部指定默認(rèn)值,以防該值被省略。讓我們用它來修改上面的飲料示例:

drink: {{.Values.favorite.drink | default "tea" | quote}}

如果我們像往常一樣運(yùn)行,我們會(huì)得到我們的 coffee:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: virtuous-mink-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"

現(xiàn)在,我們將從以下位置刪除喜歡的飲料設(shè)置 values.yaml:

favorite:
  #drink: coffee
  food: pizza

現(xiàn)在重新運(yùn)行 helm install --dry-run --debug ./mychart 會(huì)產(chǎn)生這個(gè) YAML:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: fair-worm-configmap
data:
  myvalue: "Hello World"
  drink: "tea"
  food: "PIZZA"

在實(shí)際的 chart 中,所有靜態(tài)默認(rèn)值應(yīng)該存在于 values.yaml 中,不應(yīng)該使用該 default 命令重復(fù)(否則它們將是重復(fù)多余的)。但是,default 命令對(duì)于計(jì)算的值是合適的,因?yàn)橛?jì)算值不能在 values.yaml 中聲明。例如:

drink: {{.Values.favorite.drink | default (printf "%s-tea" (include "fullname" .)) }}

在一些地方,一個(gè) if 條件可能比這 default 更適合。我們將在下一節(jié)中看到這些。

模板函數(shù)和管道是轉(zhuǎn)換信息并將其插入到 YAML 中的強(qiáng)大方法。但有時(shí)候需要添加一些比插入字符串更復(fù)雜一些的模板邏輯。在下一節(jié)中,我們將看看模板語言提供的控制結(jié)構(gòu)。

  • 運(yùn)算符函數(shù)

對(duì)于模板,運(yùn)算符(eq,ne,lt,gt,and,or 等等)都是已實(shí)現(xiàn)的功能。在管道中,運(yùn)算符可以用圓括號(hào)(())分組。

將運(yùn)算符放到聲明的前面,后面跟著它的參數(shù),就像使用函數(shù)一樣。要多個(gè)運(yùn)算符一起使用,將每個(gè)函數(shù)通過圓括號(hào)分隔。

{{/* include the body of this if statement when the variable .Values.fooString xists and is set to "foo" */}}
{{if and .Values.fooString (eq .Values.fooString "foo") }}
    {{...}}
{{end}}

{{/* do not include the body of this if statement because unset variables evaluate o false and .Values.setVariable was negated with the not function. */}}
{{if or .Values.anUnsetVariable (not .Values.aSetVariable) }}
   {{...}}
{{end}}

現(xiàn)在我們可以從函數(shù)和管道轉(zhuǎn)向流控制,條件,循環(huán)和范圍修飾符。

5.8 流程控制

5.8.1 流程控制

控制結(jié)構(gòu)(模板說法中稱為 “動(dòng)作”)為模板作者提供了控制模板生成流程的能力。Helm 的模板語言提供了以下控制結(jié)構(gòu):

  • if/else 用于創(chuàng)建條件塊
  • with 指定范圍
  • range,它提供了一個(gè) “for each” 風(fēng)格的循環(huán)

除此之外,它還提供了一些聲明和使用命名模板段的操作:

  • define 在模板中聲明一個(gè)新的命名模板
  • template 導(dǎo)入一個(gè)命名模板
  • block 聲明了一種特殊的可填寫模板區(qū)域

在本節(jié)中,我們將談?wù)?if,withrange。其他內(nèi)容在本指南后面的 “命名模板” 一節(jié)中介紹。

5.8.2 if/else

我們要看的第一個(gè)控制結(jié)構(gòu)是用于在模板中有條件地包含文本塊。這就是 if/else 塊。

條件的基本結(jié)構(gòu)如下所示:

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

注意,我們現(xiàn)在討論的是管道而不是值。其原因是要明確控制結(jié)構(gòu)可以執(zhí)行整個(gè)管道,而不僅僅是評(píng)估一個(gè)值。

如果值為如下情況,則管道評(píng)估為 false。

  • 一個(gè)布爾型的假
  • 一個(gè)數(shù)字零
  • 一個(gè)空的字符串
  • 一個(gè) nil(空或 null)
  • 一個(gè)空的集合(mapslice,tupledict,array

在其他情況下, 條件值為 true 此管道被執(zhí)行。

我們?yōu)?ConfigMap 添加一個(gè)簡(jiǎn)單的條件。如果飲料被設(shè)置為咖啡,我們將添加另一個(gè)設(shè)置:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}mug: true{{ end }}

注意 .Values.favorite.drink 必須已定義,否則在將它與 “coffee” 進(jìn)行比較時(shí)會(huì)拋出錯(cuò)誤。由于我們?cè)谏弦粋€(gè)例子中注釋掉了 drink:coffee,因此輸出不應(yīng)該包含 mug:true 標(biāo)志。但是如果我們將該行添加回 values.yaml 文件中,輸出應(yīng)該如下所示:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: true

5.8.3 控制空格

在查看條件時(shí),我們應(yīng)該快速查看模板中的空格控制方式。讓我們看一下前面的例子,并將其格式化為更容易閱讀的格式:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{if eq .Values.favorite.drink "coffee"}}
    mug: true
  {{end}}

最初,這看起來不錯(cuò)。但是如果我們通過模板引擎運(yùn)行它,我們會(huì)得到一個(gè)錯(cuò)誤的結(jié)果:

$ helm install --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key

發(fā)生了什么?由于上面的空格,我們生成了不正確的 YAML。

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
    mug: true

mug 不正確地縮進(jìn)。讓我們簡(jiǎn)單地縮進(jìn)那行,然后重新運(yùn)行:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{if eq .Values.favorite.drink "coffee"}}
  mug: true
  {{end}}

當(dāng)我們發(fā)送該信息時(shí),我們會(huì)得到有效的 YAML,但仍然看起來有點(diǎn)意思:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: telling-chimp-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"

  mug: true

請(qǐng)注意,我們?cè)?YAML 中收到了一些空行。為什么?當(dāng)模板引擎運(yùn)行時(shí),它將刪除 {{}} 中的空白內(nèi)容,但是按原樣保留剩余的空白。

YAML 中的縮進(jìn)空格是嚴(yán)格的,因此管理空格變得非常重要。幸運(yùn)的是,Helm 模板有幾個(gè)工具可以幫助我們。

首先,可以使用特殊字符修改模板聲明的大括號(hào)語法,以告訴模板引擎填充空白。{{-(添加了破折號(hào)和空格)表示應(yīng)該將格左移,而 -}} 意味著應(yīng)該刪除右空格。注意!換行符也是空格!

確保 - 和其他指令之間有空格。-3 意思是 “刪除左空格并打印 3”,而 -3 意思是 “打印 -3”。

使用這個(gè)語法,我們可以修改我們的模板來擺脫這些新行:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}
  {{- if eq .Values.favorite.drink "coffee"}}
  mug: true
  {{- end}}

為了清楚說明這一點(diǎn),讓我們調(diào)整上面的內(nèi)容,將空格替換為 *, 按照此規(guī)則將每個(gè)空格將被刪除。一個(gè)在該行的末尾的 * 指示換行符將被移除

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | default "tea" | quote}}
  food: {{.Values.favorite.food | upper | quote}}*
**{{- if eq .Values.favorite.drink "coffee"}}
  mug: true*
**{{- end}}

牢記這一點(diǎn),我們可以通過 Helm 運(yùn)行我們的模板并查看結(jié)果:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: clunky-cat-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: true

小心使用 chomping 修飾符。這樣很容易引起意外:

  food: {{.Values.favorite.food | upper | quote}}
  {{- if eq .Values.favorite.drink "coffee" -}}
  mug: true
  {{- end -}}

這將會(huì)產(chǎn)生 food: "PIZZA"mug:true,因?yàn)閯h除了雙方的換行符。

有關(guān)模板中空格控制的詳細(xì)信息,請(qǐng)參閱官方 Go 模板文檔 Official Go template documentation

最后,有時(shí)候告訴模板系統(tǒng)如何縮進(jìn)更容易,而不是試圖掌握模板指令的間距。因此,有時(shí)可能會(huì)發(fā)現(xiàn)使用 indent 函數(shù)({{indent 2 "mug:true"}})會(huì)很有用。

5.8.4 使用 with 修改范圍

下一個(gè)要看的控制結(jié)構(gòu)是 with。它控制著變量作用域?;叵胍幌拢?code>. 是對(duì)當(dāng)前范圍的引用。因此,.Values 告訴模板在當(dāng)前范圍中查找 Values 對(duì)象。

其語法 with 類似于一個(gè)簡(jiǎn)單的 if 語句:

{{with PIPELINE}}
  # restricted scope
{{end}}

范圍可以改變。with 可以允許將當(dāng)前范圍(.)設(shè)置為特定的對(duì)象。例如,我們一直在使用的 .Values.favorites。讓我們重寫我們的 ConfigMap 來改變 . 范圍來指向 .Values.favorites

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  {{- end}}

注意,現(xiàn)在我們可以引用 .drink.food 無需對(duì)其進(jìn)行限定。這是因?yàn)樵?with 聲明設(shè)置 . 為指向 .Values.favorite。在 {{end}}. 復(fù)位其先前的范圍。

但是請(qǐng)注意!在受限范圍內(nèi),此時(shí)將無法從父范圍訪問其他對(duì)象。例如,下面會(huì)報(bào)錯(cuò):

  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  release: {{.Release.Name}}
  {{- end}}

它會(huì)產(chǎn)生一個(gè)錯(cuò)誤,因?yàn)?Release.Name 它不在 . 限制范圍內(nèi)。但是,如果我們交換最后兩行,所有將按預(yù)期工作,因?yàn)榉秶?之后被重置。

  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  {{- end}}
  release: {{.Release.Name}}

看下 range,我們看看模板變量,它提供了一個(gè)解決上述范圍問題的方法。

5.8.5 循環(huán) range 動(dòng)作

許多編程語言都支持使用 for 循環(huán),foreach 循環(huán)或類似的功能機(jī)制進(jìn)行循環(huán)。在 Helm 的模板語言中,遍歷集合的方式是使用 range 操作子。

首先,讓我們?cè)谖覀兊?values.yaml 文件中添加一份披薩配料列表:

favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

現(xiàn)在我們有一個(gè)列表(模板中稱為 slice)pizzaToppings。我們可以修改我們的模板,將這個(gè)列表打印到我們的 ConfigMap 中:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  {{- end}}
  toppings: |-
    {{- range .Values.pizzaToppings}}
    - {{. | title | quote}}
    {{- end}}

讓我們仔細(xì)看看 toppings:list。該 range 函數(shù)將遍歷 pizzaToppings 列表。但現(xiàn)在發(fā)生了一些有趣的事. 就像 withsets 的范圍 .,range 操作子也是一樣。每次通過循環(huán)時(shí),. 都設(shè)置為當(dāng)前比薩餅頂部。也就是第一次 . 設(shè)定 mushrooms。第二個(gè)迭代它設(shè)置為 cheese,依此類推。

我們可以直接向管道發(fā)送 . 的值,所以當(dāng)我們這樣做時(shí) {{. | title | quote}},它會(huì)發(fā)送 . 到 title(標(biāo)題 case 函數(shù)),然后發(fā)送到 quote。如果我們運(yùn)行這個(gè)模板,輸出將是:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-dragonfly-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  toppings: |-
    - "Mushrooms"
    - "Cheese"
    - "Peppers"
    - "Onions"

現(xiàn)在,在這個(gè)例子中,我們碰到了一些棘手的事情。該 toppings: |- 行聲明了一個(gè)多行字符串。所以我們的 toppings list 實(shí)際上不是 YAML 清單。這是一個(gè)很大的字符串。我們?yōu)槭裁匆@樣做?因?yàn)?ConfigMaps 中的數(shù)據(jù) data 由鍵 / 值對(duì)組成,其中鍵和值都是簡(jiǎn)單的字符串。要理解這種情況,請(qǐng)查看 Kubernetes ConfigMap 文檔.。但對(duì)我們來說,這個(gè)細(xì)節(jié)并不重要。

YAML 中的 |- 標(biāo)記表示一個(gè)多行字符串。這可以是一種有用的技術(shù),用于在清單中嵌入大塊數(shù)據(jù),如此處所示。

有時(shí)能快速在模板中創(chuàng)建一個(gè)列表,然后遍歷該列表是很有用的。Helm 模板有一個(gè)功能可以使這個(gè)變得簡(jiǎn)單:tuple。在計(jì)算機(jī)科學(xué)中,元組是類固定大小的列表類集合,但是具有任意數(shù)據(jù)類型。這粗略地表達(dá)了 tuple 的使用方式。

  sizes: |-
    {{- range tuple "small" "medium" "large"}}
    - {{.}}
    {{- end}}
  sizes: |-
    - small
    - medium
    - large

除了list和tuple之外,range還可以用于遍歷具有鍵和值的集合(如mapdict)。當(dāng)在下一節(jié)我們介紹模板變量時(shí),將看到如何做到這一點(diǎn)。

5.9 變量

我們已經(jīng)了解了函數(shù),管道,對(duì)象和控制結(jié)構(gòu),我們可以在許多編程語言中找到更基本的用法之一:變量。在模板中,它們使用的頻率較低。我們將看到如何使用它們來簡(jiǎn)化代碼,并更好地使用 withrange

在前面的例子中,我們看到這段代碼會(huì)失?。?/p>

  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  release: {{.Release.Name}}
  {{- end}}

Release.Name 不在該 with 塊中限制的范圍內(nèi)。解決范圍問題的一種方法是將對(duì)象分配給可以在不考慮當(dāng)前范圍的情況下訪問的變量。

在 Helm 模板中,變量是對(duì)另一個(gè)對(duì)象的命名引用。它遵循這個(gè)形式 $name。變量被賦予一個(gè)特殊的賦值操作符::=。我們可以使用變量重寫上面的 Release.Name。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- $relname := .Release.Name -}}
  {{- with .Values.favorite}}
  drink: {{.drink | default "tea" | quote}}
  food: {{.food | upper | quote}}
  release: {{$relname}}
  {{- end}}

注意,在我們開始 with 塊之前,我們賦值 $relname :=.Release.Name。現(xiàn)在在 with 塊內(nèi)部,$relname 變量仍然指向發(fā)布名稱。

會(huì)產(chǎn)生這樣的結(jié)果:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: viable-badger-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  release: viable-badger

變量在 range 循環(huán)中特別有用。它們可以用于類似列表的對(duì)象以同時(shí)捕獲索引和值:

toppings: |-
    {{- range $index, $topping := .Values.pizzaToppings}}
      {{$index}}: {{ $topping }}
    {{- end}}

注意,range 首先是變量,然后是賦值運(yùn)算符,然后是列表。這將分配整數(shù)索引(從零開始)給 $index,值給 $topping。運(yùn)行它將產(chǎn)生:

  toppings: |-
      0: mushrooms
      1: cheese
      2: peppers
      3: onions

對(duì)于同時(shí)具有鍵和值的數(shù)據(jù)結(jié)構(gòu),我們可以使用 range 來獲得兩者。例如,我們可以對(duì) .Values.favorite 像這樣循環(huán):

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite}}
  {{$key}}: {{ $val | quote }}
  {{- end}}

現(xiàn)在在第一次迭代中,$keydrink,$valcoffee,第二次,$key 是 food,$val 會(huì) pizza。運(yùn)行上面的代碼會(huì)生成下面這個(gè):

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eager-rabbit-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

變量通常不是 “全局” 的。它們的范圍是它們所在的塊。之前,我們?cè)谀0宓捻攲淤x值 $relname。該變量將在整個(gè)模板的范圍內(nèi)起作用。但在我們的最后一個(gè)例子中,$key$val 只會(huì)在該 {{range...}}{{end}} 塊的范圍內(nèi)起作用。

然而,總有一個(gè)變量是全局 $ 變量 - 這個(gè)變量總是指向根上下文。當(dāng)你在需要知道 chart 發(fā)行名稱的范圍內(nèi)循環(huán)時(shí),這非常有用。

舉例說明:

{{- range .Values.tlsSecrets}}
apiVersion: v1
kind: Secret
metadata:
  name: {{.name}}
  labels:
    # Many helm templates would use `.` below, but that will not work,
    # however `$` will work here
    app.kubernetes.io/name: {{template "fullname" $}}
    # I cannot reference .Chart.Name, but I can do $.Chart.Name
    helm.sh/chart: "{{$.Chart.Name}}-{{ $.Chart.Version }}"
    app.kubernetes.io/instance: "{{$.Release.Name}}"
    app.kubernetes.io/managed-by: "{{$.Release.Service}}"
type: kubernetes.io/tls
data:
  tls.crt: {{.certificate}}
  tls.key: {{.key}}
---
{{- end}}

到目前為止,我們只查看了一個(gè)文件中聲明的一個(gè)模板。但是Helm模板語言的強(qiáng)大功能之一是它能夠聲明多個(gè)模板并將它們一起使用。我們將在下一節(jié)中討論。

5.10 命名模版

現(xiàn)在是開始創(chuàng)建超過一個(gè)模板的時(shí)候了。在本節(jié)中,我們將看到如何在一個(gè)文件中定義命名模板,然后在別處使用它們。命名模板(有時(shí)稱為部分或子模板)是限定在一個(gè)文件內(nèi)部的模板,并起一個(gè)名稱。我們有兩種創(chuàng)建方法,以及幾種不同的使用方法。

在 “流量控制” 部分中,我們介紹了聲明和管理模板三個(gè)動(dòng)作:define,template,和 block。在本節(jié)中,我們將介紹這三個(gè)動(dòng)作,并介紹一個(gè) include 函數(shù),與 template 類似功能。

在命名模板時(shí)要注意一個(gè)重要的細(xì)節(jié):模板名稱是全局的。如果聲明兩個(gè)具有相同名稱的模板,則最后加載一個(gè)模板是起作用的模板。由于子 chart 中的模板與頂級(jí)模板一起編譯,因此注意小心地使用特定 chart 的名稱來命名模板。

通用的命名約定是為每個(gè)定義的模板添加 chart 名稱:{{define "mychart.labels"}}。通過使用特定 chart 名稱作為前綴,我們可以避免由于同名模板的兩個(gè)不同 chart 而可能出現(xiàn)的任何沖突。

5.10.1 partials 和 _ 文件

到目前為止,我們已經(jīng)使用了一個(gè)文件,一個(gè)文件包含一個(gè)模板。但 Helm 的模板語言允許創(chuàng)建指定的嵌入模板,可以通過名稱訪問。

在我們開始編寫這些模板之前,有一些文件命名約定值得一提:

  • 大多數(shù)文件 templates/ 被視為包含 Kubernetes manifests
  • NOTES.txt 是一個(gè)例外
  • 名稱以下劃線(_)開頭的文件被假定為沒有內(nèi)部 manifest。這些文件不會(huì)渲染 Kubernetes 對(duì)象定義,而是在其他 chart 模板中隨處可用以供調(diào)用。

這些文件用于存儲(chǔ) partials 和輔助程序。事實(shí)上,當(dāng)我們第一次創(chuàng)建時(shí) mychart,我們看到一個(gè)叫做文件 _helpers.tpl。該文件是模板 partials 的默認(rèn)位置。

5.10.2 用 definetemplate 聲明和使用模板

六 實(shí)戰(zhàn)

6.1 制作charts

  • 將用slate做好的go2cloud-api-doc 利用helm做成charts,方便后續(xù)部署
helm create go2cloud-api-doc

[root@master go2cloud-api-doc]# tree 
.
├── charts
├── Chart.yaml
├── templates
│?? ├── deployment.yaml
│?? ├── _helpers.tpl
│?? ├── NOTES.txt
│?? ├── service.yaml
│?? └── tests
│??     └── test-connection.yaml
└── values.yaml

3 directories, 8 files

# 配置 deployment
[root@master go2cloud_api_doc_charts]# egrep "^$|^#" -v go2cloud-api-doc/templates/deployment.yaml  
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "go2cloud-api-doc.fullname" . }}
  labels:
{{ include "go2cloud-api-doc.labels" . | indent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "go2cloud-api-doc.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "go2cloud-api-doc.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
      imagePullSecrets: 
        - name: {{ .Values.imagePullSecrets }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.port }}
              protocol: TCP
          livenessProbe:
            {{- toYaml .Values.livenessProbe | nindent 12  }}
          readinessProbe:
            {{- toYaml .Values.readinessProbe | nindent 12  }}
          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 }}

# 配置service
[root@master go2cloud_api_doc_charts]# egrep "^$|^#" -v go2cloud-api-doc/templates/service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: {{ include "go2cloud-api-doc.fullname" . }}
  labels:
{{ include "go2cloud-api-doc.labels" . | indent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP
      name: http
      nodePort: {{ .Values.service.nodePort }}      
  selector:
    app.kubernetes.io/name: {{ include "go2cloud-api-doc.name" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}

# 配置values
[root@master go2cloud_api_doc_charts]# egrep "^$|^#|^[[:space:]]+#" -v go2cloud-api-doc/values.yaml
replicaCount: 1
image:
  repository: 10.234.2.218/go2cloud/go2cloud-api-doc
  tag: latest
  pullPolicy: Always
imagePullSecrets: registry-secret
nameOverride: ""
fullnameOverride: ""
service:
  type: NodePort
  port: 4567
  nodePort: 30567
ingress:
  enabled: false
  annotations: {}
  hosts:
    - host: chart-example.local
      paths: []
  tls: []
resources: 
  requests:
    cpu: 1000m
    memory: 1280Mi
  limits:
    cpu: 1000m
    memory: 1280Mi
livenessProbe:
  tcpSocket:
    port: 4567
  initialDelaySeconds: 10
  failureThreshold: 2 
  timeoutSeconds: 10
readinessProbe:
  httpGet:
    path: /#introduction
    port: http
  initialDelaySeconds: 5
  failureThreshold: 2 
  timeoutSeconds: 30
nodeSelector: {}
tolerations: []
affinity: {}

[root@master go2cloud_api_doc_charts]# egrep "^$|^#|^[[:space:]]+#" -v go2cloud-api-doc/Chart.yaml 
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: go2cloud-api-doc
version: 0.1.0

# 部署
[root@master go2cloud_api_doc_charts]# helm install -n go2cloud-api-doc -f go2cloud-api-doc/values.yaml go2cloud-api-doc/                  
NAME:   go2cloud-api-doc
LAST DEPLOYED: Wed Jul 31 14:34:21 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Deployment
NAME              READY  UP-TO-DATE  AVAILABLE  AGE
go2cloud-api-doc  0/1    1           0          0s

==> v1/Pod(related)
NAME                               READY  STATUS             RESTARTS  AGE
go2cloud-api-doc-7cfb7bb795-clrz8  0/1    ContainerCreating  0         0s

==> v1/Service
NAME              TYPE      CLUSTER-IP     EXTERNAL-IP  PORT(S)         AGE
go2cloud-api-doc  NodePort  10.96.228.251  <none>       4567:30567/TCP  0s

NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services go2cloud-api-doc)
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

[root@master go2cloud_api_doc_charts]# helm ls go2cloud-api-doc
NAME                    REVISION        UPDATED                         STATUS          CHART                   APP VERSION     NAMESPACE
go2cloud-api-doc        1               Wed Jul 31 14:34:21 2019        DEPLOYED        go2cloud-api-doc-0.1.0  1.0             default  

[root@master go2cloud_api_doc_charts]# kubectl get deployment go2cloud-api-doc
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
go2cloud-api-doc   0/1     1            0           10m

[root@master go2cloud_api_doc_charts]# kubectl get pods |grep go2cloud-api-doc
go2cloud-api-doc-7cfb7bb795-clrz8                         0/1     CrashLoopBackOff   7          10m

[root@master go2cloud_api_doc_charts]# kubectl get svc go2cloud-api-doc
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
go2cloud-api-doc   NodePort   10.96.228.251   <none>        4567:30567/TCP   10m

# 打包
[root@master go2cloud_api_doc_charts]# helm package ./go2cloud-api-doc/
Successfully packaged chart and saved it to: /data/go2cloud_api_doc_charts/go2cloud-api-doc-0.1.0.tgz
[root@master go2cloud_api_doc_charts]# tree 
.
├── go2cloud-api-doc
│?? ├── charts
│?? ├── Chart.yaml
│?? ├── templates
│?? │?? ├── deployment.yaml
│?? │?? ├── _helpers.tpl
│?? │?? ├── NOTES.txt
│?? │?? ├── service.yaml
│?? │?? └── tests
│?? │??     └── test-connection.yaml
│?? └── values.yaml
└── go2cloud-api-doc-0.1.0.tgz

4 directories, 8 files

# 升級(jí)副本數(shù)量
helm upgrade go2cloud-api-doc --set replicaCount=2 go2cloud-api-doc/

玩K8S不得不會(huì)的HELM

6.2 配置minior

將制作好的charts存放到minio上,在k8s內(nèi)部署minior

  • 創(chuàng)建本地chart目錄
mkdir minio-chart
  • 將修改好的chart文件打包
helm package redis
  • 將包拷貝至創(chuàng)建的本地chart目錄中
cp redis-8.0.5.tgz /root/minio-chart/
  • 更新/root/minio-chart/目錄下的index索引
helm repo index minio-chart/ --url http://10.234.2.204:31311/minio/common-helm-repo/

玩K8S不得不會(huì)的HELM

  • 將index.yaml 和chart包上傳至minio
mc cp index.yaml minio/common-helm-repo/
mc cp redis-8.0.5.tgz minio/common-helm-repo/
  • 將制作好的charts上傳至minio
helm repo add monocular https://helm.github.io/monocular
helm install -n monocular monocular/monocular

mc cp go2cloud-api-doc-0.1.0.tgz minio/common-helm-repo

玩K8S不得不會(huì)的HELM

可以在${HOME}/.mc/config.json中查看ak密鑰信息。

  • 驗(yàn)證

玩K8S不得不會(huì)的HELM

6.3 上傳至公共的helm倉庫

將制作好的charts包可以上傳至helm倉庫,可以放在自己的自建私有倉庫,流入:kubeapps/Monocular/minior等,可以利用helm命令一鍵安裝。

上傳至公有云公共倉庫,例如國內(nèi)的阿里目前創(chuàng)建的Apphub等,在現(xiàn)今的云原生生態(tài)當(dāng)中,已經(jīng)有很多成熟的開源軟件被制作成了 Helm Charts,使得用戶可以非常方便地下載和使用,比如 Nginx,Apache、Elasticsearch、Redis 等等。不過,在開放云原生應(yīng)用中心 App hub(Helm Charts 中國站) 發(fā)布之前,國內(nèi)用戶一直都很難直接下載使用這些 Charts。而現(xiàn)在,AppHub 不僅為國內(nèi)用戶實(shí)時(shí)同步了官方 Helm Hub 里的所有應(yīng)用,還自動(dòng)替換了這些 Charts 里所有不可訪問的鏡像 URL(比如 gcr.io, quay.io 等),終于使得國內(nèi)開發(fā)者通過 helm install “一鍵安裝”應(yīng)用成為了可能。

具體提交自己的charts可以參考:https://github.com/cloudnativeapp/charts/pulls?spm=a2c6h.13155457.1383030.1.3347b579urlAo7

此為我上傳的slate chart,Slate helps you create beautiful, intelligent, responsive API documentation.

https://developer.aliyun.com/hub/detail?spm=a2c6h.12873679.0.0.61731107C921or&name=slate&version=v2.3.1#/?_k=ayosl1

玩K8S不得不會(huì)的HELM

歡迎點(diǎn)贊。

七 相關(guān)鏈接

  • helm githab地址
  • helm 手冊(cè)

  • https://whmzsu.github.io/helm-doc-zh-cn/chart_template_guide/control_structures-zh_cn.html
  • https://github.com/helm/monocular

  • Helm3
向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