具名模板

現在該超越單一模板,開始建立其他模板了。在本節中,我們將瞭解如何在一個檔案中定義*具名模板*,然後在其他地方使用它們。*具名模板*(有時稱為*局部模板*或*子模板*)只是一個在檔案內定義並賦予名稱的模板。我們將看到兩種建立它們的方法,以及幾種不同的使用方法。

在〈流程控制〉一節中,我們介紹了三個用於宣告和管理模板的操作:definetemplateblock。在本節中,我們將涵蓋這三個操作,並介紹一個特殊用途的 include 函數,它的作用類似於 template 操作。

命名模板時要牢記的一個重要細節:**模板名稱是全域性的**。如果您用相同的名稱宣告兩個模板,則最後載入的模板將是使用的模板。由於子圖表中的模板是與頂層模板一起編譯的,因此您在命名模板時應注意使用*圖表特定的名稱*。

一種流行的命名慣例是在每個定義的模板名稱前加上圖表的名稱:{{ define "mychart.labels" }}。通過使用特定的圖表名稱作為前綴,我們可以避免因兩個不同的圖表實現了相同名稱的模板而產生的任何衝突。

這種行為也適用於圖表的不同版本。如果您有 mychart 版本 1.0.0 以一種方式定義了一個模板,而 mychart 版本 2.0.0 修改了現有的具名模板,則它將使用最後載入的那個模板。您可以通過在圖表名稱中添加版本來解決此問題:{{ define "mychart.v1.labels" }}{{ define "mychart.v2.labels" }}

局部模板和 _ 檔案

到目前為止,我們只使用了一個檔案,並且該檔案只包含一個模板。但是 Helm 的模板語言允許您建立具名的內嵌模板,這些模板可以在其他地方通過名稱存取。

在我們開始詳細介紹如何編寫這些模板之前,有一個檔案命名慣例值得一提

  • templates/ 中的大多數檔案都被視為包含 Kubernetes 資訊清單
  • NOTES.txt 是一個例外
  • 但是,名稱以底線 (_) 開頭的檔案被假定為*不*包含資訊清單。這些檔案不會呈現為 Kubernetes 物件定義,但可以在其他圖表模板中隨處使用。

這些檔案用於儲存局部模板和輔助程式。事實上,當我們第一次建立 mychart 時,我們看到了一個名為 _helpers.tpl 的檔案。該檔案是模板局部模板的預設位置。

使用 definetemplate 宣告和使用模板

define 操作允許我們在模板檔案中建立具名模板。其語法如下

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

例如,我們可以定義一個模板來封裝 Kubernetes 的標籤區塊

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

當模板引擎讀取此檔案時,它會儲存對 mychart.labels 的引用,直到呼叫 template "mychart.labels"。然後,它會將該模板呈現在行內。所以結果會是這樣

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: running-panda-configmap
  labels:
    generator: helm
    date: 2016-11-02
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

注意:除非使用模板呼叫 define,否則它不會產生輸出,就像在此範例中一樣。

按照慣例,Helm 圖表將這些模板放在局部模板檔案中,通常是 _helpers.tpl。讓我們將此函數移到那裡

{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

按照慣例,define 函數應該有一個簡單的文件區塊 ({{/* ... */}}) 來描述它們的作用。

即使此定義位於 _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 }}

如上所述,**模板名稱是全域性的**。因此,如果兩個模板使用相同的名稱宣告,則最後出現的模板將是使用的模板。由於子圖表中的模板是與頂層模板一起編譯的,因此最好使用*圖表特定的名稱*來命名您的模板。一種流行的命名慣例是在每個定義的模板名稱前加上圖表的名稱:{{ define "mychart.labels" }}

設定模板的範圍

在我們上面定義的模板中,我們沒有使用任何物件。我們只使用了函數。讓我們修改我們定義的模板,使其包含圖表名稱和圖表版本

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

如果我們呈現這個模板,我們將會得到一個像這樣的錯誤

$ helm install --dry-run moldy-jaguar ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [unknown object type "nil" in ConfigMap.metadata.labels.chart, unknown object type "nil" in ConfigMap.metadata.labels.version]

要查看呈現的內容,請使用 --disable-openapi-validation 重新執行:helm install --dry-run --disable-openapi-validation moldy-jaguar ./mychart。結果將不會如我們預期

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: moldy-jaguar-configmap
  labels:
    generator: helm
    date: 2021-03-06
    chart:
    version:

名稱和版本發生了什麼事?它們不在我們定義的模板範圍內。當呈現具名模板(使用 define 建立)時,它將接收 template 呼叫傳入的範圍。在我們的範例中,我們像這樣包含了模板

{{- template "mychart.labels" }}

沒有傳入任何範圍,因此在模板中我們無法存取 . 中的任何內容。不過,這很容易修復。我們只需將一個範圍傳遞給模板

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

請注意,我們在 template 呼叫的末尾傳遞了 .。我們可以很容易地傳遞 .Values.Values.favorite 或我們想要的任何範圍。但我們想要的是頂層範圍。

現在,當我們使用 helm install --dry-run --debug plinking-anaco ./mychart 執行此模板時,我們會得到這個結果

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: plinking-anaco-configmap
  labels:
    generator: helm
    date: 2021-03-06
    chart: mychart
    version: 0.1.0

現在,{{ .Chart.Name }} 會解析為 mychart,而 {{ .Chart.Version }} 會解析為 0.1.0

include 函數

假設我們定義了一個如下所示的簡單模板

{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- 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" . }}

如果我們呈現這個模板,我們將會得到一個像這樣的錯誤

$ helm install --dry-run measly-whippet ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [ValidationError(ConfigMap): unknown field "app_name" in io.k8s.api.core.v1.ConfigMap, ValidationError(ConfigMap): unknown field "app_version" in io.k8s.api.core.v1.ConfigMap]

要查看呈現的內容,請使用 --disable-openapi-validation 重新執行:helm install --dry-run --disable-openapi-validation measly-whippet ./mychart。輸出將不會如我們預期

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: measly-whippet-configmap
  labels:
    app_name: mychart
app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
app_name: mychart
app_version: "0.1.0"

請注意,app_version 的縮排在兩個地方都是錯誤的。為什麼?因為替換進來的模板的文字是靠左對齊的。由於 template 是一個操作,而不是一個函數,因此無法將 template 呼叫的輸出傳遞給其他函數;資料只是簡單地插入到行內。

為了應對這種情況,Helm 提供了 template 的替代方案,它將模板的內容導入到當前的管道中,在管道中可以將其傳遞給管道中的其他函數。

以下是上面的範例,已修正為使用 indent 正確縮排 mychart.app 模板

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

現在,產生的 YAML 對於每個區段都正確縮排

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-mole-configmap
  labels:
    app_name: mychart
    app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
  app_name: mychart
  app_version: "0.1.0"

在 Helm 模板中,建議使用 include 而不是 template,這樣可以更好地處理 YAML 文件的輸出格式。

有時我們想要導入內容,但不是作為模板。也就是說,我們想要逐字導入檔案。我們可以通過下一節中描述的 .Files 物件來存取檔案來實現這一點。