news 2026/9/4 17:48:47

Kubernetes 存储实战:NFS PV/PVC、ConfigMap 与探针测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes 存储实战:NFS PV/PVC、ConfigMap 与探针测试

文章目录

      • 一、前置内容错误检查与修正说明
  • Kubernetes 存储实战:NFS PV/PVC、ConfigMap 与探针测试
    • 1. 环境信息回顾
    • 2. 核心概念梳理
      • 2.1 PV 与 PVC
        • 三种访问模式
      • 2.2 NFS 存储
      • 2.3 ConfigMap 与 Secret
      • 2.4 三类探针
    • 3. 前置准备工作
      • 3.1 全节点 NFS 客户端安装与验证
      • 3.2 离线镜像导入(containerd 运行时)
    • 4. NFS 存储目录创建与权限配置
    • 5. 创建 PV 与 PVC(静态供给)
      • 5.1 Nginx 共享存储 PV/PVC
        • nginx-pv.yaml
        • nginx-pvc.yaml
      • 5.2 MySQL 独占存储 PV/PVC
        • mysql-pv.yaml
        • mysql-pvc.yaml
      • 5.3 执行创建与绑定验证
    • 6. 部署 Nginx 无状态应用(Deployment + PVC + 探针)
      • 6.1 创建 ConfigMap(Nginx 完整配置)
        • nginx-configmap.yaml
      • 6.2 Deployment + Service 完整定义
        • nginx-deployment.yaml
      • 6.3 部署与基础验证
    • 7. 部署 MySQL 有状态应用(StatefulSet + PVC + 三探针)
      • 7.1 创建 Secret(MySQL 账号密码)
      • 7.2 StatefulSet + Service 完整定义
        • mysql-statefulset.yaml
      • 7.3 部署与基础验证
    • 三、全流程测试验证 SOP
      • SOP 1:存储层验证
      • SOP 2:Nginx 无状态应用验证
      • SOP 3:MySQL 有状态应用验证
      • SOP 4:探针功能专项验证
        • 4.1 Nginx 探针验证
        • 4.2 MySQL 探针验证
    • 四、最终交付物清单

一、前置内容错误检查与修正说明

我仔细核对了你的离线集群环境与全部配置步骤,共发现6 处关键问题/遗漏,已在下文全部修正:

序号问题点影响修正方案
1离线镜像准备步骤缺失离线环境无法拉取镜像,Pod 直接启动失败补充 containerd 离线镜像导入全步骤,适配你的 RPM 离线部署模式
2Nginx 配置不完整ConfigMap 仅挂载站点配置,log_format main未定义,Nginx 启动报错修正 ConfigMap 结构,统一主配置与站点配置,确保语法完整
3MySQL 探针密码硬编码探针命令直接写死明文密码,Secret 修改后探针失效,不符合安全规范改为通过环境变量$MYSQL_ROOT_PASSWORD引用 Secret,使用 shell 解析变量
4NFS 权限适配遗漏你的 NFS 开启了all_squash映射为 UID 1000,而 MySQL 容器默认用户为 UID 999,会导致数据目录无写入权限,启动失败补充securityContext配置,统一容器运行 UID 与 NFS 匿名用户一致
5StatefulSet 静态 PV 绑定规则不明确volumeClaimTemplates生成的 PVC 名称为mysql-data-mysql-0,与静态 PV 标签匹配规则未说明,易导致 PVC 一直 Pending明确 PV 标签匹配规则,补充单实例固定 PVC 挂载方案
6验证步骤不完整缺少配置语法检查、服务连通性校验、权限验证等前置检查补充全流程分级验证步骤,每步明确预期结果与排错方法

Kubernetes 存储实战:NFS PV/PVC、ConfigMap 与探针测试

1. 环境信息回顾

本次实战严格匹配你的离线高可用集群环境,核心参数如下:

类别参数值
Kubernetes 版本v1.24.17
容器运行时containerd 1.6.21
集群架构3 Master + 2 Worker,三主两从高可用
apiserver VIPhttps://192.168.30.140:6443
网络插件Calico
Pod 网段10.244.0.0/16
Service 网段10.96.0.0/12
NFS 存储服务器192.168.30.249,共享根路径/data/k8s
NFS 配置rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000
节点系统CentOS 7.9,全量 RPM 离线部署

