Helm
安裝
$
helm version
version.BuildInfo{
Version:"v3.0.1"
,
GitCommit:"7c22ef9ce89e0ebeb7125ba2ebf7d421f3e82ffa"
,
GitTreeState:"clean"
,
GoVersion:"go1.13.4"
}
$
helm repo add stable
http:
/
/mirror.azure.cn/kubernetes/charts/
$
helm repo list
NAME URL
stable
http:
/
/mirror.azure.cn/kubernetes/charts/
$ helm search repo stable
NAME CHART VERSION APP VERSION DESCRIPTION
stable/acs-engine-autoscaler 2.2.2 2.1.1 DEPRECATED Scales worker nodes within agent pools
stable/aerospike 0.3.1 v4.5.0.5 A Helm chart
for
Aerospike
in
Kubernetes
stable/airflow 5.2.1 1.10.4 Airflow is a platform to programmatically autho...
stable/ambassador 5.1.0 0.85.0 A Helm chart
for
Datawire Ambassador
stable/anchore-engine 1.3.7 0.5.2 Anchore container analysis and policy evaluatio...
stable/apm-server 2.1.5 7.0.0 The server receives data from the Elastic APM a...
......
示例
$ helm repo
update
Hang tight while we grab the latest
from
your chart repositories...
...Successfully got an
updatefrom
the "stable" chart repository
Update
Complete. ⎈ Happy Helming
!
⎈
$helminstallstable/mysql--generate-name
NAME:mysql-1575619811
LAST DEPLOYED:FriDec616:10:142019
NAMESPACE:default
STATUS:deployed
REVISION:1
NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
mysql-1575619811.default.svc.cluster.local
......
$ kubectl
getall-
l
release=
mysql
-1575619811
NAME READY STATUS RESTARTS AGE
pod
/
mysql
-1575619811-8479
b5b796
-
dgggz
0/1
Pending
027
m
NAME TYPE CLUSTER
-
IP
EXTERNAL-
IP PORT(S) AGE
service
/
mysql
-1575619811
ClusterIP
10.106.141.228<none>3306/
TCP
27
m
NAME READY UP
-TO-DATE
AVAILABLE AGE
deployment.apps
/
mysql
-15756198110/11027
m
NAME DESIRED
CURRENT
READY AGE
replicaset.apps
/
mysql
-1575619811-8479
b5b796
11027
m
$
helm show chart stable/mysql
......
$
helm show all stable/mysql
......
$
helm
ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
mysql-1575619811 default 1 2019-12-06 16:10:14.682302 +0800 CST deployed mysql-1.5.0 5.7.27
$ helm uninstall mysql
-1575619811
release
"mysql-1575619811" uninstalled
$ kubectl
getall-
l
release=
mysql
-1575619811
No
resources found.
$ helm status mysql
-1575619811
Error:
release
:
not
found
--keep-history
引數,則會保留 release 的歷史記錄,可以獲取該 release 的狀態就是 UNINSTALLED,而不是找不到 release了:
$ helm uninstall mysql
-1575619811--keep-history
release
"mysql-1575619811" uninstalled
$ helm status mysql
-1575619811
helm status mysql
-1575619811
NAME: mysql
-1575619811
LAST
DEPLOYED: Fri
Dec616
:
47
:
142019
NAMESPACE:
default
STATUS: uninstalled
...
$ helm ls
-
a
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
mysql
-1575619811default12019-12-0616
:
47
:
14.415214+0800
CST uninstalled mysql
-1.5.05.7.27
定製
$
helm show 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
......
$catconfig.yaml
mysqlUser:
user0
mysqlPassword:user0pwd
mysqlDatabase:user0db
persistence:
enabled:false
$helminstall-fconfig.yamlstable/mysql
helminstall-fconfig.yamlmysqlstable/mysql
NAME:mysql
LAST DEPLOYED:FriDec617:46:562019
NAMESPACE:default
STATUS:deployed
REVISION:1
NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
mysql.default.svc.cluster.local
......
$ kubectl
get
pod
-
l
release=
mysql
NAME READY STATUS RESTARTS AGE
mysql
-
ddd798f48
-
gnrzd
0/1
PodInitializing
0119
s
$ kubectl
describe
pod mysql
-
ddd798f48
-
gnrzd
......
Environment:
MYSQL_ROOT_PASSWORD:
<setto
the key
'mysql-root-password'in
secret
'mysql'>
Optional:
false
MYSQL_PASSWORD:
<setto
the key
'mysql-password'in
secret
'mysql'>
Optional:
false
MYSQL_USER: user0
MYSQL_DATABASE: user0db
......
-
--values
(或者 -f):指定一個 YAML 檔案來覆蓋 values 值,可以指定多個值,最後邊的檔案優先 -
--set
:在命令列上指定覆蓋的配置
--set
將被合併到具有更高優先順序的 --values
,使用 --set
指定的值將持久化在 ConfigMap 中,對於給定的 release,可以使用 helm get values <release-name>
來檢視已經設定的值,已設定的值也透過允許 helm upgrade 並指定 --reset
值來清除。--set
選項接收零個或多個 name/value 對,最簡單的用法就是 --set name=value
,相當於 YAML 檔案中的:name: value
--set a=b,c=d
,相當於 YAML 檔案中的:a: b
c: d
--set outer.inner=value
,對應 YAML:outer
:
inner
:
value
{}
來包裹,比如 -set name={a, b, c}
,對應 YAML:
name:
-
a
-
b
-
c
--set servers[0].port=80
,對應的 YAML 為:servers:
-port:80
-set servers[0].port=80,servers[0].host=example
,對應的 YAML 為:
servers
-
port: 80
host: example
--set
選項中使用特殊的字元,這個時候可以使用反斜槓來跳脫字元,比如 --set name=value1\,value2
,對應的 YAML 為:name:"value1,value2"
--set nodeSelector."kubernetes\.io/role"=master
,對應的 YAML 檔案:nodeSelector:
kubernetes.io/role:master
--set
來表示,所以一般推薦還是使用 YAML 檔案來進行覆蓋,當然在設計 chart 模板的時候也可以結合考慮到 --set
這種用法,儘可能的提供更好的支援。更多安裝方式
-
chart 倉庫(類似於上面我們提到的)
-
本地 chart 壓縮包(helm install foo-0.1.1.tgz)
-
本地解壓縮的 chart 目錄(helm install foo path/to/foo)
-
線上的 URL(helm install fool https://example.com/charts/foo-1.2.3.tgz)
升級或回滾
$helmupgrade-fpanda.yamlmysqlstable/mysql
helmupgrade-fpanda.yamlmysqlstable/mysql
Release"mysql"hasbeenupgraded.HappyHelming!
NAME:mysql
LAST DEPLOYED:FriDec621:06:112019
NAMESPACE:default
STATUS:deployed
REVISION:2
...
mysqlRootPassword: passw0rd
helm get values
來檢視新設定是否生效:$helmgetvaluesmysql
USER-SUPPLIED VALUES:
mysqlDatabase:user0db
mysqlPassword:user0pwd
mysqlRootPassword:passw0rd
mysqlUser:user0
persistence:
enabled:false
$ helm ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
mysql
default22019-12-0621
:
06
:
11.36358+0800
CST deployed mysql
-1.5.05.7.27
$ helm history mysql
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1
Fri
Dec617
:
53
:
032019
superseded mysql
-1.5.05.7.27
Install complete
2
Fri
Dec621
:
06
:
112019
deployed mysql
-1.5.05.7.27
Upgrade complete
$ helm
rollback
mysql
1
Rollback
was a success
!
Happy Helming
!
$ kubectl
get
pods
-
l
release=
mysql
NAME READY STATUS RESTARTS AGE
mysql
-
ddd798f48
-
gnrzd
1/1Running03
h25m
$ helm
getvalues
mysql
USER-
SUPPLIED
VALUES
:
mysqlDatabase: user0db
mysqlPassword: user0pwd
mysqlUser: user0
persistence:
enabled:
false
helm history [RELEASE]
來檢視某個版本的修訂號。install/upgrade/rollback
的一些行為,要檢視完整的引數標誌,我們可以執行 helm <command> --help
來檢視,這裡我們介紹幾個有用的引數:-
--timeout
: 等待 Kubernetes 命令完成的時間,預設是 300(5分鐘) -
--wait
: 等待直到所有 Pods 都處於就緒狀態、PVCs 已經繫結、Deployments 具有處於就緒狀態的最小 Pods 數量(期望值減去 maxUnavailable)以及 Service 有一個 IP 地址,然後才標記 release 為成功狀態。它將等待與 –timeout 值一樣長的時間,如果達到超時,則 release 將標記為失敗。注意:在 Deployment 將副本設定為 1 並且作為滾動更新策略的一部分,maxUnavailable 未設定為0的情況下,–wait 將返回就緒狀態,因為它已滿足就緒狀態下的最小 Pod 數量 -
--no-hooks
: 將會跳過命令的執行 hooks -
--recreate-pods
: 僅適用於 upgrade 和 rollback,這個標誌將導致重新建立所有的 Pods。(Helm3 中啟用了)
Charts
檔案結構
wordpress/
Chart.yaml
# 包含當前 chart 資訊的 YAML 檔案
LICENSE
# 可選:包含 chart 的 license 的文字檔案
README.md
# 可選:一個可讀性高的 README 檔案
values.yaml
# 當前 chart 的預設配置 values
values.schema.json
# 可選: 一個作用在 values.yaml 檔案上的 JSON 模式
charts/
# 包含該 chart 依賴的所有 chart 的目錄
crds/
# Custom Resource Definitions
templates/
# 模板目錄,與 values 結合使用時,將渲染生成 Kubernetes 資源清單檔案
templates/NOTES.txt
# 可選: 包含簡短使用使用的文字檔案
Chart.yaml 檔案
apiVersion:chartAPI版本(必須)
name:chart名(必須)
version:SemVer2版本(必須)
kubeVersion:相容的Kubernetes版本(可選)
description:一句話描述(可選)
type:chart型別(可選)
keywords:
-當前專案關鍵字集合(可選)
home:當前專案的URL(可選)
sources:
-當前專案原始碼URL(可選)
dependencies:# chart 依賴列表 (可選)
-name:chart名稱(nginx)
version:chart版本("1.2.3")
repository:倉庫地址("https://example.com/charts")
maintainers:# (可選)
-name:維護者名字(對每個maintainer是必須的)
email:維護者的email(可選)
url:維護者URL(可選)
icon:chart的SVG或者PNG圖示URL(可選).
appVersion:包含的應用程式版本(可選).不需要SemVer版本
deprecated:chart是否已被棄用(可選,boolean)
版本
nginx-1.2.3.tgz
apiVersion 欄位
appVersion 欄位
棄用 Charts
-
更新 chart 的 Chart.yaml 來標記 chart 為棄用狀態
-
釋出該新版本到 Chart 倉庫
-
從原始碼倉庫(比如 git)中刪除 chart
Chart 型別
一個應用 chart 也可以當作庫進行使用。透過將型別設定為 library,然後該 chart 就會渲染成一個庫,可以在其中使用所有的實用性功能,chart 的所有資源物件都不會被渲染。
LICENSE, README 和 NOTES
-
chart 提供的應用程式的描述資訊
-
執行 chart 的任何先決條件或者要求
-
values.yaml 和預設值中的一些選項說明
-
與 chart 的安裝或配置有關的任何其他資訊
依賴
使用 dependencies 欄位管理依賴
dependencies:
-name:apache
version:1.2.3
repository:https://example.com/charts
-name:mysql
version:3.2.1
repository:https://another.example.com/charts
-
name 欄位是所依賴的 chart 的名稱
-
version 欄位是依賴的 chart 版本
-
repository 欄位是 chart 倉庫的完整 URL,不過需要注意,必須使用 helm repo add 在本地新增該 repo
$ helm dependency
update
foochart
Hang tight while we grab the latest
from
your chart repositories...
...Successfully got an
updatefrom
the "local" chart repository
...Successfully got an
updatefrom
the "stable" chart repository
...Successfully got an
updatefrom
the "example" chart repository
...Successfully got an
updatefrom
the "another" chart repository
Update
Complete. Happy Helming
!
Saving
2
charts
Downloading apache
from
repo https:
//
example.com
/
charts
Downloading mysql
from
repo https:
//
another.example.com
/
charts
charts/
apache-1.2.3.tgz
mysql-3.2.1.tgz
alias 欄位
# parentchart/Chart.yaml
dependencies:
-name:subchart
repository:http://localhost:10191
version:0.1.0
alias:new-subchart-1
-name:subchart
repository:http://localhost:10191
version:0.1.0
alias:new-subchart-2
-name:subchart
repository:http://localhost:10191
version:0.1.0
subchart
new-
subchart-1
new-
subchart-2
TEMPLATES 和 VALUES
-
Chart 開發人員可以在 chart 內部提供一個名為 values.yaml 的檔案,該檔案可以包含預設的 values 值內容。
-
Chart 使用者可以提供包含 values 值的 YAML 檔案,可以在命令列中透過 helm install 來指定該檔案。
模板檔案
apiVersion:v1
kind:ReplicationController
metadata:
name:deis-database
namespace:deis
labels:
app.kubernetes.io/managed-by:deis
spec:
replicas:1
selector:
app.kubernetes.io/name:deis-database
template:
metadata:
labels:
app.kubernetes.io/name:deis-database
spec:
serviceAccount:deis-database
containers:
-name:deis-database
image:
{{
.Values.imageRegistry
}}
/postgres:{{.Values.dockerTag}}
imagePullPolicy:
{{
.Values.pullPolicy
}}
ports:
-containerPort:5432
env:
-name:DATABASE_STORAGE
value:
{{
default"minio".Values.storage
}}
-
imageRegistry:Docker 映象倉庫
-
dockerTag:Docker 映象 tag
-
pullPolicy:映象拉取策略
-
storage:儲存後端,預設設定為 "minio"
預定義 Values
-
Release.Name:release 的名稱(不是 chart)
-
Release.Namespace:release 被安裝到的名稱空間
-
Release.Service:渲染當前模板的服務,在 Helm 上,實際上該值始終為 Helm
-
Release.IsUpgrade:如果當前操作是升級或回滾,則該值為 true
-
Release.IsInstall:如果當前操作是安裝,則該值為 true
-
Chart:Chart.yaml 檔案的內容,可以透過 Chart.Version 來獲得 Chart 的版本,透過 Chart.Maintainers 可以獲取維護者資訊
-
Files: 一個包含 chart 中所有非特殊檔案的 map 物件,這不會給你訪問模板的許可權,但是會給你訪問存在的其他檔案的許可權(除非使用 .helmignore 排除它們),可以使用 {{ index .Files "file.name" }} 或者 {{ .Files.Get name }} 或者 {{ .Files.GetString name }} 函式來訪問檔案,你還可以使用 {{ .Files.GetBytes }} 以 []byte 的形式獲取訪問檔案的內容
-
Capabilities:也是一個類 map 的物件,其中包含有關 Kubernetes 版本({{ .Capabilities.KubeVersion }})和支援的 Kubernetes API 版本({{ .Capabilities.APIVersions.Has "batch/v1" }})資訊。
任何未知的 Chart.yaml 欄位都會被刪除,在 Chart 物件內部無法訪問他們,所以,Chart.yaml 不能用於將任意結構化的資料傳遞到模板中,但是可以使用 values 檔案來傳遞。
Values 檔案
imageRegistry:"quay.io/deis"
dockerTag:"latest"
pullPolicy:"Always"
storage:"s3"
$ helm install
--values=myvals.yaml wordpress
storage:"gcs"
imageRegistry:"quay.io/deis"
dockerTag:"latest"
pullPolicy:"Always"
storage:"gcs"
chart 內包含的預設 values 檔案必須命名為 values.yaml,但是在命令列上指定的檔案可以任意命名。 如果在 helm install 或者 helm upgrade 的時候使用 –set 引數,則這些值將在客戶端轉換為 YAML 格式。 如果 values 檔案存在任何必須的條目,則可以使用 required 函式在 chart 模板中將它們宣告為必須選項。
.Values
物件在模板中訪問任意一個 values 值,類似於下面的模板檔案:apiVersion:v1
kind:ReplicationController
metadata:
name:deis-database
namespace:deis
labels:
app.kubernetes.io/managed-by:deis
spec:
replicas:1
selector:
app.kubernetes.io/name:deis-database
template:
metadata:
labels:
app.kubernetes.io/name:deis-database
spec:
serviceAccount:deis-database
containers:
-name:deis-database
image:
{{
.Values.imageRegistry
}}
/postgres:{{.Values.dockerTag}}
imagePullPolicy:
{{
.Values.pullPolicy
}}
ports:
-containerPort:5432
env:
-name:DATABASE_STORAGE
value:
{{
default"minio".Values.storage
}}
作用範圍、依賴和 Values
title:"My WordPress Site"# 傳遞到 WordPress 模板
mysql:
max_connections:100# 傳遞到 MySQL
password:"secret"
apache:
port:8080# 傳遞到 Apache
.Values.mysql.password
來進行訪問,但是對於 MySQL 這個 chart 本身來說,values 的範圍縮小了,名稱空間字首會被刪除,所以它只需要透過 .Values.password 就可以訪問到。全域性 Values
title:"My WordPress Site"# 傳遞到 WordPress 模板
global:
app:MyWordPress
mysql:
max_connections:100# 傳遞到 MySQL
password:"secret"
apache:
port:8080# 傳遞到 Apache
.Values.global.app
提供給所有 chart 使用。{{ .Values.global.app }}
來訪問 app,apache chart 也可以,實際上,上面的 values 檔案會這樣重新生成:title:"My WordPress Site"# 傳遞到 WordPress 模板
global:
app:MyWordPress
mysql:
global:
app:MyWordPress
max_connections:100# 傳遞到 MySQL
password:"secret"
apache:
global:
app:MyWordPress
port:8080# 傳遞到 Apache
Schema 檔案
{
"$schema"
:
"https://json-schema.org/draft-07/schema#"
,
"properties"
: {
"image"
: {
"description"
:
"Container Image"
,
"properties"
: {
"repo"
: {
"type"
:
"string"
},
"tag"
: {
"type"
:
"string"
}
},
"type"
:
"object"
},
"name"
: {
"description"
:
"Service name"
,
"type"
:
"string"
},
"port"
: {
"description"
:
"Port"
,
"minimum"
: 0,
"type"
:
"integer"
},
"protocol"
: {
"type"
:
"string"
}
},
"required"
: [
"protocol"
,
"port"
],
"title"
:
"Values"
,
"type"
:
"object"
}
-
helm install
-
helm upgrade
-
helm lint
-
helm template
name: frontend
protocol: https
port: 443
.Values
物件,而不僅僅是應用於 values.yaml 檔案,所以下面的檔案也是可以滿足 schema 要求的:name: frontend
protocol: https
--set
選項傳遞了必須的 port 屬性:$
helm install --
set
port=443
.Values
物件,這意味著父 chart 無法規避對子 chart 的限制。同樣的,如果子 chart 要求未滿足子 chart 的 values.yaml 檔案,則父 chart 必須滿足這些限制才能生效。參考文件
-
Go Template
-
額外的模板函式
-
YAML 檔案
-
JSON Schema
CRDS
crontabs/
Chart.yaml
crds/
crontab.yaml
templates/
mycrontab.yaml
kind:CustomResourceDefinition
metadata:
name:crontabs.stable.example.com
spec:
group:stable.example.com
versions:
-name:v1
served:true
storage:true
scope:Namespaced
names:
plural:crontabs
singular:crontab
kind:CronTab
apiVersion:stable.example.com
kind:CronTab
metadata:
name:
{{
.Values.name
}}
spec:
# ...
CRDs 的限制
-
CRDs 不會重新安裝,如果 Helm 確定 crds/ 目錄中的 CRD 已經存在(無論版本如何),Helm 都不會重新安裝或升級。
-
CRDs 不會在升級或回滾的時候安裝,只會在安裝操作的時候建立 CRDs。
-
CRDs 不會被刪除,刪除 CRD 會自動刪除叢集中所有 namespace 中的 CRDs 內容,所以 Helm 不會刪除 CRD。
使用 Helm 管理 Charts
$ helm
create
mychart
Created mychart/
$ helm
package
mychart
Archived mychart
-0.1
.-.tgz
$
helm lint mychart
No issues found
Chart 倉庫
模板開發
內建物件
-
Release:該物件描述了 release 本身的相關資訊,它內部有幾個物件:
-
Release.Name:release 名稱
-
Release.Namespace:release 安裝到的名稱空間
-
Release.IsUpgrade:如果當前操作是升級或回滾,則該值為 true
-
Release.IsInstall:如果當前操作是安裝,則將其設定為 true
-
Release.Revision:release 的 revision 版本號,在安裝的時候,值為1,每次升級或回滾都會增加
-
Reelase.Service:渲染當前模板的服務,在 Helm 上,實際上該值始終為 Helm
-
Values:從 values.yaml 檔案和使用者提供的 values 檔案傳遞到模板的 Values 值,預設情況下,Values 是空的。
-
Chart:獲取 Chart.yaml 檔案的內容,該檔案中的任何資料都可以訪問,例如 {{ .Chart.Name }}-{{ .Chart.Version}} 可以渲染成 mychart-0.1.0,該物件下面可用的欄位前面我們已經提到過了。
-
Files:可以訪問 chart 中的所有非特殊檔案,雖然無法使用它來訪問模板檔案,但是可以來訪問 chart 中的其他檔案。
-
Files.Get:用於根據名稱獲取檔案(比如 .Files.Get config.ini)
-
Files.GetBytes:用於以 bytes 陣列而不是字串的形式來獲取檔案內容的函式,這對於類似於圖片之類的東西很有用
-
Files.Glob:用於返回名稱於給定的 shell glob 模式匹配的檔案列表
-
Files.Lines:可以逐行讀取檔案的函式,對於遍歷檔案中的每行內容很有用
-
Files.AsSecrets:將檔案內容以 Base64 編碼的字串返回的函式
-
Files.AsConfig:將檔案正文作為 YAML 字典返回的函式
-
Capabilities:提供了獲取有關 Kubernetes 叢集支援功能的資訊的物件
-
Capabilities.APIVersions:支援的版本集合
-
Capabilities.APIVersions.Has $version:判斷一個版本(比如 batch/v1)或資源(比如 apps/v1/Deployment)是否可用
-
Capabilities.Kube.Version:Kubernetes 的版本
-
Capabilities.Kube:是 Kubernetes 版本的縮寫
-
Capabilities.Kube.Major:Kubernetes 主版本
-
Capabilities.Kube.Minor:Kubernetes 的次版本
-
Template:包含當前正在執行的模板的相關資訊
-
Name:當前模板的名稱空間檔案路徑(比如 mychart/templates/mytemplate.yaml)
-
BaePath:當前 chart 的模板目錄的名稱空間路徑(比如 mychart/templates)
Values
-
chart 檔案中的 values.yaml 檔案
-
如果這是子 chart,父 chart 的 values.yaml 檔案
-
用 -f 引數傳遞給 helm install 或 helm upgrade 的 values 值檔案(例如 helm install -f myvals.yaml ./mychart)
-
用
--set
傳遞的各個引數(例如 helm install –set foo=bar ./mychart)
--set
引數覆蓋。favoriteDrink: coffee
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
myvalue:"Hello World"
drink:
{{
.Values.favoriteDrink
}}
$helminstall--generate-name--dry-run--debug./mychart
install.go:148:
[
debug
]
Original chart version:""
install.go:165:
[
debug
]
CHART PATH:/Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
NAME:mychart-1575963545
LAST DEPLOYED:TueDec1015:39:062019
NAMESPACE:default
STATUS:pending-install
REVISION:1
TEST SUITE:None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
favoriteDrink:coffee
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575963545-configmap
data:
myvalue:"Hello World"
drink:coffee
--set
引數來覆蓋它:$helminstall--generate-name--dry-run--debug--setfavoriteDrink=slurm./mychart
install.go:148:
[
debug
]
Original chart version:""
install.go:165:
[
debug
]
CHART PATH:/Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
NAME:mychart-1575963760
LAST DEPLOYED:TueDec1015:42:432019
NAMESPACE:default
STATUS:pending-install
REVISION:1
TEST SUITE:None
USER-SUPPLIED VALUES:
favoriteDrink:slurm
COMPUTED VALUES:
favoriteDrink:slurm
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575963760-configmap
data:
myvalue:"Hello World"
drink:slurm
--set
的優先順序高於預設的 values.yaml 檔案,所以我們的模板會生成 drink: slurm。Values 值檔案也可以包含更多結構化的內容,例如我們可以在 values.yaml 檔案中建立一個 favorite 的部分,然後在其中新增幾個 keys:favorite:
drink:coffee
food:pizza
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
myvalue:"Hello World"
drink:
{{
.Values.favorite.drink
}}
food:
{{
.Values.favorite.food
}}
刪除預設 KEY
livenessProbe:
httpGet:
path:/user/login
port:http
initialDelaySeconds:120
--set livenessProbe.exec.command=[cat, docroot/CHANGELOG.txt]
將 livenessProbe 的處理程式覆蓋為 exec 而不是 httpGet,則 Helm 會將預設鍵和覆蓋鍵合併在一起,如下所示:livenessProbe:
httpGet:
path:/user/login
port:http
exec:
command:
-cat
-docroot/CHANGELOG.txt
initialDelaySeconds:120
$
helm install stable/drupal --
set
image=my-registry/drupal:0.1.0 --
set
livenessProbe.exec.command=[
cat
, docroot/CHANGELOG.txt] --
set
livenessProbe.httpGet=null
函式與管道
.Values
物件中的字串注入模板時,我們應該引用這些字串,我們可以透過在 template 指令中呼叫 quote 函式來實現,比如:apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
myvalue:"Hello World"
drink:
{{
quote.Values.favorite.drink
}}
food:
{{
quote.Values.favorite.food
}}
當我們談論 Helm 模板語言 的時候,就好像是特定於 Helm 一樣,但實際上它是 Go 模板語言加上一些額外的函式以及各種封裝程式的組合,以將某些物件暴露給模板。當我們需要學習模板的時候,Go 模板上有許多資源會對我們有所幫助的。
管道
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
myvalue:"Hello World"
drink:
{{
.Values.favorite.drink|quote
}}
food:
{{
.Values.favorite.food|quote
}}
.Values.favorite.drink | quote
,使用管道,我們可以將多個功能連結在一起:apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
myvalue:"Hello World"
drink:
{{
.Values.favorite.drink|quote
}}
food:
{{
.Values.favorite.food|upper|quote
}}
管道順序: 反轉順序是模板中常見的做法,我們會看到 .val | quote 比 quote .val 用法更多,雖然兩種方法都是可以的。
$helminstall--generate-name--dry-run--debug./mychart
install.go:148:
[
debug
]
Original chart version:""
install.go:165:
[
debug
]
CHART PATH:/Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
NAME:mychart-1575966483
LAST DEPLOYED:TueDec1016:28:042019
NAMESPACE:default
STATUS:pending-install
REVISION:1
TEST SUITE:None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
favorite:
drink:coffee
food:pizza
favoriteDrink:coffee
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575966483-configmap
data:
myvalue:"Hello World"
drink:"coffee"
food:"PIZZA"
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
myvalue:"Hello World"
drink:
{{
.Values.favorite.drink|repeat5|quote
}}
food:
{{
.Values.favorite.food|upper|quote
}}
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575966939-configmap
data:
myvalue:"Hello World"
drink:"coffeecoffeecoffeecoffeecoffee"
food:"PIZZA"
default 函式
food: {{ .Values.favorite.food |
default"rice"
| upper | quote }}
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575966939-configmap
data:
myvalue:"Hello World"
drink:"coffeecoffeecoffeecoffeecoffee"
food:"PIZZA"
favorite:
drink:coffee
# food: pizza
helm install --generate-name --dry-run --debug ./mychart
將渲染成如下的 YAML 檔案:# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575967394-configmap
data:
myvalue:"Hello World"
drink:"coffeecoffeecoffeecoffeecoffee"
food:"RICE"
food: {{ .Values.favorite.food | default (
printf"%s-rice"
(include
"fullname"
.)) }}
運算子函式
流程控制
-
if/else 條件語句
-
with 指定一個作用域範圍
-
range 提供類似於 for each 這樣的迴圈樣式
-
define 在模板內部宣告一個新的命名模板
-
template 匯入一個命名模板
-
block 聲明瞭一種特殊的可填充模板區域。
if/else
{{
if
PIPELINE }}
# Do something
{{
elseif
OTHER PIPELINE }}
# Do something else
{{
else
}}
# Default case
{{ end }}
-
布林 false
-
數字零
-
一個空字串
-
nil(empty 或者 null)
-
一個空集合(map、slice、tuple、dict、array)
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
}}
{{
ifeq.Values.favorite.drink"coffee"
}}
mug:true
{{
end
}}
favorite:
# drink: coffee
food:pizza
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575970308-configmap
data:
myvalue:"Hello World"
drink:"coffee"
food:"PIZZA"
mug:true
.Values.favorite.drink
值是否等於 "coffee",如果相等則渲染 mug: true。空格控制
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
}}
{{
ifeq.Values.favorite.drink"coffee"
}}
mug:true
{{
end
}}
$ helm install
--generate-name --dry-run --debug ./mychart
install.go:
148
: [debug] Original chart version:
""
install.go:
165
: [debug] CHART PATH: /Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
Error
: YAML parse
erroron
mychart/templates/configmap.yaml:
error
converting YAML
to
JSON: yaml:
line9
: did
not
find expected key
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575970308-configmap
data:
myvalue:"Hello World"
drink:"coffee"
food:"PIZZA"
mug:true
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
}}
{{
ifeq.Values.favorite.drink"coffee"
}}
mug:true
{{
end
}}
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575971172-configmap
data:
myvalue:"Hello World"
drink:"coffee"
food:"PIZZA"
mug:true
{{-
添加了破折號和空格表示應將左邊的空格移除,-}}
表示將右邊的空格移除,另外也需要注意的是,換行符也是空格。需要注意的時候要確保-
和指令的其餘部分之間要有空格,{{- 3 }}
表示刪除左邊的空格並列印3,但是{{-3 }}
表示列印-3
。
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
}}
{{
-ifeq.Values.favorite.drink"coffee"
}}
mug:true
{{
-end
}}
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575972373-configmap
data:
myvalue:"Hello World"
drink:"coffee"
food:"PIZZA"
mug:true
*
來代替將要刪除的每個空格,行尾的*
表示被刪除的換行符: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
}}
*
**{{-ifeq.Values.favorite.drink"coffee"}}
mug:true*
**{{-end}}
{{-
表示的就是刪除本行開頭的兩個空格以及上一行的換行符,這樣是不是就將空行都刪除了啊。
food: {{ .Values.favorite.food |
upper
| quote }}
{{-
if
eq .Values.favorite.drink
"coffee"
-}}
mug:
true
{{-
end
-}}
*
來代替空格進行分析,如下所示: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
}}
*
**{{-ifeq.Values.favorite.drink"coffee"-}}*
mug:true*
**{{-end-}}
{{-
會刪除前面的空格和前面的換行符,然後後面的-}}
會刪除當前行的換行符,這樣就會把 mug: true 移動到 food: "PIZZA" 後面去了,最終渲染過後就會變成:food: "PIZZA"mug: true,因為在兩側都去掉換行符。有關模板中空格控制的詳細資訊,可以檢視 Go 模板官方文件介紹。
使用 with 修改作用域
.Values
是告訴模板引擎在當前作用域下內查詢 Values 物件。
{{
with
PIPELINE }}
# 限制範圍
{{ end }}
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
}}
{{- with .Values.favorite }}
的語句,意思就是說在 with 語句的作用範圍內可以用 .
表示 .Values.favorite
了,所以我們可以引用 .drink
和 .food
了,但是在 {{ end }}
之後就會重置為之前的作用域了。
{{-
with.Values.favorite
}}
drink: {{
.drink
|
default"tea"
| quote }}
food: {{
.food
| upper | quote }}
release
: {{
.Release.Name
}}
{{-
end
}}
{{ end }}
之後會重置作用域。
{{-
with.Values.favorite
}}
drink: {{
.drink
|
default"tea"
| quote }}
food: {{
.food
| upper | quote }}
{{-
end
}}
release
: {{
.Release.Name
}}
range 迴圈操作
favorite:
drink: coffee
food: pizza
pizzaToppings:
-
mushrooms
-
cheese
-
peppers
-
onions
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 }}
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575975849-configmap
data:
myvalue:
"Hello World"
drink:
"coffee"
food:
"PIZZA"
toppings: |-
-
"Mushrooms"
-
"Cheese"
-
"Peppers"
-
"Onions"
多行字串可以使用|保留換行符,也可以使用>摺疊換行,如:
this:
|
Foo
Bar
that:
>
Foo
Bar
對應的意思就是:{this:'Foo\nBar\n',that:'Foo Bar\n'}
+表示保留文字塊末尾的換行,-表示刪除字串末尾的換行,如:
s1:|
Foo
s2:|+
Foo
s3:|-
Foo
對應的意思就是:{s1:'Foo\n',s2:'Foo\n\n\n',s3:'Foo'}
sizes: |-
{{-
rangetuple"small""medium""large"
}}
- {{ . }}
{{- end }}
sizes: |-
-
small
-
medium
-
large
變數
{{-
with.Values.favorite
}}
drink: {{
.drink
|
default"tea"
| quote }}
food: {{
.food
| upper | quote }}
release
: {{
.Release.Name
}}
{{-
end
}}
:=
,我們可以修改上面的模板,為 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
}}
$relname := .Release.Name
,然後在 with 語句塊中,$relname
變數仍然表示 release 的名稱,我們渲染該模板,可以得到如下的正確結果:# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575982655-configmap
data:
myvalue:"Hello World"
drink:"coffee"
food:"PIZZA"
release:mychart-1575982655
toppings: |-
{{- range
$index
,
$topping
:= .Values.pizzaToppings }}
{{
$index
}}: {{
$topping
}}
{{- end }}
$index
,並將 value 值分配給 $topping,上面的內容會被渲染成如下內容:toppings:
|-
0: mushrooms
1: cheese
2: peppers
3: onions
.Values.favorite
:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}
-configmap
data
:
myvalue:
"Hello World"
{{- range
$key
,
$val
:= .Values.favorite }}
{{
$key
}}: {{
$val
| quote }}
{{-
end
}}
$key
是 drink,$val
是 coffee,在第二次迭代中,$key
是 food,$val
是 pizza。執行上面的命令將生成下面的內容:# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1575983119-configmap
data:
myvalue:"Hello World"
drink:"coffee"
food:"pizza"
$relname
,該變數將在整個模板的範圍內,但是在我們上面的示例中,$key
和 $val
作用域只在 {{ range... }}{{ end }}
區域內。$
始終指向頂層根上下文,當我們在 range 迴圈內需要知道 chart 包的 release 名稱的時候,該功能就非常有用了,比如下面的模板檔案:
{{
-range.Values.tlsSecrets
}}
apiVersion:v1
kind:Secret
metadata:
name:
{{
.name
}}
labels:
# helm 模板經常使用 `.`,但是這裡是無效的,用 `$` 是可以生效的。
app.kubernetes.io/name:
{{
template"fullname"$
}}
# 這裡不能引用 `.Chart.Name`,但是可用使用 `$.Chart.Name`
helm.sh/chart:"{{ $.Chart.Name }}-{{ $.Chart.Version }}"
app.kubernetes.io/instance:"{{ $.Release.Name }}"
# 值來自於 Chart.yaml 檔案中的 appVersion
app.kubernetes.io/version:"{{ $.Chart.AppVersion }}"
app.kubernetes.io/managed-by:"{{ $.Release.Service }}"
type:kubernetes.io/tls
data:
tls.crt:
{{
.certificate
}}
tls.key:
{{
.key
}}
---
{{
-end
}}
命令模板
前面我們都是隻操作的一個模板,現在我們來嘗試使用多個模板檔案。在本節中,我們可以瞭解到如何在一個檔案中定義命名模板,然後在其他地方使用它們。命名模板(有時也叫子模板)只是在檔案內部定義的有名稱的模板。主要有兩種建立方式以及幾種不同的使用方式。
當使用命名模板的時候有幾個重要細節:模板名稱是全域性的,如果宣告兩個具有相同名稱的模板,則會使用最後被載入的模板。由於子chart中的模板是與頂級模板一起編譯的,所以需要謹慎命名。
一種流行的命名約定是在每個定義的模板前新增chart名稱:{{define"mychart.labels"}},透過使用特定的chart名作為字首,我們可以避免由於兩個不同的chart實現了相同名稱的模板而引起的衝突。
partials和_檔案¶
到目前為止,我們只使用了一個模板檔案,但是Helm的模板語言允許我們建立命名的嵌入式模板,可以在其他位置進行訪問。在編寫這些模板之前,有一些值得一提的命名約定:
templates/中的大多數檔案都被視為Kubernetes資源清單檔案(NOTES.txt除外)
以_開頭命名的檔案也不會被當做Kubernetes資源清單檔案
下劃線開頭的檔案不會被當做資源清單之外,還可以被其他chart模板呼叫
_開頭的這些檔案其實就是Helm中的partials檔案,所以其實我們完全可以將命名模板定義在這些partials檔案中,預設就是_helpers.tpl檔案,其實在前面我們建立的mychart包中也可以找到這個檔案。
define和template¶
define關鍵字可以讓我們在模板檔案中建立命名模板,它的語法如下所示:
{{
define"MY.NAME"
}}
# 模板內容區域
{{
end
}}
比如我們可以定義一個模板來封裝下Kubernetes的labels標籤:
{{
-define"mychart.labels"
}}
labels:
generator:helm
date:
{{
now|htmlDate
}}
{{
-end
}}
現在我們可以將該模板嵌入到前面的ConfigMap模板中,然後將其包含在模板中:
{{
-define"mychart.labels"
}}
labels:
generator:helm
date:
{{
now|htmlDate
}}
{{
-end
}}
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
{{
-template"mychart.labels"
}}
data:
myvalue:"Hello World"
{{
-range$key
,
$val:=.Values.favorite
}}
{{
$key
}}
:
{{
$val|quote
}}
{{
-end
}}
當模板引擎讀取這個檔案的時候,它會儲存mychart.labels的引用,直到該模板被呼叫,然後會內聯渲染該模板。我們渲染這個模板可以都到如下所示的結果(記得先刪掉預設生成的_helpers.tpl檔案):
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1576034036-configmap
labels:
generator:helm
date:2019-12-11
data:
myvalue:"Hello World"
drink:"coffee"
food:"pizza"
一般來說,Helm中約定將這些模板統一放到一個partials檔案中,通常就是_helpers.tpl檔案中,我們將上面的命名模板移動到該檔案(templates/_helpers.tpl)中去:
{{
/*生成基本的Label標籤*/
}}
{{
-define"mychart.labels"
}}
labels:
generator:helm
date:
{{
now|htmlDate
}}
{{
-end
}}
一般來說,我們也會用一個簡單的塊({{/*...*/}})來註釋這個命名模板的作用。
現在雖然我們把命名模板放到了_helpers.tpl檔案中,但是我們在configmap.yaml模板中還是可以訪問,因為命名模板是全域性的:
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
{{
-template"mychart.labels"
}}
data:
myvalue:"Hello World"
{{
-range$key
,
$val:=.Values.favorite
}}
{{
$key
}}
:
{{
$val|quote
}}
{{
-end
}}
因為上面我們提到過命名模板是全域性的,我們可以再渲染下上面的模板可以得到正確的結果。
設定模板範圍¶
上面我們定義的模板中,還沒有使用到任何物件,只使用了函式,現在我們來修改下定義的命名模板,包含chart的名稱和版本:
{{
/*生成基本的Label標籤*/
}}
{{
-define"mychart.labels"
}}
labels:
generator:helm
date:
{{
now|htmlDate
}}
chart:
{{
.Chart.Name
}}
version:
{{
.Chart.Version
}}
{{
-end
}}
現在我們來渲染下模板,會出現下面的錯誤:
$helminstall--generate-name--dry-run--debug./my
chart
install.go:148:
[
debug
]
Original chart version:""
install.go:165:
[
debug
]
CHART PATH:/Users/ych/devs/workspace/yidianzhishi/cour
se/k8strain/content/helm/manifests/mychart
Error: unable to build kubernetes objects from release manifest:errorvalidati
ng"":error validating data:
[
unknownobjecttype"nil"inConfigMap.metadata.
labels.chart
,
unknownobjecttype"nil"inConfigMap.metadata.labels.version
]
helm.go:76:
[
debug
]
errorvalidating"":error validating data:
[
unknownobject
type"nil"inConfigMap.metadata.labels.chart
,
unknownobjecttype"nil"inCo
nfigMap.metadata.labels.version
]
......
我們可以看到提示labels.chart為nil,這是因為我們使用的.Chart.Name不在定義的這個模板的作用域範圍內,當渲染命名模板(使用define定義)的時候,它將接收模板呼叫傳遞的作用域。在我們這個示例中,我們是這樣引用這個模板的:
{{
-template"mychart.labels"
}}
沒有傳入任何作用域,所以在模板內我們無法訪問.中的任何內容,當然要解決很簡單,我們只需要把作用域範圍傳遞給模板即可:
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
{{
-template"mychart.labels".
}}
......
我們這裡在使用template呼叫模板的時候傳遞了.,我們可以很容易傳遞.Values或者.Values.favorite或者我們想要的任何範圍,但是這裡我們想要的是頂級作用域,所以我們傳遞的是.。
現在我們再來重新渲染我們的模板,可以得到如下所示的結果:
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1576035668-configmap
labels:
generator:helm
date:2019-12-11
chart:mychart
version:0.1.0
data:
myvalue:"Hello World"
drink:"coffee"
food:"pizza"
現在
{{
.Chart.Name
}}
解析為了mychart,而
{{
.Chart.Version
}}
解析為了0.1.0。
include函式¶
假設我們定義了一個如下所示的簡單模板:
{{
-define"mychart.app"-
}}
app_name:
{{
.Chart.Name
}}
app_version:"{{ .Chart.Version }}"
{{
-end-
}}
現在我們想把上面的內容插入到模板的labels部分,在data部分也想要這個內容:
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
labels:
{{
template"mychart.app".
}}
data:
myvalue:"Hello World"
{{
-range$key
,
$val:=.Values.favorite
}}
{{
$key
}}
:
{{
$val|quote
}}
{{
-end
}}
{{
template"mychart.app".
}}
但是我們直接渲染上面的模板還是會有錯誤:
$helminstall--generate-name--dry-run--debug./my
chart
install.go:148:
[
debug
]
Original chart version:""
install.go:165:
[
debug
]
CHART PATH:/Users/ych/devs/workspace/yidianzhishi/cour
se/k8strain/content/helm/manifests/mychart
Error: unable to build kubernetes objects from release manifest:errorvalidati
ng"":error validating data:
[
ValidationError(ConfigMap):unknownfield
"app_n
ame"
inio.k8s.api.core.v1.ConfigMap
,
ValidationError(ConfigMap):unknownfield
"app_version"inio.k8s.api.core.v1.ConfigMap
]
helm.go:76:
[
debug
]
errorvalidating"":error validating data:
[
ValidationErro
r(ConfigMap):unknownfield"app_name"inio.k8s.api.core.v1.ConfigMap
,
Validat
ionError(ConfigMap):unknownfield"app_version"inio.k8s.api.core.v1.ConfigMap
]
......
因為template只是一個動作,而不是一個函式,所以無法將模板呼叫的輸出傳遞給其他函式,只是內聯插入,相當於渲染的結果是這樣的:
apiVersion:v1
kind:ConfigMap
metadata:
name:measly-whippet-configmap
labels:
app_name:mychart
app_version:"0.1.0+1478129847"
data:
myvalue:"Hello World"
drink:"coffee"
food:"pizza"
app_name:mychart
app_version:"0.1.0+1478129847"
很明顯上面的YAML檔案是不符合ConfigMap資源物件的格式要求的,所以報錯了。為解決這個問題,Helm提供了代替template的函式include,可以將模板的內容匯入到當前的管道中,這樣就可以在管道中傳遞給其他函式進行處理了,如下所示:
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
labels:
{{
include"mychart.app".|indent4
}}
data:
myvalue:"Hello World"
{{
-range$key
,
$val:=.Values.favorite
}}
{{
$key
}}
:
{{
$val|quote
}}
{{
-end
}}
{{
include"mychart.app".|indent2
}}
現在我們重新渲染就可以得到正確的結果了,這是因為我們用include函式得到模板內容後透過管道傳給了後面的indent函式來保證了縮排:
Source:mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1576036671-configmap
labels:
app_name:mychart
app_version:"0.1.0"
data:
myvalue:"Hello World"
drink:"coffee"
food:"pizza"
app_name:mychart
app_version:"0.1.0"
建議
在Helm模板中最好使用include而不是template,這樣可以更好地處理YAML文件的輸出格式。
有時候如果我們只想匯入內容而不是模板,這個時候我們可以透過下面描述的.Files物件來訪問檔案實現。
訪問檔案
在上一節中我們介紹了幾種建立和訪問命名模板的方法,這使得從另一個模板中匯入一個模板變得很容易,但是有時候需要匯入一個不是模板的檔案並注入其內容,而不透過模板渲染器獲得內容。
Helm提供了一個.Files物件對檔案的訪問,但是在模板中使用這個物件之前,還有幾個需要注意的事項值得一提:
可以在Helmchart中新增額外的檔案,這些檔案也會被打包,不過需要注意,由於Kubernetes物件的儲存限制,Charts必須小於1M
由於一些安全原因,透過.Files物件無法訪問某些檔案
無法訪問templates/下面的檔案
無法訪問使用.helmignore排除的檔案
Chart不會保留UNIX模式的資訊,所以,當使用.Files物件時,檔案級別的許可權不會對檔案的可用性產生影響。
基本示例¶
現在我們來編寫一個模板,將3個檔案讀入到ConfigMap模板中,首先我們在chart中新增3個檔案,將3個檔案都直接放置在mychart/目錄中。
config1.toml:
message=Hellofromconfig1
config2.toml:
message=Thisisconfig2
config3.toml:
message=Goodbyefromconfig3
3個檔案都是簡單的TOML檔案,我們知道這些檔案的名稱,所以我們可以使用range函式來遍歷它們,並將其內容注入到ConfigMap中去。
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
{{
-$files:=.Files
}}
{{
-rangetuple"config1.toml""config2.toml""config3.toml"
}}
{{
.
}}
:
|-
{{ $files.Get . }}
{{
-end
}}
這裡我們聲明瞭一個$files的變數來儲存.Files物件的引用,還使用了tuple函式來迴圈檔案列表,然後我們列印每個資料夾
{{
.
}}
:|-,後面使用
{{
$files.Get.
}}
獲取檔案內容。
現在我們渲染這個模板會產生包含3個檔案內容的單個ConfigMap:
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1576046462-configmap
data:
config1.toml:
|-
message = Hello from config 1
config2.toml:
|-
message = This is config 2
config3.toml:
|-
message = Goodbye from config 3
另外在處理檔案的時候,對檔案路徑本身執行一些標準操作可能非常有用,為了解決這個問題,Helm從Go的路徑包中匯入了許多功能供你使用,它們都可以使用與Go包中相同的相同名稱來訪問,但是首字母需要小寫,比如Base需要變成base,匯入的函式有:-Base-Dir-Ext-IsAbs-Clean。
Glob模式¶
隨著chart的增長,你可能需要更多地組織檔案,因此Helm提供了Files.Glob的方法來幫助我們獲取具有glob模式的檔案。
.Glob返回Files型別,所以你可以在返回的物件上呼叫任何Files方法。比如,我們的檔案目錄結構如下所示:
foo/:
foo.txtfoo.yaml
bar/:
bar.gobar.confbaz.yaml
我們可以用Glob進行多種選擇:
{{
range$path:=.Files.Glob"**.yaml"
}}
{{
$path
}}
:|
{{
.Files.Get$path
}}
{{
end
}}
或者
{{
range$path
,
$bytes:=.Files.Glob"foo/*"
}}
{{
$path
}}
:'{{ b64enc $bytes }}'
{{
end
}}
ConfigMap和Secrets¶
想要將檔案內容同時放入ConfigMap和Secrets中,以便在執行時安裝到Pod中,這種需求很常見,為了解決這個問題,Helm在Files型別上添加了一個實用的方法。
根據上面的目錄結構,我們可以按照如下的方式進行處理:
apiVersion:v1
kind:ConfigMap
metadata:
name:conf
data:
{{
(.Files.Glob"foo/*").AsConfig|indent2
}}
---
apiVersion:v1
kind:Secret
metadata:
name:very-secret
type:Opaque
data:
{{
(.Files.Glob"bar/*").AsSecrets|indent2
}}
編碼¶
我們也可以匯入一個檔案並用base64編碼進行編碼:
apiVersion:v1
kind:Secret
metadata:
name:
{{
.Release.Name
}}
-secret
type:Opaque
data:
token:
|-
{{ .Files.Get "config1.toml" | b64enc }}
上面將採用我們上面的config1.toml檔案並對其內容進行base64編碼,渲染會得到如下所示的結果:
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:Secret
metadata:
name:mychart-1576048287-secret
type:Opaque
data:
token:
|-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
Lines¶
有時,需要訪問模板中檔案的每一行內容,Helm也提供了方法的Lines方法,我們可以使用range函式遍歷沒行內容:
data:
some-file.txt:
{{
range.Files.Lines"foo/bar.txt"
}}
{{
.
}}{{
end
}}
在Helm安裝的時候無法將檔案傳遞到chart外部,所以,如果你要求使用者提供資料的話,則必須使用helminstall-f或者helminstall--set來獲取。
NOTES.txt
在本節中我們將來了解為 chart 使用者提供說明的一個 NOTES.txt 檔案,在 chart 安裝或者升級結束時,Helm 可以為使用者打印出一些有用的資訊,使用模板也可以自定義這些資訊。
要將安裝說明新增到 chart 中,只需要建立一個 templates/NOTES.txt 檔案,該檔案純文字的,但是可以像模板一樣進行處理,並具有所有常規模板的功能和可用物件。
現在讓我們來建立一個簡單的 NOTES.txt 檔案:
Thank you
for
installing
{{ .Chart.Name }
}.
Your release
is
named
{{ .Release.Name }
}.
To
learn more about the release,
try
:
$ helm status
{{ .Release.Name }
}
$ helm get
{{ .Release.Name }
}
現在我們執行 helm install ./mychart,我們就可以在底部看到這樣的訊息:
RESOURCES:
==> v1/Secret
NAMETYPE
DATA AGE
rude-cardinal-secret Opaque
10
s
==> v1/ConfigMap
NAME
DATA AGE
rude-cardinal-configmap
30
s
NOTES:
Thank you
for
installing mychart.
Your release
is
named rude-cardinal.
To
learn more about the release,
try
:
$ helm status rude-cardinal
$ helm get rude-cardinal
用這種方式可以向用戶提供一個有關如何使用其新安裝的 chart 的詳細資訊,強烈建議建立 NOTES.txt 檔案,雖然這不是必須的。
子Chart與全域性值
到現在為止,我們從單一模板,到多個模板檔案,但是都僅僅是處理的一個chart包,但是charts可能具有一些依賴項,我們稱為subcharts(子chart),接下來我們將建立一個子chart。
同樣在深入瞭解之前,我們需要了解下子chart相關的一些資訊。
子chart是獨立的,這意味著子chart不能顯示依賴其父chart
所以子chart無法訪問其父級的值
父chart可以覆蓋子chart的值
Helm中有可以被所有charts訪問的全域性值的概念
建立子chart¶
同樣還是在之前操作的mychart/這個chart包中,我們來嘗試新增一些新的子chart:
$cdmychart/charts
$helmcreatemysubchart
Creatingmysubchart
$rm-rfmysubchart/templates/*.*
和前面一樣,我們刪除了所有的基本模板,這樣我們可以從頭開始。
新增values和模板¶
接下來我們為mysubchart這個子chart建立一個簡單的模板和values值檔案,mychart/charts/mysubchart中已經有一個values.yaml檔案了,在檔案中新增下面的values:
dessert:cake
下面我們再建立一個新的ConfigMap模板mychart/charts/mysubchart/templates/configmap.yaml:
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-cfgmap2
data:
dessert:
{{
.Values.dessert
}}
因為每個子chart都是獨立的chart,所以我們可以單獨測試mysubchart:
helminstall--generate-name--dry-run--debugmychart/charts/mysubchart
install.go:148:
[
debug
]
Original chart version:""
install.go:165:
[
debug
]
CHART PATH:/Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart/charts/mysubchart
NAME:mysubchart-1576050755
LAST DEPLOYED:WedDec1115:52:362019
NAMESPACE:default
STATUS:pending-install
REVISION:1
TEST SUITE:None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
dessert:cake
HOOKS:
MANIFEST:
---
# Source: mysubchart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mysubchart-1576050755-cfgmap2
data:
dessert:cake
從父chart覆蓋values¶
我們原來的chart-mychart現在是mysubchart的父級chart了。由於mychart是父級,所以我們可以在mychart中指定配置,並將該配置傳送到mysubchart中去,比如,我們可以這樣修改mychart/values.yaml:
favorite:
drink:coffee
food:pizza
pizzaToppings:
-mushrooms
-cheese
-peppers
-onions
mysubchart:
dessert:icecream
最後兩行,mysubchart部分中的所有指令都回被髮送到mysubchart子chart中,所以,如果我們現在渲染模板,我們可以看到mysubchart的ConfigMap會被渲染成如下的內容:
# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1576051914-cfgmap2
data:
dessert:icecream
我們可以看到頂層的values值覆蓋了子chart中的值。這裡有一個細節需要注意,我們沒有將mychart/charts/mysubchart/templates/configmap.yaml模板更改為指向.Values.mysubchart.dessert,因為從該模板的絕度來看,該值仍然位於.Values.dessert,當模板引擎傳遞values值的時候,它會設定這個作用域,所以,對於mysubchart模板,.Values中僅僅提供用於該子chart的值。
但是有時候如果我們確實希望某些值可以用於所有模板,這個時候就可以使用全域性chartvalues值來完成了。
全域性值¶
全域性值是可以從任何chart或子chart中都可以訪問的值,全域性值需要顯示的宣告,不能將現有的非全域性物件當作全域性物件使用。
Values資料型別具有一個名為Values.global的保留部分,可以在其中設定全域性值,我們在mychart/values.yaml檔案中新增一個全域性值:
favorite:
drink:coffee
food:pizza
pizzaToppings:
-mushrooms
-cheese
-peppers
-onions
mysubchart:
dessert:icecream
global:
salad:caesar
由於全域性值的原因,在mychart/templates/configmap.yaml和mysubchart/templates/configmap.yaml下面都應該可以以
{{
.Values.global.salad
}}
的形式來訪問這個值。
mychart/templates/configmap.yaml:
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-configmap
data:
salad:
{{
.Values.global.salad
}}
mysubchart/templates/configmap.yaml:
apiVersion:v1
kind:ConfigMap
metadata:
name:
{{
.Release.Name
}}
-cfgmap2
data:
dessert:
{{
.Values.dessert
}}
salad:
{{
.Values.global.salad
}}
然後我們渲染這個模板,可以得到如下所示的內容:
---
# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1576053485-cfgmap2
data:
dessert:icecream
salad:caesar
---
# Source: mychart/templates/configmap.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:mychart-1576053485-configmap
data:
salad:caesar
全域性值對於傳遞這樣的資料比較有用。
共享模板¶
父級chart和子chart可以共享模板,任何chart中已定義的塊都可以用於其他chart。比如,我們可以定義一個簡單的模板,如下所示:
{{
-define"labels"
}}
from:mychart{{end}}
前面我們提到過可以使用在模板中使用include和template,但是使用include的一個優點是可以動態引入模板的內容:
{{
include$mytemplate
}}
模板除錯
除錯模板可能比較麻煩,因為渲染的模板會發送到 Kubernetes API server,而 API server 可能會因為格式以外的一些原因而拒絕 YAML 檔案。
下面這些命令可以幫助你除錯一些問題:
helm lint 是驗證 chart 是否遵循最佳實踐的首選工具
helm
install
--dry-run --
debug
或者 helm template --
debug
:前面我們已經使用了這個技巧,這個是讓伺服器渲染模板,然後返回生成的資源清單檔案的好方法,而且不會真正的去安裝這些資源
helm get manifest:這是檢視伺服器上安裝了哪些模板的好方法
當你的 YAML 檔案無法解析的時候,但你想要檢視生成的內容的時候,檢索 YAML 的一種簡單方法是註釋掉模板中的問題部分,然後重新執行 helm
install
--dry-run --
debug
:
apiVersion:
v2
# some: problem section
# {{ .Values.foo | quote }}
上面的內容將呈現並返回完整的註釋:
apiVersion:
v2
# some: problem section
# "bar"
這提供了一種檢視生成的內容的快速方法。
Chart Hooks
Helm也提供了一種Hook機制,可以允許chart開發人員在release生命週期的某些時間點進行干預。比如,可以使用hook來進行下面的操作:
在載入任何charts之前,在安裝的時候載入ConfigMap或者Secret
在安裝新的chart之前,執行一個Job來備份資料庫,然後在升級後執行第二個Job還原資料
在刪除release之前執行一個JOb,以在刪除release之前適當地取消相關服務
Hooks的工作方式類似於普通的模板,但是他們具有特殊的註解,這些註解使Helm可以用不同的方式來使用他們。
Hooks¶
在Helm中定義瞭如下一些可供我們使用的Hooks:
預安裝pre-install:在模板渲染後,kubernetes建立任何資源之前執行
安裝後post-install:在所有kubernetes資源安裝到集群后執行
預刪除pre-delete:在從kubernetes刪除任何資源之前執行刪除請求
刪除後post-delete:刪除所有release的資源後執行
升級前pre-upgrade:在模板渲染後,但在任何資源升級之前執行
升級後post-upgrade:在所有資源升級後執行
預回滾pre-rollback:在模板渲染後,在任何資源回滾之前執行
回滾後post-rollback:在修改所有資源後執行回滾請求
測試test:在呼叫Helmtest子命令的時候執行(可以檢視測試文件)
生命週期¶
Hooks允許開發人員在release的生命週期中的一些關鍵節點執行一些鉤子函式,我們正常安裝一個chart包的時候的生命週期如下所示:
使用者執行helminstallfoo
Helm庫檔案呼叫安裝API
經過一些驗證,Helm庫渲染foo模板
Helm庫將產生的資源載入到kubernetes中去
Helm庫將release物件和其他資料返回給客戶端
Helm客戶端退出
如果開發人員在install的生命週期中定義了兩個hook:pre-install和post-install,那麼我們安裝一個chart包的生命週期就會多一些步驟了:
使用者執行helminstallfoo
Helm庫檔案呼叫安裝API
在crds/目錄下面的CRDs被安裝
經過一些驗證,Helm庫渲染foo模板
Helm庫將hook資源載入到kubernetes中,準備執行pre-installhooks
Helm庫會根據權重對hooks進行排序(預設分配權重0,權重相同的hook按升序排序)
Helm庫然後載入最低權重的hook
Helm庫會等待,直到hook準備就緒
Helm庫將產生的資源載入到kubernetes中,注意如果添加了--wait引數,Helm庫會等待所有資源都準備好,在這之前不會執行post-installhook
Helm庫執行post-installhook(載入hook資源)
Helm庫等待,直到hook準備就緒
Helm庫將release物件和其他資料返回給客戶端
Helm客戶端退出
等待hook準備就緒,這是一個阻塞的操作,如果hook中宣告的是一個Job資源,Helm將等待Job成功完成,如果失敗,則釋出失敗,在這個期間,Helm客戶端是處於暫停狀態的。
對於所有其他型別,只要kubernetes將資源標記為載入(新增或更新),資源就被視為就緒狀態,當一個hook聲明瞭很多資源是,這些資源是被序列執行的。
另外需要注意的是hook建立的資源不會作為release的一部分進行跟蹤和管理,一旦Helm驗證了hook已經達到了就緒狀態,它就不會去管它了。
所以,如果我們在hook中建立了資源,那麼不能依賴helmuninstall去刪除資源,因為hook建立的資源已經不受控制了,要銷燬這些資源,你需要將helm.sh/hook-delete-policy這個annotation新增到hook模板檔案中,或者設定Job資源的生存(TTL)欄位。
編寫Hook¶
Hooks就是Kubernetes資源清單檔案,在元資料部分帶有一些特殊的註解,因為他們是模板檔案,所以你可以使用普通模板所有的功能,包括讀取.Values、.Release和.Template。
例如,在templates/post-install-job.yaml檔案中宣告一個post-install的hook:
apiVersion:batch/v1
kind:Job
metadata:
name:"{{ .Release.Name }}"
labels:
app.kubernetes.io/managed-by:
{{
.Release.Service|quote
}}
app.kubernetes.io/instance:
{{
.Release.Name|quote
}}
app.kubernetes.io/version:
{{
.Chart.AppVersion
}}
helm.sh/chart:"{{ .Chart.Name }}-{{ .Chart.Version }}"
annotations:
# 因為添加了這個 hook,所以我們這個資源被定義為了 hook
# 如果沒有這行,則當前這個 Job 會被當成 release 的一部分內容。
"helm.sh/hook":post-install
"helm.sh/hook-weight":"-5"
"helm.sh/hook-delete-policy":hook-succeeded
spec:
template:
metadata:
name:"{{ .Release.Name }}"
labels:
app.kubernetes.io/managed-by:
{{
.Release.Service|quote
}}
app.kubernetes.io/instance:
{{
.Release.Name|quote
}}
helm.sh/chart:"{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
restartPolicy:Never
containers:
-name:post-install-job
image:"alpine:3.3"
command:
[
"/bin/sleep"
,
"{{ default "10" .Values.sleepyTime }}"
]
當前這個模板成為hook的原因就是新增這個註解:
annotations:
"helm.sh/hook":post-install
一種資源也可以實現多個hooks:
annotations:
"helm.sh/hook":post-install,post-upgrade
類似的,實現給定hook的資源數量也沒有限制,比如可以將secret和一個configmap都宣告為pre-installhook。
當子chart宣告hooks的時候,也會對其進行呼叫,頂層的chart無法停用子chart所宣告的hooks。可以為hooks定義權重,這將有助於確定hooks的執行順序:
annotations:
"helm.sh/hook-weight":"5"
hook權重可以是正數也可以是負數,但是必須用字串表示,當Helm開始執行特定種類的hooks的時候,它將以升序的方式對這些hooks進行排序。
Hook刪除策略¶
我們還可以定義確定何時刪除相應hook資源的策略,hook刪除策略可以使用下面的註解進行定義:
annotations:
"helm.sh/hook-delete-policy":before-hook-creation,hook-succeeded
我們也可以選擇一個或多個已定義的註解:
before-hook-creation:執行一個新的hook之前刪除前面的資源(預設)
hook-succeeded:hook成功執行後刪除資源
hook-failed:hook如果執行失敗則刪除資源
如果未指定任何hook刪除策略註解,則預設情況下會使用before-hook-creation策略。
示例
前面介紹了Helm的基本使用,以及HelmChart包開發相關的一些知識點,下面我們用一個例項來演示下如何開發一個真正的HelmChart包。
應用¶
我們這裡為前面的完整示例WordPress開發一個Chart包,在開發Chart包之前很明顯我們最需要的就是要知道我們自己的應用應該如何使用,如何部署,不然是不可能編寫出對應的Chart包的。
我們可以用helmcreate命令來建立一個Chart包,這裡我們就完全手動來建立,首先建立一個名為wordpress的資料夾:
$mkdirwordpress&&cdwordpress
然後在目錄下面建立如下所示的Chart.yaml檔案:
apiVersion:v2
name:wordpress
description:AHelmchartforKubernetes
home:https://wordpress.org/
type:application
# chart 版本號
version:0.1.0
# wordpress 的應用版本
appVersion:5.3.2
由於WordPress應用本身是一來MySQL資料庫的,所以同樣需要新增一個依賴說明requirements.yaml:
dependencies:
-name:mysql
version:1.6.2
repository:http://mirror.azure.cn/kubernetes/charts/
condition:mysql.enabled
tags:
-wordpress-database
這裡依賴的應用我們可以透過helmsearch命令來獲取,當然也可以隨意指定一個Chart版本:
$helmsearchrepostable/mysql
NAMECHARTVERSIONAPPVERSIONDESCRIPTION
stable/mysql1.6.25.7.28Fast,reliable,scalable,andeasytouseopen-...
需要注意的是在依賴的檔案中我們添加了一個condition:mysql.enabled條件,這意味著當Values值mysql.enabled為true的時候才會真正去依賴這個子Chart,因為我們完全可以直接使用一個外部的資料庫。所以首先我們要先新增一個mysql.enabled的Values值,新增如下所示的values.yaml檔案:
##
## MySQL Chart 配置,參考如下 values.yaml
## https://github.com/helm/charts/blob/master/stable/mysql/values.yaml
##
mysql:
## 是否部署mysql服務來滿足wordpress的需求。如果要使用外部資料庫,將 enabled 設定為 false 並配置 externalDatabase 引數。
enabled:true
## todo,其他 mysql 配置
## 當 mysql.enabled=false 的時候使用外部資料庫
externalDatabase:
## 資料庫地址
host:localhost:3306
## WordPress 資料庫的非root使用者
user:wordpress
## 資料庫密碼
password:wordpress
## 資料庫名稱
database:wordpress
上面的Values配置很好理解,就是如果mysql.enabled=true則我們就用mysql這個子Chart去安裝一個MySQL資料庫,如果為false則使用下面externalDatabase的外部資料庫資訊來作為wordpress的資料庫配置,雖然現在這些配置還完全沒有任何作用,但是這些卻是一開始就很容易考慮到的事情。
接下來建立一個templates和charts目錄,並在charts目錄下面獲取mysql這個子chart:
$mkdirtemplates&&mkdircharts
$cdcharts&&helmfetchstable/mysql&&cd..
現在的整個Chart包目錄結構如下所示:
$tree.
.
├──Chart.yaml
├──charts
│└──mysql-1.6.2.tgz
├──requirements.yaml
├──templates
└──values.yaml
2directories,4files
接下來就是我們的重頭戲模板的開發了。我們可以將前面課程示例中的WordPress資源清單直接複製到templates目錄下面,將Deployment和Service分別放在不同的YAML檔案中,同樣還有資料持久化的資源清單,但是這裡我們就需要先將MySQL的部分移除掉,因為我們會透過外部資料庫或者子Chart來渲染,不需要在這裡顯示的聲明瞭,另外還需要將所有資源物件的名稱空間去掉,因為我們可以透過helminstall命令直接指定即可,現在我們的模板目錄結構就如下所示了:
$treetemplates
templates
├──deployment.yaml
├──pvc.yaml
└──service.yaml
0directories,4files
名稱¶
這個時候我們可以嘗試去安裝除錯下這個Chart模板:
$helminstall--generate-name--dry-run--debug.
install.go:148:
[
debug
]
Original chart version:""
install.go:165:
[
debug
]
CHART PATH:/Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/wordpress
load.go:112: Warning:DependenciesarehandledinChart.yamlsinceapiVersion"v2".WerecommendmigratingdependenciestoChart.yaml.
Error: rendered manifests contain a resource that already exists. Unable to continue with install: existing resource conflict: kind:Middleware,namespace:default,name:redirect-https
helm.go:76:
[
debug
]
existing resource conflict: kind:Middleware,namespace:default,name:redirect-https
renderedmanifestscontainaresourcethatalreadyexists.Unabletocontinuewithinstall
helm.sh/helm/v3/pkg/action.(*Install).Run
/home/circleci/helm.sh/helm/pkg/action/install.go:242
main.runInstall
/home/circleci/helm.sh/helm/cmd/helm/install.go:209
main.newInstallCmd.func1
/home/circleci/helm.sh/helm/cmd/helm/install.go:115
github.com/spf13/cobra.(*Command).execute
/go/pkg/mod/github.com/spf13/[email protected]/command.go:826
github.com/spf13/cobra.(*Command).ExecuteC
/go/pkg/mod/github.com/spf13/[email protected]/command.go:914
github.com/spf13/cobra.(*Command).Execute
/go/pkg/mod/github.com/spf13/[email protected]/command.go:864
main.main
/home/circleci/helm.sh/helm/cmd/helm/helm.go:75
runtime.main
/usr/local/go/src/runtime/proc.go:203
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1357
我們看到出現了錯誤,其中還有這樣的一條警告資訊load.go:112: Warning:DependenciesarehandledinChart.yamlsinceapiVersion"v2".WerecommendmigratingdependenciestoChart.yaml.,大概意思就是在Helm3中依賴項的配置被移動到了Chart.yaml檔案中,也就是在Helm2版本的時候依賴才會在requirements.yaml中宣告,這裡我們可以將requirements.yaml檔案中的內容全部複製到Chart.yaml檔案中去,然後刪除requirements.yaml檔案,這個時候重新DEBUG下就沒有這個提示資訊了。
下面的錯誤提示基本上都是類似於renderedmanifestscontainaresourcethatalreadyexists.這樣的資訊,意思就是這些資源物件在現有叢集中已經包含了,這很正常,因為我們的資源物件名稱都是寫死的,是非常有可能和現有的叢集衝突的,所以接下來我們要給這些資源清單改名,如何才能避免衝突又具有很好的擴充套件性呢?這裡我們建立一個如下所示的命名模板:
{{
/*
建立一個預設的應用名稱,擷取63個字元是因為Kubernetes的name屬性的限制(DNS命名規範)。
*/
}}
{{
-define"wordpress.fullname"-
}}
{{
-if.Values.fullnameOverride-
}}
{{
-.Values.fullnameOverride|trunc63|trimSuffix"-"-
}}
{{
-else-
}}
{{
-$name:=default.Chart.Name.Values.nameOverride-
}}
{{
-ifcontains$name.Release.Name-
}}
{{
-.Release.Name|trunc63|trimSuffix"-"-
}}
{{
-else-
}}
{{
-printf"%s-%s".Release.Name$name|trunc63|trimSuffix"-"-
}}
{{
-end-
}}
{{
-end-
}}
{{
-end-
}}
根據規範命名模板的名稱最好以Chart名作為字首,這裡我們命名成wordpress.fullname,首先就是判斷是否定義了fullnameOverride這個Values值,如果定義了則擷取63個字元並移除-這樣的前後綴作為名稱,如果沒有定義呢?就要檢查Chart名是否包含Release名稱,如果包含則擷取Release名的63個字元並移除-這樣的前後綴作為名稱,如果不包含,則將Release名稱和Chart名稱用-連線起來作為名稱,這個命名的方式也是符合大部分Chart模板中的命名,以後的模板開發中都可以直接使用。在templates目錄下面新建一個_helpers.tpl這樣的partials檔案,然後將上面定義的命名模板放置到裡面去。後面我們所有的命名模板都將在該檔案中完成。
接下來將templates目錄下面的所有資源物件name屬性全都換成上面我們定義的命名模板,由於這裡並沒有什麼空格控制之類的,我們直接使用template函式即可,同樣作為慣例,也習慣將Release和Chart名稱之類作為Label標籤,所以最終,這些資源清單的Meta資訊如下所示,如果有其他額外的資訊當然可以隨意新增:
metadata:
name:
{{
template"wordpress.fullname".
}}
labels:
app:"{{ template "wordpress.fullname" . }}"
chart:"{{ template "wordpress.chart" . }}"
release:
{{
.Release.Name|quote
}}
heritage:
{{
.Release.Service|quote
}}
然後當然也需要將Deployment的matchLabels和template下面的Label標籤進行修改,以及Service的selector匹配的標籤。
同樣還有PVC物件,以前我們直接使用的一個確定名稱的PVC物件:
volumes:
-name:wordpress-data
persistentVolumeClaim:
claimName:wordpress-pvc
但是現在我們這裡作為模板就要考慮到各種情況了,很有可能使用我們模板的使用者根本就不需要持久化,也有可能直接傳遞一個存在的PVC物件進來使用,所以這裡做了如下改變:
volumes:
-name:wordpress-data
{{
-if.Values.persistence.enabled
}}
persistentVolumeClaim:
claimName:
{{
.Values.persistence.existingClaim|default(include"wordpress.fullname".)
}}
{{
-else
}}
emptyDir:
{}
{{
end
}}
當我們定義了Values值persistence.enabled=true為true時候就表示要使用持久化了,所以要指定下面的claimName屬性,但是還需要判斷persistence.existingClaim這個Values值是否存在,如果存在則表示直接使用,如果不存在則使用我們模板裡面渲染的PVC物件,如果不需要持久化,則直接使用emptyDir:{}即可。最後我們的PVC物件模板變成了如下所示:
{{
-ifand.Values.persistence.enabled(not.Values.persistence.existingClaim)
}}
kind:PersistentVolumeClaim
apiVersion:v1
metadata:
name:
{{
template"wordpress.fullname".
}}
labels:
app:"{{ template "wordpress.fullname" . }}"
chart:"{{ template "wordpress.chart" . }}"
release:
{{
.Release.Name|quote
}}
heritage:
{{
.Release.Service|quote
}}
spec:
{{
-if.Values.persistence.storageClass
}}
storageClassName:
{{
.Values.persistence.storageClass|quote
}}
{{
-end
}}
accessModes:
-
{{
.Values.persistence.accessMode|quote
}}
resources:
requests:
storage:
{{
.Values.persistence.size|quote
}}
{{
-end-
}}
其中訪問模式、儲存容量、StorageClass、存在的PVC都透過Values來指定,增加了靈活性。對應的values.yaml配置部分我們可以給一個預設的配置:
## 是否使用 PVC 開啟資料持久化
persistence:
enabled:true
## 是否使用 storageClass,如果不適用則補配置
# storageClass: "xxx"
##
## 如果想使用一個存在的 PVC 物件,則直接傳遞給下面的 existingClaim 變數
# existingClaim: your-claim
accessMode:ReadWriteMany# 訪問模式
size:2Gi# 儲存容量
現在我們去重新做一次DEBUG,可以看到正常了,但是還遠遠不夠,接下來我們就來定製其他部分。
定製¶
比如副本數我們可以透過Values來指定,變成replicas:
{{
.Values.replicaCount
}}
。
更新策略也可以,因為更新策略並不是一層不變的,這裡和之前不太一樣,我們需要用到一個新的函式toYaml:
{{
-if.Values.updateStrategy
}}
strategy:
{{
toYaml.Values.updateStrategy|nindent4
}}
{{
-end
}}
意思就是我們將updateStrategy這個Values值轉換成YAML格式,並保留4個空格。然後新增其他的配置,比如是否需要新增nodeSelector、容忍、親和性這些,這裡我們都是使用toYaml函式來控制空格,如下所示:
{{
-if.Values.nodeSelector
}}
nodeSelector:
{{
toYaml.Values.nodeSelector|indent8
}}
{{
-end-
}}
{{
-with.Values.affinity
}}
affinity:
{{
toYaml.|indent8
}}
{{
-end
}}
{{
-with.Values.tolerations
}}
tolerations:
{{
toYaml.|indent8
}}
{{
-end
}}
接下來當然就是映象的配置了,如果是私有倉庫還需要指定imagePullSecrets:
{{
-if.Values.image.pullSecrets
}}
imagePullSecrets:
{{
-range.Values.image.pullSecrets
}}
-name:
{{
.
}}
{{
-end
}}
{{
-end
}}
containers:
-name:wordpress
image:
{{
printf"%s:%s".Values.image.name.Values.image.tag
}}
imagePullPolicy:
{{
.Values.image.pullPolicy|quote
}}
ports:
-containerPort:80
name:web
對應的Values值如下所示:
image:
name:wordpress
tag:5.3.2-apache
## 指定 imagePullPolicy 預設為 Always
pullPolicy:IfNotPresent
## 如果是私有倉庫,需要指定 imagePullSecrets
# pullSecrets:
# - myRegistryKeySecretName
然後就是最重要的環境變數配置部分了,因為涉及到資料庫的配置,同樣最核心的三個環境變數配置WORDPRESS_DB_HOST、WORDPRESS_DB_USER、WORDPRESS_DB_PASSWORD,由於可能使用我們模板的使用者可能使用外部資料庫,也有可能使用我們依賴的mysql這個子Chart,所以我們需要分別判斷來進行渲染:
env:
-name:WORDPRESS_DB_HOST
{{
-if.Values.mysql.enabled
}}
value:
{{
printf"%s:%d"(include"mysql.fullname".)(int64.Values.mysql.service.port)
}}
{{
-else
}}
value:
{{
.Values.externalDatabase.host|quote
}}
{{
-end
}}
-name:WORDPRESS_DATABASE_NAME
{{
-if.Values.mysql.enabled
}}
value:
{{
.Values.mysql.mysqlDatabase|quote
}}
{{
-else
}}
value:
{{
.Values.externalDatabase.database|quote
}}
{{
-end
}}
-name:WORDPRESS_DB_USER
{{
-if.Values.mysql.enabled
}}
value:
{{
.Values.mysql.mysqlUser|quote
}}
{{
-else
}}
value:
{{
.Values.externalDatabase.user|quote
}}
{{
-end
}}
-name:WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
{{
-if.Values.mysql.enabled
}}
name:
{{
template"mysql.fullname".
}}
key:mysql-password
{{
-else
}}
name:
{{
printf"%s-%s".Release.Name"externaldb"
}}
key:db-password
{{
-end
}}
每一個環境變數首先都是判斷mysql.enabled是否為true,才表示使用子Chart來渲染,而對應的值都是子Chart中渲染過後的值,所以也需要我們去了解子Chart的渲染行為,如果使用外部的資料庫就要簡單很多,因為只需要讀取Values值即可。另外一個值得注意的是如果是配置的密碼,我們還需要去建立一個Secret資源物件來引用。所以我們在templates目錄下面建立了一個externaldb-secrets.yaml的資原始檔,裡面配置的密碼透過b64enc函式轉換為Base64編碼格式。
{{
-ifnot.Values.mysql.enabled
}}
apiVersion:v1
kind:Secret
metadata:
name:
{{
printf"%s-%s".Release.Name"externaldb"
}}
labels:
app:
{{
printf"%s-%s".Release.Name"externaldb"
}}
chart:"{{ template "wordpress.chart" . }}"
release:
{{
.Release.Name|quote
}}
heritage:
{{
.Release.Service|quote
}}
type:Opaque
data:
db-password:
{{
.Values.externalDatabase.password|b64enc|quote
}}
{{
-end
}}
然後就是resource資源宣告,這裡我們定義一個預設的resources值,同樣用toYaml函式來控制空格:
resources:
{{
toYaml.Values.resources|indent10
}}
最後是健康檢查部分,雖然我們之前沒有做livenessProbe,但是我們開發Chart模板的時候就要儘可能考慮周全一點,這裡我們加上存活性和可讀性兩個探針,並且根據livenessProbe.enabled和readinessProbe.enabled兩個Values值來判斷是否需要新增探針,探針對應的引數也都透過Values值來配置:
{{
-if.Values.livenessProbe.enabled
}}
livenessProbe:
initialDelaySeconds:
{{
.Values.livenessProbe.initialDelaySeconds
}}
periodSeconds:
{{
.Values.livenessProbe.periodSeconds
}}
timeoutSeconds:
{{
.Values.livenessProbe.timeoutSeconds
}}
successThreshold:
{{
.Values.livenessProbe.successThreshold
}}
failureThreshold:
{{
.Values.livenessProbe.failureThreshold
}}
httpGet:
path:/wp-login.php
port:80
{{
-end
}}
{{
-if.Values.readinessProbe.enabled
}}
readinessProbe:
initialDelaySeconds:
{{
.Values.readinessProbe.initialDelaySeconds
}}
periodSeconds:
{{
.Values.readinessProbe.periodSeconds
}}
timeoutSeconds:
{{
.Values.readinessProbe.timeoutSeconds
}}
successThreshold:
{{
.Values.readinessProbe.successThreshold
}}
failureThreshold:
{{
.Values.readinessProbe.failureThreshold
}}
httpGet:
path:/wp-login.php
port:80
{{
-end
}}
這樣我們的Deployment的模板就基本上完成了,我們可以透過DEBUG模式來模擬渲染:
$helminstall--generate-name--dry-run--debug.
可以根據結果來判斷是否符合我們的需求。
最後我們還需要來對Service物件做模板化,因為前面我們是預設的NodePort型別,我們需要透過Values來定製:
apiVersion:v1
kind:Service
metadata:
name:
{{
template"wordpress.fullname".
}}
labels:
app:"{{ template "wordpress.fullname" . }}"
chart:"{{ template "wordpress.chart" . }}"
release:
{{
.Release.Name|quote
}}
heritage:
{{
.Release.Service|quote
}}
spec:
selector:
app:"{{ template "wordpress.fullname" . }}"
type:
{{
.Values.service.type
}}
{{
-if(or(eq.Values.service.type"LoadBalancer")(eq.Values.service.type"NodePort"))
}}
externalTrafficPolicy:
{{
.Values.service.externalTrafficPolicy|quote
}}
{{
-end
}}
ports:
-name:web
port:
{{
.Values.service.port
}}
targetPort:web
{{
-if(and(eq.Values.service.type"NodePort")(not(empty.Values.service.nodePort)))
}}
nodePort:
{{
.Values.service.nodePort
}}
{{
-end
}}
這樣我們就可以透過配置Values值來配置Service物件了。最後就是Ingress/IngressRoute物件了,大家可以自己嘗試講這部分補齊。
最後在templates目錄下面加上NOTES.txt檔案來說明如何使用我們的Chart包就可以了:
Get the WordPress Manifests Objects:
$kubectlgetall-lapp={{.Release.Name}}
最後我們來真正的使用我們的Chart包安裝一次來測試下:
$helminstallmychart.--setservice.type=NodePort
NAME:mychart
LAST DEPLOYED:SunMar817:52:042020
NAMESPACE:default
STATUS:deployed
REVISION:1
NOTES:
Get the WordPress Manifests Objects:
$kubectlgetall-lapp=mychart
$helmls
NAMENAMESPACEREVISIONUPDATEDSTATUSCHARTAPPVERSION
mychartdefault12020-03-08 17:52:04.826185+0800CSTdeployedwordpress-0.1.05.3.2
安裝完成後可以檢視我們的資源物件:
$kubectlgetall-lapp=mychart-wordpress
NAMEREADYSTATUSRESTARTSAGE
pod/mychart-wordpress-5f65786d89-2m45s1/1Running070s
NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE
service/mychart-wordpressNodePort10.99.239.29<none>80:30427/TCP70s
NAMEREADYUP-TO-DATEAVAILABLEAGE
deployment.apps/mychart-wordpress1/11170s
NAMEDESIREDCURRENTREADYAGE
replicaset.apps/mychart-wordpress-5f65786d8911170s
這個時候我們透過上面的NodePort就可以去訪問到我們的應用了,當然還有很多配置我們都是可以直接透過Values去進行定製的。

馬哥教育
三月IT充電計劃

