10.4 Kubernetes ConfigMap和Secrets管理:安全高效的配置管理机制
2025/8/31大约 6 分钟
10.4 Kubernetes ConfigMap和Secrets管理
Kubernetes提供了ConfigMap和Secret两种核心资源来管理应用配置。ConfigMap用于存储非敏感配置数据,而Secret专门用于存储敏感信息如密码、令牌等。合理使用这两种资源可以帮助我们构建安全、灵活的配置管理体系。
ConfigMap的创建和使用
ConfigMap是Kubernetes中用于存储非敏感配置数据的资源对象,可以以多种方式注入到Pod中。
ConfigMap创建方式
# configmap-literal.yaml - 通过字面值创建ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
labels:
app: myapp
type: configuration
data:
# 简单键值对
DATABASE_HOST: "postgres-service"
DATABASE_PORT: "5432"
LOG_LEVEL: "info"
API_TIMEOUT: "30"
# 多行配置
app.properties: |
server.port=8080
spring.profiles.active=production
logging.level.com.example=INFO
# JSON格式配置
database-config.json: |
{
"host": "postgres-service",
"port": 5432,
"database": "myapp",
"pool": {
"min": 5,
"max": 20
}
}
# YAML格式配置
cache-config.yaml: |
redis:
host: redis-service
port: 6379
ttl: 3600
memcached:
host: memcached-service
port: 11211# configmap-file.yaml - 通过文件创建ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-files
namespace: default
data:
# 从文件内容创建
nginx.conf: |
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
proxy_pass http://app-service:3000;
}
}
}
logging.conf: |
{
"appenders": {
"console": {
"type": "console",
"layout": {
"type": "pattern",
"pattern": "%d{yyyy-MM-dd HH:mm:ss} [%p] %c - %m%n"
}
}
},
"categories": {
"default": { "appenders": ["console"], "level": "info" }
}
}# 通过kubectl命令创建ConfigMap
# 从字面值创建
kubectl create configmap app-config \
--from-literal=DATABASE_HOST=postgres-service \
--from-literal=DATABASE_PORT=5432 \
--from-literal=LOG_LEVEL=info
# 从文件创建
kubectl create configmap app-config-files \
--from-file=config/nginx.conf \
--from-file=config/logging.conf
# 从目录创建
kubectl create configmap app-config-dir \
--from-file=config/ConfigMap在Pod中的使用
# pod-with-configmap.yaml - 在Pod中使用ConfigMap
apiVersion: v1
kind: Pod
metadata:
name: app-pod
labels:
app: myapp
spec:
containers:
- name: app-container
image: myapp:latest
ports:
- containerPort: 8080
env:
# 从ConfigMap注入环境变量
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
- name: DATABASE_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_PORT
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
volumeMounts:
# 将ConfigMap挂载为文件
- name: config-volume
mountPath: /etc/app/config
- name: app-properties
mountPath: /app/config/app.properties
subPath: app.properties
volumes:
# ConfigMap卷
- name: config-volume
configMap:
name: app-config
items:
- key: database-config.json
path: database.json
- key: cache-config.yaml
path: cache.yaml
- name: app-properties
configMap:
name: app-config
items:
- key: app.properties
path: app.propertiesSecret的安全管理
Secret是Kubernetes中用于存储敏感信息的资源对象,提供了比ConfigMap更高的安全级别。
Secret的创建和管理
# secret-literal.yaml - 通过字面值创建Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: default
labels:
app: myapp
type: secret
type: Opaque
data:
# 敏感数据需要base64编码
database-password: bXlzZWNyZXRwYXNzd29yZA== # mysecretpassword
api-key: YWJjZGVmZ2hpams= # abcdefghijk
jwt-secret: eW91ci1zZWNyZXQtand0LXRva2Vu # your-secret-jwt-token
stringData:
# stringData中的数据会自动进行base64编码
database-username: myapp
smtp-password: smtp-secret-password
oauth-client-secret: oauth-client-secret-value# 通过kubectl命令创建Secret
# 从字面值创建
kubectl create secret generic app-secrets \
--from-literal=database-password=mysecretpassword \
--from-literal=api-key=abcdefghijk \
--from-literal=jwt-secret=your-secret-jwt-token
# 从文件创建
kubectl create secret generic app-secrets-files \
--from-file=secrets/database-password.txt \
--from-file=secrets/api-key.txt
# 从.env文件创建
kubectl create secret generic app-env-secrets \
--from-env-file=secrets/.envSecret在Pod中的使用
# pod-with-secret.yaml - 在Pod中使用Secret
apiVersion: v1
kind: Pod
metadata:
name: app-pod-with-secret
labels:
app: myapp
spec:
containers:
- name: app-container
image: myapp:latest
ports:
- containerPort: 8080
env:
# 从Secret注入环境变量
- name: DATABASE_USERNAME
valueFrom:
secretKeyRef:
name: app-secrets
key: database-username
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: database-password
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: api-key
volumeMounts:
# 将Secret挂载为文件
- name: secret-volume
mountPath: /etc/app/secrets
readOnly: true
volumes:
# Secret卷
- name: secret-volume
secret:
secretName: app-secrets
items:
- key: database-password
path: db_password
- key: jwt-secret
path: jwt_secret配置的动态更新和热重载
Kubernetes支持配置的动态更新,但需要应用能够监听配置变化并重新加载。
ConfigMap更新和重新加载
# deployment-with-configmap.yaml - 支持配置更新的Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app-container
image: myapp:latest
ports:
- containerPort: 8080
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
volumeMounts:
- name: config-volume
mountPath: /etc/app/config
# 启用配置重新加载
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'Container started'"]
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
volumes:
- name: config-volume
configMap:
name: app-config
# 启用自动更新
optional: false# config_watcher.py - Python应用的配置监听器
import os
import json
import time
from typing import Dict, Any
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class ConfigWatcher(FileSystemEventHandler):
def __init__(self, config_path: str, callback):
self.config_path = config_path
self.callback = callback
self.last_modified = 0
def on_modified(self, event):
if event.is_directory:
return
if event.src_path == self.config_path:
# 检查是否真的被修改
current_modified = os.path.getmtime(self.config_path)
if current_modified > self.last_modified:
self.last_modified = current_modified
print(f"Config file {self.config_path} has been modified")
self.callback()
class ConfigManager:
def __init__(self, config_path: str):
self.config_path = config_path
self.config: Dict[str, Any] = {}
self._load_config()
# 启动配置监听器
self._start_watching()
def _load_config(self):
"""加载配置文件"""
try:
with open(self.config_path, 'r') as f:
if self.config_path.endswith('.json'):
self.config = json.load(f)
elif self.config_path.endswith('.yaml') or self.config_path.endswith('.yml'):
import yaml
self.config = yaml.safe_load(f)
else:
# 处理其他格式
content = f.read()
self.config = {"content": content}
print(f"Configuration loaded from {self.config_path}")
except Exception as e:
print(f"Error loading configuration: {e}")
def _start_watching(self):
"""启动配置监听"""
event_handler = ConfigWatcher(self.config_path, self._on_config_change)
observer = Observer()
observer.schedule(event_handler, os.path.dirname(self.config_path), recursive=False)
observer.start()
print(f"Started watching config file: {self.config_path}")
def _on_config_change(self):
"""配置变化回调"""
print("Configuration changed, reloading...")
self._load_config()
# 在实际应用中,这里可以触发重新连接数据库、重新加载缓存等操作
self._reload_application()
def _reload_application(self):
"""重新加载应用配置"""
print("Reloading application with new configuration...")
# 实现应用重新加载逻辑
# 例如重新连接数据库、更新缓存配置等
def get_config(self) -> Dict[str, Any]:
"""获取当前配置"""
return self.config.copy()
def get_value(self, key_path: str, default=None):
"""根据键路径获取配置值"""
keys = key_path.split('.')
value = self.config
try:
for key in keys:
value = value[key]
return value
except (KeyError, TypeError):
return default
# 使用示例
# config_manager = ConfigManager("/etc/app/config/database.json")
#
# # 获取数据库配置
# db_host = config_manager.get_value("host", "localhost")
# db_port = config_manager.get_value("port", 5432)
# print(f"Database: {db_host}:{db_port}")配置验证和监控
在生产环境中,配置的验证和监控是确保系统稳定运行的重要环节。
配置验证
# config-validation-job.yaml - 配置验证Job
apiVersion: batch/v1
kind: Job
metadata:
name: config-validation-job
labels:
app: config-validator
spec:
template:
metadata:
labels:
app: config-validator
spec:
containers:
- name: config-validator
image: config-validator:latest
env:
- name: CONFIG_PATH
value: "/etc/app/config"
volumeMounts:
- name: config-volume
mountPath: /etc/app/config
readOnly: true
- name: secret-volume
mountPath: /etc/app/secrets
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
- name: secret-volume
secret:
secretName: app-secrets
restartPolicy: Never
backoffLimit: 4# config_validator.py - 配置验证器
import os
import json
import yaml
import sys
from typing import Dict, Any, List
class ConfigValidator:
def __init__(self, config_path: str):
self.config_path = config_path
self.errors: List[str] = []
def validate(self) -> bool:
"""验证配置"""
self.errors = []
# 验证ConfigMap配置
self._validate_configmap()
# 验证Secret配置
self._validate_secrets()
# 验证配置一致性
self._validate_consistency()
return len(self.errors) == 0
def _validate_configmap(self):
"""验证ConfigMap配置"""
config_files = [
"database-config.json",
"cache-config.yaml",
"app.properties"
]
for file_name in config_files:
file_path = os.path.join(self.config_path, file_name)
if not os.path.exists(file_path):
self.errors.append(f"Config file not found: {file_path}")
continue
try:
if file_name.endswith('.json'):
with open(file_path, 'r') as f:
json.load(f)
elif file_name.endswith('.yaml') or file_name.endswith('.yml'):
with open(file_path, 'r') as f:
yaml.safe_load(f)
except Exception as e:
self.errors.append(f"Invalid config format in {file_name}: {e}")
def _validate_secrets(self):
"""验证Secret配置"""
required_secrets = [
"database-password",
"api-key",
"jwt-secret"
]
for secret_name in required_secrets:
secret_path = os.path.join(self.config_path, "secrets", secret_name)
if not os.path.exists(secret_path):
self.errors.append(f"Required secret not found: {secret_path}")
continue
# 检查密钥长度
try:
with open(secret_path, 'r') as f:
content = f.read().strip()
if len(content) < 8:
self.errors.append(f"Secret {secret_name} is too short")
except Exception as e:
self.errors.append(f"Error reading secret {secret_name}: {e}")
def _validate_consistency(self):
"""验证配置一致性"""
# 检查数据库配置一致性
db_config_path = os.path.join(self.config_path, "database-config.json")
if os.path.exists(db_config_path):
try:
with open(db_config_path, 'r') as f:
db_config = json.load(f)
# 检查必需字段
required_fields = ["host", "port", "database"]
for field in required_fields:
if field not in db_config:
self.errors.append(f"Missing required database field: {field}")
# 检查端口范围
if "port" in db_config:
port = db_config["port"]
if not isinstance(port, int) or port < 1 or port > 65535:
self.errors.append(f"Invalid database port: {port}")
except Exception as e:
self.errors.append(f"Error validating database config: {e}")
def get_errors(self) -> List[str]:
"""获取验证错误"""
return self.errors.copy()
def main():
config_path = os.getenv("CONFIG_PATH", "/etc/app/config")
validator = ConfigValidator(config_path)
is_valid = validator.validate()
if is_valid:
print("Configuration validation passed")
sys.exit(0)
else:
print("Configuration validation failed:")
for error in validator.get_errors():
print(f" - {error}")
sys.exit(1)
if __name__ == "__main__":
main()通过以上内容,我们深入探讨了Kubernetes中ConfigMap和Secret的创建、使用、动态更新和安全
现在让我创建附录D:配置管理中的常见问题与解决方案
