Kubernetes 相当于 Docker 中的环境文件

Kubernetes equivalent of env-file in Docker

背景:

目前我们正在为我们的服务使用 Docker 和 Docker Compose。我们已将不同环境的配置外部化到定义应用程序读取的环境变量的文件中。例如 prod.env 文件:

ENV_VAR_ONE=Something Prod
ENV_VAR_TWO=Something else Prod

和一个 test.env 文件:

ENV_VAR_ONE=Something Test
ENV_VAR_TWO=Something else Test

因此我们可以在启动容器时简单地使用prod.envtest.env文件:

docker run --env-file prod.env <image>

然后我们的应用程序根据 prod.env 中定义的环境变量选择其配置。

问题:

  1. 有没有一种方法可以从 Kubernetes 中的文件中提供环境变量(例如在定义 pod 时)而不是像这样对它们进行硬编码:
apiVersion: v1
kind: Pod
metadata: 
  labels: 
    context: docker-k8s-lab
    name: mysql-pod
  name: mysql-pod
spec: 
  containers: 
    - 
      env: 
        - 
          name: MYSQL_USER
          value: mysql
        - 
          name: MYSQL_PASSWORD
          value: mysql
        - 
          name: MYSQL_DATABASE
          value: sample
        - 
          name: MYSQL_ROOT_PASSWORD
          value: supersecret
      image: "mysql:latest"
      name: mysql
      ports: 
        - 
          containerPort: 3306
  1. 如果这不可能,建议的方法是什么?

使用 YAML 文件为 Kubernetes 定义 pod 时,无法直接指定包含容器环境变量的不同文件。 Kubernetes 项目表示他们将在未来改进这方面(参见 Kubernetes docs)。

同时,我建议使用配置工具并将 pod YAML 制作为模板。例如,使用 Ansible,您的 pod YAML 文件将如下所示:

文件my-pod.yaml.template:

apiVersion: v1
kind: Pod
...
spec:
  containers:
  ...
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: {{ mysql_root_pasword }}
    ...

然后你的 Ansible 剧本可以在方便的地方指定变量 mysql_root_password,并在创建资源时替换它,例如:

文件my-playbook.yaml:

- hosts: my_hosts
  vars_files: 
  - my-env-vars-{{ deploy_to }}.yaml
  tasks:
  - name: create pod YAML from template
    template: src=my-pod.yaml.template dst=my-pod.yaml
  - name: create pod in Kubernetes
    command: kubectl create -f my-pod.yaml

文件my-env-vars-prod.yaml:

mysql_root_password: supersecret

文件my-env-vars-test.yaml:

mysql_root_password: notsosecret

现在通过运行创建pod资源,例如:

ansible-playbook -e deploy=test my-playbook.yaml

您可以通过使用 Secrets or ConfigMaps 填充容器的环境变量。当您使用的数据是敏感的(例如密码)时使用 Secrets,当它不是时使用 ConfigMaps。

在您的 Pod 定义中指定容器应从 Secret 中提取值:

apiVersion: v1
kind: Pod
metadata: 
  labels: 
    context: docker-k8s-lab
    name: mysql-pod
  name: mysql-pod
spec: 
  containers:
  - image: "mysql:latest"
    name: mysql
    ports: 
    - containerPort: 3306
    envFrom:
      - secretRef:
         name: mysql-secret

请注意,此语法仅适用于 Kubernetes 1.6 或更高版本。在早期版本的 Kubernetes 上,您必须手动指定每个值,例如:

env: 
- name: MYSQL_USER
  valueFrom:
    secretKeyRef:
      name: mysql-secret
      key: MYSQL_USER

(注意env取一个数组作为值)

并为每个值重复。

无论您使用哪种方法,您现在都可以定义两种不同的 Secret,一种用于生产,一种用于开发。

开发-secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  MYSQL_USER: bXlzcWwK
  MYSQL_PASSWORD: bXlzcWwK
  MYSQL_DATABASE: c2FtcGxlCg==
  MYSQL_ROOT_PASSWORD: c3VwZXJzZWNyZXQK

产品-secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  MYSQL_USER: am9obgo=
  MYSQL_PASSWORD: c2VjdXJlCg==
  MYSQL_DATABASE: cHJvZC1kYgo=
  MYSQL_ROOT_PASSWORD: cm9vdHkK

并将正确的秘密部署到正确的 Kubernetes 集群:

kubectl config use-context dev
kubectl create -f dev-secret.yaml

kubectl config use-context prod
kubectl create -f prod-secret.yaml

现在,每当 Pod 启动时,它都会根据 Secret 中指定的值填充其环境变量。

Kubernetes (v1.6) 的新更新 允许您(几年前)要求的内容。

您现在可以在您的 yaml 文件中像这样使用 envFrom

  containers:
  - name: django
    image: image/name
    envFrom:
      - secretRef:
         name: prod-secrets

在 development-secrets 是你的秘密的地方,你可以通过以下方式创建它:

kubectl create secret generic prod-secrets --from-env-file=prod/env.txt`

其中txt文件内容为key-value:

DB_USER=username_here
DB_PASSWORD=password_here

文档仍然是示例湖,我不得不在那些地方努力搜索:

注意:创建秘密时 --from-file--from-env-file 之间存在差异,如下面的评论所述。

这对我有用:

文件env-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: env-secret
type: Opaque
stringData:
  .env: |-
    APP_NAME=Laravel
    APP_ENV=local

并入deployment.yamlpod.yaml

spec:
  ...
        volumeMounts:
        - name: foo
          mountPath: "/var/www/html/.env"
          subPath: .env
      volumes:
      - name: foo
        secret:
          secretName: env-secret
````

这是一个老问题,但它有很多观众所以我添加我的答案。 将配置与 K8s 实现分开的最佳方法是使用 Helm。每个 Helm 包都可以有一个 values.yaml 文件,我们可以轻松地在 Helm 图表中使用这些值。如果我们有一个多组件拓扑,我们可以创建一个伞形 Helm 包,父值包也可以覆盖子值文件。

这是一个老问题,但让我为未来的初学者描述一下我的答案。

您可以使用 kustomize configMapGenerator。

configMapGenerator:
  - name: example
    env: dev.env

并在 pod 定义中引用此 configMap/example

我在 tyhis 上摔了 2 个小时的头。我在文档中找到了一个非常简单的解决方案,可以最大程度地减少我(希望也是您)的痛苦。

  • 保持 env.prodenv.dev 不变。

  • 使用 oneliner 脚本将它们导入 yaml:

    kubectl create configmap my-dev-config --from-env-file=env.dev

    kubectl create configmap my-prod-config --from-env-file=env.prod

您可以看到结果(即时满足):

# You can also save this to disk
kubectl get configmap my-dev-config -o yaml

作为一个 ruby​​ist,我个人认为这个解决方案是 DRYest,因为你有一个单一的维护点(ENV bash 文件,它与 Python/Ruby 库兼容,..)然后您在一次执行中将其 YAMLize。

请注意,您需要保持您的 ENV 文件干净(我有很多评论阻止它工作,所以不得不在前面加上 cat config.original | egrep -v "^#" | tee config.cleaned)但这并没有显着改变复杂性。

全部记录在案here

您可以通过在容器中将它们指定为环境变量来引用 K8S 值。

让您的部署mongo.yml如下:

--
kind: Deployment
   --
      --
      containers:
        --
        env:
        - name: DB_URL
          valueFrom:
            configMapKeyRef:
              name: mongo-config
              key: mongo-url
        - name: MONGO_INITDB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: mongo-password  

其中 mongo-secret 用于敏感数据,例如:密码或证书

apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
type: Opaque
data:
  mongo-user: bW9uZ291c2Vy
  mongo-password: bW9uZ29wYXNzd29yZA==

和mongo-config用于非敏感数据

apiVersion: v1
kind: ConfigMap
metadata:
  name: mongo-config
data:
  mongo-url: mongo-service

您可以尝试以下步骤:

此命令会将您的文件 prod.env 放入机密中。

kubectl create secret generic env-prod --from-file=prod.env=prod.env

然后,您可以在 deployment.yaml 中引用此文件,例如

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-identity
  labels:
    app.kubernetes.io/name: api-identity
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: api-identity
  template:
    metadata:
      labels:
        app.kubernetes.io/name: api-identity
    spec:
      imagePullSecrets:
        - name: docker-registry-credential
      containers:
        - name: api-identity
          image: "api-identity:test"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8005
              protocol: TCP
          volumeMounts:
          - name: env-file-vol
            mountPath: "/var/www/html/.env"
            subPath: .env
      volumes:
      - name: env-file-vol
        secret:
          secretName: env-prod