ConfigMap 与 Secret

ConfigMap 与 Secret

为什么需要配置管理

应用程序通常需要配置信息:

  • 数据库连接地址
  • API 密钥
  • 功能开关
  • 环境相关配置

问题:如何管理这些配置?

不好的做法

  • 硬编码在代码中
  • 打包在镜像中

好的做法

  • 配置与代码分离
  • 使用 ConfigMap 和 Secret

ConfigMap - 配置数据

ConfigMap 用于存储非敏感的配置数据。

创建 ConfigMap

方法一:命令行(键值对)

kubectl create configmap app-config \
  --from-literal=database.host=mysql.example.com \
  --from-literal=database.port=3306 \
  --from-literal=log.level=info

方法二:从文件创建

# 创建配置文件
cat > app.properties <<EOF
database.host=mysql.example.com
database.port=3306
log.level=info
max.connections=100
EOF

# 创建 ConfigMap
kubectl create configmap app-config --from-file=app.properties

方法三:从目录创建

# 创建多个配置文件
mkdir config
echo "host=mysql.example.com" > config/database.conf
echo "level=info" > config/logging.conf

# 从目录创建
kubectl create configmap app-config --from-file=config/

方法四:YAML 定义

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  database.host: "mysql.example.com"
  database.port: "3306"
  log.level: "info"
  app.properties: |
    server.port=8080
    server.name=myapp
    feature.flags=enable-cache,enable-metrics

使用 ConfigMap

1. 环境变量方式

apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  containers:
  - name: app
    image: myapp:v1
    env:
    # 单个键值
    - name: DATABASE_HOST
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database.host
    # 导入所有键值
    envFrom:
    - configMapRef:
        name: app-config

2. 挂载为文件

apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  containers:
  - name: app
    image: myapp:v1
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: app-config

容器内文件结构:

/etc/config/
├── database.host    (内容: mysql.example.com)
├── database.port    (内容: 3306)
└── log.level        (内容: info)

3. 挂载特定键

volumes:
- name: config-volume
  configMap:
    name: app-config
    items:
    - key: app.properties
      path: application.properties

结果:

/etc/config/application.properties

Secret - 敏感数据

Secret 用于存储敏感数据(密码、令牌、密钥等)。

Secret 类型

  • Opaque:通用类型(默认)
  • kubernetes.io/dockerconfigjson:Docker 镜像仓库凭证
  • kubernetes.io/tls:TLS 证书
  • kubernetes.io/service-account-token:ServiceAccount 令牌

创建 Secret

方法一:命令行

kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=P@ssw0rd123

方法二:从文件

echo -n 'admin' > username.txt
echo -n 'P@ssw0rd123' > password.txt

kubectl create secret generic db-secret \
  --from-file=username=username.txt \
  --from-file=password=password.txt

方法三:YAML 定义

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: YWRtaW4=           # base64 编码的 "admin"
  password: UEBzc3cwcmQxMjM=   # base64 编码的 "P@ssw0rd123"

Base64 编码/解码:

# 编码
echo -n 'admin' | base64
# 输出: YWRtaW4=

# 解码
echo 'YWRtaW4=' | base64 -d
# 输出: admin

方法四:stringData(明文)

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
stringData:
  username: admin
  password: P@ssw0rd123

使用 Secret

1. 环境变量方式

apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  containers:
  - name: app
    image: myapp:v1
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: password

2. 挂载为文件

apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  containers:
  - name: app
    image: myapp:v1
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: db-secret

容器内文件:

/etc/secrets/
├── username    (内容: admin)
└── password    (内容: P@ssw0rd123)

Docker 镜像拉取凭证

创建镜像拉取 Secret

kubectl create secret docker-registry regcred \
  --docker-server=<registry-url> \
  --docker-username=<username> \
  --docker-password=<password> \
  --docker-email=<email>

示例:

kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword \
  --docker-email=user@example.com

使用镜像拉取 Secret

apiVersion: v1
kind: Pod
metadata:
  name: private-app
spec:
  containers:
  - name: app
    image: registry.example.com/myapp:v1
  imagePullSecrets:
  - name: regcred

TLS 证书 Secret

kubectl create secret tls tls-secret \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key

在 Ingress 中使用:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
spec:
  tls:
  - hosts:
    - example.com
    secretName: tls-secret
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp
            port:
              number: 80

实战示例

完整的应用配置

