使用 helmfile 组织部署到多个 k8s 环境

头盔文件 - 包装器 ,它允许您在一个地方描述许多 helm 版本,针对多个环境参数化它们的图表,并设置它们的部署顺序。

您可以阅读有关 helmfile 本身及其使用示例的信息 自述 и 最佳做法指南.

我们将熟悉在 helmfile 中描述版本的非显而易见的方式

假设我们有一组 Helm Charts(例如,Postgres 和一些后端应用程序)和几个环境(几个 kubernetes 集群、几个命名空间或两者兼而有之)。 我们获取 helmfile,阅读文档并开始描述我们的环境和版本:

    .
    ├── envs
    │   ├── devel
    │   │   └── values
    │   │       ├── backend.yaml
    │   │       └── postgres.yaml
    │   └── production
    │       └── values
    │           ├── backend.yaml
    │           └── postgres.yaml
    └── helmfile.yaml

helmfile.yaml

environments:
  devel:
  production:

releases:
  - name: postgres
    labels:
      app: postgres
    wait: true
    chart: stable/postgresql
    version: 8.4.0
    values:
      - envs/{{ .Environment.Name }}/values/postgres.yaml
  - name: backend
    labels:
      app: backend
    wait: true
    chart: private-helm-repo/backend
    version: 1.0.5
    needs:
      - postgres
    values:
      - envs/{{ .Environment.Name }}/values/backend.yaml

我们最终得到了 2 个环境: 与开发, 生产 — 每个都包含其自己的 helm 发布图表值。 我们将像这样部署到他们:

helmfile -n <namespace> -e <env> apply

不同环境下不同版本的 Helm Chart

如果我们需要将不同版本的后端部署到不同的环境怎么办? 如何参数化发布版本? 环境价值可通过 {{ .Values }}

helmfile.yaml

environments:
  devel:
+   values:
+   - charts:
+       versions:
+         backend: 1.1.0
  production:
+   values:
+   - charts:
+       versions:
+         backend: 1.0.5
...
  - name: backend
    labels:
      app: backend
    wait: true
    chart: private-helm-repo/backend
-   version: 1.0.5
+   version: {{ .Values.charts.versions.backend }}
...

不同环境下的不同应用程序集

很好,但是如果我们不需要怎么办? production 推出 postgres,因为我们知道我们不需要将数据库推送到 k8s,并且我们有一个很棒的独立 postgres 集群可供出售? 为了解决这个问题我们有标签

helmfile -n <namespace> -e devel apply
helmfile -n <namespace> -e production -l app=backend apply

这很棒,但我个人更喜欢不使用启动参数,而是在环境本身的描述中描述要在环境中部署哪些应用程序。 该怎么办? 您可以将版本描述放在单独的文件夹中,在环境描述中创建必要版本的列表,然后仅“选取”必要的版本,忽略其余版本

    .
    ├── envs
    │   ├── devel
    │   │   └── values
    │   │       ├── backend.yaml
    │   │       └── postgres.yaml
    │   └── production
    │       └── values
    │           ├── backend.yaml
    │           └── postgres.yaml
+   ├── releases
+   │   ├── backend.yaml
+   │   └── postgres.yaml
    └── helmfile.yaml

helmfile.yaml


  environments:
    devel:
      values:
      - charts:
          versions:
            backend: 1.1.0
      - apps:
        - postgres
        - backend

    production:
      values:
      - charts:
          versions:
            backend: 1.0.5
      - apps:
        - backend

- releases:
-    - name: postgres
-      labels:
-        app: postgres
-      wait: true
-      chart: stable/postgresql
-      version: 8.4.0
-      values:
-        - envs/{{ .Environment.Name }}/values/postgres.yaml
-    - name: backend
-      labels:
-        app: backend
-      wait: true
-      chart: private-helm-repo/backend
-     version: {{ .Values.charts.versions.backend }}
-     needs:
-       - postgres
-     values:
-       - envs/{{ .Environment.Name }}/values/backend.yaml
+ ---
+ bases:
+ {{- range .Values.apps }}
+   - releases/{{ . }}.yaml
+ {{- end }}

