主题
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.level2.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/tls | TLS证书 | HTTPS证书 |
kubernetes.io/dockerconfigjson | 镜像仓库认证 | Docker Hub私有镜像 |
kubernetes.io/basic-auth | HTTP Basic认证 | 用户名密码 |
kubernetes.io/ssh-auth | SSH密钥 | Git仓库访问 |
kubernetes.io/service-account-token | ServiceAccount令牌 | 自动创建 |
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.comyaml
# 方式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.03.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"] # 只允许访问特定Secretyaml
# 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访问模式:
| 模式 | 缩写 | 说明 |
|---|---|---|
| ReadWriteOnce | RWO | 单节点读写 |
| ReadOnlyMany | ROX | 多节点只读 |
| ReadWriteMany | RWX | 多节点读写 |
| ReadWriteOncePod | RWOP | 单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 # 选择特定标签的PV4.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: 10Gi4.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 |
|---|---|
| NFS | nfs-subdir-external-provisioner |
| Ceph RBD | rbd.csi.ceph.com |
| CephFS | cephfs.csi.ceph.com |
| AWS EBS | ebs.csi.aws.com |
| GCP PD | pd.csi.storage.gke.io |
| Azure Disk | disk.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: 10Gi5.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: 10Gi6.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服务质量等级
- 亲和性与反亲和性
💡 学习建议:
- 在测试环境练习ConfigMap和Secret的各种使用方式
- 部署一个有状态应用(如MySQL、Redis)体验PVC的使用
- 配置StorageClass实现存储的动态供给