图表开发技巧和窍门

本指南介绍了 Helm 图表开发人员在构建生产级图表时学到的一些技巧和窍门。

了解您的模板函数

Helm 使用 Go 模板 来对您的资源文件进行模板化。虽然 Go 自带了一些内置函数,但我们添加了许多其他函数。

首先,出于安全原因,我们添加了 Sprig 库 中的所有函数,除了 envexpandenv

我们还添加了两个特殊的模板函数:includerequiredinclude 函数允许您引入另一个模板,然后将结果传递给其他模板函数。

例如,此模板片段包含一个名为 mytpl 的模板,然后将结果转换为小写,然后将结果用双引号括起来。

value: {{ include "mytpl" . | lower | quote }}

required 函数允许您声明特定值条目对于模板渲染是必需的。如果值为空,则模板渲染将失败并显示用户提交的错误消息。

以下 required 函数的示例声明 .Values.who 的条目是必需的,并在该条目缺失时会打印错误消息

value: {{ required "A valid .Values.who entry required!" .Values.who }}

引用字符串,不要引用整数

在处理字符串数据时,对字符串进行引用始终比将其作为裸词更安全

name: {{ .Values.MyName | quote }}

但在处理整数时,不要对值进行引用。在许多情况下,这会导致 Kubernetes 内部出现解析错误。

port: {{ .Values.Port }}

此备注不适用于 env 变量值,即使它们表示整数,也应被视为字符串

env:
  - name: HOST
    value: "http://host"
  - name: PORT
    value: "1234"

使用 'include' 函数

Go 提供了一种使用内置 template 指令在另一个模板中包含一个模板的方法。但是,内置函数不能在 Go 模板管道中使用。

为了能够包含模板,然后对模板的输出执行操作,Helm 有一个特殊的 include 函数

{{ include "toYaml" $value | indent 2 }}

上面包含一个名为 toYaml 的模板,将 $value 传递给它,然后将该模板的输出传递给 indent 函数。

由于 YAML 对缩进级别和空白符赋予了意义,因此这是一种包含代码片段,但在相关上下文中处理缩进的绝佳方法。

使用 'required' 函数

Go 提供了一种方法,用于设置模板选项以控制在用映射中不存在的键索引映射时如何处理。这通常使用 template.Options("missingkey=option") 设置,其中 option 可以是 defaultzeroerror。虽然将此选项设置为 error 会导致执行停止并出现错误,但这将适用于映射中的每个缺失键。在某些情况下,图表开发者可能希望对 values.yaml 文件中的特定值强制执行此行为。

required 函数使开发者能够将值条目声明为模板渲染所需的条目。如果 values.yaml 中的条目为空,则模板将不会渲染,并将返回开发者提供的错误消息。

例如

{{ required "A valid foo is required!" .Values.foo }}

.Values.foo 定义时,上面的内容将渲染模板,但在 .Values.foo 未定义时,将无法渲染并退出。

使用 'tpl' 函数

tpl 函数允许开发者在模板内部将字符串评估为模板。这对于将模板字符串作为值传递给图表或渲染外部配置文件很有用。语法:{{ tpl TEMPLATE_STRING VALUES }}

示例

# values
template: "{{ .Values.name }}"
name: "Tom"

# template
{{ tpl .Values.template . }}

# output
Tom

渲染外部配置文件

# external configuration file conf/app.conf
firstName={{ .Values.firstName }}
lastName={{ .Values.lastName }}

# values
firstName: Peter
lastName: Parker

# template
{{ tpl (.Files.Get "conf/app.conf") . }}

# output
firstName=Peter
lastName=Parker

创建镜像拉取密钥

镜像拉取密钥本质上是注册表用户名密码的组合。您可能需要在部署的应用程序中使用它们,但要创建它们,需要运行 base64 两次。我们可以编写一个辅助模板来组合 Docker 配置文件,以便用作密钥的有效负载。以下是一个示例

首先,假设凭据在 values.yaml 文件中定义如下

imageCredentials:
  registry: quay.io
  username: someone
  password: sillyness
  email: someone@host.com

然后,我们将辅助模板定义如下

