写在前面
在之前的文章中,我们学习了Kubernetes的核心对象、服务发现和负载均衡机制。现在,我们需要解决两个关键问题:如何管理应用配置和敏感信息,以及如何实现数据持久化。ConfigMap和Secret提供了配置和敏感信息的管理能力,而PersistentVolume和PersistentVolumeClaim则实现了存储资源的抽象和管理。
这篇文章将详细讲解ConfigMap、Secret、PersistentVolume、PersistentVolumeClaim和StorageClass,帮助你理解Kubernetes如何管理应用配置和数据持久化。
一、ConfigMap:配置管理
1.1 ConfigMap的概念
ConfigMap是Kubernetes中用于存储非敏感配置数据的API对象。它将配置信息与容器镜像解耦,使应用配置更加灵活和可移植。
┌─────────────────────────────────────────────────────────────────────────┐
│ ConfigMap使用方式 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ConfigMap │ │
│ │ ┌─────────────────────────────────────────────────────────────┐│ │
│ │ │ 数据: ││ │
│ │ │ - 配置文件(application.properties) ││ │
│ │ │ - 环境变量(DB_HOST, DB_PORT) ││ │
│ │ │ - 命令行参数 ││ │
│ │ └─────────────────────────────────────────────────────────────┘│ │
│ │ │ │ │
│ │ ┌───────────────┼───────────────┐ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ 环境变量注入 │ │ 配置文件挂载 │ │ 命令行参数 │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ env: │ │ volumeMounts: │ │ command: │ │ │
│ │ │ - name: DB │ │ - name: config │ │ - ./app │ │ │
│ │ │ valueFrom: │ │ mountPath: │ │ args: │ │ │
│ │ │ configMap...│ │ /etc/config │ │ - --config=... │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
1.2 创建ConfigMap
使用命令行创建:
# 从字面值创建
kubectl create configmap my-config \
--from-literal=db_host=mysql \
--from-literal=db_port=3306
# 从文件创建
kubectl create configmap app-config --from-file=application.properties
# 从目录创建(目录下所有文件)
kubectl create configmap config-from-dir --from-file=./config/
# 从环境文件创建
kubectl create configmap env-config --from-env-file=.env
使用YAML创建:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
# 键值对形式
db_host: mysql-service
db_port: "3306"
db_name: myapp
# 配置文件形式
application.properties: |
server.port=8080
spring.datasource.url=jdbc:mysql://mysql-service:3306/myapp
spring.datasource.username=root
logging.level.root=INFO
nginx.conf: |
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
1.3 使用ConfigMap
方式一:环境变量注入
apiVersion: v1
kind: Pod
metadata:
name: env-pod
spec:
containers:
- name: app
image: busybox
command: ["sh", "-c", "echo $DB_HOST && echo $DB_PORT && sleep 3600"]
env:
# 引用ConfigMap中的单个键
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: db_host
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: db_port
# 引用ConfigMap中的所有键
envFrom:
- configMapRef:
name: app-config
prefix: APP_ # 可选前缀
方式二:配置文件挂载
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
items: # 可选:选择特定键
- key: application.properties
path: app.properties
- key: nginx.conf
path: nginx/nginx.conf
方式三:挂载单个文件
apiVersion: v1
kind: Pod
metadata:
name: single-file-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf # 挂载单个文件
volumes:
- name: nginx-config
configMap:
name: app-config
items:
- key: nginx.conf
path: nginx.conf
1.4 ConfigMap热更新
ConfigMap挂载为Volume时,支持自动更新。当ConfigMap更新后,Kubernetes会自动更新Pod中的文件。
# 更新ConfigMap
kubectl create configmap app-config --from-file=application.properties --dry-run=client -o yaml | kubectl apply -f -
# 或者直接编辑
kubectl edit configmap app-config
# 查看更新后的配置
kubectl exec -it volume-pod -- cat /etc/config/application.properties
注意事项:
- 环境变量方式的ConfigMap不支持热更新,需要重启Pod
- 使用
subPath挂载的文件不支持热更新 - 热更新有延迟(默认10秒同步一次)
1.5 ConfigMap管理命令
# 查看ConfigMap
kubectl get configmap
kubectl get cm
# 查看ConfigMap详情
kubectl describe configmap app-config
# 查看ConfigMap内容
kubectl get configmap app-config -o yaml
# 编辑ConfigMap
kubectl edit configmap app-config
# 删除ConfigMap
kubectl delete configmap app-config
二、Secret:敏感信息管理
2.1 Secret的概念
Secret是Kubernetes中用于存储敏感信息的API对象,如密码、OAuth令牌、SSH密钥等。与ConfigMap类似,但Secret的数据是Base64编码的,并且在使用时会被解码。
┌─────────────────────────────────────────────────────────────────────────┐
│ Secret类型 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Opaque │ │ kubernetes.io/ │ │ kubernetes.io/ │ │
│ │ │ │ dockerconfigjson│ │ tls │ │
│ │ 通用Secret │ │ Docker镜像拉取 │ │ TLS证书 │ │
│ │ │ │ 凭证 │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ kubernetes.io/ │ │ kubernetes.io/ │ │ kubernetes.io/ │ │
│ │ basic-auth │ │ ssh-auth │ │ service-account│ │
│ │ │ │ │ │ -token │ │
│ │ 基本认证 │ │ SSH认证 │ │ 服务账户令牌 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.2 创建Secret
使用命令行创建:
# 从字面值创建(通用Secret)
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password='P@ssw0rd'
# 从文件创建
kubectl create secret generic ssh-secret \
--from-file=id_rsa=/path/to/id_rsa \
--from-file=id_rsa.pub=/path/to/id_rsa.pub
# 创建TLS Secret
kubectl create secret tls my-tls-secret \
--cert=path/to/cert.pem \
--key=path/to/key.pem
# 创建Docker Registry Secret
kubectl create secret docker-registry my-registry-secret \
--docker-server=registry.example.com \
--docker-username=admin \
--docker-password=password \
--docker-email=admin@example.com
使用YAML创建:
apiVersion: v1
kind: Secret
metadata:
name: db-secret
namespace: default
type: Opaque # Secret类型
data:
# Base64编码的值
username: YWRtaW4= # echo -n 'admin' | base64
password: UEBzc3cwcmQ= # echo -n 'P@ssw0rd' | base64
stringData:
# 明文值(创建时自动编码)
username: admin
password: P@ssw0rd
2.3 使用Secret
方式一:环境变量注入
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: app
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: db-secret
key: username
envFrom:
- secretRef:
name: db-secret
方式二:配置文件挂载
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-secret
items:
- key: username
path: db_username
- key: password
path: db_password
方式三:拉取私有镜像
apiVersion: v1
kind: Pod
metadata:
name: private-image-pod
spec:
imagePullSecrets:
- name: my-registry-secret
containers:
- name: app
image: registry.example.com/my-app:v1.0
2.4 Secret安全最佳实践
# 1. 使用RBAC限制Secret访问
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: default
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-secret"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-secrets
namespace: default
subjects:
- kind: ServiceAccount
name: my-service-account
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
# 2. 使用外部密钥管理系统(如Vault)
# 安装Vault Agent Injector后
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "my-app"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/my-app"
spec:
# ...
安全注意事项:
- Secret数据只是Base64编码,不是加密
- 启用静态数据加密(Encryption at Rest)
- 使用RBAC限制Secret访问权限
- 考虑使用外部密钥管理系统
- 不要将Secret提交到版本控制系统
2.5 Secret管理命令
# 查看Secret
kubectl get secrets
kubectl get secret db-secret -o yaml
# 解码Secret数据
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 --decode
# 编辑Secret
kubectl edit secret db-secret
# 删除Secret
kubectl delete secret db-secret
三、持久化存储
3.1 存储架构
Kubernetes的存储架构将存储资源抽象为三个层次:
┌─────────────────────────────────────────────────────────────────────────┐
│ Kubernetes存储架构 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Pod │ │
│ │ ┌─────────────────────────────────────────────────────────────┐│ │
│ │ │ volumeMounts: ││ │
│ │ │ - name: data ││ │
│ │ │ mountPath: /data ││ │
│ │ └─────────────────────────────────────────────────────────────┘│ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐│ │
│ │ │ volumes: ││ │
│ │ │ - name: data ││ │
│ │ │ persistentVolumeClaim: ││ │
│ │ │ claimName: my-pvc ││ │
│ │ └─────────────────────────────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ PersistentVolumeClaim (PVC) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐│ │
│ │ │ 存储声明: ││ │
│ │ │ - 存储大小:10Gi ││ │
│ │ │ - 访问模式:ReadWriteOnce ││ │
│ │ │ - 存储类:standard ││ │
│ │ └─────────────────────────────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ PersistentVolume (PV) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐│ │
│ │ │ 存储资源: ││ │
│ │ │ - 容量:10Gi ││ │
│ │ │ - 访问模式:ReadWriteOnce ││ │
│ │ │ - 存储后端:NFS / Ceph / 云盘 ││ │
│ │ └─────────────────────────────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ StorageClass (SC) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐│ │
│ │ │ 存储类定义: ││ │
│ │ │ - 供应者:kubernetes.io/aws-ebs ││ │
│ │ │ - 参数:type=gp2 ││ │
│ │ │ - 回收策略:Delete ││ │
│ │ └─────────────────────────────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.2 Volume类型
Kubernetes支持多种Volume类型:
|-----------------------|-----------------|------------|
| 类型 | 说明 | 使用场景 |
| emptyDir | 临时存储,Pod删除时数据丢失 | 临时缓存、中间数据 |
| hostPath | 挂载宿主机路径 | 开发测试、节点级数据 |
| nfs | NFS网络存储 | 共享存储 |
| persistentVolumeClaim | 持久化存储声明 | 生产环境 |
| configMap | 配置文件 | 应用配置 |
| secret | 敏感信息 | 密码、证书 |
| downwardAPI | Pod元数据 | 获取Pod信息 |
emptyDir示例:
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: cache
mountPath: /cache
volumes:
- name: cache
emptyDir:
sizeLimit: 500Mi # 大小限制
medium: Memory # 可选:Memory(使用内存)
hostPath示例:
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: host-data
mountPath: /data
volumes:
- name: host-data
hostPath:
path: /var/data
type: DirectoryOrCreate # 类型:DirectoryOrCreate/Directory/File/FileOrCreate
3.3 PersistentVolume (PV)
PV是集群级别的存储资源,由管理员创建或动态供应。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem # Filesystem或Block
accessModes:
- ReadWriteOnce # RWO:单节点读写
- ReadOnlyMany # ROX:多节点只读
- ReadWriteMany # RWX:多节点读写
persistentVolumeReclaimPolicy: Retain # Retain/Recycle/Delete
storageClassName: nfs
nfs:
server: 192.168.1.100
path: /data/pv
访问模式说明:
|------------------|------|--------------------------|
| 访问模式 | 缩写 | 说明 |
| ReadWriteOnce | RWO | 单节点读写 |
| ReadOnlyMany | ROX | 多节点只读 |
| ReadWriteMany | RWX | 多节点读写 |
| ReadWriteOncePod | RWOP | 单Pod读写(Kubernetes 1.22+) |
回收策略:
|---------|------------|
| 策略 | 说明 |
| Retain | 保留数据,需手动清理 |
| Recycle | 删除数据(已弃用) |
| Delete | 删除存储资源 |
3.4 PersistentVolumeClaim (PVC)
PVC是命名空间级别的存储声明,由用户创建。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard # 指定存储类
selector: # 选择特定的PV
matchLabels:
environment: production
在Pod中使用PVC:
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
3.5 StorageClass
StorageClass定义了存储的类型和动态供应方式。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs # 供应者
parameters:
type: gp2 # 存储类型
zone: us-east-1a # 可用区
reclaimPolicy: Delete # 回收策略
allowVolumeExpansion: true # 允许扩容
volumeBindingMode: WaitForFirstConsumer # 绑定模式
allowedTopologies:
- matchLabelExpressions:
- key: topology.kubernetes.io/zone
values:
- us-east-1a
常用存储供应者:
|--------------------------|---------------------|
| 供应者 | 说明 |
| kubernetes.io/aws-ebs | AWS EBS |
| kubernetes.io/gce-pd | GCE Persistent Disk |
| kubernetes.io/azure-disk | Azure Disk |
| kubernetes.io/cinder | OpenStack Cinder |
| nfs-client | NFS动态供应 |
| rancher.io/local-path | 本地路径供应 |
| csi.* | CSI驱动 |
本地存储StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
3.6 动态存储供应
动态存储供应允许PVC自动创建PV:
# StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
---
# PVC(自动创建PV)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast
resources:
requests:
storage: 10Gi
3.7 存储扩容
# 修改PVC大小
kubectl patch pvc my-pvc -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
# 查看扩容状态
kubectl describe pvc my-pvc
3.8 存储管理命令
# 查看PV
kubectl get pv
kubectl get persistentvolumes
# 查看PVC
kubectl get pvc
kubectl get persistentvolumeclaims
# 查看StorageClass
kubectl get sc
kubectl get storageclass
# 查看PV详情
kubectl describe pv pv-nfs
# 查看PVC详情
kubectl describe pvc my-pvc
# 删除PVC
kubectl delete pvc my-pvc
# 删除PV
kubectl delete pv pv-nfs
四、实战案例:部署MySQL数据库
让我们通过一个完整的案例,演示ConfigMap、Secret和持久化存储的综合应用。
4.1 创建配置和密钥
# mysql-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=500
innodb_buffer_pool_size=256M
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
---
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
stringData:
root-password: Root@123456
database: myapp
username: appuser
password: App@123456
4.2 创建持久化存储
# mysql-storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /data/mysql
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node-1
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 10Gi
4.3 部署MySQL
# mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql-secret
key: database
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
- name: mysql-config
mountPath: /etc/mysql/conf.d
readOnly: true
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
exec:
command:
- mysqladmin
- ping
- -h
- localhost
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command:
- mysqladmin
- ping
- -h
- localhost
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: mysql-data
persistentVolumeClaim:
claimName: mysql-pvc
- name: mysql-config
configMap:
name: mysql-config
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
type: ClusterIP
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
4.4 部署和验证
# 创建配置和密钥
kubectl apply -f mysql-config.yaml
# 创建存储
kubectl apply -f mysql-storage.yaml
# 部署MySQL
kubectl apply -f mysql-deployment.yaml
# 查看部署状态
kubectl get pods -l app=mysql
# 查看PVC状态
kubectl get pvc mysql-pvc
# 查看MySQL日志
kubectl logs -l app=mysql
# 连接MySQL测试
kubectl run -it --rm mysql-client --image=mysql:8.0 --restart=Never -- \
mysql -h mysql -u root -p'Root@123456'
# 验证数据持久化
# 删除Pod后,数据仍然存在
kubectl delete pod -l app=mysql
kubectl get pods -l app=mysql
总结
这篇文章详细讲解了Kubernetes的存储和配置管理,主要内容包括:
- ConfigMap:非敏感配置数据管理,支持环境变量注入和配置文件挂载
- Secret:敏感信息管理,支持多种类型和加密存储
- 持久化存储:PV、PVC、StorageClass三层架构,实现存储资源的抽象和管理
- 实战案例:完整的MySQL部署示例,展示配置、密钥和存储的综合应用
ConfigMap和Secret实现了配置与镜像的解耦,使应用配置更加灵活;PV/PVC/StorageClass则提供了存储资源的标准化管理方式,支持动态供应和存储扩容。这些机制共同构成了Kubernetes配置和存储管理的核心能力。