Helm vs Kustomize 深度比较

Helm和Kustomize都是流行的Kubernetes集群部署管理工具,本文比较了两者的优缺点,方便读者根据项目实际情况采用适合的方案。原文: Helm vs Kustomize: why, when, and how

挑战

开始讨论之前,先来看看为什么要使用 Helm 或 Kustomize。

这么多环境,这么多 YAML 文件!

Kubernetes 帮助我们非常容易的为不同用例创建不同的环境,可以在同一个集群甚至多个集群上使用命名空间,可以托管开发、测试、QA、UAT、预发、生产......等等不同的环境。但问题是:如何管理所有这些环境?

第一种也是最直接的方法是创建相同manifest的副本,并为每个副本命名。也就是说,把源文件复制粘贴到每个环境上。

对于只需对每个环境做出极少改动的简单项目,上述方法可能很适用。例如,除了镜像外,所有 YAML 清单都完全相同。可以打开每个目录中的 deployment.yaml 文件进行更改,保存后运行 kubectl apply -f .,就大功告成了。

然而,在大多数情况下,环境之间的差异并不那么简单。请看下面的例子:

  • 开发环境通过某些容器命令参数进行调试,而这些参数在 QA 或生产环境中不可用。
  • QA 部署了一些边车,用于运行测试,开发和生产环境不具备这种能力。
  • 出于显而易见的原因,生产环境的 RBAC 比其他两个环境的限制性更强。

还有其他更多的可能性:

  • 应用变得越来越大,需要其他依赖服务。例如,MySQL 后端和 Redis 缓存服务器。每个服务都有自己的清单、配置设置和环境差异。
  • 需要实施 CI/CD 流水线,将应用程序(连同其依赖项)测试、构建和部署到多个环境中。

如你所见,单独使用 kubectl 会变成一场噩梦,这就是我们开始探索更高级工具(特指 Helm 和 Kustomize)的原因。让我们先来探讨一下它们各自是如何应对上述挑战的。

Helm

作为 Kubernetes 的包管理器,Helm 提供了一种以"图表(charts)"形式打包、分发和管理应用程序的方法。Helm chart由模板(template)和值(value)文件集合组成,其中模板定义 Kubernetes 资源(如Deployment、Service、ConfigMap),值文件允许自定义模板值。

这样就可以拥有一组模板,为在不同部署(或环境)中发生变化的参数提供占位符。例如,下面是一个 Helm 部署模板,它从值文件中获取副本数量、镜像名称和标签、容器端口和容器启动参数:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-deployment
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          ports:
            - containerPort: {{ .Values.containerPort }}
          args:
            - {{ .Values.startupArguments }}

{{}} 之间的内容都是动态的。也就是说,在chart部署时,它们会被实际值取代。相应的值文件如下所示:

yaml 复制代码
replicaCount: 3

image:
  repository: myapp/image
  tag: v1.0.0

containerPort: 8080

startupArguments: arg1 arg2 arg3

注意: .Release.Name.Chart.Name 变量取自 Chart.yaml,可视为另一个参数来源,用于为集群中的 Kubernetes 组件赋予唯一的名称,这样我们就能在同一个集群中部署同一chart的多个版本。

当 Helm 应用于集群时,Kubernetes API 服务器会收到这些信息:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp/image:v1.0.0
          ports:
            - containerPort: 8080
          args:
            - arg1
            - arg2
            - arg3

这样就可以为每种环境/用例设置不同的值文件。

对于整个环境的更改,只需修改一次源模板。而对于特定环境的更改,可以应用每个环境对应的值文件。

Kustomize

Kustomize 的目标是一样的,但不使用模板。相反,它在一个目录中保留完整 版本的 YAML 文件。按照惯例,这个文件被称为 base,但也可以根据自己的喜好给它命名。然后可以为每个环境/场景/用例创建一个目录(或目录树),每个目录都需要一个名为 kustomization.yaml 的 YAML 文件,该文件的目的是告知 Kustomize 应该考虑哪些 manifest 文件,以及需要对这些文件进行哪些修改。下面通过例子来说明这,看看如何使用 Kustomize 得出与 Helm 相同的结果。

首先创建一个目录结构:

csharp 复制代码
myapp/
├── kustomization.yaml
├── base
│   └── deployment.yaml
└── overlay
    └── deployment.yaml

myapp/kustomization.yaml 的内容如下:

yaml 复制代码
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - base/deployment.yaml

patchesStrategicMerge:
  - overlay/deployment.yaml

base/deployment.yaml 看起来像这样:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: myapp
          image: myapp/image:v1.0.0
          ports:
            - containerPort: 8080

请注意,这是一个完全有效的 YAML,如果需要,也可以按原样应用。

要更改该部署以适应环境需求,可以使用 overlay/deployment.yaml 文件:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  template:
    spec:
      containers:
        - name: myapp
          args:
            - arg1
            - arg2
            - arg3

这样,发送到 Kubernetes API 服务器的文件就变成了

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: myapp
          image: myapp/image:v1.0.0
          ports:
            - containerPort: 8080
          args:
            - arg1
            - arg2
            - arg3

把同样的机制应用到三个环境中,目录结构可以是这样的:

csharp 复制代码
myapp/
├── kustomization.yaml
├── base
│   └── deployment.yaml
├── overlays
│   ├── dev
│   │   └── kustomization.yaml
│   ├── qa
│   │   └── kustomization.yaml
│   └── prod
│       └── kustomization.yaml
└── patches
    └── deployment-patch.yaml

如果需要对整个环境进行更改,只需在 base/deployment 文件中进行一次更改,就会传播到所有地方。针对特定环境的更改在相应环境的自定义文件中完成。

现在我们知道了每种工具是如何应对挑战的,下面来看看其优缺点。

第1回合:安装和设置

需要在服务器上安装 Helm,请参阅Five ways to install Helm

虽然可以从 kubectl.docs.kubernetes.io/installatio... 下载单独的 Kustomize 软件包,但从 1.14 版开始,就已经与 kubectl 捆绑在一起了。因此,除非你的系统中没有(或不需要)kubectl,否则只需运行 kubectl -k 即可调用 Kustomize。

优胜者:Kustomize

第2回合:软件包管理

由于 Helm 顾名思义是软件包管理器,它提供的软件仓库可以搜索和下载特定版本的chart,也可以在同一集群中同时安装多个版本的chart。Kustomize 不会将文件打包成可部署的单元,不过我们可以通过 Kustomize 手动实现同样的效果(Git 发布是其中一种选择)。不过,Helm 提供了开箱即用的功能。

优胜者:Helm

第3回合:模板化能力

Helm 完全依赖 Go 模板,此外还从 Sprig库中借用了一些函数,使模板功能更加多样化。Kustomize 完全不使用模板,而是在将 YAML 清单应用到集群之前,使用overlay和patch对其进行即时修改。

Go 是一种成熟的编程语言,提供了强大的文本操作技术。例如

  • 循环和条件式,如(range)和条件式(ifelsewith),这在生成重复资源或根据用户提供的值进行决策时非常有用。
  • 模板功能通过 Sprig 库实现,该库提供了各种功能,如defaultpickomittrimupperlowerquote等。

而 Kustomize 却无法做到这一点。不过,它也有一些小技巧。例如

  • ConfigMaps 和 Secrets 的生成器。这些都是声明式指定的,Kustomize 会在构建最终 YAML 时生成资源。
  • Variants(变体):Kustomize 使用overlay层来管理同一应用程序的不同变体,这有助于管理不同的环境(开发、预发、生产)。
  • 用于更新资源字段的转换器(transformers)。常见的转换器包括为资源名称添加前缀/后缀、更新标签和注释以及更新命名空间。转换器可以有选择的应用于不同的资源,从而提供高度的控制。

优胜者:不定(取决于所追求的定制化程度)

第4回合:调试

很明显,在将 YAML 文件应用到群集之前,需要测试这些文件是否存在错误。YAML 使用空格和缩进来定义对象、列表和其他组件,一个不正确的缩进可能会毁掉整个部署。Helm 和 Kustomize 都允许我们在将 YAML 清单应用到群集之前就"查看"这些清单。

Kustomize 有build命令,在将所有patch、overlay、转换器(transformers)等应用到一个包含整个有效负载的大文件后,会生成最终的清单。不过,也可以运行 kubectl apply -k --dry-run 来依赖 API 服务器验证 YAML 清单。

Helm有几种方法可以做同样的事情:

可以使用 helm template 在 YAML 清单发送到 API 服务器之前对其进行渲染,还可以使用 helm lint 根据最佳实践检查chart。

使用 helm install --dry-run (或 helm upgrade )还可以针对 API 服务器测试清单。也就是说,即使 YAML 在语法上是正确的,API 服务器也可能因为其他原因而拒绝接受(例如,缺少 CRD 或接入控制器)。Helm 允许我们在将有效负载应用到 Kubernetes 之前捕获这些错误,从而避免卸载和重新安装有问题的chart。

优胜者:不定

第5回合:版本控制和回滚

如前所述,Helm 能够同时在同一集群中部署同一chart的多个版本。Helm 将部署版本称为revision(修订版),并保留了部署到群集的revision版本历史记录,允许我们在需要时回滚到之前的revision版本。虽然 Kustomize 也可以做同样的事情,但过程复杂且容易出错。

优胜者:Helm

第6回合:Secrets管理

许多情况下,我们需要存储一些敏感信息,作为应用程序部署的一部分。比如 API 密钥、用户凭证、令牌等。在所有情况下,Kubernetes 都提供了 Secret 对象,可以在其中保存机密信息。让我们看看每个工具是如何处理 Secret 创建的:

Helm

将隐私数据存储在 values.yaml 文件中,并使用 b64enc 函数在Secret YAML 清单中将其即时转换为 base64。例如

yaml 复制代码
# values.yaml
database:
  username: admin
  password: secret

以及

yaml 复制代码
# templates/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: {{ .Values.database.username | b64enc }}
  password: {{ .Values.database.password | b64enc }}

由此产生的 YAML 可以是这样的

yaml 复制代码
---
# Source: my-chart/templates/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: YWRtaW4=
  password: c2VjcmV0

这里的问题显而易见: 需要将 Values 文件(其中包含纯文本证书)提交到版本控制中。一个可行的解决方案是创建单独的 Values 文件来存储敏感信息,并通过将其添加到 .gitignore 文件来避免将其包含在 git 仓库中。不过这样需要管理多个Values文件,又增加了复杂性。

Kustomize

可以使用 Kustomize secretGenerator 自动从纯文件创建Secret YAML。例如,可以创建如下凭证文件:

bash 复制代码
# Create the secret file
echo -n 'admin' > ./username.txt
echo -n 'secret' > ./password.txt

Kustomization 文件看起来会像这样:

yaml 复制代码
# kustomization.yaml
secretGenerator:
- name: db-secret
  files:
  - username.txt
  - password.txt

由此产生的清单将是

yaml 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: db-secret-8h5h97g6k8
type: Opaque
data:
  username.txt: YWRtaW4=
  password.txt: c2VjcmV0

虽然 username.txtpassword.txt 也会被添加到 .gitignore,但除非想修改凭据,否则无需在每次部署时都重新创建它们(在运行 git clonegit pull 后)。

显然,用 Base64 存储敏感信息和使用纯文本是一样的,因为 Base64 是一种编码格式,而不是加密方法。也就是说,任何人都可以使用命令行工具将 Base64 字符串转换为原始格式。因此,最佳实践要求我们对secret数据进行加密。Helm 和 Kustomize 都可以使用第三方插件实现这一功能。

例如,如果使用 Kustomize,可以使用 kustomize-secret-generator 插件,它能让你从 Google Cloud Secret Manager、AWS Secrets Manager 或 HashiCorp 获取secret。这样做的目的是将secret以加密形式存储在其中某个支持的平台中。需要时,用户可以依靠插件获取secret、解密并将其应用于群集。下面演示了 Kustomize 如何利用 HashiCorp 的 Vault 实现这一功能:

yaml 复制代码
# kustomization.yaml
secretGenerator:
- name: db-secret
  kvSources:
  - pluginType: vault
    name: my-vault
    namespace: default
    path: secret/data/my-service
    key: db-password

