Kubernetes equivalent of env-file in Docker

Background:

Currently we're using Docker and Docker Compose for our services. We have externalized the configuration for different environments into files that define environment variables read by the application. For example a prod.env file:

ENV_VAR_ONE=Something Prod
ENV_VAR_TWO=Something else Prod

and a test.env file:

ENV_VAR_ONE=Something Test
ENV_VAR_TWO=Something else Test

Thus we can simply use the prod.env or test.env file when starting the container:

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

Our application then picks up its configuration based on the environment variables defined in prod.env.

Questions:

  1. Is there a way to provide environment variables from a file in Kubernetes (for example when defining a pod) instead of hardcoding them like this:
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. If this is not possible, what is the suggested approach?
72140 次浏览

当使用 YAML 文件为 Kubernetes 定义 pod 时,没有直接的方法为容器指定包含环境变量的不同文件。库伯内特项目表示,他们将在未来改善这一领域(见 库伯内特医生)。

同时,我建议使用一个供应工具并将 pod YAML 作为一个模板。例如,使用安赛尔,您的 pod YAML 文件将看起来像:

文件 my-pod.yaml.template:

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

然后你的安塞尔剧本可以在某个方便的地方指定变量 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

您可以通过使用 秘密配置图来填充容器的环境变量。如果你使用的数据是敏感的(例如密码) ,使用 Secret; 如果不是,使用 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

(Note that env take an array as value)

每个价值都在重复。

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

dev-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

prod-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

并将正确的秘密部署到正确的库伯内特群集:

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

在发展的秘密是你的秘密的地方,你可以通过以下方式创造它:

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

其中 txt 文件内容是键值:

DB_USER=username_here
DB_PASSWORD=password_here

这些医生仍然是一个个例子,我不得不在这些地方努力寻找:

Note: there's a difference between --from-file and --from-env-file when creating secret as described in the comments below.

这对我有用:

文件 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
````

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

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

您可以使用自定义 configMapGenerator。

configMapGenerator:
- name: example
env: dev.env

并在 pod 定义中引用这个 configMap/example

我的头被 Tyhis 砸了两个小时。我在文件中找到了一个非常简单的解决方案,可以最大限度地减少我的(希望也是你的)痛苦。

  • 保持 env.prodenv.dev,因为你有他们。

  • 使用一个一线程脚本将它们导入 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

As a rubyist, I personally find this solution the DRYest as you have a single point to maintain (the ENV bash file, which is compatible with Python/Ruby libraries, ..) and then you YAMLize it in a single execution.

请注意,您需要保持您的 ENV 文件的整洁(我有很多注释阻止这种工作,所以必须预先设置一个 cat config.original | egrep -v "^#" | tee config.cleaned) ,但这并没有实质性地改变复杂性。

这些都是有记录的 给你

您可以通过在容器中将 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

Where mongo-secret is used for senective data, e.g.: passwords or certificates

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

然后,您可以在 loyment.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