# 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 名称这样的前缀，避免冲突。


---
[点击查看本文视频](https://youdianzhishi.com/course/6n8xd6/)

扫描下面的二维码(或微信搜索`k8s技术圈`)关注我们的微信公众帐号，在微信公众帐号中回复 **加群** 即可加入到我们的 kubernetes 讨论群里面共同学习。

![k8s技术圈二维码](https://www.qikqiak.com/img/posts/qrcode_for_gh_d6dd87b6ceb4_430.jpg)
