Helm: 建议:允许在 values.yaml 中进行模板化

创建于 2017-05-24  ·  101评论  ·  资料来源: helm/helm

您好 Helm 团队,

我想建议在values.yaml文件中添加有限的模板渲染功能。 我相信这将是与子图表交互的一个有价值的功能。

例如,假设我正在为 Web 应用程序部署定义一个图表,它有一个简单的值文件,如下所示:

image:
    repository: xyz
    tag: latest
    pullPolicy: Always
port: 80

然后我想将此服务与nginx-ingress控制器捆绑在一起,因此我添加了依赖项和一些设置,包括一个告诉kubernetes-incubator/external-dns控制器如何将流量路由到 nginx 服务的设置:

image:
    repository: xyz
    tag: latest
    pullPolicy: Always
port: 80

nginx-ingress:
    enabled: true
    controller:
        service:
            annotations:
                external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}.example.com

就目前而言,这是不可能自动完成的。 初始安装后,用户需要helm upgrade <release-name> chart-name --set nginx-ingress.controller.service.annotations.external-dns.alpha.kubernetes.io/hostname=<release-name>.example.com ,因为在安装时无法将发行版名称作为顶级变量访问。

在我看来,此级别可用的变量非常有限,但包括.Chart.Release中的一些常见变量

如果提案被接受,我很乐意参与将此功能添加到 helm。

proposal

最有用的评论

围绕这个问题已经有很多好的评论。 我想更清楚地说明这个决定,并尝试合并分布在各种问题和 PR 中的所有信息。 首先,我完全明白这将解决一些痛苦的用例。 我不仅是 Helm 的维护者,而且我写了很多图表并且之前遇到过这个问题。 然而,我们不想实现这个有几个重要的原因:

tl;博士
在最简单的情况下,不想这样做的原因是您没有对提供模板值的文件进行模板化。 此外,Helm 3 将淘汰此功能

渲染顺序和逻辑
如果您对提供值的文件进行模板化,则会在呈现顺序、循环值依赖性和逻辑错误/故障方面引入大量复杂性。 当您开始向值添加完整的逻辑块时,这是尝试呈现清单时的又一个失败点。 这使得调试和诊断变得更加困难(因此评论了它会产生的错误数量)。 它还使模板更难编写,因为您必须考虑模板化值文件引入的可变性

向后兼容性
模板值文件将非 YAML 语法引入 YAML 文件。 这可能会破坏现有的生成 values.yaml 的模板解决方案。 根据经验,我使用了生成的值文件,其中文件中包含非 YAML 会破坏我们的工作流程。 鉴于许多人已经拥有围绕 Helm 值的工具,这将使模板化值文件成为向后兼容性中断。 我承认我们在保持向后兼容性方面并不完美,但在大多数情况下,我们在维护它方面做得不错,这样的功能肯定会破坏现有的工作流程

语言复杂度
模板化的值文件将需要 go-templates 的自定义方言,因为当 YAML 作为发布的一部分传递时,它本身不能是模板。 这也引入了在其他步骤之前创建专门的上下文以评估模板的复杂性。

Helm 3 已过时
在 Helm 3 中,作为事件/挂钩系统的一部分,我们将允许基于 Lua 的值修改以一种比模板化更简单的方式。 鉴于我们正在进行 Helm 3 的开发,添加具有上述所有缺点的功能将导致比增加的价值更多的流失。 所以我们并没有完全拒绝这一点,而是正在为 Helm 3 实施它(尽管以不同的方式)

希望这对每个人都有更多的澄清!

所有101条评论

您是否建议将此假设的 _values.yaml_ 文件中的文字值插入图表的模板,然后再次插入这些整个模板以扩展版本名称(使用您的示例),或者您是否想先插入 _values.yaml_ 文件,然后将其扩展形式作为插入图表模板其余部分的输入?

之前已经详细讨论过这个问题,但由于各种技术原因,它是有问题的。 但是一个问题少得多的解决方案是支持扩展看起来和行为类似于环境变量的事物。

因此,例如,您可以在 values.yaml 中执行myval: ${RELEASE_NAME}.foo.bar

在 Golang 方面,它应该像使用应该扩展的自定义变量列表调用os.Exand()一样简单。

就@seh而言,决定何时_ 运行扩展有点棘手。 我认为您希望在合并值之后但在将值发送到模板引擎之前执行此操作。 这将带来一个警告,即变量必须表示为格式良好的 YAML。 但我认为这是可行的

@seh我个人认为它更接近第一个选项,其中文字值在渲染之前被放入模板中。

@technosophos考虑到这一点,我认为您的建议听起来是一个很好的解决方案。

像这样工作的系统面临的一个挑战是简单的插值通常是不够的。 替换值必须作为词法有效的替换出现,然后导致需要一些可用的函数将提供的值转换为合适的值。

我还没有反对这个想法。 我们只需要怀疑上面提到的@technosophos的简单替换就足够了。

但是如果它是通过 values.yaml 文件进入的,那么转换和验证仍然可以在模板中进行,对吧? 例如: myPort: ${port}仍然可以像这样在模板中访问: {{ .myPort | int }}

更大的限制是values.yaml文件必须始终是有效的 YAML 文件。

这就是我想到的约束:如果“端口”变量持有该值会发生什么

“一个

或其他导致无效 YAML 的序列? 正如您所说,尽管可以解决它们,但要牢记语法限制:

myport: '${port}'

是的,因此必须以某种方式对值进行转义或引用,例如在 values.yaml 中:

portValue: "{{ .myPort | int }}"

根据到目前为止我想要的情况,我怀疑需要完整的模板,而不仅仅是简单的插值。

将 values.yaml 中的文字值插入到模板中,然后在文字值实际上是模板的情况下再次插入模板的想法很有趣,但似乎它取决于模板的编写方式。 例如,这不会按预期工作:

值.yaml:

otherServiceName: "{{ .Release.Name }}-my-service"

部署.yaml

otherService: {{ .Values.otherServiceName | lower }}

首先插入 values.yaml 是否存在问题(显然无法访问.Values )然后使用生成的 values.yaml 插入其他模板?

@braedon是的,有几个问题:

  1. values.yaml 文件必须是处于未解析状态的有效 YAML。 这对模板指令设置了有趣的限制。
  2. 值是分阶段合并的,有些阶段无法访问模板子系统(即 --set 解析、requirements.yaml 约束和 -f 合并操作。)
  3. 必须先计算所有值,然后才能初始化模板系统的某些部分。 否则, {{ define }}{{ include }}不能按预期工作, {{ template }}也不能。

本质上,一个 env var 语法很容易嵌入到values.yaml中,我们可以在不引入太多复杂性的情况下做到这一点。 但是使用成熟的模板是一项重大任务,即使成功,当人们遇到边缘情况时,也会导致提交一系列无休止的错误报告。

但我一定遗漏了一些东西......我不明白你上面的例子。 正在提议的是:

值.yaml

otherServiceName: "${RELEASE_NAME}-my-service"

部署.yaml

otherService: {{ .Values.otherServiceName | lower }}

操作顺序将在创建 Release 对象之后但在编译或呈现模板之前呈现values.yaml 。 如果发布名称是FOO ,则上述结果将是otherService: foo-my-service

@technosophos我设想在进行任何其他解析/合并/等之前在 values.yaml 文件上运行模板引擎。 文件已完成(如果此功能无法实现,我已考虑运行单独的模板系统来生成 values.yaml 文件,然后传递给 Helm)。

似乎这应该避免问题 1 和 2,并且是一个非常直观的系统? 但是,我不知道在 Helm 的代码库中实现这有多难,并且 3 仍然是一个问题(不确定禁止这些功能会有多大限制?)。

我的示例参考了@seh@benjigoldberg正在讨论的解决方案,而不是您的建议。
在他们的建议中(假设我理解正确)我的示例将在第一次通过后导致:

otherService: {{ .release.name }}-my-service

(即来自 values.yaml 的otherServiceName的原始值将被传递给 deployment.yaml,并小写)
然后在第二遍时不会按预期运行。

我当时在想的是:

如果/templates/values.yaml文件存在,那么逻辑是从/values.yaml读取变量来渲染模板,然后渲染的values.yaml完全接管,在所有其他模板渲染之前. 换句话说, /values.yaml引导模板。

我还遇到了一个我认为会从中受益的用例。 我怀疑它可能类似于@braedonotherService示例。

我遇到的问题是,当通过values.yaml覆盖服务名称时,目前无法包含.Release.Name #$ 。 用例是构建完全解耦的图表,然后可以使用父图表连接在一起。

例如,elasticsearch 图表的values.yaml文件中可能有一个serviceNameOverride值,这将允许用户覆盖服务名称。 类似地,kibana 图表的values.yaml文件中可能有一个elasticsearch.host值,用户可以使用它来设置 elasticsearch 集群的位置。 这些图表对彼此的实现细节一无所知。 它们是独立的,例如允许用户部署 kibana 来监控未使用 helm 部署的现有 elasticsearch 集群。

理想情况下,这些独立图表也可以用作创建父“elasticsearch-kibana”图表的构建块,通过子图表将它们“连接”在一起。 父图表将通过在values.yaml中设置以下内容将服务连接在一起:

elasticsearch.serviceNameOverride: {{ .Release.Name }}-elasticsearch
kibana.elasticsearch.host: {{ .Release.Name }}-elasticsearch

目前可以在值中不包含{{ .Release.Name }}的情况下执行此操作,但这很不方便,因为它会阻止用户在同一命名空间中安装多个“父”图表而不更改每次安装的值文件。

@alexbrand您可以使用tpl函数来呈现包含变量的值:

值.yaml

elasticsearch.serviceNameOverride: {{ .Release.Name }}-elasticsearch
kibana.elasticsearch.host: {{ .Release.Name }}-elasticsearch

模板.yaml:

name: {{.Values.elasticsearch.serviceNameOverride | quote | tpl }}

@eicnix要做到这一点,需要控制子图中的template.yaml ,并希望在需要的地方明确添加对此的支持。

我所追求的是一种编写父图表的方法,该图表可以执行此类操作,同时将子图表视为黑匣子。 我认为这也是@alexbrand所说的吗?

上面有人已经提到了最近添加的tpl函数(#1978)。 我可能误解了这里涉及的一些复杂性,但是对于我的用例和上面描述的其他一些用例,似乎所需要的只是在渲染模板之前通过tpl或等效项对值进行隐式传递. (我想您可能需要继续进行传递,直到没有进行任何更改,以应对递归引用。)我想您可能希望通过命令上的--expand-values参数来启用它行,但是这样的解决方案还有其他问题吗?

@eicnix感谢您的建议!

我无法让 template.yaml 中的管道正常工作,但下面为我工作(上面有 values.yaml):

模板.yaml

name: {{ tpl .Values.elasticsearch.serviceNameOverride . }}

我认为这和#2133 是同一个问题,顺便说一句。 (虽然不确定哪个最好保持开放。)

我们在 Helm 服务器的 2.6.1 中似乎还可以(但在 2.3.0 中不行)

@obriensystems你能详细说明一下这个想法吗? :)

这个针对 v2.6 的提案似乎还不错,但在 v2.3 中却不行? 也许您指的是另一张票?

我喜欢@travishaagen@braedon提出的方法。 我也可以想象使用多个 -f values.yaml 参数,例如

helm install -f raw_values.yaml -tf values_rendered_with_template.yaml ...

首先它会读取raw_values.yaml这必须是有效的 yaml。 然后 helm 将读取values_rendered_with_templating.yaml并通过模板引擎运行它,该引擎可以访问raw_values.yaml中的变量。 然后将渲染的模板馈送到下一个阶段,依此类推。

我在上面的命令行中使用tf来表示它是一个值文件,应该在使用之前用模板呈现。 @travishaagen建议的文件结构将是默认设置。

这个问题和 #2399 是相当大的障碍,真正影响从子图表组成父图表。 就像现在的情况一样,helm 不允许为任何重要的事情组合图表。 我想知道是否有更好的组合图表解决方案可以解决手头的问题以及#2399。

问题在 90 天不活动后变得陈旧。
使用/remove-lifecycle stale将问题标记为新问题。
陈旧的问题在额外的 30 天不活动后腐烂并最终关闭。