虽然 Helm 有 Helm-Secrets 插件,但不提供从其他平台获取secret的本地支持。相反,它使用 Mozilla SOPS 进行加密。密钥本身可以存储在各种密钥管理系统中,如 AWS KMS、GCP KMS、Azure Key Vault 和 PGP。例如

bash 复制代码
helm secrets enc secrets.yaml

上述命令对 Secret 模板进行了静态加密,然后可以直接提交到 Git。当我们在另一台机器上重新获取时,需要先解密,然后再将其应用到 Kubernetes:

bash 复制代码
helm secrets dec secrets.yaml

优胜者:Kustomize

第7回合:处理超大型应用程序

如果应用程序有数百个清单,包含数千行内容,那么使用 Helm 模板处理这些清单很快就会变得力不从心,这里 Kustomize 可能是更好的选择。

例如著名的基于 Kubernetes 的机器学习平台 Kubeflow,正在使用 Kustomize 作为部署工具。原因是该平台过于庞大,而且有许多依赖项需要按特定顺序部署。为了解释的更清楚,这是需要部署的资源的一个子集(我们甚至还没有考虑patch或overlay):

优胜者:Kustomize

第8回合:与 CI/CD 工具集成

Helm 已被广泛采用,被许多 CI/CD 工具所支持。对 Kustomize 的支持也在增加,但并不广泛。

优胜者:Helm

第9回合(最后一轮):次级组成部分和依赖关系

Helm 内置支持依赖关系处理。如果chart需要一些先决条件(数据库、缓存服务器、OAuth 服务等),可以轻松的在 Chart.yaml 文件中将它们添加为dependencies(依赖项)。Helm 将确保在运行主chart前下载并提供这些先决条件,并且可以选择所需版本。而 Kustomize 则完全由用户手动处理。

优胜者:Helm

获胜者是

Helm!

不过,这里没有输赢之分。每种工具都有自己的优缺点,完全取决于项目的目标、规模、需要部署的环境数量以及复杂程度。这场"对决"的目的只是展示这两种工具之间的区别,而不是鼓吹其中一种优于另一种。

尽管如此,许多项目事实上会在同一个代码库中同时使用这两种工具。不过,本文篇幅过长,无法讨论 Kustomize 的这一功能。不过,可以通过以下链接查看文档:github.com/kubernetes-...

结论

Helm 和 Kustomize 的目标是一致的:以 DevOps 的方式更轻松的部署包含许多相互依赖的 YAML 清单的大型应用程序。不过,每种工具都有其优于其他工具的用例。在本文中,我们试图让这两种工具面对面比较,看看它们的优缺点。在下一个项目中选择使用 Helm 还是 Kustomize 很大程度上取决于多个因素,但我们希望本文能帮助你做出正确的决定。

希望这篇关于 Helm 和 Kustomize 的文章对你有所帮助。如果深入了解并精通 Helm,强烈推荐 Udemy 上的课程:Helm - Kubernetes 包管理器实践。该课程适合各种水平的学习者,包含大量实践示例和精辟技巧。


你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!

本文由mdnice多平台发布

相关推荐
AI云原生43 分钟前
如何使用Docker快速运行Firefox并实现远程访问本地火狐浏览器的教程
运维·docker·云原生·容器·serverless·firefox·kubeless
泡沫冰@2 小时前
K8S集群管理(3)
云原生·容器·kubernetes
LQ深蹲不写BUG4 小时前
微服务事务管理利器:Seata 核心原理与实践指南
微服务·云原生·架构
nathan05295 小时前
Kubernetes 实战练习指南
云原生·容器·kubernetes
無名之輩6 小时前
Nvidia Device Plugin入门二之envvar策略
kubernetes
云和数据.ChenGuang7 小时前
微服务技术栈
微服务·云原生·架构
syty20207 小时前
K8s是什么
容器·kubernetes·dubbo
江团1io09 小时前
微服务雪崩问题与系统性防御方案
微服务·云原生·架构
Evan Wang10 小时前
使用Terraform管理阿里云基础设施
阿里云·云原生·terraform
向上的车轮10 小时前
基于go语言的云原生TodoList Demo 项目,验证云原生核心特性
开发语言·云原生·golang