Chart 文件结构
一个 Chart 包由以下几个配置文件组成:
wordpress/Chart.yaml # Yaml文件,用于描述 Chart 的基本信息,包括名称版本等LICENSE # [可选] 文本格式的协议README.md # [可选] 应用介绍、使用说明requirements.yaml # [可选] 用于存放当前 Chart 依赖的其它 Chart 的说明文件values.yaml # Chart 的默认值配置文件charts/ # [可选] 该目录中放置当前 Chart 依赖的其它 Charttemplates/ # [可选] 部署文件模版目录,模版填入 values.yaml 中相应值,生成最终的
kubernetes 配置文件├── NOTES.txt [可选] 使用指南├── _helpers.tpl 下划线开头的文件,可以被其他模板引用├── deployment.yaml 创建k8s资源deployment的yaml文件├── ingress.yaml 创建k8s资源ingress的yaml文件└── service.yaml 创建k8s资源service的yaml文件├── .....Chart.yaml 文件apiVersion: [必须] Chart API 版本,可用值 v1
name: [必须] Chart 名称
version: [必须] 版本,遵循 [SemVer 2 标准](https://semver.org/)
kubeVersion: [可选] 兼容的 Kubernetes 版本,遵循 [SemVer 2 标准](https://semver.org/)
description: [可选] 一句话的应用描述
keywords:- [可选] 应用关键字列表
home: [可选] 应用主页 URL
sources:- [可选] 当前应用下载地址列表
maintainers: [可选]- name: [必须] name email: [可选] emailurl: [可选] url
engine: [可选] 模板引擎,默认值是 gotpl
icon: [可选] SVG 或者 PNG 格式的图片地址
appVersion: [可选] 应用版本
deprecated: [可选] boolean 类型,是否不建议使用
tillerVersion: [可选] Chart 需要的 Tiller 版本,遵循 [SemVer 2 标准](https://semver.org/),需要 ">2.0.0"Requirements.yaml 文件和 Charts 目录Chart支持两种方式表示依赖关系,可以写入 requirements.yaml 文件动态链接[推荐],也可以将依赖的 Chart 放入 charts 目录手动管理。requirements.yaml 文件样例:dependencies:- name: apacheversion: 1.2.3repository: http://example.com/charts- name: mysqlversion: 3.2.1repository: http://another.example.com/chartsname:Chart 名称version:Chart 版本repository: Chart 仓库 URL 地址有了 requirements.yaml 文件,可以运行 helm dependency update,依赖的 Chart 会被自动的下载到 charts 目录下。
Values.yaml 文件和 Templates 目录values.yaml 文件中记录了模板中引用的默认值。 templates 目录中存放了 Kubernetes 部署文件的模版,遵循 Go template 语法templates 中模板文件样例:apiVersion: v1
kind: ReplicationController
metadata:name: deis-databasenamespace: deislabels:app.kubernetes.io/managed-by: deis
spec:replicas: 1selector:app.kubernetes.io/name: deis-databasetemplate:metadata:labels:app.kubernetes.io/name: deis-databasespec:serviceAccount: deis-databasecontainers:- name: deis-databaseimage: {
{.Values.imageRegistry}}/postgres:{
{.Values.dockerTag}}imagePullPolicy: {
{.Values.pullPolicy}}ports:- containerPort: 5432env:- name: DATABASE_STORAGEvalue: {
{default "minio" .Values.storage}}上述样例是一个 Kubernetes 中 replication controller 的模板文件定义,其中引用了以下几个值(一般定义在 values.yaml 中)imageRegistry:Docker 映像仓库dockerTag: Docker 映像标签pullPolicy: 下载映像策略storage: 存储后端,默认值是 "minio"values.yaml 文件样例:imageRegistry: "quay.io/deis"
dockerTag: "latest"
pullPolicy: "Always"
storage: "s3"
开始
1 简单示例
1.1 生成案例结构 mycahrt
$ helm create mychart
Creating mychart
1.2 helm 客户端会自动的为我们创建些文件:
mychart
├── Chart.yaml
├── charts
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yaml
└── values.yaml
2 第一个模板
2.1 删除所有文件,我们自己手动,有助于深入理解
rm -rf mychart/templates/*.*
删除掉 Helm 生成的模板文件,我们自己来实现一个模板。
我们的第一个模板是创建一个 ConfigMap。创建一个 mychart/templates/configmap.yaml
文件,并写入如下内容:
apiVersion: v1
kind: ConfigMap
metadata:name: mychart-configmap
data:myvalue: "Hello World"
注意:Template 目录下的文件没有严格的命名规范。但我们推荐用 .yaml
的后缀表示 YAML 文件,用 .tpl
表示帮助文件。
现在我们来部署它:
$ helm install wubo ./mychart
NAME: full-coral
LAST DEPLOYED: Tue Nov 09 17:36:01 2020
NAMESPACE: default
STATUS: DEPLOYEDRESOURCES:
==> v1/ConfigMap
NAME DATA AGE
mychart-configmap 1 1m
我们可以来查看下部署信息
$ helm get manifest wubo
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: mychart-configmap
data:myvalue: "Hello World"
helm get manifest
这条命令可以通过 release 名来打印其相关信息。release的名字此时就是wubo
现在,删除掉刚刚发布的 release:helm delete wubo
3 赋值示例
3.1内置对象
- Release:用来描述 release 本身
- Release.Name
- Release.Time
- Release.Namespace
- Release.Service:值总是
Tiller 注:在
Helm 2 中,Tiller 是作为一个 Deployment 部署在 kube-system 命名空间中,很多情况下,我们会为 Tiller 准备一个 ServiceAccount ,这个 ServiceAccount 通常拥有集群的所有权限。 用户可以使用本地 Helm 命令,自由地连接到 Tiller 中并通过 Tiller 创建、修改、删除任意命名空间下的任意资源。 在 Helm 3 中,Tiller 被移除了。新的 Helm 客户端会像 kubectl 命令一样,读取本地的 kubeconfig 文件,使用我们在 kubeconfig 中预先定义好的权限来进行一系列操作。 - Release.Revision:release 版本号。从 1 开始,每次执行
helm upgrade
,数加 1 - Release.IsUpgrade:本次操作是否为升级
- Release.IsInstall:本次操作是否为安装
- Values:
- Chart:
Chart.yaml
里的内容 - Files:
File.Get
通过名字获取文件(.Files.Get config.ini
)File.getBytes
以字节流的方式获取,在获取类型图片时比较有用
- Capabilities:
- Template:
3.2 Values Files
前面讲了内置对像 Values
,它的值有四个来源:
values.yaml
文件- 如果这是个子 chart,其父 chart 的
Values.yaml
文件 - 在
helm install
或helm upgrade
时,通过-f
指定的文件 - 通过
--set
指定的参数( 例:helm install --set foo=bar ./mychart )
优先级从上到下依次增加,即 --set
最高。
现在让我们来编辑 mychart/values.yaml
,删除默认值,只写一个参数:
cat values.yamlfavoriteDrink:coffee
在模板中使用刚刚写的参数:
apiVersion: v1
kind: ConfigMap
metadata:name: {
{ .Release.Name }}-configmap
data:myvalue: "Hello World"drink: {
{ .Values.favoriteDrink}}
注意最后一行,我们通过访问 Values 属性的方式 {
{ .Values.favoriteDrink}}
来获取 favoriteDrink 值。
来看下渲染的结果:
$ helm install wubo --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
NAME: wubo
TARGET NAMESPACE: default
CHART: mychart 0.1.0
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: geared-marsupi-configmap
data:myvalue: "Hello World"drink: coffee
还可以通过 --set
覆盖掉这个值:
helm install wubo --dry-run --debug --set favoriteDrink=slurm ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
NAME: wubo
TARGET NAMESPACE: default
CHART: mychart 0.1.0
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: solid-vulture-configmap
data:myvalue: "Hello World"drink: slurm
由于 --set
的优先级高于 values.yaml
,所有我们的模板最终输出为 drink: slurm
。
Values 文件还可以包含结构内容。
favorite:drink: coffeefood: pizza
现在我们需要修改下模板:
apiVersion: v1
kind: ConfigMap
metadata:name: {
{ .Release.Name }}-configmap
data:myvalue: "Hello World"drink: {
{ .Values.favorite.drink }}food: {
{ .Values.favorite.food }}
看下效果2中方式
1 此种方式可以拿到Release相关的内置属性值
root@localhost:~/go/test1/mychart # helm install wubo -f values.yaml --dry-run --debug .
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/go/test1/mychartNAME: wubo
LAST DEPLOYED: Wed Sep 23 17:07:52 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
favorite:drink: coffeefood: pizzaCOMPUTED VALUES:
favorite:drink: coffeefood: pizzaHOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: wubo-configmap
data:myvalue: "Hello World"drink: coffeefood: pizza
2 此种方式只是根据values和temp模板生成最终可供k8s的kubectl create -f 执行的一个yaml文件
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: RELEASE-NAME-configmap
data:myvalue: "Hello World"drink: coffeefood: pizza
3.3. 删除默认Key
如果你想从默认值中删除一个key,你可以通过传 null
值,这样 Helm 在合并时就会删除这个 key。
举个例子, 名为 Drupal 的 chart 中配置的有存活检测。下面是它的默认设置:
livenessProbe:httpGet:path: /user/loginport: httpinitialDelaySeconds: 120
如果你要使用 exec 替换 httpGet,可以通过 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt],Helm 会合并默认值和传进去的值,结果如下:
livenessProbe:httpGet:path: /user/loginport: httpexec:command:- cat- docroot/CHANGELOG.txtinitialDelaySeconds: 120
这时,k8s 就会出错,因为你定义了两个 liveness handler
。要解决这个问题,你可以通过给 livenessProbe.httpGet
传个 null
值来删除它:
helm install stable/drupal --set image=my-registry/drupal:0.1.0 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --set livenessProbe.httpGet=null
4 模板函数和Pipelines(管道)
让我们从一个练习开始:当我们往 .Values
里注入一个字符串时,应当用引号将它们括起来,在模板中可以直接使用 quote
函数来实现:
apiVersion: v1
kind: ConfigMap
metadata:name: {
{ .Release.Name }}-configmap
data:myvalue: "Hello World"drink: {
{ quote .Values.favorite.drink }}food: {
{ quote .Values.favorite.food }}
模板函数的语法如下:functionName arg1 arg2...
。在上面的小例子中, quote .Values.favorite.drink
, 使用了 quote
函数并传递了一个参数。
4.1. Pipelines(管道)
通过管道可以在一行里干多件事,我们使用 pipeline 重写上面的例子:
apiVersion: v1
kind: ConfigMap
metadata:name: {
{ .Release.Name }}-configmap
data:myvalue: "Hello World"drink: {
{ .Values.favorite.drink | quote }}food: {
{ .Values.favorite.food | quote }}
通过 pipeline(管道),我们可以链式的调用多个函数:
apiVersion: v1
kind: ConfigMap
metadata:name: {
{ .Release.Name }}-configmap
data:myvalue: "Hello World"drink: {
{ .Values.favorite.drink | quote }}food: {
{ .Values.favorite.food | upper | quote }}
4.2 使用 Default 函数
这个函数允许你指定一个默认值:
drink: {
{ .Values.favorite.drink | default "tea" | quote }}
4.3. OPERATORS ARE FUNCTIONS
操作符是按照函数的方式实现的,返回一个布尔值。使用 eq, ne, lt, gt, and, or, not
时,要将它们放到句子的最前面,后面跟上对应的参数。多个操作符一起使用时,可以用小括号包起来。
{
{/* include the body of this if statement when the variable .Values.fooString exists and is set to "foo" */}}
{
{ if and .Values.fooString (eq .Values.fooString "foo") }}{
{ ... }}
{
{ end }}意思就是Values.fooString不能为空且Values.fooString的值等于foo{
{/* do not include the body of this if statement because unset variables evaluate to false and .Values.setVariable was negated with the not function. */}}
{
{ if or .Values.anUnsetVariable (not .Values.aSetVariable) }}{
{ ... }}
{
{ end }}
4.4控制流
Helm 的模板语言提供下面几种控制结构:
if / else
with
range
提供类型for each
的循环
另外,还提供了几种方式来声明和使用命名模板:
define
template
block
这节只讨论 if
, with
, 和 range
。其它的会在之后的 “Named Templates” 那节介绍。
4.4.1 . IF / ELSE
基本结构如下
{
{ if PIPELINE }}# Do something
{
{ else if OTHER PIPELINE }}# Do something else
{
{ else }}# Default case
{
{ end }}
注意这里我们用 pipelines
面不是 values
,是为了表明这个控制结构是可以运行完全的 pipeline
的,而不仅仅只能放个值。
下面情况其值会被认为是 false:
- bool 型的 false
- 数字 0
- 空字符串
- nil
- 空集合(
map, slice, tuple, dict, array
)
让我们来修改下 ConfigMap
。当 drink
是 coffee
时,增加一个设置:
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 }}注意mug: true前后不能有空格
注意, .Values.favorite.drink
最好是判断下是否为空,否则当它和 coffee
作比较时会报错。最后的输出就变成:
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: RELEASE-NAME-configmap
data:myvalue: "Hello World"drink: "coffee"food: "PIZZA"mug: tru
或
root@localhost:~/go/test1/mychart # helm install wubo -f values.yaml --dry-run --debug . 1 ?
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/go/test1/mychartNAME: wubo
LAST DEPLOYED: Wed Sep 23 17:18:12 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
favorite:drink: coffeefood: pizzaCOMPUTED VALUES:
favorite:drink: coffeefood: pizzaHOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: wubo-configmap
data:myvalue: "Hello World"drink: "coffee"food: "PIZZA"mug: true
4.4.2. 空白管理
空白的使用在模板中是受限制的。下面把之前的代码格式修改下,使它更易阅读:
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}}
它看起来不错,但当真正运行时,会报错:
$ 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
这就是因为空格导致的:
这是翻译之后的最终数据 发现最后一样有问题 多2个空格
# 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
的嵌套位置不正确。让我们简单的修改下:
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}}
再次运行,发现生成的 YAML 格式正确了,但是有点丑:
root@localhost:~/go/test1/mychart # helm install wubo -f values.yaml --dry-run --debug . 20 ?
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/go/test1/mychartNAME: wubo
LAST DEPLOYED: Wed Sep 23 17:20:22 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
favorite:drink: coffeefood: pizzaCOMPUTED VALUES:
favorite:drink: coffeefood: pizzaHOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: wubo-configmap
data:myvalue: "Hello World"drink: "coffee"food: "PIZZA"mug: trueroot@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: RELEASE-NAME-configmap
data:myvalue: "Hello World"drink: "coffee"food: "PIZZA"mug: true
注意,YAML 中有行是空的。
{
{
- 表示去掉左边或前的空格 }}
{
{
表示去掉右边或后的空格 -}}
{
{
- 表示去掉左边或前和右边或后的空格 -}}
好好理解一下 案例如下
root@localhost:~/go/test1/mychart # cat templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: {
{ .Release.Name }}-configmapnamespace: {
{ .Release.Namespace }}time: {
{ .Release.Time }}
data:myvalue: "Hello World"{
{ with .Values.favorite }}{
{ if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }}mug: true{
{ end }}name: wubo{
{ end }}age: 12{
{ if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}mug: true{
{ end }}drink: {
{ .Values.favorite.drink | default "tea" | quote }}food: {
{ .Values.favorite.food | upper | quote }}
执行效果
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml 20 ?
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: RELEASE-NAME-configmapnamespace: defaulttime:
data:myvalue: "Hello World"mug: truename: wuboage: 12mug: truedrink: "coffee"food: "PIZZA"
改成
apiVersion: v1
kind: ConfigMap
metadata:name: {
{ .Release.Name }}-configmapnamespace: {
{ .Release.Namespace }}time: {
{ .Release.Time }}
data:myvalue: "Hello World"{
{- with .Values.favorite }}{
{- if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }}mug: true{
{- end }}name: wubo{
{- end }}age: 12{
{- if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}mug: true{
{- end }}drink: {
{ .Values.favorite.drink | default "tea" | quote }}food: {
{ .Values.favorite.food | upper | quote }
效果
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml 20 ?
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: RELEASE-NAME-configmapnamespace: defaulttime:
data:myvalue: "Hello World"mug: truename: wuboage: 12mug: truedrink: "coffee"food: "PIZZA"
分析内容如下:
myvalue: "Hello World"
{
{- with .Values.favorite }}
{
{- if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }}
mug: true
{
{- end }}
name: wubo
{
{- end }}
age: 12
{
{- if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}
mug: true
{
{- end }}
drink: {
{ .Values.favorite.drink | default "tea" | quote }}
food: {
{ .Values.favorite.food | upper | quote }}
解释:
myvalue: "Hello World"和 mug: true之间会有2个空行,因为有两个流程操作{ { with }} 和 { { if }}
mug: true和 name: wubo之间有一个空行,因为有一个操作{ { end }}
name: wubo和 age: 12之间也有一个操作同上
age: 12和mug: true之间也有一个同上
mug: true和drink: { { .Values.favorite.drink | default "tea" | quote }}之间也有一个
drink: { { .Values.favorite.drink | default "tea" | quote }}和 food: { { .Values.favorite.food | upper | quote }}之间没有
然后就是怎么去除空行
myvalue: "Hello World"
{
{- with .Values.favorite }} "-" 写在前代表的是删除上面或左边的所有空行,但是右边或后面还有一个空行没有处理呢
{
{- if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }} 同理如上
mug: true
上面这个还可以这样写
myvalue: "Hello World"
{
{- with .Values.favorite -}} "-" 写在前和后代表的是删除上面或左边和后面或右边的空行,就是把前后所有的空行都删除了,注意是所有的空行,不是一个空行
{
{ if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }} 此时{
{- if }} 也是可以的,因为他的前面或左边确实有空行。{
{- with -}} 共同删除空行没毛病。但是{
{ if -}} 后面加个”-" 就会报错了,因为后面就是 mug: true没有空行了。
mug: true
4.5. 修改作用域
通过 .
可以引用当前的作用域。 .Values
告诉模板在当前作用域上查找 Values
。
可以通过 with
来调整作用域
{
{ with PIPELINE }}# restricted scope
{
{ end }}
with 允许你把当前作用域(.)指到一个特定的对象上。举个例子,将 . 指到 .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 }}drink: {
{ .drink | default "tea" | quote }}food: {
{ .food | upper | quote }}wubo: {
{ .Values.favorit.food }} 此处获取不到release: {
{ .Release.Name }} 也获取不到
{
{- end }}
此时有两中解决办法,定义变量和全局获取
1种解决办法.由于下方的with语句引入相对命令空间,无法通过.Release引入,提前定义relname变量
2种解决办法.者可以使用$符号,引入全局命名空间
{
{- $relname := .Values.favorit.food }}
{
{- with .Values.favorite }}drink: {
{ .drink | default "tea" | quote }}food: {
{ .food | upper | quote }}wubo: {
{ $relname }} # 或者可以使用$符号,引入全局命名空间wubo: {
{$.Values.favorit.food}}release: {
{ $.Release.Name }}
{
{- end }}
4.6. range 操作
Helm 可以通过 range
操作符来迭代集合。
在 values.yaml
中增加列表: