K8s(10)NFS 的动态 PV 创建数据库给k8s的mysql和redis
2026/6/11 8:55:50 网站建设 项目流程

K8s 本地集群实战全流程复盘

(基于你的网络拓扑:141 Master / 142 Node1 / 143 Node2)

环境基线

  • Master (控制机):192.168.222.141(操作机,存放 yaml)

  • Node1 (工作节点):192.168.222.142(运行 Pod)

  • Node2 (NFS 服务器):192.168.222.143(提供存储)

  • NFS 共享目录:/opt/k8s

  • Master 工作目录:/root/k8s/nfs-provisioner


第一步:搭建 NFS 底层存储服务 (在 Node2 上)

📍 主机/IP192.168.222.143(Node2)

📂 当前目录:任意(直接在命令行操作)

1. 核心动作

安装 NFS 服务,创建共享目录,并配置 exports 文件。

bash

bash

# 1. 安装软件 yum install -y nfs-utils rpcbind # 2. 创建共享目录 mkdir -p /opt/k8s # 3. 授权 (关键!K8s Pod 内 UID 通常是 root 或非 root,必须给足权限) chmod 777 /opt/k8s # 4. 配置 exports echo "/opt/k8s *(rw,sync,no_root_squash)" > /etc/exports # 5. 启动服务 systemctl start rpcbind && systemctl enable rpcbind systemctl start nfs && systemctl enable nfs

🧠 核心知识点

  • NFS 原理:Node1/Node2 作为 Client,通过 RPC 协议挂载 Node2 的/opt/k8s

  • 权限no_root_squash:如果不加这个,K8s Pod 内的进程(即使是 root)在 NFS 目录下写入会被拒绝(变成nfsnobody),导致 PV 挂载进去但无法读写。

🔥 排错核心

现象

原因

排查命令

其他节点挂载失败

NFS 服务未启动或防火墙拦截

systemctl status nfs,showmount -e 192.168.222.143

Pod 报错Permission Denied

目录权限不足或未加no_root_squash

检查 Node2 上的/opt/k8s权限是否为 777


第二步:部署 NFS Provisioner (在 Master 上)

📍 主机/IP192.168.222.141(Master)

📂 当前目录/root/k8s/nfs-provisioner

1. 核心动作

编写并应用 YAML,目的是让 K8s 拥有一个能自动创建 PV 的“动态供应器”。

存放路径/root/k8s/nfs-provisioner

1. RBAC 权限 (rbac.yaml)

赋予 Provisioner 操作 PV/PVC 的权限

yaml

yaml

apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner namespace: default --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: nfs-client-provisioner-runner rules: - apiGroups: [""] resources: ["nodes", "persistentvolumes", "persistentvolumeclaims"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims/status"] verbs: ["update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: run-nfs-client-provisioner subjects: - kind: ServiceAccount name: nfs-client-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-runner apiGroup: rbac.authorization.k8s.io

2. Deployment (deployment.yaml)

注意修改NFS_SERVER为你的 Node2 IP

yaml

yaml

apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner namespace: default spec: replicas: 1 selector: matchLabels: app: nfs-client-provisioner strategy: type: Recreate template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2 volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: fuseim.pri/ifs # 必须与 StorageClass 里的 provisioner 一致 - name: NFS_SERVER value: 192.168.222.143 # 👈 改成你的 Node2 IP - name: NFS_PATH value: /opt/k8s # 👈 改成你的 NFS 目录 volumes: - name: nfs-client-root nfs: server: 192.168.222.143 # 👈 改成你的 Node2 IP path: /opt/k8s # 👈 改成你的 NFS 目录

3. StorageClass (class.yaml)

yaml

yaml

apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-storage provisioner: fuseim.pri/ifs # 👈 必须与 Deployment 里的 PROVISIONER_NAME 一致 parameters: archiveOnDelete: "false" reclaimPolicy: Delete volumeBindingMode: Immediate

bash

bash

cd /root/k8s/nfs-provisioner # 这里准备好 deployment.yaml, rbac.yaml, class.yaml kubectl apply -f .

2. 关键 YAML 配置点

  • Deployment (nfs-client-provisioner)

    • env:NFS_SERVER=192.168.222.143,NFS_PATH=/opt/k8s

    • volumes: 这里挂载的其实是用来放deployment.yaml等文件的本地目录(如果用了 hostPath)。

  • StorageClass

    • provisioner: fuseim.pri/ifs(名字自己起,但要和 Deployment 里环境变量一致)

🧠 核心知识点

  • StorageClass 的作用:取代静态kubectl create -f pv.yaml。以后只要 PVC 申请了这种 StorageClass,Provisioner 就会自动在 Node2 的/opt/k8s下建一个子目录作为 PV。

  • RBAC (Role/ClusterRole):Provisioner 需要权限去操作 K8s 的 PVC 和 PV,这是新手最容易漏的。

🔥 排错核心

现象

原因

排查命令

Provisioner Pod 一直 Pending

可能是 NFS 连不上,或者 imagePullBackOff

kubectl describe pod nfs-client-provisioner-xxx -n default看 Events

PVC 一直 Pending

检查 StorageClass 名字是否写错,或者 Provisioner 容器没跑起来

kubectl get sc,kubectl get pvc


第三步:部署 MySQL (在 Master 上操作,调度到 Node2)

📍 主机/IP192.168.222.141(Master)

📂 当前目录/root/k8s/mysql(假设你新建了这个目录)

1. 核心动作

编写 Deployment 和 Service。这里利用NodeSelector​ 强制把 MySQL 调度到 143 (NFS 服务器) 上。

bash

bash

cd /root/k8s/mysql kubectl apply -f mysql-deploy.yaml kubectl apply -f mysql-svc.yaml

2. 关键 YAML 配置点

  • Deployment:

    • volumes: 定义一个PersistentVolumeClaim,指向第二步创建的StorageClass

    • nodeSelector:disktype: nfs(假设你在 143 上打了这个标签)。

  • Service:

    • ClusterIP: 暴露内部端口。

    • NodePort(可选): 如果想从宿主机 141 访问,开 30036 端口。

存放路径/root/k8s/mysql

1. Service (mysql-svc.yaml)

使用 ClusterIP,让 Redis 能通过mysql这个名字访问

yaml

yaml

apiVersion: v1 kind: Service metadata: name: mysql spec: clusterIP: None # Headless Service selector: app: mysql ports: - port: 3306

2. StatefulSet (mysql-sts.yaml)

注意修改nodeSelector的 IP 或标签

yaml

yaml

apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: # 强制调度到 Node2 (NFS 服务器) nodeSelector: kubernetes.io/hostname: "k8s-node2" # 或者 node-143,看你实际的 hostname securityContext: runAsUser: 999 fsGroup: 999 containers: - name: mysql image: mysql:5.7 imagePullPolicy: IfNotPresent env: - name: MYSQL_ROOT_PASSWORD value: "123456" ports: - containerPort: 3306 volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: mysql-data spec: accessModes: ["ReadWriteOnce"] storageClassName: nfs-storage # 👈 使用上面定义的 StorageClass resources: requests: storage: 5Gi

🧠 核心知识点

  • 为什么要把 MySQL 放在 NFS 节点?

    • 因为 MySQL 数据文件存在/opt/k8s下。如果 Pod 飘到 Node1,而 Node1 没有挂载 NFS,数据就丢了。数据必须跟着存储走

  • 持久化声明 (PVC):它像一个“申请单”,告诉 Provisioner:“给我一块硬盘,类型是 nfs”。

🔥 排错核心

现象

原因

排查命令

MySQL Pod CrashLoopBackOff

通常是权限问题。Pod 里的 mysql 用户 UID 和 NFS 目录权限不匹配

kubectl logs mysql-pod-name,看是否有Permission denied

Pod 无法调度 (Pending)

找不到带有disktype: nfs标签的节点

kubectl get nodes -o wide检查标签是否打上


第四步:部署 Redis (在 Master 上操作,调度到 Node1)

📍 主机/IP192.168.222.141(Master)

📂 当前目录/root/k8s/redis

1. 核心动作

与 MySQL 类似,但因为 Redis 不需要强依赖 NFS(可以存内存或存本地盘,这里为了练手也可以存 NFS),我们可以把它调度到 Node1 (142)。

bash

bash

cd /root/k8s/redis kubectl apply -f redis-deploy.yaml kubectl apply -f redis-svc.yaml

2. 关键 YAML 配置点

  • Deployment:

    • nodeSelector:disktype: ssd(假设你在 142 上打了这个标签)。

    • command: 如果是 Redis,可能需要设置密码或持久化路径。

存放路径/root/k8s/redis

1. Service (redis-svc.yaml)

yaml

yaml

apiVersion: v1 kind: Service metadata: name: redis spec: clusterIP: None # Headless Service selector: app: redis ports: - port: 6379

2. StatefulSet (redis-sts.yaml)

极简版,无 ConfigMap,避免挂载错误

yaml

yaml

apiVersion: apps/v1 kind: StatefulSet metadata: name: redis spec: serviceName: redis replicas: 1 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: # 强制调度到 Node1 (142) nodeSelector: kubernetes.io/hostname: "k8s-node1" # 或者 node-142 securityContext: runAsUser: 0 fsGroup: 0 containers: - name: redis image: redis:6.2 imagePullPolicy: IfNotPresent command: - redis-server - "--appendonly yes" ports: - containerPort: 6379 volumeMounts: - name: redis-data mountPath: /data volumeClaimTemplates: - metadata: name: redis-data spec: accessModes: ["ReadWriteOnce"] storageClassName: nfs-storage resources: requests: storage: 2Gi

🧠 核心知识点

  • Service 的 ClusterIP:MySQL 和 Redis 的 Service 名字,可以在同一个 Namespace 下互相解析(比如 Redis 连mysql.default.svc.cluster.local)。

🔥 排错核心

现象

原因

排查命令

Redis 连接不上 MySQL

服务名写错,或者 Service 没有 Endpoints

kubectl get endpoints看后端有没有 Pod

Redis 数据重启后丢失

没有配置 PVC 或 RDB/AOF 持久化

检查 Deployment 是否挂载了 Volume


总结:排错思维导图

text

text

1. 先看 Pod 状态 (kubectl get pod -o wide) ├── Pending? --> 检查节点标签 (kubectl get node) 或 资源不够 ├── ImagePullBackOff? --> 检查 Docker 镜像是否 load 进去了 └── CrashLoopBackOff? --> 看日志 (kubectl logs) 2. 日志报 Permission Denied? ├── 查 NFS Server (143) 的 /opt/k8s 权限 (chmod 777) └── 查 NFS 导出配置 (exports) 是否有 no_root_squash 3. PVC 一直 Pending? ├── 查 StorageClass 是否存在 (kubectl get sc) └── 查 Provisioner Pod 是否活着 4. 服务连不上? ├── 查 Service 是否有 Endpoint (kubectl get endpoints) └── 查 Pod 是否 Ready (kubectl get pod)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询