releases/postgres.yaml

releases:
  - name: postgres
    labels:
      app: postgres
    wait: true
    chart: stable/postgresql
    version: 8.4.0
    values:
      - envs/{{ .Environment.Name }}/values/postgres.yaml

releases/backend.yaml

releases:
  - name: backend
    labels:
      app: backend
    wait: true
    chart: private-helm-repo/backend
    version: {{ .Values.charts.versions.backend }}
    needs:
      - postgres
    values:
      - envs/{{ .Environment.Name }}/values/backend.yaml

注意

当使用 bases: 需要使用yaml分隔符 ---,以便您可以使用环境中的值来模板发布(以及其他部分,例如 helmDefaults)

在这种情况下,postgres 版本甚至不会包含在生产描述中。 很舒服!

发布的可重写全局值

当然,您可以为每个环境设置 helm 图表的值,但是如果我们描述了多个环境,并且我们希望为所有环境设置相同的值,该怎么办? affinity,但我们不想在存储在萝卜中的图表本身中默认配置它。

在这种情况下,对于每个版本,我们可以指定 2 个带有值的文件:第一个带有默认值,它将确定图表本身的值,第二个带有环境值,这反过来将覆盖图表本身的值。默认的。

    .
    ├── envs
+   │   ├── default
+   │   │   └── values
+   │   │       ├── backend.yaml
+   │   │       └── postgres.yaml
    │   ├── devel
    │   │   └── values
    │   │       ├── backend.yaml
    │   │       └── postgres.yaml
    │   └── production
    │       └── values
    │           ├── backend.yaml
    │           └── postgres.yaml
    ├── releases
    │   ├── backend.yaml
    │   └── postgres.yaml
    └── helmfile.yaml

releases/backend.yaml

releases:
  - name: backend
    labels:
      app: backend
    wait: true
    chart: private-helm-repo/backend
    version: {{ .Values.charts.versions.backend }}
    needs:
      - postgres
    values:
+     - envs/default/values/backend.yaml
      - envs/{{ .Environment.Name }}/values/backend.yaml

envs/default/values/backend.yaml

affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
      podAffinityTerm:
        labelSelector:
          matchExpressions:
          - key: app.kubernetes.io/name
            operator: In
            values:
            - backend
        topologyKey: "kubernetes.io/hostname"

在环境级别定义所有版本的 Helm Chart 的全局值

假设我们在多个版本中创建多个入口 - 我们可以为每个图表手动定义 hosts:,但在我们的例子中,域是相同的,那么为什么不将其放入某个全局变量中并简单地将其值替换到图表中呢? 为此,那些具有我们想要参数化值的文件必须具有扩展名 .gotmpl,这样helmfile就知道它需要通过模板引擎来运行。

    .
    ├── envs
    │   ├── default
    │   │   └── values
-   │   │       ├── backend.yaml
-   │   │       ├── postgres.yaml
+   │   │       ├── backend.yaml.gotmpl
+   │   │       └── postgres.yaml.gotmpl
    │   ├── devel
    │   │   └── values
    │   │       ├── backend.yaml
    │   │       └── postgres.yaml
    │   └── production
    │       └── values
    │           ├── backend.yaml
    │           └── postgres.yaml
    ├── releases
    │   ├── backend.yaml
    │   └── postgres.yaml
    └── helmfile.yaml

helmfile.yaml

  environments:
    devel:
      values:
      - charts:
          versions:
            backend: 1.1.0
      - apps:
        - postgres
        - backend
+     - global:
+         ingressDomain: k8s.devel.domain

    production:
      values:
      - charts:
          versions:
            backend: 1.0.5
      - apps:
        - backend
+     - global:
+         ingressDomain: production.domain
  ---
  bases:
  {{- range .Values.apps }}
    - releases/{{ . }}.yaml
  {{- end }}

envs/default/values/backend.yaml.gotmpl

