K8s(10)NFS 的动态 PV 创建数据库给k8s的mysql和redis

发布时间:2026/6/11 8:55:51

K8s(10)NFS 的动态 PV 创建数据库给k8s的mysql和redis 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/k8sMaster 工作目录:/root/k8s/nfs-provisioner第一步搭建 NFS 底层存储服务 (在 Node2 上) 主机/IP192.168.222.143(Node2) 当前目录任意直接在命令行操作1. 核心动作安装 NFS 服务创建共享目录并配置 exports 文件。bashbash# 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.143Pod 报错Permission Denied目录权限不足或未加no_root_squash检查 Node2 上的/opt/k8s权限是否为 777第二步部署 NFS Provisioner (在 Master 上) 主机/IP192.168.222.141(Master) 当前目录/root/k8s/nfs-provisioner1. 核心动作编写并应用 YAML目的是让 K8s 拥有一个能自动创建 PV 的“动态供应器”。存放路径/root/k8s/nfs-provisioner1. RBAC 权限 (rbac.yaml)赋予 Provisioner 操作 PV/PVC 的权限yamlyamlapiVersion: 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.io2. Deployment (deployment.yaml)注意修改NFS_SERVER为你的 Node2 IPyamlyamlapiVersion: 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)yamlyamlapiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-storage provisioner: fuseim.pri/ifs # 必须与 Deployment 里的 PROVISIONER_NAME 一致 parameters: archiveOnDelete: false reclaimPolicy: Delete volumeBindingMode: Immediatebashbashcd /root/k8s/nfs-provisioner # 这里准备好 deployment.yaml, rbac.yaml, class.yaml kubectl apply -f .2. 关键 YAML 配置点Deployment (nfs-client-provisioner)env:NFS_SERVER192.168.222.143,NFS_PATH/opt/k8svolumes: 这里挂载的其实是用来放deployment.yaml等文件的本地目录如果用了 hostPath。StorageClassprovisioner: fuseim.pri/ifs(名字自己起但要和 Deployment 里环境变量一致) 核心知识点StorageClass 的作用取代静态kubectl create -f pv.yaml。以后只要 PVC 申请了这种 StorageClassProvisioner 就会自动在 Node2 的/opt/k8s下建一个子目录作为 PV。RBAC (Role/ClusterRole)Provisioner 需要权限去操作 K8s 的 PVC 和 PV这是新手最容易漏的。 排错核心现象原因排查命令Provisioner Pod 一直 Pending可能是 NFS 连不上或者 imagePullBackOffkubectl describe pod nfs-client-provisioner-xxx -n default看 EventsPVC 一直 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 服务器) 上。bashbashcd /root/k8s/mysql kubectl apply -f mysql-deploy.yaml kubectl apply -f mysql-svc.yaml2. 关键 YAML 配置点Deployment:volumes: 定义一个PersistentVolumeClaim指向第二步创建的StorageClass。nodeSelector:disktype: nfs(假设你在 143 上打了这个标签)。Service:ClusterIP: 暴露内部端口。NodePort(可选): 如果想从宿主机 141 访问开 30036 端口。存放路径/root/k8s/mysql1. Service (mysql-svc.yaml)使用 ClusterIP让 Redis 能通过mysql这个名字访问yamlyamlapiVersion: v1 kind: Service metadata: name: mysql spec: clusterIP: None # Headless Service selector: app: mysql ports: - port: 33062. StatefulSet (mysql-sts.yaml)注意修改nodeSelector的 IP 或标签yamlyamlapiVersion: 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 deniedPod 无法调度 (Pending)找不到带有disktype: nfs标签的节点kubectl get nodes -o wide检查标签是否打上第四步部署 Redis (在 Master 上操作调度到 Node1) 主机/IP192.168.222.141(Master) 当前目录/root/k8s/redis1. 核心动作与 MySQL 类似但因为 Redis 不需要强依赖 NFS可以存内存或存本地盘这里为了练手也可以存 NFS我们可以把它调度到 Node1 (142)。bashbashcd /root/k8s/redis kubectl apply -f redis-deploy.yaml kubectl apply -f redis-svc.yaml2. 关键 YAML 配置点Deployment:nodeSelector:disktype: ssd(假设你在 142 上打了这个标签)。command: 如果是 Redis可能需要设置密码或持久化路径。存放路径/root/k8s/redis1. Service (redis-svc.yaml)yamlyamlapiVersion: v1 kind: Service metadata: name: redis spec: clusterIP: None # Headless Service selector: app: redis ports: - port: 63792. StatefulSet (redis-sts.yaml)极简版无 ConfigMap避免挂载错误yamlyamlapiVersion: 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 的 ClusterIPMySQL 和 Redis 的 Service 名字可以在同一个 Namespace 下互相解析比如 Redis 连mysql.default.svc.cluster.local。 排错核心现象原因排查命令Redis 连接不上 MySQL服务名写错或者 Service 没有 Endpointskubectl get endpoints看后端有没有 PodRedis 数据重启后丢失没有配置 PVC 或 RDB/AOF 持久化检查 Deployment 是否挂载了 Volume总结排错思维导图texttext1. 先看 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)

相关新闻