在现代的云原生应用开发中,将应用配置(如数据库连接字符串、日志级别、外部API地址)硬编码到容器镜像中是一种反模式。这种做法不仅使得跨环境部署变得困难,也严重阻碍了CI/CD的效率。
Kubernetes 提供了 ConfigMap 这一核心资源,专门用于存储非敏感的配置数据,并允许应用在运行时消费这些数据,从而实现配置与代码的彻底解耦。
本文将通过一个实操示例,展示如何定义 ConfigMap,并通过两种主流方式将其注入到应用中。
1. 创建 ConfigMap 资源
首先,我们定义一个名为 app-config 的 ConfigMap,用于存储我们的应用参数。我们将配置一个日志级别和后端 API 地址。
文件名: configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
LOG_LEVEL: "INFO"
API_ENDPOINT: "https://api.prod.example.com/v1"
MAX_THREADS: "10"
部署 ConfigMap:
kubectl apply -f configmap.yaml
2. 方法一:通过环境变量注入配置
这是最常用且最简单的配置消费方式。Deployment 或 Pod 定义可以直接引用 ConfigMap 中的键值对,将其注入为容器内的环境变量。
注意: 当 ConfigMap 更新时,通过环境变量注入的配置不会自动更新。需要重启 Pod 才能生效。
文件名: deployment-env.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api-deployment
spec:
replicas: 1
selector:
matchLabels:
app: my-api
template:
metadata:
labels:
app: my-api
spec:
containers:
- name: my-app-container
image: python:3.9-slim
command: ["python", "/app/read_config.py"]
volumeMounts:
# 挂载应用代码,假设 read_config.py 在这里
- name: app-volume
mountPath: /app
env:
# 注入单个键值对
- name: API_URL
valueFrom:
configMapKeyRef:
name: app-config
key: API_ENDPOINT
# 批量注入 ConfigMap 的所有数据
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
3. 方法二:通过卷挂载文件获取配置
对于需要将配置视为文件的应用(例如配置文件或证书),可以将 ConfigMap 挂载为卷(Volume)。Kubernetes 会将 ConfigMap 的每个键视为卷中的一个文件,文件名即为键名,文件内容即为键值。
文件名: deployment-file.yaml
apiVersion: apps/v1
kind: Deployment
# ... (metadata, selector omitted for brevity)
spec:
template:
spec:
volumes:
# 1. 定义一个引用 ConfigMap 的卷
- name: config-volume
configMap:
name: app-config
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
# 2. 将卷挂载到容器内的指定路径
# 容器可以通过 /etc/config/LOG_LEVEL 和 /etc/config/API_ENDPOINT 访问配置
- name: config-volume
mountPath: /etc/config
readOnly: true
4. 应用代码如何消费配置
如果使用方法一(环境变量),Python 应用可以通过 os.environ 来读取配置:
文件名: read_config.py (适用于方法一)
import os
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'DEBUG')
API_URL = os.environ.get('API_URL', 'http://default.local')
print(f"Application Initialized with ConfigMap data:")
print(f"-> Log Level: {LOG_LEVEL}")
print(f"-> API Endpoint: {API_URL}")
如果使用方法二(文件挂载),应用需要从挂载路径读取文件内容:
文件名: read_config_file.py (适用于方法二)
CONFIG_PATH = "/etc/config/"
# 读取 LOG_LEVEL 配置
try:
with open(CONFIG_PATH + "LOG_LEVEL", 'r') as f:
log_level = f.read().strip()
except FileNotFoundError:
log_level = "DEFAULT_LEVEL"
# 读取 API_ENDPOINT 配置
try:
with open(CONFIG_PATH + "API_ENDPOINT", 'r') as f:
api_endpoint = f.read().strip()
except FileNotFoundError:
api_endpoint = "http://default.local"
print(f"Application Initialized with mounted file data:")
print(f"-> Log Level: {log_level}")
print(f"-> API Endpoint: {api_endpoint}")
总结
ConfigMap 是实现配置解耦的关键工具。对于大多数应用,通过环境变量注入配置(方法一)是简单高效的选择。如果需要利用 ConfigMap 的动态更新特性(Pod 不重启,自动更新挂载文件,通常在 Kubernetes 1.8+ 版本生效,但可能存在延迟)或者应用本身需要配置文件格式,则应采用卷挂载(方法二)。通过这种方式,我们可以安全、高效地管理不同环境下的生产参数,确保容器镜像的通用性和可移植性。
汤不热吧