万字干货|K8s存储与配置核心全家桶:emptyDir/hostPath/NFS/PV/PVC/ConfigMap/Secret 一站式实战
摘要:本文系统梳理 Kubernetes 存储卷与配置管理的核心知识点,从最基础的 emptyDir、hostPath,到 NFS 网络存储,再到 PV/PVC 持久化架构,最后覆盖 ConfigMap 配置管理与 Secret 敏感信息加密。每个知识点都配有完整的 YAML 示例、实操命令和验证结果,适合作为 K8s 二阶段考核复习资料,也可直接作为运维手册查阅。
关键词:Kubernetes、Volume、emptyDir、hostPath、NFS、PersistentVolume、PVC、ConfigMap、Secret、容器存储
一、Kubernetes Volume 基础
1.1 环境准备
在开始实验前,先创建专用命名空间并切换上下文:
bash
root@master30:~# kubectl create ns storage
root@master30:~# kubectl config set-context --current --namespace storage
1.2 Volume 类型总览
Kubernetes 支持非常多的 Volume 类型,常见的有:
| Volume 类型 | 说明 |
|---|---|
| emptyDir | 临时目录,Pod 删除即消失 |
| hostPath | 挂载 Node 主机上的目录 |
| nfs | NFS 网络文件系统 |
| persistentVolumeClaim | 绑定 PV 持久卷 |
| secret | 存储敏感信息 |
| configMap | 存储配置数据 |
| downwardAPI | 暴露 Pod 元数据 |
| cephfs / rbd | Ceph 存储(已逐步弃用,转向 CSI) |
| local | 节点本地持久存储 |
二、emptyDir:临时卷
2.1 原理说明
emptyDir 是最基础的临时卷:
- 当 Pod 被调度到 Node 上时,自动创建 emptyDir 目录
- 只要 Pod 在该 Node 上运行,卷就一直存在
- Pod 被删除时,emptyDir 数据永久删除
- 同一个 Pod 内的多个容器可以共享 emptyDir
典型场景:同一个 Pod 中的两个容器需要共享文件(比如一个生成日志,另一个采集日志)。
2.2 实验:多容器共享 emptyDir
编写 pod-with-emptyDir.yaml:
yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox # Pod资源名称,namespace下不能重名
labels:
app: busybox # 标签,用于筛选、匹配Service等
spec:
volumes:
- name: datavolume # emptyDir卷名称
emptyDir: {} # 临时空卷,Pod生命周期绑定
containers:
- name: busybox1 # 容器1名称
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent # 本地存在镜像则不远程拉取
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data # 挂载到容器内/data路径
name: datavolume # 关联卷(必须与上面的定义的名字一样)
- name: busybox2 # 容器2名称,同Pod共享emptyDir
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data # 同样挂载/data,实现容器间共享
name: datavolume
配置说明:
spec.volumes:定义卷,类型为 emptyDirspec.containers.volumeMounts:容器内挂载点,通过 name 关联卷
创建 Pod:
bash
root@master30:~# kubectl apply -f pod-with-emptyDir.yaml
查看容器 ID 和所在节点:
bash
root@master30:~# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID: containerd://cb9d649a0c15
Container ID: containerd://cc7b82600fd4
root@master30:~# kubectl describe pod busybox |grep Node
Node: worker31.laoma.cloud/10.1.8.31
Node-Selectors: <none>
登录到对应 Node,用 crictl 查看挂载路径:
bash
root@worker31:~# crictl inspect cb9d649a0c15 |grep datavolume
"hostPath": "/var/lib/kubelet/pods/c2dbcc63-e1ac-45cc-88c8-94e7e510086e/volumes/kubernetes.io~empty-dir/datavolume",
"host_path": "/var/lib/kubelet/pods/c2dbcc63-e1ac-45cc-88c8-94e7e510086e/volumes/kubernetes.io~empty-dir/datavolume"
"source": "/var/lib/kubelet/pods/c2dbcc63-e1ac-45cc-88c8-94e7e510086e/volumes/kubernetes.io~empty-dir/datavolume",
可以看到 emptyDir 实际位于:
/var/lib/kubelet/pods/<PodUID>/volumes/kubernetes.io~empty-dir/datavolume
验证数据共享:在 busybox1 中创建文件,busybox2 中能看到:
bash
root@master30:~# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
root@master30:~# kubectl exec busybox -c busybox2 -- ls /data
b1-f1
验证 Pod 删除后数据消失:
bash
root@master30:~# kubectl delete pod busybox --force
登录 Node 查看对应目录,已经不存在。
注意:容器删除后,临时卷数据会延迟一段时间才被清理。
三、hostPath:节点主机目录映射
3.1 原理说明
hostPath 将 Node 文件系统中的单个文件或者目录 挂载到 Pod 内部,靠 type 字段控制:
| type | 含义 |
|---|---|
Directory |
路径必须是已经存在的目录,不存在 Pod 起不来 |
DirectoryOrCreate |
目录不存在就自动新建目录 |
File |
路径必须是已经存在的文件,不存在 Pod 起不来 |
FileOrCreate |
如果文件不存在,kubelet 自动创建一个空文件(权限 0644) |
- Pod 删除后,hostPath 卷数据保留在 Node 上
- 适合需要访问 Node 上文件的场景(如采集容器日志)
- 不推荐生产环境使用,存在安全风险和可移植性问题
⚠️ 注意:
FileOrCreate只会创建文件,不会自动创建上级目录 。比如/etc/test/abc.txt,如果/etc/test文件夹不存在,Pod 依然启动失败!
3.2 实验:挂载节点目录并验证读写权限
编写 pod-with-hostPath.yaml:
yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
volumes:
- name: datavolume
hostPath:
path: /busyboxdir
containers:
- name: busybox1
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
- name: busybox2
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
readOnly: false
创建 Pod:
bash
root@master30:~# kubectl apply -f pod-with-hostpath.yaml
验证数据写入和共享:
bash
root@master30:~# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
root@master30:~# kubectl exec busybox -c busybox2 -- ls /data
b1-f1
登录 Node 查看目录:
bash
root@worker32:~# ls /busyboxdir
b1-f1
删除 Pod 后验证数据保留:
bash
root@master30:~# kubectl delete pod busybox --force
root@worker32:~# ls /busyboxdir
b1-f1
Pod 删除后,hostPath 卷数据依然保留在 Node 上。
四、NFS 网络存储
4.1 搭建 NFS Server
在 master 节点安装 NFS 服务端:
bash
root@master30:~# apt install -y nfs-kernel-server
创建共享目录并写入测试文件:
bash
root@master30:~# mkdir -m 777 /nfsshares
root@master30:~# echo hello fb > /nfsshares/index.html
配置 NFS 导出,允许所有客户端访问:
bash
root@master30:~# vim /etc/exports
/nfsshares *(rw)
重启 NFS 服务:
bash
root@master30:~# systemctl restart nfs-server.service
所有 Worker 节点安装 NFS 客户端并验证:
bash
root@work31和32:~# apt install -y nfs-common
root@worker31和32:~# showmount -e master30
Export list for master30:
/nfsshares *
4.2 Pod 直接挂载 NFS
编写 pod-with-nfs.yaml:
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
volumes:
- name: nfs
nfs:
server: 10.1.8.30
path: "/nfsshares"
containers:
- image: hub.laoma.cloud/library/nginx
name: nginx
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
创建 Pod:
bash
root@master30:~# kubectl apply -f pod-with-nfs.yaml
查看 Pod IP 并访问测试:
bash
root@master30:~# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 55s 10.224.19.2 worker31.laoma.cloud <none> <none>
root@master30:~# curl 10.224.19.2
hello fb
在 Pod 内创建文件,NFS 服务端同步可见:
bash
root@master30:~# kubectl exec nginx -- touch /usr/share/nginx/html/test.html
root@master30:~# ls /nfsshares
index.html test.html
删除 Pod 后,NFS 上的数据依然保留:
bash
root@master30:~# kubectl delete pod nginx --force
root@master30:~# ls /nfsshares
index.html test.html
五、持久化存储 PV 与 PVC
5.1 PV/PVC 架构原理
Kubernetes 使用 PV(Persistent Volume)架构为集群提供持久化存储,核心思想是分离管理员与开发人员的职责:
- PV(Persistent Volume):集群级别资源,由管理员创建,代表实际存储,只能绑定1个PVC
- PVC(Persistent Volume Claim):命名空间级别资源,由用户创建,存储申请
- Pod:通过 PVC 声明存储需求,不关心底层存储细节
开发人员只需要写 PVC 申请多少存储,不需要关心底层是 NFS、Ceph 还是云盘。
5.2 创建 PV 和 PVC
创建 PV(NFS 后端):
yaml
root@master30:~# vim pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
nfs:
path: /nfsshares
server: 10.1.8.30
应用并查看:
bash
root@master30:~# kubectl apply -f pv.yaml
root@master30:~# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
web 5Gi RWO Retain Available <unset>
创建 PVC:
yaml
root@master30:~# vim pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
应用并查看:
bash
root@master30:~# kubectl apply -f pvc.yaml
root@master30:~# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
webclaim Bound web 5Gi RWO <unset> 11s
5.3 Pod 使用 PVC
编写 Pod YAML:
yaml
root@master30:~# vim pod-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx
name: web
ports:
- containerPort: 80
name: web-port
volumeMounts:
- name: web-persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: web-persistent-storage
persistentVolumeClaim:
claimName: webclaim
创建并验证:
bash
root@master30:~# kubectl apply -f pod-web.yaml
root@master30:~# kubectl get pod web -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 25s 10.224.19.3 worker31.laoma.cloud <none> <none>
root@master30:~# curl 10.224.19.3
hello fb
5.4 PV 访问模式 accessModes
| 缩写 | 全称 | 说明 |
|---|---|---|
| RWO | ReadWriteOnce | 卷可被一个节点以读写方式挂载 |
| ROX | ReadOnlyMany | 卷可被多个节点以只读方式挂载 |
| RWX | ReadWriteMany | 卷可被多个节点以读写方式挂载 |
| RWOP | ReadWriteOncePod | 卷只能被单个 Pod 读写(K8s 1.22+) |
重要:每个卷同一时刻只能以一种访问模式挂载,即使它支持多种模式。
常见存储后端支持情况:
| 存储类型 | RWO | ROX | RWX |
|---|---|---|---|
| NFS | ✓ | ✓ | ✓ |
| CephFS | ✓ | ✓ | ✓ |
| Ceph RBD | ✓ | ✓ | - |
| HostPath | ✓ | - | - |
| iSCSI | ✓ | ✓ | - |
5.5 PV 卷模式 volumeModes
两种卷模式:
- Filesystem(默认):文件系统模式,挂载到 Pod 目录下
- Block:原始块设备模式,直接使用块设备,无需文件系统,适合数据库等高性能场景
5.6 PVC 与 PV 匹配规则
PVC 绑定 PV 的匹配条件:
- 存储容量:PV 的容量 ≥ PVC 请求容量
- 访问模式(AccessModes):PV 必须包含 PVC 要求的访问模式
访问模式没有 ROX<RWO<RWX 的优先级排序!只是能力集合匹配,不是大小比较。
举例:PV 访问模式只有 RWO,PVC 要 RWX → 不能绑定。
- PVC 申请 RWO → PV 必须支持 RWO
- PVC 申请 RWX → PV 必须支持 RWX
- PVC 申请 ROX → PV 必须支持 ROX
- StorageClass 一致
- PV 定义里的
storageClassName和 PVC 的storageClassName必须相同; - 两者都为空,代表都不指定 SC,也可以匹配;
- 一个有 SC、一个为空 → 不匹配。
- PV 定义里的
- PV 的 Selector(标签选择器,可选)
如果 PV 配置了spec.selector(标签选择器),那么PVC 必须带有匹配的标签,否则无法绑定。
5.7 PV 回收策略
| 回收策略 | 说明 |
|---|---|
| Retain(保留) | PVC 删除后,PV 变为 Released 状态,数据保留,需手动清理 |
| Recycle(回收) | 已废弃,建议使用动态制备 |
| Delete(删除) | 删除 PV 同时删除后端存储资源(如云盘) |
Retain 策略详解
删除 PVC 后,PV 状态变为 Released:
bash
root@master30:~# kubectl delete pod web
root@master30:~# kubectl delete pvc webclaim
root@master30:~# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
web 5Gi RWO Retain Released storage/webclaim <unset> 19m
此时 PV 不会自动绑定新的 PVC,需要手动编辑 PV,删除 claimRef 部分才能重新分配。
如何让 PV 只绑定给特定 PVC?
在 PV 定义中指定 claimRef:
yaml
root@master30:~# kubectl edit pv web
spec:
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: webclaim # 指定PVC 名字
namespace: storage # 指定PVC 所在命名空间(PVC 是命名空间资源,必须写 ns)
resourceVersion: "14992"
uid: e2c9cc0e-65f2-477e-ad62-38ed90ca679f
六、ConfigMap:配置管理
6.1 ConfigMap 介绍
ConfigMap 用于存储非敏感的配置数据,以键值对形式保存:
data:UTF-8 字符串binaryData:二进制数据(base64 编码)- 单个 ConfigMap 上限 1 MiB
- 设计上不适合存大量数据,配置文件和环境变量是最佳场景
- 挂载默认只读
6.2 ConfigMap 创建方式
方式一:键值对(--from-literal)
bash
root@master30:~# kubectl create configmap mysql --from-literal=password=redhat
方式二:单文件(--from-file)
bash
root@master30:~# echo Hello World > index.html
root@master30:~# kubectl create configmap web1 --from-file=./index.html
方式三:目录(--from-file=目录)
bash
root@master30:~# mkdir web2
root@master30:~# echo error > web2/error.html
root@master30:~# cp index.html web2/
root@master30:~# kubectl create configmap web2 --from-file=./web2
方式四:变量文件(--from-env-file=文件)
bash
root@master30:~# vim mysql-env.yaml
MYSQL_ROOT_PASSWORD=123
MYSQL_USER=blog
MYSQL_PASSWORD=123
MYSQL_DATABASE=blog
root@master30:~# kubectl create secret generic mysql --from-env-file=./mysql-env.yaml
6.3 环境变量方式引用
Pod YAML 示例:
yaml
root@master30:~# vim pod-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
containers:
- image: hub.laoma.cloud/library/mysql:latest
imagePullPolicy: IfNotPresent
name: mysql
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
configMapKeyRef:
name: mysql
key: password
验证环境变量:
bash
root@master30:~# kubectl apply -f pod-env.yaml
root@master30:~# kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat
6.4 Volume 方式引用
引用整个 ConfigMap:
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2
只引用特定 Key(items 方式):
yaml
volumes:
- name: webcontent # 卷名称,和volumeMounts的name对应关联
configMap:
name: web2 # 指定要引用的ConfigMap资源名称
items: # 只挑选CM中部分key进行挂载
- key: index.html # 数据源:ConfigMap内部data下的键名
path: index.html # 在容器挂载目录下生成的文件名(相对路径)
ConfigMap 以 Volume 方式挂载时,内容会自动更新(约 30 秒同步周期)。
七、Secret:敏感信息加密
7.1 Secret 介绍
Secret 与 ConfigMap 类似,但专门用于保存敏感信息(密码、令牌、密钥):
- Secret 数据使用 base64 编码(不是加密!只是编码)
- 避免在 Pod 定义或镜像中硬编码敏感信息
- etcd 中默认以 base64 编码存储,可开启加密静态数据
7.2 Secret 三种类型
1. generic(通用键值对)
bash
root@master30:~# kubectl create secret generic mysecret1 \
--from-literal=user=tom \
--from-literal=password1=redhat
查看 Secret 内容(base64 编码):
bash
root@master30:~# kubectl get secrets mysecret1 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
user: dG9t
kind: Secret
metadata:
creationTimestamp: "2026-10-21T15:44:58Z"
name: mysecret1
namespace: storage
resourceVersion: "20330"
uid: 3577c1c4-3303-4af0-80fa-3a74e251a815
type: Opaque
手动解码验证:
bash
root@master30:~# echo -n redhat | base64
cmVkaGF0
redhatroot@master30:~# echo -n cmVkaGF0 | base64 -d
redhat
其他创建方式:
bash
# 文件准备
root@master30:~# echo -n tom > user
root@master30:~# echo -n redhat > password1
# 从文件创建
root@master30:~# kubectl create secret generic mysecret2 --from-file=./user --from-file=./password1
# 从 env 文件创建
root@master30:~# echo 'user=tom password=redhat' > env.txt
root@master30:~# kubectl create secret generic mysecret4 --from-env-file=./env.txt
2. docker-registry(镜像仓库凭据)
用于拉取私有 Harbor 镜像:
bash
root@master30:~# kubectl create secret docker-registry registry \
--docker-username=laoma \
--docker-password=redhat \
--docker-email=admin@laoma.cloud \
--docker-server=hub.laoma.cloud
3. tls(TLS 证书)
先生成证书:
bash
root@master30:~# mkdir certs && cd certs
# 1. 生成私钥
root@master30:~# openssl genrsa -out www.key 2048
# 2. 生成 CSR
root@master30:~# openssl req -new -key www.key -out www.csr \
-subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=www.laoma.cloud/emailAddress=admin@laoma.cloud"
# 3. 自签名证书
root@master30:~# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
创建 TLS Secret:
bash
root@master30:~# kubectl create secret tls www-tls --cert=./www.crt --key=./www.key
7.3 Secret 引用方式
与 ConfigMap 类似,支持环境变量和 Volume 两种方式:
环境变量方式:
yaml
root@master30:~# vim pod-with-env-from-secret.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
containers:
- image: hub.laoma.cloud/library/mysql:latest
imagePullPolicy: IfNotPresent
name: mysql
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
# 原先通过value设置环境变量值
valueFrom:
secretKeyRef:
name: mysecret1
key: password1
Volume 方式引用整个 Secret:
yaml
root@master30:~# echo Hello world > index.html
root@master30:~# echo error > error.html
root@master30:~# kubectl create secret generic web --from-file=./index.html --from-file=./error.html
root@master30:~# vim pod-with-volume-from-secret.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
# mountPath值是一个挂载点,将secrete中所有键值对挂载过来
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
secret:
secretName: web
Volume 方式只引用特定 Key:
yaml
root@master30:~# vim pod-secret-volume-single.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
secret:
secretName: web
items:
- key: index.html
# 通过path设置挂载的文件名,这里我们path值与key一致。
path: index.html
7.4 文件权限控制
Secret 挂载到容器后,文件默认权限是 644(八进制)。
设置所有文件默认权限(注意:JSON 不支持八进制,用十进制):
yaml
volumes:
- name: foo
secret:
secretName: mysecret1
defaultMode: 256 # 十进制 256 = 八进制 400(只读)
设置单个文件权限:
yaml
volumes:
- name: foo
secret:
secretName: mysecret1
items:
- key: user
path: user
mode: 511 # 十进制 511 = 八进制 777
八、综合实战:haproxy + 多 Web 后端
需求:
- 创建 webapp-1、webapp-2 两个 nginx Pod,各自用 ConfigMap 提供首页内容
- 创建 haproxy Pod,配置文件用 ConfigMap 挂载,反向代理到两个 Web Pod
第一步:创建 webapp-1 的 ConfigMap 和 Pod
bash
root@master30:~# kubectl create cm webapp-1 \
--from-literal=index.html="hello webapp-1" \
--from-literal=error.html="sorry, error."
yaml
root@master30:~# vim pod-web1.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: webapp-1
name: webapp-1
spec:
containers:
- name: nginx
image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: config
mountPath: "/usr/share/nginx/html"
readOnly: true
volumes:
- name: config
configMap:
name: webapp-1
root@master30:~# kubectl apply -f pod-web1.yaml
第二步:创建 webapp-2 的 ConfigMap 和 Pod
bash
root@master30:~# kubectl create cm webapp-2 \
--from-literal=index.html="hello webapp-2" \
--from-literal=error.html="sorry, error."
yaml
root@master30:~# vim pod-web2.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: webapp-2
name: webapp-2
spec:
containers:
- name: nginx
image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: config
mountPath: "/usr/share/nginx/html"
readOnly: true
volumes:
- name: config
configMap:
name: webapp-2
root@master30:~# kubectl apply -f pod-web2.yaml
第三步:获取 Pod IP,编写 haproxy.cfg
bash
root@master30:~# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webapp-1 1/1 Running 0 2m14s 10.224.113.131 worker32.laoma.cloud <none> <none>
webapp-2 1/1 Running 0 3s 10.224.19.5 worker31.laoma.cloud <none> <none>
创建 haproxy 配置文件:
bash
root@master30:~# vim haproxy.cfg
global
daemon # 后台守护进程方式运行haproxy
maxconn 256 # 全局最大并发连接数256
defaults
mode http # 默认工作模式为HTTP七层代理
timeout connect 5000ms # 连接后端服务器超时:5秒
timeout client 50000ms # 客户端空闲超时:50秒
timeout server 50000ms # 后端服务空闲超时:50秒
frontend http-in # 前端:接收客户端请求
bind *:8080 # 监听本机所有网卡的8080端口
default_backend servers # 默认把请求转发给后端组 servers
backend servers # 后端服务器池,真实业务节点
server app1 10.224.113.131:80 check # 后端节点1,填写webapp‑1的PodIP,check开启健康检查
server app2 10.224.19.5:80 check # 后端节点2,填写webapp‑2的PodIP,check开启健康检查
创建 haproxy ConfigMap:
bash
root@master30:~# kubectl create cm haproxy.cfg --from-file=haproxy.cfg=./haproxy.cfg
第四步:创建 haproxy Pod
yaml
root@master30:~# vim pod-haproxy.yaml
apiVersion: v1
kind: Pod
metadata:
name: haproxy
spec:
containers:
- name: haproxy
image: hub.laoma.cloud/library/haproxy
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: true
volumeMounts:
- name: config
mountPath: "/usr/local/etc/haproxy"
readOnly: true
volumes:
- name: config
configMap:
name: haproxy.cfg
root@master30:~# kubectl apply -f pod-haproxy.yaml
验证负载均衡效果:
bash
root@master30:~# kubectl get pod haproxy -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
haproxy 1/1 Running 0 27s 10.224.113.132 worker32.laoma.cloud <none> <none>
root@master30:~# curl http://10.224.113.132:8080
root@master30:~# curl http://10.224.113.132:8080
多次访问会轮询返回:
hello webapp-1
hello webapp-2
九、总结
本文覆盖了 Kubernetes 存储与配置管理的核心知识点:
| 类别 | 核心对象 | 生命周期 | 典型用途 |
|---|---|---|---|
| 临时存储 | emptyDir | 随 Pod 删除 | 同 Pod 多容器共享数据 |
| 节点存储 | hostPath | 数据保留在 Node | 访问 Node 本地文件(测试用) |
| 网络存储 | NFS Volume | 数据保留在 NFS | 多 Pod 共享网络文件 |
| 持久化存储 | PV + PVC | 由回收策略决定 | 生产环境持久化数据 |
| 配置管理 | ConfigMap | 独立于 Pod | 应用配置、环境变量 |
| 敏感信息 | Secret | 独立于 Pod | 密码、证书、仓库凭据 |
考试重点速记:
- emptyDir 随 Pod 删除而消失,hostPath 和 NFS 数据保留
- PV 是集群级资源,PVC 是命名空间级资源
- PVC 匹配 PV 看三个条件:访问模式、容量、StorageClass
- PV 回收策略:Retain(默认保留)、Recycle(已废弃)、Delete(云盘)
- ConfigMap 不加密,Secret 用 base64 编码(注意:不是加密!)
- ConfigMap 和 Secret 都支持环境变量和 Volume 两种引用方式
- Volume 方式挂载 ConfigMap/Secret 支持动态更新,环境变量方式不支持