k8s存储持久化之 NFS + StorageClass
一、PV/PVC/StorageClass
在 Kubernetes 中,Pod 是“短暂的”(ephemeral),但有些应用(如数据库、博客系统)需要 数据持久保存,不能随 Pod 消失而丢失。
- 重启、升级、节点故障 → Pod 被重建
- 容器内的文件系统也随之消失
所以 Kubernetes 提供了 PV/PVC/StorageClass 机制,来管理持久化存储。
1、PV(PersistentVolume)—— “真实的房子”
- 是集群中的一块持久化存储资源
- 由集群管理员手动创建,或由 StorageClass 自动创建
- 属于集群资源,不隶属于某个 Namespace
# pv-nfs.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-ghost-data spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain nfs: path: /data/ghost server: 192.168.10.100 storageClassName: nfs-storage
这表示:有一块 10GB 的 NFS 存储,路径是 /data/ghost
,可用作持久卷。
2、pv状态
阶段 | 说明 |
---|---|
Available |
可用状态:PV 已创建,尚未被 PVC 绑定
|
Bound |
已绑定:PV 已与某个 PVC 成功绑定,正在被使用
|
Released |
已释放:PVC 被删除,PV 被“释放”,但数据仍保留(取决于回收策略)
|
Failed |
绑定失败:自动供应失败、挂载失败等
|
2、PVC(PersistentVolumeClaim)—— “租客的需求”
1、什么是pvc
- 是用户(开发者)对存储的“申请”
- 指定需要多少空间、访问模式等
- 会自动绑定到一个满足条件的 PV
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql57-pvc namespace: db spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi storageClassName: nfs-sc # 你之前创建的 StorageClass 名
这表示:“我需要一个至少 10GB、支持多节点读写的存储”,Kubernetes 会自动帮它找到合适的 PV 并绑定。
2、pvc状态
状态 |
说明
|
Pending |
等待绑定:正在寻找合适的 PV,或等待动态创建
|
Bound |
已绑定:成功与一个 PV 绑定,可以被 Pod 使用
|
Lost |
丢失:PVC 曾经绑定 PV,但 PV 被外部删除或不可用
|
3、StorageClass —— “自动造房子的中介”
是“动态供应”(Dynamic Provisioning)的核心
当 PVC 被创建时,如果指定的 storageClassName
对应一个 StorageClass,Kubernetes 会自动创建 PV
StorageClass 本身就是 Kubernetes 集群内置的资源类型(API 对象),无需额外安装即可使用
如果使用本地存储(如 local-path-provisioner
)或 NFS,需手动部署对应的 CSI 驱动
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-sc provisioner: nfs.csi.k8s.io parameters: server: 192.168.1.143 # NFS 服务器 IP share: /healsci/docker/volumes/mysqlm_data/_data reclaimPolicy: Retain mountOptions: - hard - nfsvers=4.1 volumeBindingMode: WaitForFirstConsumer
4、三者关系图解
+------------------+ | StorageClass | ← 定义“如何造房子” | (ebs, nfs, ceph) | 如:用 AWS EBS 创建 10GB 磁盘 +--------+---------+ | | 动态创建 ↓ +----------------+ +----------------------------+ +------------------+ | PVC (申请) | ↔ | PV (实际存储) | ←→ | NFS / EBS / Ceph | | - 5Gi | | - 10Gi | | (底层存储设备) | | - RWO | | - RWO | +------------------+ | - sc: fast | | - sc: fast | +----------------+ +----------------------------+ 用户创建 集群资源(手动或自动)
注意
1、PVC 和 PV 不是强制一一对应,但 一旦绑定,就是一对一的独占关系。
2、PVC 和 PV 的匹配字段(必须一致)
⚠️ 注意:PVC 请求的资源不能超过 PV 的容量,但 PV 可以更大(多出部分无法被使用)
字段 |
是否必须匹配
|
说明
|
storage (存储大小) |
✅ 是
|
PVC 请求 5Gi,PV 必须 ≥ 5Gi(但绑定后按 PVC 的大小使用)
|
accessModes |
✅ 是
|
如
ReadWriteOnce 、ReadWriteMany 必须兼容 |
storageClassName |
✅ 是
|
必须相同(除非都为空)
|
volumeMode |
✅ 是
|
Filesystem (默认)或Block (裸设备) |
selector (如果有) |
✅ 是
|
PVC 可通过 label selector 筛选 PV
|
二、部署NFS + StorageClass
1、安装 NFS Server
# CentOS / Rocky / RHEL yum install -y nfs-utils # Ubuntu / Debian apt update && apt install -y nfs-kernel-server
创建共享目录
mkdir -p /healsci/docker/volumes/mysqlm_data/_data/ chmod 777 /healsci/docker/volumes/mysqlm_data/_data/
编辑配置
vi /etc/exports /healsci/docker/volumes/mysqlm_data/_data *(rw,sync,no_subtree_check,no_root_squash) # 应用 exportfs -rav systemctl enable nfs-server --now
验证
showmount -e localhost
2、部署 NFS CSI 插件(K8s 内)
nfs 需要CSI插件才能使用 StorageClass
1、部署方式有多种,这里使用本地安装
https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/install-csi-driver-v4.2.0.md
git clone https://github.com/kubernetes-csi/csi-driver-nfs.git cd csi-driver-nfs ./deploy/install-driver.sh v4.2.0 local
2、镜像
# 用到的镜像 csi-node-driver-registrar_v2.6.2.tar csi-provisioner_v3.3.0.tar livenessprobe_v2.8.0.tar nfsplugin_v4.2.0.tar # kubectl get pods -n kube-system | grep nfs csi-nfs-controller-7b79548448-w72zk 3/3 Running 0 40h csi-nfs-node-rn8nl 3/3 Running 0 40h csi-nfs-node-vxgrx 3/3 Running 0 40h csi-nfs-node-zvp2q 3/3 Running 0 40h
三、部署ghost博客网站
1、准备nfs 数据目录
cat /etc/exports /healsci/docker/volumes/mariadb_data/_data *(rw,sync,no_subtree_check,no_root_squash) /healsci/docker/volumes/ghost_data/_data *(rw,sync,no_subtree_check,no_root_squash)
注意设置好权限问题:
# 确保目录存在 sudo mkdir -p /healsci/docker/volumes/mariadb_data/_data # 设置所有者为 UID 1001(Bitnami 镜像使用的用户) sudo chown -R 1001:1001 /healsci/docker/volumes/mariadb_data/_data # 设置权限 sudo chmod -R 755 /healsci/docker/volumes/mariadb_data/_data
2、准备pvc、storageclass
mariadb-sc.yaml
# storageclass-mariadb.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-sc-mariadb provisioner: nfs.csi.k8s.io parameters: server: 192.168.1.242 share: /healsci/docker/volumes/mariadb_data/_data reclaimPolicy: Retain mountOptions: - hard - nfsvers=4.1 volumeBindingMode: WaitForFirstConsumer
mariadb-pvc.yaml
# mariadb-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mariadb-pvc namespace: ghost spec: accessModes: - ReadWriteMany resources: requests: storage: 50Gi storageClassName: nfs-sc-mariadb # 使用专用 SC
ghost-sc.yaml
# storageclass-ghost.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-sc-ghost provisioner: nfs.csi.k8s.io parameters: server: 192.168.1.242 share: /healsci/docker/volumes/ghost_data/_data reclaimPolicy: Retain mountOptions: - hard - nfsvers=4.1 volumeBindingMode: WaitForFirstConsumer
ghost-pvc.yaml
# ghost-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ghost-pvc namespace: ghost spec: accessModes: - ReadWriteMany resources: requests: storage: 50Gi storageClassName: nfs-sc-ghost
验证pvc
# k get pvc -n ghost NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE ghost-pvc Bound pvc-340a5636-af71-4078-98a6-f885f3389783 50Gi RWX nfs-sc-ghost <unset> 8m3s mariadb-pvc Bound pvc-eb5b688c-054c-45d7-8bd0-aadb54244f5b 50Gi RWX nfs-sc-mariadb <unset> 14m
3、部署并暴露端口
mariadb kind: Deployment + kind: Service
# mariadb-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: mariadb namespace: ghost labels: app: mariadb spec: replicas: 1 selector: matchLabels: app: mariadb template: metadata: labels: app: mariadb spec: containers: - name: mariadb image: bitnami/mariadb:10.11 # 已本地加载 env: - name: MARIADB_ROOT_PASSWORD value: "rootpass123" - name: MARIADB_DATABASE value: "ghost" - name: MARIADB_USER value: "ghost" - name: MARIADB_PASSWORD value: "ghostpass123" ports: - containerPort: 3306 volumeMounts: - name: mariadb-storage mountPath: /bitnami/mariadb volumes: - name: mariadb-storage persistentVolumeClaim: claimName: mariadb-pvc --- apiVersion: v1 kind: Service metadata: name: mariadb-svc namespace: ghost spec: selector: app: mariadb ports: - protocol: TCP port: 3306 targetPort: 3306
ghost-deployment.yaml
注意这里:url 等信息通过环境变量注入
# ghost-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: ghost namespace: ghost labels: app: ghost spec: replicas: 1 selector: matchLabels: app: ghost template: metadata: labels: app: ghost spec: containers: - name: ghost image: ghost:5-alpine ports: - containerPort: 2368 env: - name: url value: "http://192.168.1.141:30089" - name: database__client value: "mysql2" - name: database__connection__host value: "mariadb-svc" - name: database__connection__port value: "3306" - name: database__connection__database value: "ghost" - name: database__connection__user value: "ghost" - name: database__connection__password value: "ghostpass123" - name: NODE_ENV value: "production" volumeMounts: - name: ghost-content mountPath: /var/lib/ghost/content volumes: - name: ghost-content persistentVolumeClaim: claimName: ghost-pvc
ghost-nodeport-svc.yaml
apiVersion: v1 kind: Service metadata: name: ghost-nodeport namespace: ghost spec: type: NodePort selector: app: ghost ports: - protocol: TCP port: 80 targetPort: 2368 nodePort: 30089
4、访问后台管理员和前台博客
后台 http://ip:30089/ghost