ingress:
  enabled: true
  paths:
    - /api
  hosts:
    - {{ .Values.global.ingressDomain }}

envs/default/values/postgres.yaml.gotmpl

ingress:
  enabled: true
  paths:
    - /
  hosts:
    - postgres.{{ .Values.global.ingressDomain }}

注意

显然,postgres 图表中的 ingress 是非常可疑的东西,因此本文仅作为真空中的球形示例给出,以免仅仅为了描述 ingress 而在文章中引入一些新版本

用环境值替换秘密

类比上面的例子,你可以使用加密的来替换 掌舵的秘密 含义。 我们可以简单地在版本 default.yaml.gotmpl 中定义将从定义的变量中获取的值,而不是为每个版本创建自己的秘密文件(在该文件中我们可以为图表定义加密值)环境水平。 而我们不需要对任何人隐藏的值可以在特定环境下的发布值中轻松地重新定义。

    .
    ├── envs
    │   ├── default
    │   │   └── values
    │   │       ├── backend.yaml
    │   │       └── postgres.yaml
    │   ├── devel
    │   │   ├── values
    │   │   │   ├── backend.yaml
    │   │   │   └── postgres.yaml
+   │   │   └── secrets.yaml
    │   └── production
    │       ├── values
    │       │   ├── backend.yaml
    │       │   └── postgres.yaml
+   │       └── secrets.yaml
    ├── releases
    │   ├── backend.yaml
    │   └── postgres.yaml
    └── helmfile.yaml

helmfile.yaml

  environments:
    devel:
      values:
      - charts:
          versions:
            backend: 1.1.0
      - apps:
        - postgres
        - backend
      - global:
          ingressDomain: k8s.devel.domain
+     secrets:
+       - envs/devel/secrets.yaml

    production:
      values:
      - charts:
          versions:
            backend: 1.0.5
      - apps:
        - backend
      - global:
          ingressDomain: production.domain
+     secrets:
+       - envs/production/secrets.yaml
  ---
  bases:
  {{- range .Values.apps }}
    - releases/{{ . }}.yaml
  {{- end }}

envs/devel/secrets.yaml

secrets:
    elastic:
        password: ENC[AES256_GCM,data:hjCB,iv:Z1P6/6xBJgJoKLJ0UUVfqZ80o4L84jvZfM+uH9gBelc=,tag:dGqQlCZnLdRAGoJSj63rBQ==,type:int]
...

envs/production/secrets.yaml

secrets:
    elastic:
        password: ENC[AES256_GCM,data:ZB/VpTFk8f0=,iv:EA//oT1Cb5wNFigTDOz3nA80qD9UwTjK5cpUwLnEXjs=,tag:hMdIUaqLRA8zuFBd82bz6A==,type:str]
...

envs/default/values/backend.yaml.gotmpl

elasticsearch:
  host: elasticsearch
  port: 9200
  password: {{ .Values | getOrNil "secrets.elastic.password" | default "password" }}

envs/devel/values/backend.yaml

elasticsearch:
  host: elastic-0.devel.domain

envs/production/values/backend.yaml

elasticsearch:
  host: elastic-0.production.domain

注意

顺便说一下, getOrNil - helmfile 中 go 模板的特殊函数,即使 .Values.secrets 将不存在,不会抛出错误,但将允许使用该函数的结果 default 替换默认值

结论

描述的事情看起来很明显,但是关于使用 helmfile 方便地描述部署到多个环境的信息非常稀缺,而且我喜欢 IaC(基础设施即代码)并且希望对部署状态有清晰的描述。

最后,我想补充一点,默认环境的变量可以反过来用将启动部署的某个运行器的操作系统的环境变量进行参数化,从而获得动态环境

helmfile.yaml

environments:
  default:
    values:
    - global:
        clusterDomain: {{ env "CLUSTER_DOMAIN" | default "cluster.local" }}
        ingressDomain: {{ env "INGRESS_DOMAIN" }}

来源: habr.com

添加评论