将多个 k8s 秘密组合成一个环境变量

Combining multiple k8s secrets into an env variable

我的 k8s 命名空间包含一个 Secret,它是在部署时创建的(由 svcat),因此这些值是事先不知道的。

apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: my-database-credentials
data:
  hostname: ...
  port: ...
  database: ...
  username: ...
  password: ...

A Deployment 需要以稍微不同的格式注入这些值:

...

containers:
  env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: my-database-credentials
        key: jdbc:postgresql:<hostname>:<port>/<database> // ??

  - name: DATABASE_USERNAME
    valueFrom:
      secretKeyRef:
        name: my-database-credentials
        key: username

  - name: DATABASE_PASSWORD
    valueFrom:
      secretKeyRef:
        name: my-database-credentials
        key: password

DATABASE_URL 需要由先前定义的秘密中的 hostnameport、“数据库”组成。

这个作文有什么办法吗?

有几种方法(按复杂程度递增的顺序):

  1. 在将参数放入 Secret 之前对其进行处理(扩展您用来在此处插入信息的任何内容)。

  2. 将脚本添加到您的 Pod/Container 中以将传入参数(环境变量或命令参数)分解为所需的内容。如果您不能或不想拥有自己的容器镜像,您可以将额外的脚本作为卷添加到容器中,并且 set the Container's command field to override the container image start command.

  3. 向您的 Kubernetes 添加一个工具来进行自动处理 "behind the scenes":您可以添加一个 Dynamic Admission Controller to do your mangling, or you can create a Kubernetes Operator and add a Custom Resource Definition(CRD 会告诉操作员要监视哪些 Secrets更改,操作员将读取这些值并生成您想要的任何其他条目)。

你可以做一些我能想到的事情:

  1. 使用 secrets volume 并制作一个启动脚本,从卷中读取机密,然后使用 DATABASE_URL 环境变量启动您的应用程序。

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: mypod
        image: your_db_container
        command: [ "yourscript.sh" ]
        volumeMounts:
        - name: mycreds
          mountPath: "/etc/credentials"
      volumes:
      - name: mycreds
        secret:
          secretName: my-database-credentials
          defaultMode: 256
    
  2. 在容器规范的 command 键中传递环境变量:

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: mypod
        image: your_db_container
        command: [ "/bin/sh", "-c", "DATABASE_URL=jdbc:postgresql:<hostname>:<port>/<database>/$(DATABASE_USERNAME):$(DATABASE_PASSWORD) /start/yourdb" ]
    
        env:
        - name: DATABASE_USERNAME
          valueFrom:
            secretKeyRef:
              name: my-database-credentials
              key: username
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
            name: my-database-credentials
            key: password
    

Kubernetes 允许您在配置的其他地方使用先前定义的环境变量作为后续环境变量的一部分。来自 Kubernetes API reference docs:

Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables.

$(...) 语法为容器定义 interdependent environment variables

因此,您可以先将所需的秘密值提取到环境变量中,然后用这些变量组成DATABASE_URL

...

containers:
  env:
  - name: DB_URL_HOSTNAME               // part 1
    valueFrom:
      secretKeyRef:
        name: my-database-credentials
        key: hostname

  - name: DB_URL_PORT                   // part 2
    valueFrom:
      secretKeyRef:
        name: my-database-credentials
        key: port

  - name: DB_URL_DBNAME                 // part 3
    valueFrom:
      secretKeyRef:
        name: my-database-credentials
        key: database

  - name: DATABASE_URL                  // combine
    value: jdbc:postgresql:$(DB_URL_HOSTNAME):$(DB_URL_PORT)/$(DB_URL_DBNAME)

...

如果所有前置变量都定义为环境变量:

-  { name: DATABASE_URL, value: '{{ printf "jdbc:postgresql:$(DATABASE_HOST):$(DATABASE_PORT)/$(DB_URL_DBNAME)" }}'}

使用此语句,您还可以从 values.yaml 文件中引入值:

例如:

如果您可能已经在值文件中定义了 DB_URL_DBNAME:

-  { name: DATABASE_URL, value: '{{ printf "jdbc:postgresql:$(DATABASE_HOST):$(DATABASE_PORT)/%s" .Values.database.DB_URL_DBNAME }}'}