{{- define "imagePullSecret" }}
{{- with .Values.imageCredentials }}
{{- printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":\"%s\",\"email\":\"%s\",\"auth\":\"%s\"}}}" .registry .username .password .email (printf "%s:%s" .username .password | b64enc) | b64enc }}
{{- end }}
{{- end }}

最后,我们在一个更大的模板中使用辅助模板来创建密钥清单

apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: {{ template "imagePullSecret" . }}

自动回滚部署

通常情况下,ConfigMap 或密钥会被注入到容器中的配置文件中,或者存在其他需要滚动 Pod 的外部依赖项更改。根据应用程序的不同,如果这些配置在随后的 helm upgrade 中更新,则可能需要重新启动,但如果部署规范本身没有更改,应用程序将继续使用旧配置运行,从而导致部署不一致。

sha256sum 函数可用于确保如果另一个文件发生更改,则部署的注释部分将更新

kind: Deployment
spec:
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]

注意:如果您将此添加到库图表中,则无法在 $.Template.BasePath 中访问您的文件。相反,您可以使用 {{ include ("mylibchart.configmap") . | sha256sum }} 来引用您的定义。

如果您始终希望回滚部署,可以使用与上面类似的注释步骤,但将 sha256sum 替换为随机字符串,以便它始终发生变化并导致部署回滚

kind: Deployment
spec:
  template:
    metadata:
      annotations:
        rollme: {{ randAlphaNum 5 | quote }}
[...]

每次调用模板函数都会生成一个唯一的随机字符串。这意味着,如果需要同步多个资源使用的随机字符串,则所有相关资源都需要位于同一个模板文件中。

这两种方法都允许您的部署利用内置的更新策略逻辑来避免停机。

注意:过去,我们建议使用 --recreate-pods 标志作为另一个选项。此标志在 Helm 3 中被标记为已弃用,取而代之的是上面更具声明性的方法。

告诉 Helm 不要卸载资源

有时,某些资源在 Helm 运行 helm uninstall 时不应被卸载。图表开发者可以在资源中添加一个注释,以阻止其被卸载。

kind: Secret
metadata:
  annotations:
    helm.sh/resource-policy: keep
[...]

注释 helm.sh/resource-policy: keep 指示 Helm 在 helm 操作(如 helm uninstallhelm upgradehelm rollback)导致其被删除时,跳过删除此资源。但是,此资源将成为孤儿。Helm 将不再以任何方式管理它。如果对已卸载但保留了资源的发布使用 helm install --replace,则会导致问题。

使用“部分”和模板包含

有时您想在图表中创建一些可重用的部分,无论是代码块还是模板部分。并且通常,将它们保存在它们自己的文件中更简洁。

templates/ 目录中,任何以下划线 (_) 开头的文件都不应输出 Kubernetes 清单文件。因此,按照惯例,辅助模板和部分被放置在 _helpers.tpl 文件中。

具有许多依赖项的复杂图表

CNCF Artifact Hub 中的许多图表都是创建更高级应用程序的“构建块”。但图表可用于创建大型应用程序的实例。在这种情况下,一个顶级的伞形图表可能有多个子图表,每个子图表充当整体的一部分。

目前将复杂应用程序从离散部分组合在一起的最佳实践是,创建一个公开全局配置的顶层伞形图表,然后使用 charts/ 子目录嵌入每个组件。

YAML 是 JSON 的超集

根据 YAML 规范,YAML 是 JSON 的超集。这意味着任何有效的 JSON 结构都应该是有效的 YAML。

这有一个优点:有时模板开发者可能会发现使用类似 JSON 的语法来表达数据结构比处理 YAML 的空白符敏感性更容易。

作为最佳实践,模板应遵循类似 YAML 的语法,除非 JSON 语法大幅降低了出现格式问题的风险。

小心使用生成随机值

Helm 中有一些函数允许您生成随机数据、加密密钥等。这些函数可以使用。但请注意,在升级过程中,模板会重新执行。当模板运行生成的与上次运行不同的数据时,将触发该资源的更新。

使用一条命令安装或升级发布

Helm 提供了一种将安装或升级作为单个命令执行的方式。使用 `helm upgrade` 命令并带上 `--install` 选项。这将使 Helm 检查发布版本是否已安装。如果没有安装,它将执行安装。如果已安装,则会升级现有的发布版本。

$ helm upgrade --install <release name> --values <values file> <chart directory>