如果这个问题现在可以安全关闭,请使用/close来关闭。

向 sig-testing、kubernetes/test-infra 和/或fejta发送反馈。
/生命周期陈旧

/删除-生命周期陈旧

我想我们不一定要让 helm 复杂化。 例如,像https://github.com/roboll/helmfile这样的简单工具可用于值模板化。

@mumoshu我遇到了几种情况,我需要访问values.yaml中的发布名称(最近的 Kafka 图表可以设置为不使用 Zookeeper 图表作为要求,但随后需要Zookeeper 位置为Value ,在我们的部署中基于发布名称...)。 但是,我不希望需要一些额外的工具并将其强制我们的用户来实现这一点:微笑:

@NicolasT感谢您分享您的用例👍

你可能已经知道了,但对于那些还没有的人来说——你可以用helm install incubator/zookeeper --name myzookeeper你的 Zookeeper 版本命名一个具体的名称,然后将该名称重新用作 Kafka 版本的值。

该线程中建议的解决方案似乎不支持您的用例。 抱歉,如果我没有关注您,但您无法从另一个独立版本访问另一个版本的名称,对吗?

您的似乎更多是关于“扩展可以从模板引用的内容”,而不是“允许在值中进行模板化”。

然而,我不希望需要一些额外的工具并将其强制我们的用户来实现这一点😄

我不能同意更多😄
支持每个人的用例的多合一工具真的是天赐之物。

实际上,我曾经希望 helm 是否可以与具有动态服务依赖关系解析(您的用例)功能(如atlassian/smith )的声明式工作流管理器集成。

想象一下 Helm CRD + Smith 的集成解决方案 - 希望优雅地解决您的问题?

然而,另一方面,我觉得将像这样的严肃工作流引擎集成/实现到用于其他目的的工具中对于维护而言将是一场噩梦。

所以,我宁愿希望 helm 核心保持简单,并且 helm 插件系统以某种方式扩展以支持像“值转换器”这样的扩展点,它能够在从一个传递到另一个之前修改 values.yaml。 嗯,这听起来几乎像史密斯!

我的主要用例是#2506。 在一般情况下,我需要一个父图表能够将其自己的值传递给子图表,而不必在打包时知道这些值。

你好大师,我对这个讨论感到困惑。 提出的解决方案无法解决我的用例问题:

值.yml:
ui_secret:# 在此处使用“{{ tpl randAlpha 8 }}”会报错。

模板1.yml:
UI_SECRET: {{ .Values.ui_secret }}

模板2.yml:
UI_SECRET: {{ .Values.ui_secret }}

我知道参数 ui_secret 可以在 helm 外部生成并通过 --set 'ui_secret=xxx' 传递。 但是为什么不让 helm 生成一个默认的随机值呢?
默认随机值可用于 salt/password/ca_certificate/certificate_pair 等。

但是为什么不让 helm 生成一个默认的随机值呢?

你好! 您希望如何实现这一目标?

对我来说,在values.yaml中进行模板化似乎并不能解决它。 如果你有一个 IMAGINARY values.yaml 怎么办:

ui_secret: "{{ randAlpha 8 }}"

你运行helm upgrade --install yourrelease yourchart -f values.yaml多次?
对我来说,helm/tiller 似乎没有办法何时执行/不生成 ui_secret。

所以,我相信你应该像你提到的那样通过--set提供一个预先生成的秘密,不管这个提议是否被接受。

相反,您是否有可能只在 k8s 密钥中生成一个随机值?
这样所有通过 helm 图表创建的 pod 都可以通过名称引用该秘密。

@mumoshu升级场景:
1) 如果在 values.yaml 中设置了 ui_secret,则没有影响;
2)否则 helm 应该从某个地方(可能是 Tiller)检索现有 values.yaml,然后将这个现有 values.yaml 与“-f values.yaml”合并

helm 应该是自包含的,用于生成随机 salt/password/ca_certificate/certificate_pair,并且不需要最终用户生成外部 helm。 这提供了更好的可用性。

“在 k8s 密钥中生成随机值”是可行的,但与 'ui_secret: {{ randAlpha 8 }}' 相比并不简单

否则 helm 应该从某个地方(可能是 Tiller)检索现有的 values.yaml,然后将这个现有的 values.yaml 与“-f values.yaml”合并

并没有真正冒犯,但它似乎引入了更多的边缘案例。
鉴于您的 values.yaml 始终包含ui_secret: {{ randAlpha 8 }} ,helm/tiller 如何区分该值是否已设置,以及何时覆盖是否来自--set-f values.yaml #$ ?

“在 k8s 密钥中生成随机值”是可行的,但与 'ui_secret: {{ randAlpha 8 }}' 相比并不简单

同意。 但无论如何,模板化似乎不是我的解决方案。

您需要像https://github.com/atlassian/smith这样的工作流引擎来处理服务依赖项(能够传递值以供后续的、依赖的helm install s 使用)。

我想您可以向 helm 提交功能请求以支持此用例(工作流引擎、服务依赖管理)。 但是,我不知道这在舵机/分蘖范围内是否可行。

安装图表时,'--set' > '-f values.yaml' > values.yaml 在图表中
升级图表时,'--set' > '-f values.yaml' > 为已安装的图表生成 values.yaml

我建议在 BOSH https://bosh.io/docs/variable-types.html 中实现。 BOSH 支持显式变量类型定义,而 helm chart 中的 values.yaml 支持纯 yaml。 所以在 values.yaml 中支持模板函数会很棒,例如 'ui_secret: {{ randAlpha 8 }}'。

虽然这将为某些用户提供更好的可用性,但它也会使 helm 膨胀。

我对结合 helm 处理任何类型的秘密的建议是在秘密存储(kube 秘密、保险库等)中管理它们,并从图表中引用它们。

我建议在 BOSH https://bosh.io/docs/variable-types.html 中实现。 BOSH 支持显式变量类型定义,而 helm chart 中的 values.yaml 支持纯 yaml

我已经查看了 Bosh 关于变量插值的文档。 在我看来,Bosh 不支持(1)从应用程序的输出(在我们的例子中由 helm/tiller 创建的 k8s 对象)填充变量,以及(2)通过模板生成持久值。 请参阅https://bosh.io/docs/cli-int.html中的-v internal_ip=192.168.56.6的概念。

甚至 Bosh 似乎也不支持您的开箱即用用例。
然后,我最好的猜测仍然是您需要像我之前解释过的工作流引擎之类的东西。
还是我还缺少什么..?

@johngmyers嗨! 很抱歉,您的用例似乎不受单独模板的支持。

为了支持您的用例,不仅是模板,helm/tiller 还需要管理图表及其版本的协调,以及从先前版本到依赖图表的出/入传播。

AFAICS,目前不知道什么不是 helm/tiller。 正如@eicnix指出的那样,在 helm/tiller 范围内执行此操作会大大膨胀。

要分开讨论,我可以建议您提交另一个问题。 但另一方面,老实说,我没有可行的方法来在 helm/tiller 内部实现它而不使其膨胀。

所以你最好的选择是调查另一个工作流引擎,比如 atlassian/smith(我不是 smith 的作者支付的。请告诉我是否有其他选择😉)+ 可能即将推出的掌舵 CRD。

@mumoshu我相信#2506 充分描述了我的用例。 我看不出提交另一个问题有什么帮助。

我看不出模板values.yaml不能处理我的用例。 Helm 支持子图表,这些子图表由requirements.yaml协调或将子图表嵌入到charts目录中。 父图表可以通过以下方式传递值:

subchart:
  serviceName: {{.Chart.Name}}
  serviceVersion: {{.Chart.AppVersion}}

当然,模板化 values.yaml 并不是实现允许图表将打包时未知的值传递给子图表的唯一方法。 但如果它是“臃肿”的 Helm,那么添加几乎任何功能都是“臃肿”的 Helm。

也许我们可以在模板过程之前添加一个有限值渲染。 我只允许访问.Chart.Release以避免复杂的问题,例如值之间的循环引用。 但这仍然可以解决许多人们希望从值中引用发布或图表实例的用例。

我有一个用例,我需要一个父图表来传递源自其 .Values 的值。 这允许将关注点分离到子图中,而父级不会暴露它是使用子图实现的事实。

例如,我们将有一个“平台”子图,它封装了在我们的内部平台库中实现的服务所需的常见内容。 它希望有一个值“platform.oauthClient”,服务图表将传入该值以指示服务是否需要配置为 OAuth 客户端。 其实现可能是将.Values.oauthClient向下传递到“平台”子图的子图。 将来,我们可能希望将“平台”图表的该功能的实现更改为其他内容。

模板化的值可能希望位于顶层values.yaml (可能是templates/_values.yaml )以外的其他地方,以便它们看起来不是父图表的外部 API 的一部分。 这也可以解决循环引用问题。

@johngmyers父图表值也应该在子图表中可用。 我相信我之前提出的方法也可以解决您的问题。 您可以像前面描述的那样在父图表中创建一个模板值,然后将其传递给子图表。

