跳转到内容

K8s存储与配置管理

课程目标

通过本课程的学习,你将能够:

  • 掌握ConfigMap和Secret的配置管理方法
  • 理解K8s存储体系(PV/PVC/StorageClass)
  • 实现应用的配置外部化和敏感信息管理
  • 配置有状态应用的持久化存储
  • 掌握存储类的动态供给机制
  • 应用存储和配置的最佳实践

前置要求:已完成《Pod和Deployment管理》课程,了解K8S基础资源

一、配置管理概述

1.1 为什么需要配置管理

在容器化环境中,应用配置管理面临以下挑战:

挑战说明解决方案
环境差异开发/测试/生产环境配置不同配置外部化
敏感信息密码、密钥需要安全管理Secret加密存储
动态更新配置变更无需重建镜像热更新机制
版本控制配置变更需要可追溯版本化管理

1.2 K8s配置管理方案

┌─────────────────────────────────────────────────────────────┐
│                    Kubernetes配置管理                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐  │
│  │  ConfigMap   │    │    Secret    │    │  DownwardAPI │  │
│  │  (普通配置)   │    │  (敏感数据)   │    │  (元数据注入) │  │
│  └──────┬───────┘    └──────┬───────┘    └──────┬───────┘  │
│         │                   │                   │          │
│         └───────────────────┼───────────────────┘          │
│                             ▼                              │
│  ┌──────────────────────────────────────────────────────┐ │
│  │                    Pod中的应用                        │ │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────────┐   │ │
│  │  │ 环境变量  │  │ 挂载卷    │  │ 命令行参数        │   │ │
│  │  └──────────┘  └──────────┘  └──────────────────┘   │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、ConfigMap配置管理

2.1 ConfigMap基础

ConfigMap 用于存储非敏感的配置数据,如配置文件、环境变量等。

创建ConfigMap的三种方式

yaml
# 方式1:从字面量创建
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  # 键值对形式
  database.host: "mysql.default.svc.cluster.local"
  database.port: "3306"
  cache.enabled: "true"
  log.level: "info"
yaml
# 方式2:从文件创建
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  # 文件内容作为值
  nginx.conf: |
    server {
      listen 80;
      server_name localhost;
      
      location / {
        root /usr/share/nginx/html;
        index index.html;
      }
      
      location /health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
      }
    }
  
  # 多个配置文件
  default.conf: |
    server {
      listen 80 default_server;
      server_name _;
      return 444;
    }
bash
# 方式3:从命令行创建
# 从字面量创建
kubectl create configmap app-config \
  --from-literal=database.host=mysql.default.svc.cluster.local \
  --from-literal=database.port=3306 \
  --from-literal=log.level=info

# 从文件创建
kubectl create configmap nginx-config \
  --from-file=nginx.conf=/path/to/nginx.conf \
  --from-file=default.conf=/path/to/default.conf

# 从目录创建(目录下所有文件)
kubectl create configmap app-configs \
  --from-file=/path/to/configs/

2.2 在Pod中使用ConfigMap

方式1:挂载为卷(推荐)

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        volumeMounts:
        - name: config-vol
          mountPath: /etc/nginx/conf.d
          readOnly: true
      volumes:
      - name: config-vol
        configMap:
          name: nginx-config
          # 可选:指定特定的key
          items:
          - key: nginx.conf
            path: nginx.conf
          - key: default.conf
            path: default.conf
          # 可选:设置文件权限
          defaultMode: 0644

方式2:注入环境变量

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: app
        image: myapp:1.0
        env:
        # 直接引用ConfigMap的key
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: database.host
        - name: DB_PORT
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: database.port
        # 批量注入所有key
        envFrom:
        - configMapRef:
            name: app-config
            optional: false  # ConfigMap不存在时是否继续

方式3:在命令行参数中使用

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: worker-app
spec:
  template:
    spec:
      containers:
      - name: worker
        image: worker:1.0
        command: ["/bin/sh"]
        args:
        - -c
        - |
          echo "Starting worker with config:"
          echo "Log level: $(LOG_LEVEL)"
          echo "Cache enabled: $(CACHE_ENABLED)"
          /app/worker --log-level=$(LOG_LEVEL)
        env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: log.level

2.3 ConfigMap热更新

ConfigMap挂载为卷时支持热更新(无需重启Pod):

bash
# 修改ConfigMap
kubectl edit configmap nginx-config

# 或从文件更新
kubectl create configmap nginx-config \
  --from-file=nginx.conf=new-nginx.conf \
  --dry-run=client -o yaml | kubectl apply -f -

# 验证更新(Pod内文件会自动更新)
kubectl exec -it <pod-name> -- cat /etc/nginx/conf.d/nginx.conf

注意:环境变量方式不支持热更新,需要重启Pod才能生效。

2.4 ConfigMap最佳实践

yaml
# 1. 按环境分离ConfigMap
# configmap-dev.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: dev
data:
  log.level: "debug"
  database.host: "mysql-dev.default.svc.cluster.local"

---
# configmap-prod.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: prod
data:
  log.level: "warn"
  database.host: "mysql-prod.default.svc.cluster.local"
yaml
# 2. 使用不可变ConfigMap提升性能
apiVersion: v1
kind: ConfigMap
metadata:
  name: static-config
immutable: true  # 创建后不可修改
data:
  app.name: "myapp"
  app.version: "1.0.0"
yaml
# 3. 配置大小限制(1MB)
# 大配置文件建议使用卷挂载而非ConfigMap
# 或使用Init Container下载配置

三、Secret敏感数据管理

3.1 Secret基础

Secret 用于存储敏感数据,如密码、令牌、密钥等。数据以Base64编码存储。

Secret类型

类型用途示例
Opaque通用Secret自定义密码、配置
kubernetes.io/tlsTLS证书HTTPS证书
kubernetes.io/dockerconfigjson镜像仓库认证Docker Hub私有镜像
kubernetes.io/basic-authHTTP Basic认证用户名密码
kubernetes.io/ssh-authSSH密钥Git仓库访问
kubernetes.io/service-account-tokenServiceAccount令牌自动创建

3.2 创建Secret

bash
# 方式1:从命令行创建(自动Base64编码)
kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password='MyP@ssw0rd!'

# 方式2:从文件创建
echo -n 'admin' > username.txt
echo -n 'MyP@ssw0rd!' > password.txt
kubectl create secret generic db-secret \
  --from-file=username=username.txt \
  --from-file=password=password.txt

# 方式3:创建TLS Secret
kubectl create secret tls my-tls-secret \
  --cert=path/to/cert.crt \
  --key=path/to/key.key

# 方式4:创建Docker Registry Secret
kubectl create secret docker-registry regcred \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=myusername \
  --docker-password=mypassword \
  --docker-email=myemail@example.com
yaml
# 方式5:YAML文件创建(手动Base64编码)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  # echo -n 'admin' | base64
  username: YWRtaW4=
  # echo -n 'MyP@ssw0rd!' | base64
  password: TXlQQHNzdzByZCE=

3.3 在Pod中使用Secret

方式1:挂载为卷(推荐敏感文件)

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:1.0
        volumeMounts:
        - name: secret-vol
          mountPath: "/etc/secrets"
          readOnly: true
      volumes:
      - name: secret-vol
        secret:
          secretName: db-secret
          # Secret会自动解码,文件内容为原始值
          defaultMode: 0400  # 只读权限

方式2:注入环境变量

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  template:
    spec:
      containers:
      - name: api
        image: api:1.0
        env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password
              optional: false  # Secret必须存在

方式3:使用镜像仓库Secret

yaml
apiVersion: v1
kind: Pod
metadata:
  name: private-image-pod
spec:
  imagePullSecrets:
  - name: regcred  # Docker Registry Secret
  containers:
  - name: app
    image: myusername/private-image:1.0

3.4 Secret安全最佳实践

yaml
# 1. 启用Secret加密(etcd中加密存储)
# 在kube-apiserver配置中添加:
# --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: <base64-encoded-32-byte-key>
    - identity: {}  # 允许未加密读取
yaml
# 2. 限制Secret访问(RBAC)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list"]
  resourceNames: ["app-secret"]  # 只允许访问特定Secret
yaml
# 3. 使用外部Secret管理(如Vault)
# 通过CSI驱动或Operator集成外部密钥管理系统

四、持久化存储

4.1 K8s存储体系

┌─────────────────────────────────────────────────────────────────────┐
│                        Kubernetes存储体系                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌──────────────┐         ┌──────────────┐         ┌────────────┐ │
│   │     Pod      │ ──────▶ │     PVC      │ ──────▶ │     PV     │ │
│   │              │         │  (存储申请)   │         │  (存储资源) │ │
│   └──────────────┘         └──────────────┘         └─────┬──────┘ │
│          │                                                │        │
│          │                                                ▼        │
│          │                                         ┌────────────┐ │
│          │                                         │StorageClass│ │
│          │                                         │ (存储类型)  │ │
│          │                                         └─────┬──────┘ │
│          │                                               │        │
│          ▼                                               ▼        │
│   ┌────────────────────────────────────────────────────────────┐ │
│   │                      底层存储系统                            │ │
│   │   NFS  │  iSCSI  │  Ceph  │  AWS EBS  │  GCP PD  │  Azure   │ │
│   └────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.2 持久卷(PV)

PV 是集群中的一块存储资源,由管理员预先创建或动态供给。

yaml
# 手动创建PV(NFS示例)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv-001
  labels:
    type: nfs
    env: production
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany  # 多节点读写
  persistentVolumeReclaimPolicy: Retain  # 回收策略
  storageClassName: nfs-standard
  nfs:
    server: 192.168.1.100
    path: /exports/data001
    readOnly: false
  mountOptions:
    - hard
    - nfsvers=4.1

访问模式

模式缩写说明
ReadWriteOnceRWO单节点读写
ReadOnlyManyROX多节点只读
ReadWriteManyRWX多节点读写
ReadWriteOncePodRWOP单Pod读写(1.22+)

回收策略

策略说明
Retain保留数据,需要手动清理
Delete删除PV和底层存储
Recycle清空数据(已废弃)

4.3 持久卷声明(PVC)

PVC 是用户对存储的请求,K8s会自动绑定符合条件的PV。

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard  # 指定StorageClass
  selector:
    matchLabels:
      env: production  # 选择特定标签的PV

4.4 在Pod中使用PVC

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: config
          mountPath: /etc/mysql/conf.d
      volumes:
      - name: config
        configMap:
          name: mysql-config
  volumeClaimTemplates:  # 动态创建PVC
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: standard
      resources:
        requests:
          storage: 10Gi

4.5 StorageClass动态供给

StorageClass 定义存储的"类型",支持动态创建PV。

yaml
# NFS StorageClass示例
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-standard
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
  server: 192.168.1.100
  path: /exports
  archiveOnDelete: "false"
reclaimPolicy: Delete
allowVolumeExpansion: true  # 允许扩容
mountOptions:
  - vers=4.1
volumeBindingMode: Immediate  # 立即绑定
yaml
# AWS EBS StorageClass示例
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3-standard
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  encrypted: "true"
  kmsKeyId: alias/aws/ebs
volumeBindingMode: WaitForFirstConsumer  # 延迟绑定到节点

常用Provisioner

存储类型Provisioner
NFSnfs-subdir-external-provisioner
Ceph RBDrbd.csi.ceph.com
CephFScephfs.csi.ceph.com
AWS EBSebs.csi.aws.com
GCP PDpd.csi.storage.gke.io
Azure Diskdisk.csi.azure.com
本地存储local-volume-provisioner

五、实战案例:有状态应用部署

5.1 部署MySQL主从集群

yaml
# 1. ConfigMap - MySQL配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
data:
  master.cnf: |
    [mysqld]
    server-id=1
    log-bin=mysql-bin
    binlog-format=ROW
    
  slave.cnf: |
    [mysqld]
    server-id=2
    relay-log=mysql-relay-bin
    read-only=1

---
# 2. Secret - MySQL密码
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  root-password: cm9vdDEyMw==  # root123
  repl-password: cmVwbDEyMw==  # repl123