2. 核心概念梳理

2.1 PV 与 PVC

  • PV(PersistentVolume):集群级存储资源,由管理员提前创建,对应后端真实存储,生命周期独立于 Pod,数据不会随 Pod 删除而消失。
  • PVC(PersistentVolumeClaim):用户对存储的需求声明(容量、访问模式、标签),K8s 自动匹配并绑定符合条件的 PV,Pod 通过 PVC 间接使用存储。
三种访问模式
模式缩写含义适用场景
ReadWriteOnceRWO仅单个节点可挂载读写单机数据库、单实例有状态应用
ReadOnlyManyROX多节点只读挂载静态资源共享、配置文件分发
ReadWriteManyRWX多节点同时读写多实例共享日志、文件存储、静态资源

2.2 NFS 存储

NFS 是中小规模离线集群最常用的共享存储方案:

  • 天然支持ReadWriteMany多节点共享
  • 静态供给:手动创建目录 + 手动创建 PV,适配离线环境
  • 注意:all_squash模式下所有客户端访问都会映射为指定 UID/GID,容器内用户必须与其匹配

2.3 ConfigMap 与 Secret

类型用途安全性典型场景
ConfigMap存储非敏感配置、脚本、环境变量明文存储Nginx 配置、应用参数、启动脚本
Secret存储密码、密钥、证书Base64 编码存储数据库账号、认证令牌、TLS 证书

2.4 三类探针

探针类型作用失败触发行为
startupProbe(启动探针)检测应用是否启动完成超时未成功则重启容器;保护慢启动应用不被误杀
livenessProbe(存活探针)检测容器进程是否正常运行连续失败则重启容器,实现故障自愈
readinessProbe(就绪探针)检测服务是否可对外提供流量连续失败则将 Pod 从 Service 端点中摘除,停止转发流量

3. 前置准备工作

3.1 全节点 NFS 客户端安装与验证

所有 Master/Worker 节点必须安装 NFS 客户端,否则无法挂载 NFS 卷。

# 离线环境提前将 nfs-utils 及依赖 RPM 导入本地源 yum install -y nfs-utils # 验证 NFS 连通性与共享列表 showmount -e 192.168.30.249

✅ 预期输出:

/data/k8s 192.168.30.0/24

3.2 离线镜像导入(containerd 运行时)

离线环境无法在线拉取镜像,必须提前将镜像包导入所有 Worker 节点的 containerd。

# 1. 提前准备镜像包(nginx:1.25.3、mysql:8.0.36),传输到所有 Worker 节点 # 2. 导入镜像到 k8s.io 命名空间(K8s 默认命名空间) ctr -n k8s.io images import nginx-1.25.3.tar ctr -n k8s.io images import mysql-8.0.36.tar # 3. 验证镜像导入成功 ctr -n k8s.io images list | grep -E "nginx|mysql"

✅ 预期输出中包含docker.io/library/nginx:1.25.3docker.io/library/mysql:8.0.36


4. NFS 存储目录创建与权限配置

NFS 服务器(192.168.30.249)上执行,按应用隔离目录并匹配 NFS 权限配置。

# 1. 创建应用独立目录 mkdir -p /data/k8s/nginx/logs /data/k8s/nginx/html mkdir -p /data/k8s/mysql/data # 2. 统一权限为 1000:1000,与 NFS anonuid/anongid 匹配 chown -R 1000:1000 /data/k8s/nginx /data/k8s/mysql chmod -R 755 /data/k8s/nginx /data/k8s/mysql # 3. 刷新 NFS 共享 exportfs -r

5. 创建 PV 与 PVC(静态供给)

5.1 Nginx 共享存储 PV/PVC

Nginx 为无状态多实例部署,共享日志与静态资源,使用ReadWriteMany模式。

nginx-pv.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: nginx-pv labels: app: nginx type: nfs spec: capacity: storage: 5Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: server: 192.168.30.249 path: /data/k8s/nginx
nginx-pvc.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nginx-pvc namespace: default spec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi selector: matchLabels: app: nginx

5.2 MySQL 独占存储 PV/PVC

MySQL 为有状态单实例部署,数据独占,使用ReadWriteOnce模式。

mysql-pv.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: mysql-pv labels: app: mysql type: nfs spec: capacity: storage: 20Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain nfs: server: 192.168.30.249 path: /data/k8s/mysql/data
mysql-pvc.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc namespace: default spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi selector: matchLabels: app: mysql

5.3 执行创建与绑定验证

# 批量创建 kubectl apply -f nginx-pv.yaml -f nginx-pvc.yaml kubectl apply -f mysql-pv.yaml -f mysql-pvc.yaml # 验证绑定状态,STATUS 为 Bound 即为成功 kubectl get pv,pvc

✅ 预期结果:

NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv/nginx-pv 5Gi RWX Retain Bound default/nginx-pvc 10s pv/mysql-pv 20Gi RWO Retain Bound default/mysql-pvc 10s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/nginx-pvc Bound nginx-pv 5Gi RWX 10s persistentvolumeclaim/mysql-pvc Bound mysql-pv 20Gi RWO 10s

6. 部署 Nginx 无状态应用(Deployment + PVC + 探针)

Nginx 是典型无状态应用:请求不依赖本地上下文,所有 Pod 完全对等,扩缩容灵活,标准部署方式为Deployment
本次挂载 NFS 共享卷,实现日志统一收集、静态资源多实例共享。

6.1 创建 ConfigMap(Nginx 完整配置)

修正配置完整性,包含完整的主配置与站点配置,避免日志格式未定义错误。

nginx-configmap.yaml
apiVersion: v1 kind: ConfigMap metadata: name: nginx-conf data: nginx.conf: | user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; sendfile on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; } default.conf: | server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log warn; location / { try_files $uri $uri/ =404; } # 健康检查端点,用于探针 location = /healthz { access_log off; return 200 "ok\n"; add_header Content-Type text/plain; } }

创建并验证:

kubectl apply -f nginx-configmap.yaml kubectl get configmap nginx-conf

6.2 Deployment + Service 完整定义

nginx-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nginx labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: # 匹配 NFS 匿名用户 UID,确保写入权限 securityContext: runAsUser: 1000 fsGroup: 1000 containers: - name: nginx image: nginx:1.25.3 imagePullPolicy: IfNotPresent ports: - containerPort: 80 name: http # 卷挂载 volumeMounts: - name: nginx-main-conf mountPath: /etc/nginx/nginx.conf subPath: nginx.conf - name: nginx-site-conf mountPath: /etc/nginx/conf.d/default.conf subPath: default.conf - name: nginx-data mountPath: /var/log/nginx subPath: logs - name: nginx-data mountPath: /usr/share/nginx/html subPath: html # 存活探针:检测进程是否正常 livenessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 5 periodSeconds: 10 timeoutSeconds: 2 failureThreshold: 3 # 就绪探针:检测是否可承接流量 readinessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 3 periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 2 # 卷声明 volumes: - name: nginx-main-conf configMap: name: nginx-conf items: - key: nginx.conf path: nginx.conf - name: nginx-site-conf configMap: name: nginx-conf items: - key: default.conf path: default.conf - name: nginx-data persistentVolumeClaim: claimName: nginx-pvc --- # 集群内访问服务 apiVersion: v1 kind: Service metadata: name: nginx spec: selector: app: nginx type: ClusterIP ports: - port: 80 targetPort: 80

6.3 部署与基础验证

kubectl apply -f nginx-deployment.yaml # 等待 Pod 全部 Ready kubectl get pods -l app=nginx -w

7. 部署 MySQL 有状态应用(StatefulSet + PVC + 三探针)

MySQL 是典型有状态应用:依赖本地持久化数据、需要稳定的网络标识,标准部署方式为StatefulSet
本次使用单实例部署,配置完整的三类探针,数据持久化到 NFS 独立卷。

7.1 创建 Secret(MySQL 账号密码)

敏感信息使用 Secret 存储,不使用 ConfigMap。

kubectl create secret generic mysql-secret \ --from-literal=root-password=Root@k8s2025 \ --from-literal=user-password=Ops@k8s2025

验证:

kubectl get secret mysql-secret

7.2 StatefulSet + Service 完整定义

修正探针密码硬编码问题,通过环境变量引用 Secret;补充安全上下文匹配 NFS 权限。

mysql-statefulset.yaml
# Headless Service:StatefulSet 必备,用于 Pod 固定域名解析 apiVersion: v1 kind: Service metadata: name: mysql-headless labels: app: mysql spec: selector: app: mysql clusterIP: None ports: - port: 3306 name: mysql --- # 普通业务服务:数据库访问入口 apiVersion: v1 kind: Service metadata: name: mysql spec: selector: app: mysql type: ClusterIP ports: - port: 3306 targetPort: 3306 --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: "mysql-headless" replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: # 匹配 NFS 匿名用户 UID,解决 all_squash 权限问题 securityContext: runAsUser: 1000 fsGroup: 1000 containers: - name: mysql image: mysql:8.0.36 imagePullPolicy: IfNotPresent env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: root-password - name: MYSQL_DATABASE value: opsdb - name: MYSQL_USER value: opsuser - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: user-password ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-data mountPath: /var/lib/mysql # 启动探针:MySQL 启动慢,先等待启动完成再激活其他探针 startupProbe: tcpSocket: port: 3306 initialDelaySeconds: 15 periodSeconds: 5 failureThreshold: 30 # 存活探针:检测 MySQL 进程是否存活,通过环境变量读取密码 livenessProbe: exec: command: - /bin/sh - -c - mysqladmin ping -uroot -p$MYSQL_ROOT_PASSWORD periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 # 就绪探针:检测数据库是否可正常读写 readinessProbe: exec: command: - /bin/sh - -c - mysql -uroot -p$MYSQL_ROOT_PASSWORD -e "SELECT 1;" periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 2 # 单实例直接挂载预先创建的固定 PVC volumes: - name: mysql-data persistentVolumeClaim: claimName: mysql-pvc

说明:单实例 MySQL 直接使用固定 PVC 更清晰;若需多实例主从集群,可改用volumeClaimTemplates配合 StorageClass 动态创建。

7.3 部署与基础验证

kubectl apply -f mysql-statefulset.yaml # 等待 Pod 启动完成 kubectl get pods -l app=mysql -w

三、全流程测试验证 SOP

按「存储层 → 配置层 → 应用层 → 功能层 → 持久化 → 探针」分级验证,每步明确预期结果与排错方法。

SOP 1:存储层验证

目标:确认 PV/PVC 绑定正常,NFS 挂载权限正确

步骤操作命令预期结果异常排查
1-1kubectl get pv,pvc所有 PV/PVC 状态为 Bound若 Pending:检查容量、访问模式、标签是否匹配;检查节点 NFS 客户端是否安装
1-2任选一个 Nginx Pod 进入,执行df -h /var/log/nginx显示 NFS 挂载路径192.168.30.249:/data/k8s/nginx若无挂载:查看 Pod events,检查 NFS 网络连通性
1-3容器内执行touch /var/log/nginx/test.tmp && ls -l /var/log/nginx/test.tmp文件创建成功,权限为 1000:1000若权限报错:检查 NFS 目录 owner 与 securityContext 配置
1-4NFS 服务器执行ls -l /data/k8s/nginx/logs/能看到容器内创建的 test.tmp 文件若看不到:检查 NFS 导出配置、IP 白名单

SOP 2:Nginx 无状态应用验证

目标:确认配置生效、服务可用、无状态特性正常

步骤操作命令预期结果异常排查
2-1kubectl exec -it <nginx-pod> -- nginx -t输出test is successful,配置语法正确若报错:检查 ConfigMap 挂载路径与配置内容
2-2kubectl run curl-test --rm -it --image=curlimages/curl -- curl http://nginx.default.svc.cluster.local/healthz返回ok,状态码 200若失败:检查 Service 选择器、Pod 端口、就绪探针状态
2-3kubectl get endpoints nginxENDPOINTS 包含 3 个 Pod IP,与副本数一致若数量不足:检查就绪探针是否通过
2-4删除任意 Pod:kubectl delete pod <nginx-pod-name>,再执行kubectl get pods -l app=nginx自动重建新 Pod,名称与原 Pod 不同,总副本数保持 3若不重建:检查 Deployment 配置、节点资源
2-5重建后进入新 Pod,执行ls /var/log/nginx/之前的日志文件依然存在若丢失:检查 PVC 挂载是否正确

SOP 3:MySQL 有状态应用验证

目标:确认数据库可用、数据持久化、有状态特性正常

步骤操作命令预期结果异常排查
3-1kubectl get statefulset mysqlPod 名称固定为mysql-0,READY 为 1/1若名称不对:检查 StatefulSet 配置
3-2连接测试:kubectl run mysql-client --rm -it --image=mysql:8.0.36 -- mysql -h mysql -uroot -pRoot@k8s2025 -e "show databases;"显示opsdb等默认数据库若连接失败:检查 Service、端口、就绪探针、密码
3-3创建测试数据:kubectl run mysql-client --rm --image=mysql:8.0.36 -- mysql -h mysql -uroot -pRoot@k8s2025 -e "use opsdb; create table test(id int); insert into test values(1);"执行成功无报错若表创建失败:检查数据目录写入权限
3-4删除 Pod 重建:kubectl delete pod mysql-0,等待重建完成Pod 名称依然是mysql-0若名称变化:检查是否误用 Deployment
3-5重建后验证数据:kubectl run mysql-client --rm --image=mysql:8.0.36 -- mysql -h mysql -uroot -pRoot@k8s2025 -e "use opsdb; select * from test;"查询到之前插入的数据1若数据丢失:检查 PVC 挂载、PV 回收策略

SOP 4:探针功能专项验证

目标:确认三类探针按预期触发对应动作

4.1 Nginx 探针验证
步骤操作预期结果
4.1.1修改 ConfigMap,将/healthz返回码改为 403,滚动更新Pod READY 变为 0/1,Endpoints 中移除该 Pod(就绪探针生效)
4.1.2进入容器执行nginx -s stop几秒后 Pod 自动重启,RESTARTS 计数 +1(存活探针生效)
4.2 MySQL 探针验证
步骤操作预期结果
4.2.1观察 MySQL 启动前 15 秒存活/就绪探针不触发,仅启动探针检测(启动探针生效)
4.2.2进入容器执行killall mysqldPod 自动重启,RESTARTS 计数 +1(存活探针生效)
4.2.3模拟数据库锁表/只读Pod READY 变为 0/1,Service 端点移除(就绪探针生效)

四、最终交付物清单

序号文件名类型说明
1nginx-pv.yaml/nginx-pvc.yamlPV/PVC 配置Nginx 共享存储卷
2mysql-pv.yaml/mysql-pvc.yamlPV/PVC 配置MySQL 独占存储卷
3nginx-configmap.yamlConfigMap 配置Nginx 完整主配置+站点配置
4nginx-deployment.yaml应用部署配置Nginx Deployment + Service + 双探针
5mysql-statefulset.yaml应用部署配置MySQL StatefulSet + 双 Service + 三探针
6k8s-storage-test-sop.md验证文档全流程分级测试验证步骤与排错指南
7离线镜像包二进制文件nginx-1.25.3.tarmysql-8.0.36.tar

需要我把所有 YAML 文件打包整理成一个可直接执行的部署脚本,或者补充 NFS StorageClass 动态供给的配置吗?

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 17:48:40

重要!一文带您了解graph.invoke和graph.stream的应用场景

invoke 和 stream 概述invoke 和 stream 都是编译后 graph 的执行入口&#xff0c;用来启动整个图运行&#xff1b;底层真正干活的是同一套 Pregel BSP 引擎。invoke&#xff1a;一次性同步阻塞调用&#xff0c;全部 Superstep 跑完&#xff0c;返回最终完整 state。stream&…

作者头像 李华
网站建设 2026/9/4 17:47:45

基于SpringBoot 智慧课堂管理系统的设计与实现毕业设计项目源码

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华
网站建设 2026/9/4 17:46:44

从近似0基础开始FPGA开发 -- part.7 FPGA的网络通信设计

UDP 网络通信FPGA的工程应用离不开通信功能&#xff0c;当前电子学场景泛用的通信方式为以太网通信&#xff0c;大规模的工程化项目基本离不开组网场景。因此本文从以太网为引&#xff0c;展开介绍如何通过FPGA纯逻辑实现网络通信。 以太网是一种传输规则&#xff0c;收…

作者头像 李华
网站建设 2026/9/4 17:44:14

单片机陀螺仪校准实战:从原理到代码解决MPU6050角度漂移

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 17:42:03

Firebird数据库实战指南:从零掌握跨平台嵌入式数据库开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华