仅供参考,我已经关联的 PR(即#3252)实现了这个功能,我认为可能满足这些不同的用例。 我们在内部使用它已经有一段时间了,将父值传递给子图表是我们用它解决的问题之一。

到目前为止,PR 没有取得太大进展。 任何反馈,无论是关于 PR 本身还是在构建中尝试该功能,显然都是非常受欢迎的。

@eicnix Helm 文档指出:

但是较低级别的图表无法访问父图表中的内容,因此 MySQL 将无法访问 title 属性。 就此而言,它也不能访问 apache.port。

所以这似乎是不可能的。

@johngmyers子图表无法访问父值,但父图表可以将值传递给子图表。 假设您有一个名为 mychild 的子图表,您可以通过这种方式将父 values.yaml 中的值传递给它:

mychild:
  overridenValue: true

@eicnix但是这些值必须在 helm 打包时知道。 你甚至读过我的用例吗?

Helm 团队是否知道这个问题/提案? 它于 10 个月前的 2017 年 5 月 25 日创建。 这里有很多讨论,并且创建了几个重复/类似的问题。
此问题尚未分配给任何人。

我们看到评论“它会膨胀”。 那么哪种解决方案不会膨胀? Helm 团队能否为此提案提供计划和预计到达时间,并至少给出合理的解决方法/解决方案?

似乎 PR https://github.com/kubernetes/helm/pull/3252可以支持 values.yml 中的模板。 如果 Helm Team 这么认为,请加快合并。
谢谢。

@mparryhttps://github.com/kubernetes/helm/pull/3252中的解决方案似乎可以优雅地处理我的用例。 要是能合并就好了!

看起来与此问题相关的拉取请求尚未被接受,但我刚刚发布了一个包,它消除了我所看到的导致我遇到此问题的痛点。 它被称为墨卡托,可以在这里找到。 我知道它不适合所有这些用例,但希望有人会发现它有用!

问题在 90 天不活动后变得陈旧。
使用/remove-lifecycle stale将问题标记为新问题。
陈旧的问题在额外的 30 天不活动后腐烂并最终关闭。

如果这个问题现在可以安全关闭,请使用/close来关闭。

向 sig-testing、kubernetes/test-infra 和/或fejta发送反馈。
/生命周期陈旧

/删除-生命周期陈旧

@johngmyers你有没有为https://github.com/helm/helm/issues/2492#issuecomment -370895073 找到一个好的解决方案? 我遇到了同样的封装问题。 我真的很想避免为helm编写一个包装器,以递归地将我的模板化值预渲染到 values.yaml 文件中。

我没有一个好的解决方案。 我有一个解决方法,它使用应用了#3471 的 Helm 私有构建,允许我在打包时(冗余地)将父图表名称和版本传递给子图表。

这和糟糕的合并支持基本上阻止了我进行任何合理的封装。 我希望 Helm 3 对 LUA 的使用将有助于解决这个问题。

由于 Tiller 的模板渲染引擎工作原理背后的各种技术和设计原因,我们不打算支持预处理 values.yaml 作为 Helm 2 或 Helm 3 的 Go 模板。但是,我们强烈鼓励社区编写包装器或 Helm为其用例支持此功能的插件! 我们从用户那里听到的一个这样的例子是gomplate ,但我们希望看到其他可以在运行helm install之前提供此功能的预处理器。

重申此线程中先前的评论:在 values.yaml 中添加对扩展环境变量的支持将是一个容易解决的问题,而不会给渲染引擎带来太多复杂性,但使用成熟的模板将是一项重大任务。

即使我们能够支持此功能,当人们最终遇到边缘情况时,它也会导致提交的错误报告大量增加。 鉴于当前问题队列的数量以及有限数量的维护者和社区成员参与以帮助权衡问题,我们根本没有人力资源来支持这项工作。

给您带来的不便,我们深表歉意,感谢您的理解!

@bacongobbler - 在我认为我真正完全理解如何在我的工作流程中有效地使用 helm 之前,我最初打开了这个问题。 我完全同意关闭票的决定。 我把它打开主要是因为最终有大量关于这个问题的社区讨论。 自从我打开这张票并学习了更多“标准”工作流程以来,我实际上并不认为我曾经发现自己想要这个特殊功能。 所以,无论如何,👍

@benjigoldberg你能澄清一下你是如何解决这个问题的吗? “标准”工作流程是什么意思? 我们的问题是简单地能够在多个子图表中设置相同的密码,但在顶级伞形图表中只公开一个密码值。 澄清一下,我们的应用程序有一个顶级图表,其中包括 prometheus 和 grafana 的子图表。 我们希望所有三个的密码都相同。 现在,我们不得不暴露图表的内部,并且分别设置了 3 次密码,这严重破坏了封装性。

@nuwang在那种情况下,我们通常会使用您所描述的顶级伞形图表,在顶级创建一个秘密,然后让每个子图表使用该秘密来访问凭证。 希望这会有所帮助——我也可能误解了你的描述。 让我知道!

@benjigoldberg如果您不控制子图表怎么办? 例如,grafana 是第 3 方。 我们不能轻易更改其模板以使用伞形图中定义的秘密。

@dtshepherd我们肯定遇到了这些问题——在这些情况下,我们已经分叉了 helm charts repo 并将选项贡献回上游,然后使用我们自己的 fork chart 直到合并更改(然后我们迁移回主 repo图表)。 有活动的部分,但现在它不再发生那么多了。 主图表存储库已经相当成熟,许多图表都具有这种开箱即用的灵活性。

是的,大多数 3rd 方图表模板都很好。 但是,如果我有一个封装图表来封装这些第 3 方图表,我无法在不将tpl放入第 3 方图表的情况下对某些公共变量进行规范化。

@dtshepherd您可以从该顶级值文件中控制变量/秘密的名称,然后将其推送到子图表,对吗?

在 Elasticsearch/Kibana 的情况下,服务名称派生自发布名称。 但它不等于发布名称。 有人可以建议如何仅使用变量重命名将生成的服务名称传递给 Kibana?

@benjigoldberg是的,但这会破坏封装,因为顶层图表需要知道第 3 方图表中的变量名称。 中间的图表应该是抽象层,但不能没有模板化的 values.yaml 文件。

@benjigoldberg感谢您的回复。 我们不能使用秘密的原因是因为秘密名称必须作为单个变量传递给子图表。 但是,由于值文件不支持模板,您必须传递一个硬编码的常量名称,而不是在其前面加上版本名称(因为版本名称是一个变量,自然需要模板化)。 这意味着每个命名空间只能安装一个图表实例,这对我们来说是一个不可接受的限制。 这几乎消除了使用秘密作为选项,我们又陷入了不得不修改我们无法控制的第三方图表的陷阱,或者在我们的案例中单独输入密码三次,完全暴露了实现细节的伞形图。

因此,无论哪种方式,这都会破坏封装,破坏独特的发布,破坏 DRY,并且通常是上述所有内容的组合。

我们又遇到了一些我想到的:

  • 需要在多个子图表之间共享的持久卷声明,但还必须支持多个版本。
  • 用户提供的共享变量,需要推送到多个子图表。

Helm 无法封装是我们的主要痛点。 就在前一周,我就这个问题向 Helm 的一家大型云服务公司发出了警告。

这张票有点过分了。 要求是允许父图表将值传递给子图表,其中该值在打包时未知,但源自父图表自己的.Value.Chart和/或.Release 。 我相信模板是必需的,但它不必在 values.yaml 中。

我上面关于templates/_values.yaml的建议可能是实现这一目标的一种方法,而没有 #2133 的边缘条件和复杂性。 维护者表示他们不会支持 values.yaml 模板化,但尚不清楚他们是否会考虑这种替代值模板化提案。

围绕这个问题已经有很多好的评论。 我想更清楚地说明这个决定,并尝试合并分布在各种问题和 PR 中的所有信息。 首先,我完全明白这将解决一些痛苦的用例。 我不仅是 Helm 的维护者,而且我写了很多图表并且之前遇到过这个问题。 然而,我们不想实现这个有几个重要的原因:

tl;博士
在最简单的情况下,不想这样做的原因是您没有对提供模板值的文件进行模板化。 此外,Helm 3 将淘汰此功能

渲染顺序和逻辑
如果您对提供值的文件进行模板化,则会在呈现顺序、循环值依赖性和逻辑错误/故障方面引入大量复杂性。 当您开始向值添加完整的逻辑块时,这是尝试呈现清单时的又一个失败点。 这使得调试和诊断变得更加困难(因此评论了它会产生的错误数量)。 它还使模板更难编写,因为您必须考虑模板化值文件引入的可变性

向后兼容性
模板值文件将非 YAML 语法引入 YAML 文件。 这可能会破坏现有的生成 values.yaml 的模板解决方案。 根据经验,我使用了生成的值文件,其中文件中包含非 YAML 会破坏我们的工作流程。 鉴于许多人已经拥有围绕 Helm 值的工具,这将使模板化值文件成为向后兼容性中断。 我承认我们在保持向后兼容性方面并不完美,但在大多数情况下,我们在维护它方面做得不错,这样的功能肯定会破坏现有的工作流程

语言复杂度
模板化的值文件将需要 go-templates 的自定义方言,因为当 YAML 作为发布的一部分传递时,它本身不能是模板。 这也引入了在其他步骤之前创建专门的上下文以评估模板的复杂性。

Helm 3 已过时
在 Helm 3 中,作为事件/挂钩系统的一部分,我们将允许基于 Lua 的值修改以一种比模板化更简单的方式。 鉴于我们正在进行 Helm 3 的开发,添加具有上述所有缺点的功能将导致比增加的价值更多的流失。 所以我们并没有完全拒绝这一点,而是正在为 Helm 3 实施它(尽管以不同的方式)

希望这对每个人都有更多的澄清!

感谢您抽出宝贵时间提供此详细说明@thomastaylor312 ,这可以更好地清除此决定。

我认为核心问题是将图表的外部接口与其内部实现细节分开。 这似乎需要一些简单的机制来将图表作为外部值文件公开的内容与这些值如何转换和分发到第三方子图表分开,即使在 Helm 3 中也是如此。Lua 事件挂钩很可能是答案,或者也许这对于可能是天生的特征来说太复杂了,但从我的无知立场很难评估它。 因此,请将此视为 Helm 3 的功能请求,并感谢 Helm 的所有辛勤工作 - 使用它很愉快!

外部参考: https ://github.com/helm/community/blob/master/helm-v3/002-events.md

感谢@nuwang 的精彩反馈! 我们将确保在 Helm 3 的所有工作中都考虑到这一点

这是一个解决方法。

只需在values.yaml中编写一些模板表达式作为纯 yaml 字符串。
喜欢。

helloworld: Hello World!!
sometemplate: '{{ .Values.helloworld }}'

并使用tpl来评估模板文件中的sometemplate

{{ $global := . }}
{{ tpl (trimAll "\"'" .Values.sometemplate) $global }}

输出将是

你好世界!

@wpc009是的,这行得通,唯一的不便是tpl只对字符串进行一次评估,这不是一件坏事,但您应该记住:

publicHost: "{{ .Release.Name }}-{{ .Release.Namespace }}-paymentgw.{{ .Values.global.publicDomain }}"
callbackUrl: "{{ .Values.global.publicDomainSchema }}://{{ .Release.Name }}-{{ .Release.Namespace }}-paymentgw.{{ .Values.global.publicDomain }}/validate" # this will work
# callbackUrl: "{{ .Values.global.publicDomainSchema }}://{{ paymentgw.publicHost }}/validate" # this will not

@wpc009是的,这行得通,唯一的不便是tpl只对字符串进行一次评估,这不是一件坏事,但您应该记住:

publicHost: "{{ .Release.Name }}-{{ .Release.Namespace }}-paymentgw.{{ .Values.global.publicDomain }}"
callbackUrl: "{{ .Values.global.publicDomainSchema }}://{{ .Release.Name }}-{{ .Release.Namespace }}-paymentgw.{{ .Values.global.publicDomain }}/validate" # this will work
# callbackUrl: "{{ .Values.global.publicDomainSchema }}://{{ paymentgw.publicHost }}/validate" # this will not

这是真的。 被引用的值必须是一个常数。
或者你需要编写一个嵌套的tpl

@eicnix尝试过{{ .Values.labels | quote | tpl | toYaml | indent 4 }} ,但收到此错误: executing "financial-accounting/templates/worker-deployment.yaml" at <tpl>: wrong number of args for tpl: want 2 got 0

值.yaml

labels:
  revision: revision-{{ .Release.Revision }}-{{ .Release.Revision | quote | b64enc }}

开发.yaml

metadata:
  labels:
{{ .Values.labels | quote | tpl | toYaml | indent 4 }}

我得到了它的工作:

值.yaml

dbName: test_{{ .Release.Namespace }}

部署.yaml

env:
  - name: DB_NAME
    value: {{ tpl .Values.dbName . | quote }}

我正在使用 fabric8.io 公开控制器,它为公开的服务提供了一个默认 URL。 现在我想根据命名空间创建一个自定义 URL,比如
fabric8.io/exposeUrl: https://{{ tpl .Release.Namespace }}.jx.org.co ,这是暴露控制器要选择的东西,所以我没有使用 {{ tpl ... }} 的选项。 有没有办法可以在 values.yaml 本身中完成名称解析?

其他人在各种子线程中指出的几种解决方法:

  1. 使用 Helm 全局值将变量从父级传递给子级( ref )。
  2. 使用 YAML 锚来改善 values.yaml 文件中的干燥度(参考,感谢@szwed)。

这些并没有涵盖所有用例,但是它们很适合放在您的工具带中。

我也想在我的 values.yml 中进行模板化,主要是为了使用图表来堆叠复杂性和特定配置。 因此,我从通用图表开始,在具有大多数配置的顶部构建我自己的图表,并在部署时留下一些最终细节需要定义。 YAML 锚是不够的:它们不能替换内部 YAML 值(如多行配置文件字符串),并且缺乏像子图表值这样的 Helm 依赖逻辑。

我相信我们需要更强大的功能来分层图表而不改变底层图表来构建 Helm 图表社区。 现在需要上游社区图表来启用复杂逻辑中的所有各种用例,而不是分层。 而且这种逻辑需要上游更改来启用许多功能,而分层需要的上游更改要少得多。 只需查看大多数社区图表中公开的所有属性,这些属性和工作方式的不一致,以及 _helpers.tpl 文件中的重复。 最糟糕的是,每个社区图表都需要自己明确启用这些设置。 这么浪费精力。

@nicorikken Sry 用于无耻插件,但我建议尝试helmfile用于模板值和分层模板化 helm 版本和helm-x用于修改图表而不分叉,甚至 helmfile + helm-x 的组合。

我同意我们需要一些东西来处理你提到的问题。 另一方面,我不想在范围内蠕变掌舵。 这就是为什么我自己开始维护这两个树外项目的原因。

@mumoshu感谢您的插件。 我将其视为前面提到的潜在解决方案。 我会看看它。 它将带来额外工具的成本,我们可能还必须与 Argo-CD 集成(这甚至可能吗?)。 现在我已经分叉了 repo 并参数化了命名空间,并对上游稳定图表 repo 进行了 PR。 让我们看看维护者是怎么做的。

@nicorikken感谢您的回复!

我想我了解额外工具的成本。 但另一方面,我在质疑自己——如果有一个涵盖所有 helm + helmfile + helm-x 用例的巨大工具,它会容易学习/使用吗?

恕我直言,这几乎是文档问题。 如果附加工具有足够好的文档将其与上游工具和第三方工具(在此特定情况下为 helm 和 argo cd)集成,则它与 in-tree 解决方案相当。

这甚至可能吗?

是的 :) helmfile template与 argo-cd 的“配置管理插件”功能配合得很好。

对于指定命名空间的特定用例,我已经对 Jenkins Helm 图表https://github.com/helm/charts/pull/15202进行了上游 PR 这添加了一个namespaceOverride参数。 我希望这种模式将被更广泛地采用,以实现集群范围的 Helm 图表部署。
我将不得不花一些时间来更多地研究 Helmfile。 通过在上游图表之上启用高级集群范围模板并与 Argo-CD 集成,它似乎确实满足了我们的要求。

在为affinity规则设置某些内容时,有谁知道如何执行此操作? 例如,在stable/nginx-ingress的 NGINX Ingress 图表上,我想将controller.affinity值设置为以下内容:

值.yaml

controller:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 5
        podAffinityTerm:
          topologyKey: "kubernetes.io/hostname"
          labelSelector:
            matchLabels:
              app: {{ template "nginx-ingress.name" . }}
              component: "{{ .Values.controller.name }}"
              release: {{ .Release.Name }}

component键使用{{ tpl .Values.controller.name }}有效,但我还需要apprelease部分,它们在上面显示时不起作用。

其他人是否需要调用下游template函数和Release.Name值?

@sc250024嘿! 您在 values.yaml 模板中的何处定义nginx-ingress.name模板?

它需要预先用{{define ...定义,如https://golang.org/pkg/text/template/#hdr -Actions 中所述

此外 - {{.Release.Name}}仅适用于 $# helmfile.yaml releases[].values下的字符串。

这应该有效:

releases:
- ...
  values:
  - {{`{{.Release.Name}}`}}/values.yaml

这不起作用:

releases:
- ...
  values:
  - foo: {{`{{.Release.Name}}`}}/values.yaml

如果您需要其中一个,我认为值得提出专门的功能请求。

@mumoshu它没有在我的值文件中定义,它包含在位于stable/nginx-ingress的 NGINX Helm 图表中。 这实际上是我的观点,我正在尝试使用该图表中已经存在的模板。

作为记录,我没有使用stable/nginx-ingress作为我正在开发的图表中的子图表; 我只是直接使用它来部署 NGINX 入口。

它没有在我的值文件中定义,它包含在位于 stable/nginx-ingress 的 NGINX Helm 图表中。 这实际上是我的观点,我正在尝试使用该图表中已经存在的模板。

不幸的是,这是不可能的,因为 Helmfile(值)模板独立于 Helm(图表)模板工作,并且nginx-ingress.name是在 Helm 图表中定义的用于图表模板的模板。

可能唯一的方法是将 nginx 图表中定义的{{define "nginx-ingress.name"}}块复制到您的 helmfile 值模板values.yaml中?

我已经考虑过有关此问题的解决方法,并且我做到了。 这可能是不好的做法,但我会分享它以防万一。 您可以在要模板化的文本中放置一个占位符,然后将该占位符替换为您希望在模板中的 yaml 文件中使用的模板。 所以这里有一个例子:

在 values.yaml 中:

keys:
  - name: key_for_test
    value: CHANGEFORRELEASE-my-value

我将在秘密 yaml 中使用它:

secrets_kv.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-test-kv
  annotations:
    description: Key/Value pairs to save in test datastore
  labels:
    app: test
    tier: backend
    vendor: test
    support: {{ template "supportMethod" . }}
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
type: Opaque
data:
  {{- $var := .Release.Name }}
  kv.yaml: {{ toYaml .Values.keys | replace "CHANGEFORRELEASE" $var | b64enc | quote }}

我希望它对某人有所帮助。

这是我遇到的第一个线程,而不是在这里发表评论......
另请参阅#2514

:) 值得庆幸的是,最新的 Helm 手册说明了如何实现这一点。
https://helm.sh/docs/howto/charts_tips_and_tricks/#using -the-tpl-function

诀窍是将变量封闭在"或 yaml 块中|- ,而不是在模板中将其引用为{{ tpl .Values.variable . }}
这似乎让 Helm 很高兴。

例子:

$ cat Chart.yaml | grep appVersion
appVersion: 0.0.1-SNAPSHOT-d2e2f42


$ cat platform/shared/t/values.yaml | grep -A2 image:
image: 
  tag: |-
    {{ .Chart.AppVersion }}


$ cat templates/deployment.yaml | grep image:
          image: "{{ .Values.image.repository }}:{{ tpl .Values.image.tag . }}"


$ helm template . --values platform/shared/t/values.betradar.yaml | grep image
          image: "docker-registry.default.svc:5000/namespace/service:0.0.1-SNAPSHOT-d2e2f42"
          imagePullPolicy: Always
      image: busybox

否则会抛出错误..

$ cat platform/shared/t/values.yaml | grep -A1 image:
image: 
  tag: {{ .Chart.AppVersion }}

1 $ helm template . --values platform/shared/t/values.yaml | grep image
Error: failed to parse platform/shared/t/values.yaml: error converting YAML to JSON: yaml: invalid map key: map[interface {}]interface {}{".Chart.AppVersion":interface {}(nil)}

现在,我在 CI 工作中所做的是在 values.yaml 文件上运行helm template 。 它工作得很好atm。

cp values.yaml templates/
helm template $CI_BUILD_REF_NAME ./ | sed -ne '/^# Source: templates\/values.yaml/,/^---/p' > values.yaml
rm templates/values.yaml

helm upgrade --install ...

如果您有多个-f values.yml文件,这会中断,但我正在考虑编写一个小的helm包装器,它基本上运行每个values.yaml文件的 bash 脚本。

有一个用例,您需要将部署名称传递给您无法控制的依赖关系图表。

例如,我正在尝试为 zookeeper 设置 podAffinity。 我有一个应用程序掌舵图,它将 zookeeper 设置为依赖项。 在这种情况下,我通过值将 pod 反亲和性传递给 zookeeper。 所以在我的应用程序 values.yaml 文件中,我有一个 zookeeper.affinity 部分。 如果我有能力在值 yaml 中获取发布名称,我只需将其设置为默认值并完成它。

但是现在对于每个部署我都必须覆盖这个值,这是一个大问题。

@thomastaylor312和其他人:

我已经阅读了关于这个一般主题的许多相关问题,似乎模板values.yaml的概念是不切实际的,不平凡的,等等。

在图表模板/部署/等期间支持一些额外的中间阶段是否合理? 其中一些任意(即用户提供的)脚本(即 Python、BASH、Lua 等)接收静态values.input.yaml (又名values.yaml )的副本并可以访问各种变量(即.Release.* ),并且脚本可以负责动态创建“最终” values.output.yaml文件(实际上是其余图表模板直接使用的)? 如果 Helm 能够为此提供“钩子”,这似乎是一个可行的折衷方案,即“这里有所有数据和一个钩子来触发你的脚本创建values.output.yaml ,玩得开心!”。

想到的一件事是这种方法是否可以正确处理子图表以及.变化的范围/含义,所以我必须遵从 Helm 维护人员来决定这是否可以解决或者只是另一罐蠕虫(即这个阶段是否需要为子图表递归触发)。

我现在正在做这样的事情,但它需要我为我的图表提供一个“启动器”脚本,所以下游消费者不能简单地“掌舵安装”我的图表(有点类似于helmfile的用例) . 这是因为需要模板化第三方图表/容器的值(出于技术和法律原因,我无法对其进行修改)。 在这些图表中,我有等同于.Release.Namespace的硬编码值出现在任何地方(在 K8S 服务的集群内 FQDN、主机名数组、环境变量等的 URI/URL 字符串内部)。 每次我需要将顶级图表部署到不同的命名空间时,如果不是为了我的帮助脚本,我必须修改values.yaml中的大约 30 个条目。 如果有某种方法可以对此进行标准化(即使不是通过我上面描述的实现),那将会很有帮助。

@IAXES新的渲染后东西可能有助于解决这个问题吗?

@thomastaylor312在我自己的特定用例中,绝对是的。 在其他人描述的用例中(即非平凡的,需要一定程度的动态生成values.yaml文件,以及访问部署时变量,例如.Release.Namespace等.),我强烈怀疑它也可以解决这些问题(需要其他想要/需要此线程/问题顶部描述的原始功能的人提供一些额外的反馈)。

虽然我不精通 Helm 代码库,但我可能会参与或提供设计/测试支持(或者如果我可以 ping 协作者/维护者以获取有关注入位置的建议,则可以将其作为概念验证来解决我的代码片段来创建“钩子”系统)。

如果这是值得探索的事情,那么 +CC helmfile维护者/所有者(咨询/意见/反馈/等)可能也是一个好主意,因为它可能会挂钩现有的解决方案概念验证。 如果可以设置这些钩子并为“填补空白”提供任意解决方案步骤,最终消费者/用户仍然只需要执行helm install (可能加上 apt/yum/apk 安装一些依赖项) , 那将是真棒!

@benjigoldberg @fsniper我上面描述的用例是否会以合理的方式解决您的用例?

@IAXES如果我理解正确你的建议,helm 为外部应用程序提供钩子,然后将它们的输出用作静态。

这是一个值得探索的有趣想法,同时完全无需 helm 的帮助就可以实现。 你已经承认你一直在使用这个过程。 我相信所有相关方都可以自己实施。

考虑到 helm 包括一个足以做到这一点的模板引擎,我对这种方法并不满意。 首先,这需要设置外部应用程序并将其集成到可能已经在运行的进程中,例如 CI/CD,其次更重要的是需要一个未明确定义的进程外模板。 正如我在这个定义之前所说的那样,标准化已经在 helm 到位。 那么为什么不在 helm 中使用它并为已经请求的功能提供方法呢?

或者实现已经讨论和记录的 lua 脚本可以用于此。 当然它也不可用。

目前我的方法是使用 helmsman values 文件环境值替换。 它是有限的,但可以与一些啤酒花一起使用。

这个问题使得为所有应用程序使用带有父掌舵图的蓝绿色部署更具挑战性,至少如果您的任何值是特定于环境的 URL 或数据库连接。 现在我将尝试@leox-phq 的解决方法。

我还想知道是否可以将创建templates/values.yaml文件而不是values.yaml文件用作告诉 Helm 进行值替换的约定...不确定这是否有助于解决一些问题提到的在 Helm 中本地实现这个的问题......

更新:@leox-phq 的解决方法可以通过使用 $#$ helm template $#$ 的--show-only选项来改进以避免使用sed进行替换:

helm template . --show-only templates/values.yaml > values.yaml

我正在寻找一种在 values.yaml 文件中使用Chart.yaml appVersion的方法。 不确定是否已经有解决方案,或者这个问题是必需的。

@nodesocket它已经存在于.Charthttps://docs.helm.sh/docs/chart_template_guide/builtin_objects/

@thomastaylor312我以为您只能在模板中使用.Chart.AppVersion而不是在 values.yaml 文件中使用?

啊抱歉,我错过了那部分。 是的,由于这里提到的原因,没有值文件的模板。

在阅读了values.yaml中的模板并尝试了不同的解决方案之后,这似乎是目前最干净的解决方案(就不必查看太多地方而最具可读性而言):

  • 通过 Terraform 部署 Helm 图表,提供外部文件values.yaml.tmpl ,并使用templatefile("values.yaml.tmpl",..)来渲染 Terraform 变量。
  • 通过 Helmfile 部署 Helm 图表,内联提供所有值,并使用{{ requiredEnv "..." }}渲染环境变量。

还有 #6876 是解决此问题的较新方法,解决了https://github.com/helm/helm/issues/2492#issuecomment -413635332 提出的问题。 但是,它很大,并且需要很长时间才能得到审查。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

danielcb picture danielcb  ·  3评论

bq1756 picture bq1756  ·  3评论

KavinduZoysa picture KavinduZoysa  ·  3评论

macknight picture macknight  ·  3评论

adam-sandor picture adam-sandor  ·  3评论