写在前面
本文是 K8s 学习笔记系列的第五篇,介绍配置管理(ConfigMap、Secret)和存储(PV/PVC、StorageClass)。前置知识:服务发现与网络(第四篇)。
一、ConfigMap
ConfigMap 用于存储非敏感的配置数据,让配置和镜像解耦。
1.1 创建 ConfigMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 从字面量创建
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=APP_PORT=8080 \
--from-literal=DB_HOST=mysql.prod
# 从文件创建
kubectl create configmap nginx-config --from-file=nginx.conf
# 从目录创建(每个文件变成一个 key)
kubectl create configmap app-config --from-file=./config/
# 从 YAML 创建
kubectl apply -f configmap.yaml
|
1.2 ConfigMap YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
# 键值对
APP_ENV: "production"
APP_PORT: "8080"
DB_HOST: "mysql.prod"
# 完整配置文件
application.yml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql.prod:3306/mydb
|
1.3 在 Pod 中使用
方式1:环境变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
spec:
containers:
- name: app
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
# 或者全部加载为环境变量
envFrom:
- configMapRef:
name: app-config
|
方式2:挂载为文件
1
2
3
4
5
6
7
8
9
10
11
12
|
spec:
containers:
- name: app
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
|
挂载后文件结构:
1
2
3
4
5
|
/etc/config/
├── APP_ENV # 内容:production
├── APP_PORT # 内容:8080
├── DB_HOST # 内容:mysql.prod
└── application.yml # 内容:完整 YAML
|
方式3:挂载单个文件
1
2
3
4
5
6
7
|
volumes:
- name: config-volume
configMap:
name: app-config
items: # 只挂载指定 key
- key: application.yml
path: application.yml
|
1.4 更新 ConfigMap
1
2
3
4
5
|
# 直接编辑
kubectl edit configmap app-config
# 更新后,挂载为文件的 Pod 会自动刷新(有延迟,约1分钟)
# 通过环境变量注入的不会自动更新,需要重启 Pod
|
二、Secret
Secret 用于存储敏感信息(密码、Token、证书),数据以 base64 编码存储。
Secret 只是编码不是加密,生产环境建议开启 EncryptionConfiguration 或用外部密钥管理(Vault)。
2.1 创建 Secret
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 从字面量创建
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password='P@ssw0rd'
# 从文件创建
kubectl create secret generic tls-secret \
--from-file=tls.crt=./server.crt \
--from-file=tls.key=./server.key
# 创建 Docker 仓库认证 Secret
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=password
|
2.2 Secret YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque # 通用类型
data:
username: YWRtaW4= # base64 编码的 "admin"
password: UEBzc3cwcmQ= # base64 编码的 "P@ssw0rd"
# 也可以用 stringData(明文,K8s 会自动编码)
stringData:
username: admin
password: P@ssw0rd
|
2.3 Secret 类型
1
2
3
4
5
6
|
Opaque — 通用(默认)
docker-registry — Docker 仓库认证
tls — TLS 证书
basic-auth — 基础认证
ssh-auth — SSH 认证
token — Token
|
2.4 在 Pod 中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# 环境变量
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
# 挂载为文件
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-secret
# 拉取私有镜像
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.example.com/my-app:1.0
|
2.5 查看 Secret 内容
1
2
3
4
5
6
7
8
|
# 查看列表
kubectl get secrets
# 查看详情(不显示内容)
kubectl describe secret db-secret
# 解码查看内容
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 --decode
|
三、Volume
Volume 解决容器数据持久化问题。容器重启后文件会丢失,Volume 让数据独立于容器生命周期。
3.1 Volume 类型
1
2
3
4
5
|
emptyDir — 临时目录,Pod 删除后数据丢失(Pod 内容器共享)
hostPath — 挂载宿主机目录(单节点,不适合多节点)
configMap — 挂载 ConfigMap
secret — 挂载 Secret
persistentVolumeClaim — 挂载持久化存储(推荐)
|
3.2 emptyDir
Pod 内容器共享临时目录,Pod 删除后数据丢失:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
spec:
containers:
- name: app
volumeMounts:
- name: cache
mountPath: /cache
- name: sidecar
volumeMounts:
- name: cache
mountPath: /cache
volumes:
- name: cache
emptyDir: {}
|
3.3 hostPath
挂载宿主机目录(开发/测试用,不推荐生产):
1
2
3
4
5
|
volumes:
- name: data
hostPath:
path: /data/app # 宿主机路径
type: DirectoryOrCreate
|
四、持久化存储(PV/PVC)
PV(PersistentVolume)是集群级别的存储资源,PVC(PersistentVolumeClaim)是用户对存储的申请。
4.1 存储架构
1
2
3
4
5
6
7
|
管理员创建 PV(或由 StorageClass 动态创建)
↓
用户创建 PVC(声明需要的存储大小和访问模式)
↓
K8s 绑定 PVC 到合适的 PV
↓
Pod 通过 PVC 使用存储
|
4.2 PV 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-10gi
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce # 单节点读写
persistentVolumeReclaimPolicy: Retain # 释放后保留数据
storageClassName: standard
hostPath: # 本地存储(测试用)
path: /data/pv-10gi
|
4.3 PVC 示例
1
2
3
4
5
6
7
8
9
10
11
|
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
|
4.4 在 Pod 中使用 PVC
1
2
3
4
5
6
7
8
9
10
|
spec:
containers:
- name: app
volumeMounts:
- name: data
mountPath: /var/lib/data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
|
4.5 访问模式
1
2
3
4
|
ReadWriteOnce(RWO) — 单节点读写(最常用)
ReadOnlyMany(ROX) — 多节点只读
ReadWriteMany(RWX) — 多节点读写(需要存储后端支持)
ReadWriteOncePod(RWOP) — 单 Pod 读写(K8s 1.27+)
|
4.6 回收策略
1
2
3
|
Retain — 保留数据,需要手动清理(生产推荐)
Delete — 自动删除 PV 和底层存储
Recycle — 已废弃,用 Delete 替代
|
五、StorageClass
StorageClass 定义存储类型,支持动态创建 PV。
5.1 StorageClass 示例
1
2
3
4
5
6
7
8
9
10
|
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs # 存储提供者
parameters:
type: gp3
fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
|
5.2 PVC 动态分配
1
2
3
4
5
6
7
8
9
10
11
12
|
# 不需要手动创建 PV,StorageClass 自动创建
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd # 指定 StorageClass
resources:
requests:
storage: 10Gi
|
5.3 minikube 的默认存储
1
2
3
4
5
6
7
8
9
|
# minikube 自带 standard StorageClass
kubectl get storageclass
# NAME PROVISIONER RECLAIMPOLICY
# standard (default) k8s.io/minikube-hostpath Delete
# 创建 PVC 后自动创建 PV
kubectl apply -f pvc.yaml
kubectl get pv # 自动创建的 PV
kubectl get pvc # PVC 自动绑定
|
六、StatefulSet + PVC 模式
StatefulSet 配合 volumeClaimTemplates,每个 Pod 自动获得独立的持久化存储:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 10Gi
|
1
2
3
4
5
6
|
自动创建的 PVC:
mysql-data-mysql-0 → mysql-0 的存储
mysql-data-mysql-1 → mysql-1 的存储
mysql-data-mysql-2 → mysql-2 的存储
Pod 重建后,PVC 不会删除,Pod 自动绑定回原来的 PVC
|
七、开发者视角:配置管理最佳实践
7.1 配置分层
1
2
3
4
|
环境变量 → 非敏感配置(APP_ENV、APP_PORT)
ConfigMap → 配置文件、环境变量(不同环境不同 ConfigMap)
Secret → 密码、Token、证书
PVC → 需要持久化的数据
|
7.2 应用适配
1
2
3
4
5
6
7
8
|
func main() {
// 从环境变量读取配置
dbHost := os.Getenv("DB_HOST")
dbPassword := os.Getenv("DB_PASSWORD")
// 本地开发用 .env 文件,K8s 用 ConfigMap/Secret 注入
// 代码不需要感知配置来源
}
|
7.3 不同环境管理
1
2
3
4
5
|
dev 环境配置 → app-config-dev
staging 配置 → app-config-staging
prod 配置 → app-config-prod
部署时通过 -n 或 -f 指定对应的 ConfigMap
|
八、小结
本文学习了 K8s 的配置与存储:
- ConfigMap:非敏感配置管理
- Secret:敏感信息管理
- Volume:容器存储
- PV/PVC:持久化存储
- StorageClass:动态存储分配
- StatefulSet + PVC 模式
- 配置管理最佳实践
下一篇将学习调度与扩缩:标签选择器、节点亲和性和 HPA。