我在将应用程序部署到 Digital Ocean 中的 Kubernetes 集群时遇到问题,但我一生都无法弄清楚如何解决这个问题。
我正在使用 cloudamqp 使用 Celery 创建一个 python Flask api 来处理我的 api 路由的任务。 所有这些都运行良好,但问题与环境变量有关。 在我的本地机器上,一切正常。 当我将应用程序部署到 kubernetes 集群时,容器(一个用于 Flask-api,另一个用于 api-worker)将无法启动,因为两个容器都无法读取环境变量。 我使用了 kubernetes 文档中的秘密文件方法: kubernetes 处理秘密
env.yaml 文件:
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: backend
type: Opaque
data:
APP_PORT: <base 64 encoded number>
JWT_KEY: <base 64 encoded string>
CLOUDAMQP_URL: <base 64 encoded string>
deployment.yaml 文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: backend
spec:
replicas: 1
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: my-api:latest
resources:
requests:
cpu: "200m"
memory: "300Mi"
limits:
cpu: "300m"
memory: "600Mi"
envFrom:
- secretRef:
name: my-secret
ports:
- containerPort: 5000
- name: api-worker
image: api-worker:latest
resources:
requests:
cpu: "200m"
memory: "300Mi"
limits:
cpu: "300m"
memory: "600Mi"
envFrom:
- secretRef:
name: my-secret
---
apiVersion: v1
kind: Service
metadata:
name: api-server
namespace: backend
spec:
selector:
app: api-server
ports:
- protocol: TCP
port: 80
targetPort: 5000
当我使用 kubectl 命令部署到 kubernetes 时,我的容器失败,因为代码无法识别 env 变量。
例如:
from celery import Celery
import os
def create_celery_app():
broker_url = os.environ['CLOUDAMQP_URL']
celery = Celery('tasks', broker=broker_url, backend='rpc://')
celery.conf.broker_connection_retry_on_startup = True
return celery
celery = create_celery_app()
问题肯定是 env 变量(在本例中为 CLOUDAMQP_URL。它存在于我的秘密文件中,我已经测试过这些值是否显示。当我将这些值硬编码到上面的代码中时,该应用程序在部署到我的我认为这个问题与应用程序在环境变量设置之前启动有关(但我可能弄错了)。该应用程序似乎在启动时失败,例如,如果上面的函数位于 main.py 文件中并且该应用程序正在启动,那么它会崩溃,或者我检查了容器的外壳(当应用程序没有崩溃时),我可以看到 env 变量以及我在 env.yaml 文件中放入的值,并且它们是准确的。
所以我让它工作的一种方法是使用 configMap 而不是秘密。 我仍然会尝试让秘密发挥作用,但现在,这就是我让它发挥作用的方式。我想为此创建一个自动化流程,因此我可以创建一个脚本来读取我的环境变量并用新值覆盖现有的 configMap,或者删除旧的 configMap 并用新的替换(我必须测试哪个有效)。
使用 kubectl,我在命名空间中创建了一个名为 backend 的 configMap。
kubectl create configmap my-config-map \
--from-literal=APP_PORT=<my_port>\
--from-literal=JWT_KEY=<my_jwt_key> \
--from-literal=CLOUDAMQP_URL=<my_url> \
-n backend
这里需要注意的一件事是,如果您要存储数字,则当您在应用程序中读取环境变量时,必须将它们转换为整数或浮点数,如下所示:
import os
app_port = int(os.environ['APP_PORT'])
我还更改了我的deployment.yaml 文件。 为了完整起见,如果有人需要结构方面的帮助,我提供了完整的部署文件。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: backend
spec:
replicas: 1
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: my-api:latest
envFrom:
- configMapRef:
name: my-config-map
resources:
requests:
cpu: "200m"
memory: "300Mi"
limits:
cpu: "300m"
memory: "600Mi"
ports:
- containerPort: 5000
- name: api-worker
image: api-worker:latest
envFrom:
- configMapRef:
name: my-config-map
resources:
requests:
cpu: "200m"
memory: "300Mi"
limits:
cpu: "300m"
memory: "600Mi"
---
apiVersion: v1
kind: Service
metadata:
name: api-server
namespace: backend
spec:
selector:
app: api-server
ports:
- protocol: TCP
port: 80
targetPort: 5000
今天遇到了类似的问题,如果配置映射值正常工作而秘密不起作用,那么要检查的一件事是需要存储在秘密中的值是否已经是 Base64 字符串? 如果是,则将其再次转换为 Base64,然后将其放入 Secret 中,因为在读取 Secret 时,它会从 Base64 转换回来 在我的例子中,API 秘密已经是一个 Base64 字符串,直接作为秘密中的值添加,当应用程序尝试读取该值时,它不会读取,因此需要再次进行 Base64 转换