云原生应用管理:原理与实践
上QQ阅读APP看书,第一时间看更新

2.10.6 自定义模板

本节我们将学习两种创建自定义模板的方式,然后在其他文件里面引用这个自定义的模板,并分析两种方法的异同。

在Helm中有3个关键字用来声明和管理模板,分别是def ine、template和block。本节我们会分别介绍这3个功能,同时还会学习include关键字,这个include功能和template很类似。

在学习自定义模板前有一个重要的前提,自定义模板是全局的,如果你在两个文件中定义了相同名称的模板,那么前一个模板就会被后一个模板覆盖,由于子Chart会和父Chart一起被编译,因此在定义模板时需要注意尽量避免和子Chart的模板名称相冲突。

一种比较流行的定义方式是使用Chart的名字作为模板名前缀,{def ine"myChart.labels"}},通过使用这种方式,我们就能最大限度地避免不同Chart模板间发生冲突。

1.以“_”开头的特殊文件

到目前为止,我们已经创建了一个模板,这个模板内含有一个单独的文件。Helm允许创建内嵌的自定义模板,同时能够使用名字来调用指定的模板功能。

在我们开始创建模板前,有一些需要提前说明的约定:

·大部分在templates/文件夹下的文件都被认为是Kubernetes资源文件;

·NOTES.txt是一个例外;

·但是以“_”开头的文件一般默认里面没有Kubernetes资源文件。这些文件不会被Helm渲染,但是这些文件内声明的函数可以在Chart的其他地方被调用。

这些文件一般被用来存放帮助函数。当我们创建第一个Chart myChart时,应该留意到一个名为_helpers.tpl的文件,这就是默认的存放帮助文件的地方。

2.使用define和template创建模板

def ine关键字允许在文件中创建一个自定义模板,基本格式如下所示。


{{ define "MY.NAME" }}
  # body of template here
{{ end }}

举个例子,我们可以这样定义一个标签函数来简化生成标签的逻辑。


{{- define "myChart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

现在我们就可以在ConfigMap中使用template关键字来引用这个模板。


{{- 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 }}

当Helm模板引擎读到模板myChart.labels时,就会寻找这个模板的定义,然后运行定义去重新渲染当前的模板,最终的运行结果如下所示。


apiVersion: v1
kind: ConfigMap
metadata:
  name: running-panda-configmap
  labels:
    generator: helm
    date: 2019-09-02
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

一般来说,Helm的编写规范是把这样的函数移到_helpers.tpl文件中,同时在该文件中,应该有一些注释来表明该段模板的功能,一般书写格式如下所示。


{{/* Generate basic labels */}}
{{- 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 }}

3.给自定义模板限定范围

在上面的模板定义中,我们没有使用任何内嵌对象。下面我们修改一下定义,增加Chart名称。


{{/* Generate basic labels */}}
{{- define "myChart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
    Chart: {{ .Chart.Name }}
    version: {{ .Chart.Version }}
{{- end }}

如果我们运行这个模板,会得到如下的结果:


apiVersion: v1
kind: ConfigMap
metadata:
  name: moldy-jaguar-configmap
  labels:
    generator: helm
    date: 2019-09-02
    Chart:
    version:

产生这样结果的原因是什么呢?主要是因为这个对象不在我们模板的定义范围内。当一个被def ine定义的模板渲染时,它的调用范围是被外部template传递进来的。在我们的例子中,它是通过{{-template"myChart.labels"}}调用的,没有任何的调用范围被传递进来,因此在这个模板定义中我们是拿不到“.”这个对象的任何信息的。不过我们可以通过template将调用范围传递过去。


apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "myChart.labels" . }}

请注意,我们是在template后面传递的“.”。其实我们也可以传递任何对象,比如.Values或者.Values.favorite。现在我们再次运行helm install--dry-run--debug./myChart,会得到如下信息。


apiVersion: v1
kind: ConfigMap
metadata:
  name: plinking-anaco-configmap
  labels:
    generator: helm
    date: 2019-09-02
    Chart: myChart
    version: 0.1.0

4.include函数

首先定义一个简单的模板。


{{- define "myChart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}"
{{- 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" . }}

这个模板的输出结果如下。


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"

注意,app_version这里的缩进是错误的,因为template模板是右对齐的,而且template是一个自定义模板引用,而不是一个函数,没有办法给template传递参数,所以所有的数据都是简单插入的。

在这种情况下,Helm提供了一种解决办法,既能将内容插入指定的模板中,又能以管道的方式继续调用其他函数。下面我们修改一下上述模板,使用nindent来将指定的内容向右缩进指定的空格。


apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{- include "myChart.app" . | nindent 4 }}
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}
  {{- include "myChart.app" . | nindent 2 }}

下面一起看看输出的结果。


apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-mole-configmap
  labels:
    app_name: myChart
    app_version: "0.1.0+1478129987"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
  app_name: myChart
  app_version: "0.1.0+1478129987"

在实际使用中,更推荐使用include替代template,因为template能实现的功能,include都能提供,而且include的功能更强大,能够使用管道方式在后面继续使用函数处理。