提交 484e68ce 编写于 作者: 阳明的博客's avatar 阳明的博客

[add] Helm Article

上级 71ef54ad
......@@ -71,3 +71,5 @@
* [Helm 模板之模板函数与管道](docs/45.Helm模板之模板函数与管道.md)
* [Helm 模板之控制流程](docs/46.Helm模板之控制流程.md)
* [Helm 模板之命名模板](docs/47.Helm模板之命名模板.md)
* [Helm 模板之其他注意事项](docs/48.Helm模板之其他注意事项.md)
* [Helm Hooks](docs/49.Helm Hooks.md)
......@@ -21,7 +21,7 @@ defaultEntryPoints = ["http", "https"]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
......
# 48. Helm模板之其他注意事项
上节课我们学习了**命名模板**的使用,命名模板是 Helm 模板中非常重要的一个功能,在我们实际开发 Helm Chart 包的时候非常有用,到这里我们基本上就把 Helm 模板中经常使用到的一些知识点和大家介绍完了。但是仍然还是有一些在开发中值得我们注意的一些知识点,比如 NOTES.txt 文件的使用、子 Chart 的使用、全局值的使用,这节课我们就来和大家一起了解下这些知识点。
## NOTES.txt 文件
我们前面在使用 helm install 命令的时候,Helm 都会为我们打印出一大堆介绍信息,这样当别的用户在使用我们的 chart 包的时候就可以根据这些注释信息快速了解我们的 chart 包的使用方法,这些信息就是编写在 NOTES.txt 文件之中的,这个文件是纯文本的,但是它和其他模板一样,具有所有可用的普通模板函数和对象。
现在我们在前面的示例中 templates 目录下面创建一个 NOTES.txt 文件:
```shell
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 }}
```
我们可以看到我们在 NOTES.txt 文件中也使用 Chart 和 Release 对象,现在我们在 mychart 包根目录下面执行安装命令查看是否能够得到上面的注释信息:
```shell
$ helm install .
Error: release nomadic-deer failed: ConfigMap in version "v1" cannot be handled as a ConfigMap: v1.ConfigMap: Data: ReadString: expects " or n, but found [, error found in #10 byte of ...|rselist":[{"0":"K8s"|..., bigger context ...|:{"app":"mychart","chart":"mychart","courselist":[{"0":"K8s"},{"1":"Python"},{"2":"Search"},{"3":"Go|...
```
我们可以看到出现了上面的错误信息,但是如果我们去执行 debug 命令来调试的话是没有任何问题的,是可以正常渲染的,但是为什么在正式安装的时候确出现了问题呢?这是因为我们在 debug 调试阶段只是检验模板是否可以正常渲染,并没有去检查对应的 kubernetes 资源对象对 yaml 文件的格式要求,所以我们说 debug 测试通过,并不代表 chart 就真正的是可用状态,比如我们这里是一个 ConfigMap 的资源对象,ConfigMap 对 data 区域的内容是有严格要求的,比如我们这里出现了下面这样的内容:
```yaml
web: true
courselist:
- 0: "K8s"
- 1: "Python"
- 2: "Search"
- 3: "Golang"
```
这的确是一个合法的 yaml 文件的格式,但是对 ConfigMap 就不合法了,需要把 true 变成字符串,下面的字典数组变成一个多行的字符串,这样就是一个合法的 ConfigMap了:(templates/config.yaml)
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{- include "mychart.labels" . | indent 4}}
data:
app: mychart
myvalue: {{ .Values.hello | default "Hello World" | quote }}
{{- $releaseName := .Release.Name }}
{{- with .Values.course }}
k8s: {{ .k8s | upper | quote }}
python: {{ .python | repeat 5 | quote }}
release: {{ $releaseName }}
{{- if eq .python "django" }}
web: "true"
{{- end }}
{{- end }}
courselist: |
{{- range $index, $course := .Values.courselist }}
{{ $course | title | quote }}
{{- end }}
{{- range $key, $val := .Values.course }}
{{ $key }}: {{ $val | upper | quote }}
{{- end }}
{{- include "mychart.labels" . | indent 2 }}
```
现在我们再来执行安装命令:
```shell
$ helm install .
NAME: nosy-pig
LAST DEPLOYED: Sat Sep 29 19:26:16 2018
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
nosy-pig-configmap 11 0s
NOTES:
Thank you for installing mychart.
Your release is named nosy-pig.
To learn more about the release, try:
$ helm status nosy-pig
$ helm get nosy-pig
```
现在已经安装成功了,而且下面的注释部分也被渲染出来了,我们可以看到 NOTES.txt 里面使用到的模板对象都被正确渲染了。
为我们创建的 chart 包提供一个清晰的 NOTES.txt 文件是非常有必要的,可以为用户提供有关如何使用新安装 chart 的详细信息,这是一种非常友好的方式方法。
## 子 chart 包
我们到目前为止都只用了一个 chart,但是 chart 也可以有 子 chart 的依赖关系,它们也有自己的值和模板,在学习字 chart 之前,我们需要了解几点关于子 chart 的说明:
* 子 chart 是**独立**的,所以子 chart 不能明确依赖于其父 chart
* 子 chart 无法访问其父 chart 的值
* 父 chart 可以覆盖子 chart 的值
* Helm 中有全局值的概念,可以被所有的 chart 访问
### 创建子 chart
现在我们就来创建一个子 chart,还记得我们在创建 mychart 包的时候,在根目录下面有一个空文件夹 charts 目录吗?这就是我们的子 chart 所在的目录,在该目录下面添加一个新的 chart:
```shell
$ cd mychart/charts
$ helm create mysubchart
Creating mysubchart
$ rm -rf mysubchart/templates/*.*
$ tree ..
..
├── charts
│   └── mysubchart
│   ├── charts
│   ├── Chart.yaml
│   ├── templates
│   └── values.yaml
├── Chart.yaml
├── templates
│   ├── configmap.yaml
│   ├── _helpers.tpl
│   └── NOTES.txt
└── values.yaml
5 directories, 7 files
```
同样的,我们将子 chart 模板中的文件全部删除了,接下来,我们为子 chart 创建一个简单的模板和 values 文件了。
```shell
$ cat > mysubchart/values.yaml <<EOF
in: mysub
EOF
$ cat > mysubchart/templates/configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap2
data:
in: {{ .Values.in }}
EOF
```
我们上面已经提到过每个子 chart 都是独立的 chart,所以我们可以单独给 mysubchart 进行测试:
```shell
$ helm install --dry-run --debug ./mysubchart
[debug] Created tunnel using local port: '33568'
......
---
# Source: mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: washed-indri-configmap2
data:
in: mysub
```
我们可以看到正常渲染出了结果。
### 值覆盖
现在 mysubchart 这个子 chart 就属于 mychart 这个父 chart 了,由于 mychart 是父级,所以我们可以在 mychart 的 values.yaml 文件中直接配置子 chart 中的值,比如我们可以在 mychart/values.yaml 文件中添加上子 chart 的值:
```yaml
course:
k8s: devops
python: django
courselist:
- k8s
- python
- search
- golang
mysubchart:
in: parent
```
注意最后两行,mysubchart 部分内的任何指令都会传递到 mysubchart 这个子 chart 中去的,现在我们在 mychart 根目录中执行调试命令,可以查看到子 chart 也被一起渲染了:
```shell
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '44798'
......
---
# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ideal-ostrich-configmap2
data:
in: parent
---
# Source: mychart/templates/configmap.yaml
......
```
我们可以看到子 chart 中的值已经被顶层的值给覆盖了。但是在某些场景下面我们还是希望某些值在所有模板中都可以使用,这就需要用到全局 chart 值了。
### 全局值
全局值可以从任何 chart 或者子 chart中进行访问使用,values 对象中有一个保留的属性是`Values.global`,就可以被用来设置全局值,比如我们在父 chart 的 values.yaml 文件中添加一个全局值:
```yaml
course:
k8s: devops
python: django
courselist:
- k8s
- python
- search
- golang
mysubchart:
in: parent
global:
allin: helm
```
我们在 values.yaml 文件中添加了一个 global 的属性,这样的话无论在父 chart 中还是在子 chart 中我们都可以通过`{{ .Values.global.allin }}`来访问这个全局值了。比如我们在 mychart/templates/configmap.yaml 和 mychart/charts/mysubchart/templates/configmap.yaml 文件的 data 区域下面都添加上如下内容:
```yaml
...
data:
allin: {{ .Values.global.allin }}
...
```
现在我们在 mychart 根目录下面执行 debug 调试模式:
```shell
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '32775'
......
MANIFEST:
---
# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: wistful-spaniel-configmap2
data:
allin: helm
in: parent
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: wistful-spaniel-configmap
......
data:
allin: helm
......
```
我们可以看到两个模板中都输出了`allin: helm`这样的值,全局变量对于传递这样的信息非常有用,不过也要注意我们不能滥用全局值。
另外值得注意的是我们在学习命名模板的时候就提到过父 chart 和子 chart 可以共享模板。任何 chart 中的任何定义块都可用于其他 chart,所以我们在给命名模板定义名称的时候添加了 chart 名称这样的前缀,避免冲突。
# 49. Helm Hooks
和 Kubernetes 里面的容器一样,Helm 也提供了 [Hook](https://docs.helm.sh/developing_charts/#hooks) 的机制,允许 chart 开发人员在 release 的生命周期中的某些节点来进行干预,比如我们可以利用 Hooks 来做下面的这些事情:
* 在加载任何其他 chart 之前,在安装过程中加载 ConfigMap 或 Secret
* 在安装新 chart 之前执行作业以备份数据库,然后在升级后执行第二个作业以恢复数据
* 在删除 release 之前运行作业,以便在删除 release 之前优雅地停止服务
值得注意的是 Hooks 和普通模板一样工作,但是它们具有特殊的注释,可以使 Helm 以不同的方式使用它们。
Hook 在资源清单中的 metadata 部分用 annotations 的方式进行声明:
```yaml
apiVersion: ...
kind: ....
metadata:
annotations:
"helm.sh/hook": "pre-install"
# ...
```
接下来我们就来和大家介绍下 Helm Hooks 的一些基本使用方法。
## Hooks
在 Helm 中定义了如下一些可供我们使用的 Hooks:
* 预安装`pre-install`:在模板渲染后,kubernetes 创建任何资源之前执行
* 安装后`post-install`:在所有 kubernetes 资源安装到集群后执行
* 预删除`pre-delete`:在从 kubernetes 删除任何资源之前执行删除请求
* 删除后`post-delete`:删除所有 release 的资源后执行
* 升级前`pre-upgrade`:在模板渲染后,但在任何资源升级之前执行
* 升级后`post-upgrade`:在所有资源升级后执行
* 预回滚`pre-rollback`:在模板渲染后,在任何资源回滚之前执行
* 回滚后`post-rollback`:在修改所有资源后执行回滚请求
* `crd-install`:在运行其他检查之前添加 CRD 资源,只能用于 chart 中其他的资源清单定义的 CRD 资源。
## 生命周期
Hooks 允许开发人员在 release 的生命周期中的一些关键节点执行一些钩子函数,我们正常安装一个 chart 包的时候的生命周期如下所示:
* 用户运行`helm install foo`
* chart 被加载到服务端 Tiller Server 中
* 经过一些验证,Tiller Server 渲染 foo 模板
* Tiller 将产生的资源加载到 kubernetes 中去
* Tiller 将 release 名称和其他数据返回给 Helm 客户端
* Helm 客户端退出
如果开发人员在 install 的生命周期中定义了两个 hook:`pre-install``post-install`,那么我们安装一个 chart 包的生命周期就会多一些步骤了:
* 用户运行`helm install foo`
* chart 被加载到服务端 Tiller Server 中
* 经过一些验证,Tiller Server 渲染 foo 模板
* Tiller 将 hook 资源加载到 kubernetes 中,准备执行`pre-install` hook
* Tiller 会根据权重对 hook 进行排序(默认分配权重0,权重相同的 hook 按升序排序)
* Tiller 然后加载最低权重的 hook
* Tiller 等待,直到 hook 准备就绪
* Tiller 将产生的资源加载到 kubernetes 中
* Tiller 执行`post-install` hook
* Tiller 等待,直到 hook 准备就绪
* Tiller 将 release 名称和其他数据返回给客户端
* Helm 客户端退出
等待 hook 准备就绪,这是一个阻塞的操作,如果 hook 中声明的是一个 Job 资源,那么 Tiller 将等待 Job 成功完成,如果失败,则发布失败,在这个期间,Helm 客户端是处于暂停状态的。
对于所有其他类型,只要 kubernetes 将资源标记为加载(添加或更新),资源就被视为**就绪**状态,当一个 hook 声明了很多资源是,这些资源是被串行执行的。
另外需要注意的是 hook 创建的资源不会作为 release 的一部分进行跟踪和管理,一旦 Tiller Server 验证了 hook 已经达到了就绪状态,它就不会去管它了。
所以,如果我们在 hook 中创建了资源,那么不能依赖`helm delete`去删除资源,因为 hook 创建的资源已经不受控制了,要销毁这些资源,需要在`pre-delete`或者`post-delete`这两个 hook 函数中去执行相关操作,或者将`helm.sh/hook-delete-policy`这个 annotation 添加到 hook 模板文件中。
## 写一个 hook
上面我们也说了 hook 和普通模板一样,也可以使用普通的模板函数和常用的一些对象,比如`Values``Chart``Release`等等,唯一和普通模板不太一样的地方就是在资源清单文件中的 metadata 部分会有一些特殊的注释 annotation。
例如,现在我们来创建一个 hook,在前面的示例 templates 目录中添加一个 post-install-job.yaml 的文件,表示安装后执行的一个 hook:
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-post-install-job
lables:
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
annotations:
# 注意,如果没有下面的这个注释的话,当前的这个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:
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
spec:
restartPolicy: Never
containers:
- name: post-install-job
image: alpine
command: ["/bin/sleep", "{{ default "10" .Values.sleepTime }}"]
```
上面的 Job 资源中我们添加一个 annotations,要注意的是,如果我们没有添加下面这行注释的话,这个资源就会被当成是 release 的一部分资源:
```yaml
annotations:
"helm.sh/hook": post-install
```
当然一个资源中我们也可以同时部署多个 hook,比如我们还可以添加一个`post-upgrade`的钩子:
```yaml
annotations:
"helm.sh/hook": post-install,post-upgrade
```
另外值得注意的是我们为 hook 定义了一个权重,这有助于建立一个确定性的执行顺序,权重可以是正数也可以是负数,但是必须是字符串才行。
```yaml
annotations:
"helm.sh/hook-weight": "-5"
```
最后还添加了一个删除 hook 资源的策略:
```yaml
annotations:
"helm.sh/hook-delete-policy": hook-succeeded
```
删除资源的策略可供选择的注释值:
* `hook-succeeded`:表示 Tiller 在 hook 成功执行后删除 hook 资源
* `hook-failed`:表示如果 hook 在执行期间失败了,Tiller 应该删除 hook 资源
* `before-hook-creation`:表示在删除新的 hook 之前应该删除以前的 hook
当 helm 的 release 更新时,有可能 hook 资源已经存在于群集中。默认情况下,helm 会尝试创建资源,并抛出错误**"... already exists"**
我们可以选择 "helm.sh/hook-delete-policy": "before-hook-creation",取代 "helm.sh/hook-delete-policy": "hook-succeeded,hook-failed" 因为:
例如为了手动调试,将错误的 hook 作业资源保存在 kubernetes 中是很方便的。
出于某种原因,可能有必要将成功的 hook 资源保留在 kubernetes 中。同时,在 helm release 升级之前进行手动资源删除是不可取的。
"helm.sh/hook-delete-policy": "before-hook-creation" 在 hook 中的注释,如果在新的 hook 启动前有一个 hook 的话,会使 Tiller 将以前的release 中的 hook 删除,而这个 hook 同时它可能正在被其他一个策略使用。
# 50. Helm 部署应用示例
到这里我们就把 Helm 涉及到的一些知识点基本上和大家讲解完了,今天我们就用一个完整的示例来给大家演示下如何从0开始部署一个 Helm 应用。
大家是否还记得前面我们手动部署的 wordpress 示例,如果现在我们来使用 Helm Chart 来部署 wordpress 应用应该怎么操作呢?
## 创建 chart
首先同样的我们还是手动创建一个 chart 包,执行下面的创建命令:
```shell
$ helm create wordpress
Creating wordpress
```
然后我们就需要来编写 helm 的模板了,但是应该如何开始呢?这个我们就要去回顾下之前在没有使用 helm 的情况下,我们是怎样部署 wordpress 应用的,我们可以前往[github 仓库](https://github.com/cnych/kubernetes-learning/blob/master/wordpress/wordpress-all.yaml)查看之前应用的资源清单,我们知道 wordpress 是依赖于 mysql 数据库的,所以我们在部署的时候是将 wordpress 和 mysql 两个应用独立部署的,然后在 wordpress 的 Pod 中通过环境变量的形式将 mysql 数据库的链接地址、端口以及数据库名称注入到应用中去的,为了解决服务依赖的问题,我们还用了一个 initContainer 来检查 mysql 数据库是否已经启动,保证 wordpress 应用启动之前能够正常连接。所以第一步我们完全就可以将之前的资源清单文件内容直接照搬过来。
将 Deployment 资源中的 yaml 文件复制到 templates/deployment.yaml 文件中,将 Service 资源中的 yaml 文件内容复制到 templates/service.yaml 文件中,其他的暂时不用考虑。
然后同样的我们可以先使用 debug 命令调试模板是否能够正常渲染:
```shell
$ helm install --dry-run --debug .
```
由于我们之前是将应用安装在 blog 这个 namespace 下面的,所以我们需要在提前添加一个 namespace,但是如果我们在 chart 模板中就将这些资源固定在某个 namespace 下面显然是不合适的,我们在使用`helm install`命令安装的时候可以通过`--namespace`参数来指定将我们的应用安装在某个 namespace 下面,所以我们在 chart 模板中都不会带上 namespace 的声明,所以这里我们将 deployment.yaml 和 service.yaml 文件中的 namespace 字段都移除掉。然后执行安装命名:
```shell
$ kubectl create ns blog
namespace "blog" created
$ helm install . --namespace blog
NAME: viable-ant
LAST DEPLOYED: Sun Oct 7 00:43:40 2018
NAMESPACE: blog
STATUS: DEPLOYED
RESOURCES:
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.97.198.88 <none> 3306/TCP 0s
wordpress NodePort 10.100.28.2 <none> 80:31306/TCP 0s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
mysql-deploy 1 1 1 0 0s
wordpress-deploy 1 1 1 0 0s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
mysql-deploy-67dfd986dd-86bdx 0/1 ContainerCreating 0 0s
wordpress-deploy-5df954b8c-t2h7t 0/1 Init:0/1 0 0s
NOTES:
1. Get the application URL by running these commands:
export POD_NAME=$(kubectl get pods --namespace blog -l "app=wordpress,release=viable-ant" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
```
我们在安装的时候指定了 namespace 参数,我们可以看到 RESOURCES 区域现在已经是我们指点的资源对象了,NOTES 区域的显示是默认的信息,可以暂时忽略,现在我们可以查看下 blog 这个 namespace 下面的资源对象:
```shell
$ kubectl get pods -n blog
NAME READY STATUS RESTARTS AGE
mysql-deploy-67dfd986dd-86bdx 1/1 Running 0 5m
wordpress-deploy-5df954b8c-t2h7t 1/1 Running 0 5m
$ kubectl get service -n blog
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mysql ClusterIP 10.97.198.88 <none> 3306/TCP 5m
wordpress NodePort 10.100.28.2 <none> 80:31306/TCP 5m
```
隔一会儿时间等镜像下载完成、Pod 启动起来后就可以通过 NodeIP:31306 去访问下我们部署的服务是否能够正常访问。
![wordpress ui](./images/wordpress-ui.jpg)
## 定制数据
尽管现在我们通过上面的方法可以直接使用 helm 来安装我们的应用了,但是完全不能定制化,比如我们希望资源的名称和 helm release 的名称产生关联,比如我希望可以通过参数化的形式去控制应用的资源大小,比如我还希望能够自己去控制数据库的数据持久化,因为在生产环境下面很大的可能是使用 PV/PVC 或者 StorageClass,而不是使用 hostPath。所以其实现在还远远不够。
### 名称
首先,我们将资源的名称定义成一个命名模板,因为除了 Deployment 里面需要使用,Service 资源里面也可以使用,这样两个地方直接引用同一个命名模板就可以了,我们打开 templates/_helpers.tpl 文件,其实可以看到默认的 partial 文件中就已经有这样的名称的命名模板的定义了:
```yaml
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "wordpress.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "wordpress.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "wordpress.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
```
其中`wordpress.fullname`这个命名模板就是我们需要的,但是我们需要仔细分析下这个命名模板的逻辑:
* 首先是如果定义了值 fullnameOverride,则直接截取前63个字符(这是因为 kubernetes 的命名机制决定的),然后使用`-`进行连接
* 如果没有定义值 fullnameOverride,那么就直接取值 nameOverride(默认是 chart 的名称):
* 如果值 nameOverride 包含了当前 release 的名称,则同样直接截取前63个字符,用`-`进行连接
* 如果值 nameOverride 没包含当前 release 的名称,则将 release 的名称和值 nameOverride 用`-`进行拼接,然后截取前63个字符作为应用的名称
所以现在我们将 templates 目录下面的 deployment.yaml 和 service.yaml 文件中关于 wordpress 的 name 替换成命名模板:
```yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{ template "wordpress.fullname" . }}
......
apiVersion: v1
kind: Service
metadata:
name: {{ template "wordpress.fullname" . }}
......
```
然后将 values.yaml 文件中的内容全部清空,添加下面两个值定义:
```yaml
nameOverride: ""
fullnameOverride: ""
```
将 templates 目录下面的 ingress.yaml 和 NOTES.txt 文件删除,后面我们再手动添加,然后使用 debug 命令来查看模板是否能够按照我们的要求就行渲染:
```shell
$ helm install --dry-run --debug .
helm install --dry-run --debug .
[debug] Created tunnel using local port: '38888'
......
---
# Source: wordpress/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
......
---
# Source: wordpress/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: old-fox-wordpress
......
---
# Source: wordpress/templates/deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: mysql-deploy
......
---
# Source: wordpress/templates/deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: old-fox-wordpress
labels:
app: wordpress
......
```
我们可以看到 Deployment 和 Service 的名称都是 old-fox-wordpress,这是因为我们没有定义 fullnameOverride 和 nameOverride 这两个值,所以最后渲染的时候就是使用的 release 的名称拼接上 chart 的名称,如果我们用 --set 参数来指定下值 fullnameOverride 呢:
```shell
$ helm install --dry-run --debug --set fullnameOverride=mywordpress .
[debug] Created tunnel using local port: '41110'
......
---
# Source: wordpress/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: mywordpress
......
---
# Source: wordpress/templates/deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: mywordpress
labels:
app: wordpress
......
```
可以看到资源名称被我们指定的值覆盖了,一般情况下面我们还会为我们的资源添加上合适的 labels 标签,比如我们这里可以给 wordpress 的 Deployment 和 Service 都添加上下面的 labels 标签:
```yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{ template "wordpress.fullname" . }}
labels:
app: {{ .Chart.Name }}
chart: {{ template "wordpress.chart" . }}
release: {{ .Release.Name }}
......
apiVersion: v1
kind: Service
metadata:
name: {{ template "wordpress.fullname" . }}
labels:
app: {{ .Chart.Name }}
chart: {{ template "wordpress.chart" . }}
release: {{ .Release.Name }}
```
然后使用 debug 模式查看下模板渲染结果:
```shell
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '40645'
......
---
# Source: wordpress/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: youngling-clam-wordpress
labels:
app: wordpress
chart: wordpress-0.1.0
release: youngling-clam
......
---
# Source: wordpress/templates/deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: youngling-clam-wordpress
labels:
app: wordpress
chart: wordpress-0.1.0
release: youngling-clam
......
```
有的同学可能已经发现了,我们这个地方的应用是依赖于 mysql 的,那么为什么我们只是把 wordpress 相关的数据来做了定制呢?当然我们也可以在我们的这个 chart 中来定制 mysql,但是这却不是最好的方法,最好的方法是让我们去依赖一个独立的 mysql chart,这样可以将 wordpress 和 mysql 之间的耦合关系降低,后面我们再和大家来看看怎样解耦。
为了不影响对 wordpress 的操作,我们可以临时将 templates 目录下面的 mysql 的资源对象单独提取出来,比如我们这里统一放到一个叫 mysql.yaml 的资源文件中,现在我们的结构就是这样的了:
```shell
$ tree .
.
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── mysql.yaml
│   └── service.yaml
└── values.yaml
2 directories, 6 files
```
### 镜像
现在我们使用的镜像还是固定的`wordpress:latest`,为了方便其他人使用,我们在编写 chart 包的时候会提供一个定制参数值,可以自由指定使用的镜像,包括 tag 版本。我们可以先去添加 values.yaml 文件中的内容:
```yaml
nameOverride: ""
fullnameOverride: ""
## 官方 WordPress 镜像
## 引用:https://hub.docker.com/r/library/wordpress/tags/
image:
registry: docker.io
repository: wordpress
tag: 4.9.8
## 指定一个 imagePullPolicy
## 如果镜像 tag 是'latest',则默认是'Always',否则设置'IfNotPresent'
## 引用: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images
##
pullPolicy: IfNotPresent
## 指定一组 imagePullSecretes[可选]
## Secrets 必须在 namespace 下面手动创建
## 引用: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
##
# pullSecrets:
# - myRegistrKeySecretName
```
我们在 values.yaml 文件中添加了一个 image 的对象,里面包含仓库地址、镜像名称、镜像版本,这是因为一个标准的镜像就包含这3个部分,每一个部分都是可能被定制的,然后指定一个镜像拉取策略的参数 imagePullPolicy,还不算完,为什么呢?如果我们要使用的镜像是一个私有仓库的镜像怎么办?所以我们这里还预留了一个参数:pullSecrets,用来指定私有仓库地址的 Secrets,现在我们再去修改 templates/deployment.yaml 文件就简单很多了:
```yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{ template "wordpress.fullname" . }}
labels:
app: {{ .Chart.Name }}
chart: {{ template "wordpress.chart" . }}
release: {{ .Release.Name }}
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: {{ .Chart.Name }}
chart: {{ template "wordpress.chart" . }}
release: {{ .Release.Name }}
spec:
initContainers:
- name: init-db
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'until nslookup mysql;do echo waiting for mysql service; sleep 2; done;']
{{- if .Values.image.pullSecrets }}
imagePullSecrets:
{{- range .Values.image.pullSecrets }}
- name: {{ . }}
{{- end }}
{{- end }}
containers:
- name: {{ template "wordpress.fullname" . }}
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
......
```
我们首先判断是否存在值`pullSecrets`,如果存在,则将 Secrets 循环渲染出来,然后是容器的名称还是使用命名模板 wordpress.fullname 的定义,然后就是 image 的地址以及 imagePullPolicy,这样我们就完成了对镜像的定制,默认的值直接写入到 values.yaml 文件中,现在我们使用 debug 命令查看下模板渲染的结果:
```yaml
helm install --dry-run --debug .
[debug] Created tunnel using local port: '46735'
......
---
# Source: wordpress/templates/deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: eponymous-narwhal-wordpress
labels:
app: wordpress
chart: wordpress-0.1.0
release: eponymous-narwhal
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: wordpress
chart: wordpress-0.1.0
release: eponymous-narwhal
spec:
initContainers:
- name: init-db
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'until nslookup mysql;do echo waiting for mysql service; sleep 2; done;']
containers:
- name: eponymous-narwhal-wordpress
image: "docker.io/wordpress:4.9.8"
imagePullPolicy: "IfNotPresent"
......
```
假如现在我们的镜像地址是 youdianzhishi.com/wordpress:4.9,那么我们在安装的就可以覆盖 image 对象中的相关参数了:
```yaml
$ helm install --dry-run --debug --set image.registry=youdianzhishi.com --set image.tag=4.9 .
[debug] Created tunnel using local port: '36449'
......
# Source: wordpress/templates/deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: peddling-prawn-wordpress
labels:
app: wordpress
chart: wordpress-0.1.0
release: peddling-prawn
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: wordpress
chart: wordpress-0.1.0
release: peddling-prawn
spec:
initContainers:
- name: init-db
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'until nslookup mysql;do echo waiting for mysql service; sleep 2; done;']
containers:
- name: peddling-prawn-wordpress
image: "youdianzhishi.com/wordpress:4.9"
imagePullPolicy: "IfNotPresent"
......
```
我们可以看到镜像地址是不是就被替换了,当然如果你需要覆盖的值比较多,最好还是通过指定一个 yaml 文件来覆盖默认的这些 values 值。
### 健康检查、资源限制
按照我们前面的资源文件声明,接下来我们就应该定制健康检查部分和资源限制部分,同样还是先添加模板值:(values.yaml)
```yaml
......
## liveness 和 readliness probes 配置
## 引用: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes)
livenessProbe:
initialDelaySeconds: 120
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
readinessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
```
我们在 values.yaml 文件中添加了 livenessProbe 和 readinessProbe 这两个对象,里面都是健康检测的相关属性,然后我们需要将这些值都嵌入到模板中去,按照以前的方法我们是不是一个属性一个属性的添加,但是这样太麻烦了,我们可以用一个函数`toYaml`将这两个对象中的属性一次性全部输出到模板中去:
```yaml
containers:
- name: {{ template "wordpress.fullname" . }}
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
ports:
- name: wdport
containerPort: 80
livenessProbe:
tcpSocket:
port: 80
{{ toYaml .Values.livenessProbe | indent 10 }}
readinessProbe:
tcpSocket:
port: 80
{{ toYaml .Values.readinessProbe | indent 10 }}
```
可以看到我们这里使用的方法是`{{ toYaml .Values.livenessProbe | indent 10 }}`,至于为什么是保留10个空格呢?这个就需要大家对这些对象的层级关系要比较清楚才行。现在我们用 debug 来查看下模板渲染后的结果呢:
```shell
helm install --dry-run --debug .
[debug] Created tunnel using local port: '40467'
......
containers:
- name: mouthy-crocodile-wordpress
image: "docker.io/wordpress:4.9.8"
imagePullPolicy: "IfNotPresent"
ports:
- name: wdport
containerPort: 80
livenessProbe:
tcpSocket:
port: 80
failureThreshold: 6
initialDelaySeconds: 120
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
tcpSocket:
port: 80
failureThreshold: 6
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
......
```
可以看到符合我们的渲染结果的。然后就是我们的 resource 资源部分,因为并不是所有的应用资源消耗情况都是一样的,还需要结合自己的集群去进行定制
```yaml
## Configure resource requests and limits
## ref: http://kubernetes.io/docs/user-guide/compute-resources/
##
resources:
requests:
memory: 512Mi
cpu: 300m
```
todo......
......@@ -13,13 +13,13 @@ data:
python: {{ .python | repeat 5 | quote }}
release: {{ $releaseName }}
{{- if eq .python "django" }}
web: true
web: "true"
{{- end }}
{{- end }}
courselist:
{{- range $index, $course := .Values.courselist }}
- {{ $index }}: {{ $course | title | quote }}
{{- end }}
courselist: |
{{- range $index, $course := .Values.courselist }}
{{ $course | title | quote }}
{{- end }}
{{- range $key, $val := .Values.course }}
{{ $key }}: {{ $val | upper | quote }}
{{- end }}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册