# ConfigMap - 应用配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
data:
  app.env: "production"
  log.level: "info"
  server.port: "8080"
  config.yaml: |
    server:
      port: 8080
      host: 0.0.0.0
    database:
      max_connections: 100
      timeout: 30s
    cache:
      enabled: true
      ttl: 3600

---
# Secret - 敏感配置
apiVersion: v1
kind: Secret
metadata:
  name: webapp-secret
type: Opaque
stringData:
  db.username: "admin"
  db.password: "SecureP@ssw0rd"
  api.key: "sk-1234567890abcdef"

---
# Deployment - 使用配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: myapp:v1
        ports:
        - containerPort: 8080
        env:
        # 从 ConfigMap 加载
        - name: APP_ENV
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: app.env
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: webapp-config
              key: log.level
        # 从 Secret 加载
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: webapp-secret
              key: db.username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: webapp-secret
              key: db.password
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: webapp-secret
              key: api.key
        volumeMounts:
        # 挂载配置文件
        - name: config
          mountPath: /etc/config
        # 挂载密钥文件
        - name: secrets
          mountPath: /etc/secrets
          readOnly: true
      volumes:
      - name: config
        configMap:
          name: webapp-config
          items:
          - key: config.yaml
            path: config.yaml
      - name: secrets
        secret:
          secretName: webapp-secret

应用和测试

# 应用配置
kubectl apply -f webapp-full.yaml

# 查看 ConfigMap
kubectl get configmap webapp-config
kubectl describe configmap webapp-config

# 查看 Secret
kubectl get secret webapp-secret
kubectl describe secret webapp-secret

# 查看 Pod 环境变量
kubectl exec -it <pod-name> -- env | grep -E 'APP_ENV|DB_'

# 查看挂载的配置文件
kubectl exec -it <pod-name> -- cat /etc/config/config.yaml

# 查看挂载的密钥文件
kubectl exec -it <pod-name> -- cat /etc/secrets/db.username

更新配置

更新 ConfigMap

# 方法一:编辑
kubectl edit configmap app-config

# 方法二:重新应用
kubectl apply -f configmap.yaml

# 方法三:patch
kubectl patch configmap app-config -p '{"data":{"log.level":"debug"}}'

配置更新生效

环境变量方式:需要重启 Pod

kubectl rollout restart deployment webapp

挂载文件方式:自动更新(延迟 1-2 分钟)

应用需要实现配置热加载才能自动生效。

常用命令

# ConfigMap
kubectl create configmap <name> --from-literal=key=value
kubectl create configmap <name> --from-file=<file>
kubectl get configmap
kubectl describe configmap <name>
kubectl edit configmap <name>
kubectl delete configmap <name>

# Secret
kubectl create secret generic <name> --from-literal=key=value
kubectl create secret generic <name> --from-file=<file>
kubectl get secret
kubectl describe secret <name>
kubectl get secret <name> -o yaml
kubectl delete secret <name>

# 查看 Secret 内容
kubectl get secret <name> -o jsonpath='{.data.password}' | base64 -d

最佳实践

1. 配置与敏感信息分离

# ConfigMap - 非敏感配置
data:
  database.host: "mysql.example.com"
  database.port: "3306"

# Secret - 敏感配置
data:
  database.username: "..."
  database.password: "..."

2. 使用有意义的名称

metadata:
  name: webapp-config        # 好
  # name: config1            # 不好

3. 配置版本化管理

metadata:
  name: webapp-config-v2
  labels:
    version: v2

4. 不要在 Secret 中存储大文件

Secret 有大小限制(1MB),不适合存储大文件。

5. 限制 Secret 访问权限

使用 RBAC 控制 Secret 访问:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["db-secret"]
  verbs: ["get"]

6. 考虑使用外部密钥管理

生产环境可以使用:

  • HashiCorp Vault
  • AWS Secrets Manager
  • Azure Key Vault
  • Google Secret Manager

小结

ConfigMap 和 Secret 是 Kubernetes 中管理配置的核心资源:

ConfigMap

  • 存储非敏感配置
  • 支持键值对、文件、多行文本
  • 可以环境变量或文件方式使用

Secret

  • 存储敏感数据
  • Base64 编码(非加密)
  • 支持多种类型
  • 应限制访问权限

关键原则

  • 配置与代码分离
  • 敏感信息使用 Secret
  • 版本化管理
  • 使用 RBAC 控制访问

下一章我们将学习 Volume 存储卷,实现数据持久化。