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
✅ 是
ReadWriteOnceReadWriteMany必须兼容
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

image

 

posted @ 2020-04-30 10:33  凡人半睁眼  阅读(1235)  评论(0)    收藏  举报