helm 相似于Linux系统下的包管理器,如yum/apt等,能够方便快捷的将以前打包好的yaml文件快速部署进kubernetes内,方便管理维护。html
简单来讲:node
Tiller 在处理 Chart 时,直接将 Chart 以及其依赖的全部 Charts 合并为一个 Release,同时传递给 Kubernetes。所以 Tiller 并不负责管理依赖之间的启动顺序。Chart 中的应用须要可以自行处理依赖关系。mysql
# 在helm客户端主机上,通常为master主机 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 复制代码
初始化tiller会自动读取~/.kube
目录,因此须要确保config文件存在并认证成功linux
tiller配置rbac,新建rabc-config.yaml并应用git
# 在: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 # 须要注意点参数 –client-only:也就是不安装服务端应用,这在 CI&CD 中可能须要,由于一般你已经在 k8s 集群中安装好应用了,这时只需初始化 helm 客户端便可; –history-max:最大历史,当你用 helm 安装应用的时候,helm 会在所在的 namespace 中建立一份安装记录,随着更新次数增长,这份记录会愈来愈多; –tiller-namespace:默认是 kube-system,你也能够设置为其它 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可能运行在node节点,将tiller镜像下载到node节点并修改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"}
复制代码
因为国外不少镜像网站国内没法访问,例如gcr.io ,建议使用阿里源,developer.aliyun.com/hub。github
AppHub 是一个托管在国内公有云上、全公益性的 Helm Hub “中国站”,它的后端由阿里云容器平台团队的三位工程师利用 20% 时间开发完成。redis
而这个站点的一个重要职责,就是把全部 Helm 官方 Hub 托管的应用自动同步到国内;同时,自动将 Charts 文件中的 gcr.io 等全部有网络访问问题的 URL 替换成为稳定的国内镜像 URL。sql
目前helm3已经不依赖于tiller,Release 名称可在不一样 ns 间重用。docker
Helm3 不须要安装tiller,下载到 Helm 二进制文件直接解压到 $PATH 下就可使用了。shell
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"}
复制代码
helm repo add apphub https://apphub.aliyuncs.com
helm search guestbook
helm install guestbook apphub/guestbook
复制代码
http://hub.kubeapps.com/
completion # 为指定的shell生成自动完成脚本(bash或zsh)
create # 建立一个具备给定名称的新 chart
delete # 从 Kubernetes 删除指定名称的 release
dependency # 管理 chart 的依赖关系
fetch # 从存储库下载 chart 并(可选)将其解压缩到本地目录中
get # 下载一个命名 release
help # 列出全部帮助信息
history # 获取 release 历史
home # 显示 HELM_HOME 的位置
init # 在客户端和服务器上初始化Helm
inspect # 检查 chart 详细信息
install # 安装 chart 存档
lint # 对 chart 进行语法检查
list # releases 列表
package # 将 chart 目录打包成 chart 档案
plugin # 添加列表或删除 helm 插件
repo # 添加列表删除更新和索引 chart 存储库
reset # 从集群中卸载 Tiller
rollback # 将版本回滚到之前的版本
search # 在 chart 存储库中搜索关键字
serve # 启动本地http网络服务器
status # 显示指定 release 的状态
template # 本地渲染模板
test # 测试一个 release
upgrade # 升级一个 release
verify # 验证给定路径上的 chart 是否已签名且有效
version # 打印客户端/服务器版本信息
dep # 分析 Chart 并下载依赖
复制代码
helm install --name els1 -f values.yaml stable/elasticsearch
复制代码
helm upgrade --set mysqlRootPassword=passwd db-mysql stable/mysql
helm upgrade go2cloud-api-doc go2cloud-api-doc/
复制代码
helm rollback db-mysql 1
复制代码
helm delete --purge db-mysql
复制代码
helm install/upgrade xxx --dry-run --debug
复制代码
myapp/ # Chart 目录
├── charts # 这个 charts 依赖的其余 charts,始终被安装
├── Chart.yaml # 描述这个 Chart 的相关信息、包括名字、描述信息、版本等
├── templates # 模板目录
│ ├── deployment.yaml # deployment 控制器的 Go 模板文件
│ ├── _helpers.tpl # 以 _ 开头的文件不会部署到 k8s 上,可用于定制通用信息
│ ├── ingress.yaml # ingress 的模板文件
│ ├── NOTES.txt # Chart 部署到集群后的一些信息,例如:如何使用、列出缺省值
│ ├── service.yaml # service 的 Go 模板文件
│ └── tests
│ └── test-connection.yaml
└── values.yaml # 模板的值文件,这些值会在安装时应用到 GO 模板生成部署文件
复制代码
[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 # 部署相关资源
│ ├── _helpers.tpl # 模版助手
│ ├── ingress.yaml # ingress资源
│ ├── NOTES.txt # chart的帮助文本,运行helm install展现给用户
│ ├── service.yaml # service端点
│ └── tests
│ └── test-connection.yaml
└── values.yaml
3 directories, 8 files
复制代码
rm -rf mychart/templates/* # 咱们首先建立一个名为 mychart/templates/configmap.yaml: apiVersion: v1 kind: ConfigMap metadata: name: mychart-configmap data: myvalue: "Hello World" 复制代码
因为建立的yaml文件在template下,tiller读取此文件,会将其发送给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)并打印出上传到服务器的全部 Kubernetes 资源。每一个文件都以 ---
开始做为 YAML 文档的开始,而后是一个自动生成的注释行,告诉咱们该模板文件生成的这个 YAML 文档。
从那里开始,咱们能够看到 YAML 数据正是咱们在咱们的 configmap.yaml
文件中所设计的 。
如今咱们能够删除咱们的 release:helm delete enervated-dolphin
。
[root@master mychart]# helm delete enervated-dolphin
release "enervated-dolphin" deleted
复制代码
硬编码 name:
成资源一般被认为是很差的作法。名称应该是惟一的一个版本。因此咱们可能但愿经过插入 release 名称来生成一个名称字段。
提示: name: 因为 DNS 系统的限制,该字段限制为 63 个字符。所以,release 名称限制为 53 个字符。Kubernetes 1.3 及更早版本仅限于 24 个字符(即 14 个字符名称)。
修改下以前的configmap为以下内容
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
data:
myvalue: "Hello World"
复制代码
name:
如今这个值发生了变化成了 {{.Release.Name}}-configmap
。
模板指令包含在 {{
和 }}
块中。
模板指令 {{.Release.Name}}
将 release 名称注入模板。传递给模板的值能够认为是 namespace 对象,其中 dot(.)分隔每一个 namespace 元素。
Release 前面的前一个小圆点表示咱们从这个范围的最上面的 namespace 开始(咱们将稍微谈一下 scope)。因此咱们能够这样理解 .Release.Name:
"从顶层命名空间开始,找到 Release 对象,而后在里面查找名为 Name
的对象"。
该 Release 对象是 Helm 的内置对象之一,稍后咱们将更深刻地介绍它。但就目前而言,这足以说明这会显示 Tiller 分配给咱们发布的 release 名称。
如今,当咱们安装咱们的资源时,咱们会当即看到使用这个模板指令的结果:
[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" 复制代码
咱们看过了基础的模板:YAML 文件嵌入了模板指令,经过 。在下一部分中,咱们将深刻研究模板。但在继续以前,有一个快速技巧可使构建模板更快:当您想测试模板渲染,但实际上没有安装任何东西时,可使用 helm install --debug --dry-run ./mychart
。这会将 chart 发送到 Tiller 服务器,它将渲染模板。但不是安装 chart,它会将渲染模板返回,以即可以看到输出:
对象从模板引擎传递到模板中。你的代码能够传递对象(咱们将在说明 with
和 range
语句时看到示例)。甚至有几种方法在模板中建立新对象,就像咱们稍后会看的 tuple
函数同样。
对象能够很简单,只有一个值。或者他们能够包含其余对象或函数。例如,Release
对象包含多个对象(如 Release.Name
)而且 Files
对象具备一些函数。
在上一节中,咱们使用 {{.Release.Name}}
将 release 的名称插入到模板中。Release
是能够在模板中访问的顶级对象之一。
Release
:这个对象描述了 release 自己。它里面有几个对象:Release.Name
:release 名称Release.Time
:release 的时间Release.Namespace
:release 的 namespace(若是清单未覆盖)Release.Service
:release 服务的名称(始终是 Tiller
)。Release.Revision
:此 release 的修订版本号。它从 1 开始,每 helm upgrade
一次增长一个。Release.IsUpgrade
:若是当前操做是升级或回滚,则将其设置为 true
。Release.IsInstall
:若是当前操做是安装,则设置为 true
。Values
:从 values.yaml
文件和用户提供的文件传入模板的值。默认状况下,Values 是空的。Chart
:Chart.yaml
文件的内容。任何数据 Chart.yaml 将在这里访问。例如 {{.Chart.Name}}-{{.Chart.Version}} 将打印出来 mychart-0.1.0。chart 指南中 Charts Guide 列出了可用字段Files
:这提供对 chart 中全部非特殊文件的访问。虽然没法使用它来访问模板,但可使用它来访问 chart 中的其余文件。请参阅 "访问文件" 部分。Files.Get
是一个按名称获取文件的函数(.Files.Get config.ini
)Files.GetBytes
是将文件内容做为字节数组而不是字符串获取的函数。这对于像图片这样的东西颇有用。Capabilities
:这提供了关于 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
:包含有关正在执行的当前模板的信息Name
:到当前模板的 namespace 文件路径(例如 mychart/templates/mytemplate.yaml
)BasePath
:当前 chart 模板目录的 namespace 路径(例如 mychart/templates)。这些值可用于任何顶级模板。咱们稍后会看到,这并不意味着它们将在任何地方都要有。
内置值始终以大写字母开头。这符合Go的命名约定。当你建立本身的名字时,你能够自由地使用适合你的团队的惯例。一些团队,如Kubernetes chart团队,选择仅使用首字母小写字母来区分本地名称与内置名称。在本指南中,咱们遵循该约定。
在上一节中,咱们看了 Helm 模板提供的内置对象。四个内置对象之一是 Values。该对象提供对传入 chart 的值的访问。其内容来自四个来源:
values.yaml
文件values.yaml
文件helm install -f myvals.yaml ./mychart
)--set
(例如 helm install --set foo=bar ./mychart
)上面的列表按照特定的顺序排列:values.yaml 在默认状况下,父级 chart 的能够覆盖该默认级别,而该 chart values.yaml 又能够被用户提供的 values 文件覆盖,而该文件又能够被 --set 参数覆盖。
值文件是纯 YAML 文件。咱们编辑 mychart/values.yaml
,而后来编辑咱们的 ConfigMap
模板。
删除默认带的 values.yaml,咱们只设置一个参数:
# 编辑values.yaml domain: anchnet.com # 在模版中引用 apiVersion: v1 kind: ConfigMap metadata: name: {{.Release.Name}}-configmap data: myvalue: "Hello World" domain: {{.Values.domain}} 复制代码
注意咱们在最后一行 {{ .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 复制代码
因为 domain
在默认 values.yaml
文件中设置为 anchnet.com
,这就是模板中显示的值。咱们能够轻松地在咱们的 helm install 命令中经过加一个 --set
添标志来覆盖:
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
比默认 values.yaml
文件具备更高的优先级
若是您须要从默认值中删除一个键,能够覆盖该键的值为 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
复制代码
目前为止,咱们已经知道如何将信息放入模板中。可是这些信息未经修改就被放入模板中。有时咱们想要转换这些数据,使得他们对咱们来讲更有用。
让咱们从一个最佳实践开始:当从. 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 函数并将一个参数传递给它。
Helm 拥有超过 60 种可用函数。其中一些是由 Go 模板语言 Go template language 自己定义的。其余大多数都是 Sprig 模板库 Sprig template library 的一部分。在咱们讲解例子进行的过程当中,咱们会看到不少。
虽然咱们将 Helm 模板语言视为 Helm 特有的,但它其实是 Go 模板语言,一些额外函数和各类包装器的组合,以将某些对象暴露给模板。Go 模板上的许多资源在了解模板时可能会有所帮助。
模板语言的强大功能之一是其管道概念。利用 UNIX 的一个概念,管道是一个连接在一块儿的一系列模板命令的工具,以紧凑地表达一系列转换。换句话说,管道是按顺序完成几件事情的有效方式。咱们用管道重写上面的例子。
apiVersion: v1 kind: ConfigMap metadata: name: {{.Release.Name}}-configmap data: myvalue: "Hello World" drink: {{.Values.favorite.drink | quote}} food: {{.Values.favorite.food | quote}} 复制代码
在这个例子中,没有调用 quote ARGUMENT
,咱们调换了顺序。咱们使用管道(|)将 “参数” 发送给函数:.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
更常见。练习也是。
当评估时,该模板将产生以下结果:
# Source: mychart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: trendsetting-p-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" 复制代码
请注意,咱们的原来 pizza
如今已经转换为 "PIZZA"
。
当有像这样管道参数时,第一个评估(.Values.favorite.drink
)的结果将做为函数的最后一个参数发送。咱们能够修改上面的饮料示例来讲明一个带有两个参数的函数 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 函数将回送给定的字符串和给定的次数,因此咱们将获得这个输出:
# Source: mychart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: melting-porcup-configmap data: myvalue: "Hello World" drink: "coffeecoffeecoffeecoffeecoffee" food: "PIZZA" 复制代码
常用的一个函数是 default
:default DEFAULT_VALUE GIVEN_VALUE
。该功能容许在模板内部指定默认值,以防该值被省略。让咱们用它来修改上面的饮料示例:
drink: {{.Values.favorite.drink | default "tea" | quote}} 复制代码
若是咱们像往常同样运行,咱们会获得咱们的 coffee:
# Source: mychart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: virtuous-mink-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" 复制代码
如今,咱们将从如下位置删除喜欢的饮料设置 values.yaml:
favorite: #drink: coffee food: pizza 复制代码
如今从新运行 helm install --dry-run --debug ./mychart
会产生这个 YAML:
# Source: mychart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: fair-worm-configmap data: myvalue: "Hello World" drink: "tea" food: "PIZZA" 复制代码
在实际的 chart 中,全部静态默认值应该存在于 values.yaml 中,不该该使用该 default 命令重复(不然它们将是重复多余的)。可是,default 命令对于计算的值是合适的,由于计算值不能在 values.yaml 中声明。例如:
drink: {{.Values.favorite.drink | default (printf "%s-tea" (include "fullname" .)) }} 复制代码
在一些地方,一个 if
条件可能比这 default
更适合。咱们将在下一节中看到这些。
模板函数和管道是转换信息并将其插入到 YAML 中的强大方法。但有时候须要添加一些比插入字符串更复杂一些的模板逻辑。在下一节中,咱们将看看模板语言提供的控制结构。
对于模板,运算符(eq,ne,lt,gt,and,or 等等)都是已实现的功能。在管道中,运算符能够用圆括号((
和 )
)分组。
将运算符放到声明的前面,后面跟着它的参数,就像使用函数同样。要多个运算符一块儿使用,将每一个函数经过圆括号分隔。
{{/* 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}} 复制代码
如今咱们能够从函数和管道转向流控制,条件,循环和范围修饰符。
控制结构(模板说法中称为 “动做”)为模板做者提供了控制模板生成流程的能力。Helm 的模板语言提供了如下控制结构:
if/else
用于建立条件块with
指定范围range
,它提供了一个 “for each” 风格的循环除此以外,它还提供了一些声明和使用命名模板段的操做:
define
在模板中声明一个新的命名模板template
导入一个命名模板block
声明了一种特殊的可填写模板区域在本节中,咱们将谈论 if
,with
和 range
。其余内容在本指南后面的 “命名模板” 一节中介绍。
咱们要看的第一个控制结构是用于在模板中有条件地包含文本块。这就是 if/else 块。
条件的基本结构以下所示:
{{if PIPELINE}} # Do something {{else if OTHER PIPELINE}} # Do something else {{else}} # Default case {{end}} 复制代码
注意,咱们如今讨论的是管道而不是值。其缘由是要明确控制结构能够执行整个管道,而不只仅是评估一个值。
若是值为以下状况,则管道评估为 false。
nil
(空或 null)map
,slice
,tuple
,dict
,array
)在其余状况下, 条件值为 true 此管道被执行。
咱们为 ConfigMap 添加一个简单的条件。若是饮料被设置为咖啡,咱们将添加另外一个设置:
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” 进行比较时会抛出错误。因为咱们在上一个例子中注释掉了 drink:coffee
,所以输出不该该包含 mug:true
标志。可是若是咱们将该行添加回 values.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 复制代码
在查看条件时,咱们应该快速查看模板中的空格控制方式。让咱们看一下前面的例子,并将其格式化为更容易阅读的格式:
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}} 复制代码
最初,这看起来不错。可是若是咱们经过模板引擎运行它,咱们会获得一个错误的结果:
$ 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 复制代码
发生了什么?因为上面的空格,咱们生成了不正确的 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 不正确地缩进。让咱们简单地缩进那行,而后从新运行:
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,但仍然看起来有点意思:
# Source: mychart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: telling-chimp-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" mug: true 复制代码
请注意,咱们在 YAML 中收到了一些空行。为何?当模板引擎运行时,它将删除 {{
和 }}
中的空白内容,可是按原样保留剩余的空白。
YAML 中的缩进空格是严格的,所以管理空格变得很是重要。幸运的是,Helm 模板有几个工具能够帮助咱们。
首先,可使用特殊字符修改模板声明的大括号语法,以告诉模板引擎填充空白。{{-
(添加了破折号和空格)表示应该将格左移,而 -}}
意味着应该删除右空格。注意!换行符也是空格!
确保
-
和其余指令之间有空格。-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}} {{- 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}}* **{{- if eq .Values.favorite.drink "coffee"}} mug: true* **{{- end}} 复制代码
牢记这一点,咱们能够经过 Helm 运行咱们的模板并查看结果:
# 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 -}} 复制代码
这将会产生 food: "PIZZA"mug:true,由于删除了双方的换行符。
有关模板中空格控制的详细信息,请参阅官方 Go 模板文档 Official Go template documentation
最后,有时候告诉模板系统如何缩进更容易,而不是试图掌握模板指令的间距。所以,有时可能会发现使用 indent
函数({{indent 2 "mug:true"}}
)会颇有用。
下一个要看的控制结构是 with
。它控制着变量做用域。回想一下,.
是对当前范围的引用。所以,.Values
告诉模板在当前范围中查找 Values
对象。
其语法 with 相似于一个简单的 if 语句:
{{with PIPELINE}} # restricted scope {{end}} 复制代码
范围能够改变。with 能够容许将当前范围(.
)设置为特定的对象。例如,咱们一直在使用的 .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}} 复制代码
注意,如今咱们能够引用 .drink
和 .food
无需对其进行限定。这是由于该 with
声明设置 .
为指向 .Values.favorite
。在 {{end}}
后 .
复位其先前的范围。
可是请注意!在受限范围内,此时将没法从父范围访问其余对象。例如,下面会报错:
{{- with .Values.favorite}} drink: {{.drink | default "tea" | quote}} food: {{.food | upper | quote}} release: {{.Release.Name}} {{- end}} 复制代码
它会产生一个错误,由于 Release.Name 它不在 .
限制范围内。可是,若是咱们交换最后两行,全部将按预期工做,由于范围在 以后被重置。
{{- with .Values.favorite}} drink: {{.drink | default "tea" | quote}} food: {{.food | upper | quote}} {{- end}} release: {{.Release.Name}} 复制代码
看下 range
,咱们看看模板变量,它提供了一个解决上述范围问题的方法。
range
动做许多编程语言都支持使用 for
循环,foreach
循环或相似的功能机制进行循环。在 Helm 的模板语言中,遍历集合的方式是使用 range
操做子。
首先,让咱们在咱们的 values.yaml
文件中添加一份披萨配料列表:
favorite: drink: coffee food: pizza pizzaToppings: - mushrooms - cheese - peppers - onions 复制代码
如今咱们有一个列表(模板中称为 slice)pizzaToppings。咱们能够修改咱们的模板,将这个列表打印到咱们的 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}} 复制代码
让咱们仔细看看 toppings
:list。该 range 函数将遍历 pizzaToppings 列表。但如今发生了一些有趣的事. 就像 with
sets 的范围 .
,range
操做子也是同样。每次经过循环时,.
都设置为当前比萨饼顶部。也就是第一次 .
设定 mushrooms。第二个迭代它设置为 cheese
,依此类推。
咱们能够直接向管道发送 .
的值,因此当咱们这样作时 {{. | title | quote}}
,它会发送 .
到 title(标题 case 函数),而后发送到 quote
。若是咱们运行这个模板,输出将是:
# 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" 复制代码
如今,在这个例子中,咱们碰到了一些棘手的事情。该 toppings: |-
行声明了一个多行字符串。因此咱们的 toppings list 实际上不是 YAML 清单。这是一个很大的字符串。咱们为何要这样作?由于 ConfigMaps 中的数据 data
由键 / 值对组成,其中键和值都是简单的字符串。要理解这种状况,请查看 Kubernetes ConfigMap 文档.。但对咱们来讲,这个细节并不重要。
YAML 中的
|-
标记表示一个多行字符串。这能够是一种有用的技术,用于在清单中嵌入大块数据,如此处所示。
有时能快速在模板中建立一个列表,而后遍历该列表是颇有用的。Helm 模板有一个功能可使这个变得简单:tuple
。在计算机科学中,元组是类固定大小的列表类集合,可是具备任意数据类型。这粗略地表达了 tuple 的使用方式。
sizes: |- {{- range tuple "small" "medium" "large"}} - {{.}} {{- end}} sizes: |- - small - medium - large 复制代码
除了list和tuple以外,range
还能够用于遍历具备键和值的集合(如map
或 dict
)。当在下一节咱们介绍模板变量时,将看到如何作到这一点。
咱们已经了解了函数,管道,对象和控制结构,咱们能够在许多编程语言中找到更基本的用法之一:变量。在模板中,它们使用的频率较低。咱们将看到如何使用它们来简化代码,并更好地使用 with
和 range
。
在前面的例子中,咱们看到这段代码会失败:
{{- with .Values.favorite}}
drink: {{.drink | default "tea" | quote}}
food: {{.food | upper | quote}}
release: {{.Release.Name}}
{{- end}}
复制代码
Release.Name
不在该 with
块中限制的范围内。解决范围问题的一种方法是将对象分配给能够在不考虑当前范围的状况下访问的变量。
在 Helm 模板中,变量是对另外一个对象的命名引用。它遵循这个形式 $name
。变量被赋予一个特殊的赋值操做符::=
。咱们可使用变量重写上面的 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。如今在 with
块内部,$relname
变量仍然指向发布名称。
会产生这样的结果:
# 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
循环中特别有用。它们能够用于相似列表的对象以同时捕获索引和值:
toppings: |-
{{- range $index, $topping := .Values.pizzaToppings}}
{{$index}}: {{ $topping }}
{{- end}}
复制代码
注意,range
首先是变量,而后是赋值运算符,而后是列表。这将分配整数索引(从零开始)给 $index
,值给 $topping
。运行它将产生:
toppings: |- 0: mushrooms 1: cheese 2: peppers 3: onions 复制代码
对于同时具备键和值的数据结构,咱们可使用 range
来得到二者。例如,咱们能够对 .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: eager-rabbit-configmap data: myvalue: "Hello World" drink: "coffee" food: "pizza" 复制代码
变量一般不是 “全局” 的。它们的范围是它们所在的块。以前,咱们在模板的顶层赋值 $relname
。该变量将在整个模板的范围内起做用。但在咱们的最后一个例子中,$key
和 $val
只会在该 {{range...}}{{end}}
块的范围内起做用。
然而,总有一个变量是全局 $
变量 - 这个变量老是指向根上下文。当你在须要知道 chart 发行名称的范围内循环时,这很是有用。
举例说明:
{{- 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}} 复制代码
到目前为止,咱们只查看了一个文件中声明的一个模板。可是Helm模板语言的强大功能之一是它可以声明多个模板并将它们一块儿使用。咱们将在下一节中讨论。
如今是开始建立超过一个模板的时候了。在本节中,咱们将看到如何在一个文件中定义命名模板,而后在别处使用它们。命名模板(有时称为部分或子模板)是限定在一个文件内部的模板,并起一个名称。咱们有两种建立方法,以及几种不一样的使用方法。
在 “流量控制” 部分中,咱们介绍了声明和管理模板三个动做:define
,template
,和 block
。在本节中,咱们将介绍这三个动做,并介绍一个 include
函数,与 template
相似功能。
在命名模板时要注意一个重要的细节:模板名称是全局的。若是声明两个具备相同名称的模板,则最后加载一个模板是起做用的模板。因为子 chart 中的模板与顶级模板一块儿编译,所以注意当心地使用特定 chart 的名称来命名模板。
通用的命名约定是为每一个定义的模板添加 chart 名称:{{define "mychart.labels"}}
。经过使用特定 chart 名称做为前缀,咱们能够避免因为同名模板的两个不一样 chart 而可能出现的任何冲突。
_
文件到目前为止,咱们已经使用了一个文件,一个文件包含一个模板。但 Helm 的模板语言容许建立指定的嵌入模板,能够经过名称访问。
在咱们开始编写这些模板以前,有一些文件命名约定值得一提:
templates/
被视为包含 Kubernetes manifestsNOTES.txt
是一个例外_
)开头的文件被假定为没有内部 manifest。这些文件不会渲染 Kubernetes 对象定义,而是在其余 chart 模板中随处可用以供调用。这些文件用于存储 partials 和辅助程序。事实上,当咱们第一次建立时 mychart,咱们看到一个叫作文件 _helpers.tpl
。该文件是模板 partials 的默认位置。
define
和 template
声明和使用模板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 # 升级副本数量 helm upgrade go2cloud-api-doc --set replicaCount=2 go2cloud-api-doc/ 复制代码
将制做好的charts存放到minio上,在k8s内部署minior
mkdir minio-chart
复制代码
helm package redis
复制代码
cp redis-8.0.5.tgz /root/minio-chart/
复制代码
helm repo index minio-chart/ --url http://10.234.2.204:31311/minio/common-helm-repo/
复制代码
mc cp index.yaml minio/common-helm-repo/
mc cp redis-8.0.5.tgz minio/common-helm-repo/
复制代码
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
复制代码
能够在${HOME}/.mc/config.json
中查看ak密钥信息。
将制做好的charts包能够上传至helm仓库,能够放在本身的自建私有仓库,流入:kubeapps/Monocular/minior等,能够利用helm命令一键安装。
上传至公有云公共仓库,例如国内的阿里目前建立的Apphub等,在现今的云原生生态当中,已经有不少成熟的开源软件被制做成了 Helm Charts,使得用户能够很是方便地下载和使用,好比 Nginx,Apache、Elasticsearch、Redis 等等。不过,在开放云原生应用中心 App hub(Helm Charts 中国站) 发布以前,国内用户一直都很难直接下载使用这些 Charts。而如今,AppHub 不只为国内用户实时同步了官方 Helm Hub 里的全部应用,还自动替换了这些 Charts 里全部不可访问的镜像 URL(好比 gcr.io, quay.io 等),终于使得国内开发者经过 helm install “一键安装”应用成为了可能。
具体提交本身的charts能够参考:github.com/cloudnative…
此为我上传的slate chart,Slate helps you create beautiful, intelligent, responsive API documentation.
developer.aliyun.com/hub/detail?…
欢迎点赞。