---
# 3. Service - MySQL主库
apiVersion: v1
kind: Service
metadata:
  name: mysql-master
  labels:
    app: mysql
    role: master
spec:
  ports:
  - port: 3306
    name: mysql
  selector:
    app: mysql
    role: master

---
# 4. StatefulSet - MySQL主库
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-master
spec:
  serviceName: mysql-master
  replicas: 1
  selector:
    matchLabels:
      app: mysql
      role: master
  template:
    metadata:
      labels:
        app: mysql
        role: master
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: config
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
      volumes:
      - name: config
        configMap:
          name: mysql-config
          items:
          - key: master.cnf
            path: master.cnf
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: standard
      resources:
        requests:
          storage: 10Gi

5.2 验证部署

bash
# 查看PV和PVC
kubectl get pv,pvc

# 进入MySQL主库
kubectl exec -it mysql-master-0 -- mysql -uroot -p

# 查看主库状态
SHOW MASTER STATUS;

# 创建复制用户
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl123';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;

六、存储和配置最佳实践

6.1 配置管理清单

yaml
# ✅ 推荐做法

# 1. 敏感数据使用Secret
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
data:
  password: <base64-encoded>

---
# 2. 普通配置使用ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  config.yaml: |
    server:
      port: 8080
      logLevel: info

---
# 3. 需要热更新的配置挂载为卷
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: app
        volumeMounts:
        - name: config
          mountPath: /app/config
      volumes:
      - name: config
        configMap:
          name: app-config

---
# 4. 有状态应用使用StatefulSet + PVC
apiVersion: apps/v1
kind: StatefulSet
spec:
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: standard
      resources:
        requests:
          storage: 10Gi

6.2 存储选型建议

应用场景推荐方案原因
数据库StatefulSet + RWO PVC数据持久化,单节点访问
文件共享NFS + RWX PVC多Pod共享访问
缓存EmptyDir临时存储,Pod删除即清理
日志收集HostPath / Sidecar节点级日志访问
静态资源ConfigMap配置与代码分离
敏感配置Secret + 卷挂载安全存储,限制访问

6.3 常见问题排查

bash
# 1. PVC无法绑定
kubectl describe pvc <pvc-name>
# 检查:PV是否存在、容量是否足够、访问模式是否匹配

# 2. Pod无法挂载卷
kubectl describe pod <pod-name>
# 检查:PVC是否已绑定、存储是否可访问

# 3. 权限问题
kubectl exec -it <pod-name> -- ls -la <mount-path>
# 检查:securityContext、fsGroup设置

# 4. 存储扩容
kubectl patch pvc <pvc-name> -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
# 检查:StorageClass是否支持扩容

七、总结

核心概念回顾

配置管理
├── ConfigMap - 非敏感配置
│   ├── 字面量、文件、目录创建
│   ├── 卷挂载(支持热更新)
│   └── 环境变量注入

├── Secret - 敏感数据
│   ├── Base64编码存储
│   ├── 支持加密(etcd)
│   └── 访问控制(RBAC)

└── 最佳实践
    ├── 配置与镜像分离
    ├── 按环境隔离
    └── 不可变配置提升性能

存储管理
├── PV - 存储资源
│   ├── 手动创建
│   └── 动态供给

├── PVC - 存储申请
│   ├── 容量请求
│   ├── 访问模式
│   └── StorageClass选择

├── StorageClass - 存储类型
│   ├── Provisioner
│   ├── 参数配置
│   └── 回收策略

└── 有状态应用
    └── StatefulSet + volumeClaimTemplates

下节预告

在下一节《K8s调度与资源管理》中,我们将学习:

  • Pod调度策略和节点选择
  • 资源请求与限制配置
  • HPA自动扩缩容
  • QoS服务质量等级
  • 亲和性与反亲和性

💡 学习建议

  1. 在测试环境练习ConfigMap和Secret的各种使用方式
  2. 部署一个有状态应用(如MySQL、Redis)体验PVC的使用
  3. 配置StorageClass实现存储的动态供给

评论区

专业的Linux技术学习平台,从入门到精通的完整学习路径