Files
Deploy-Laboratory/docs/03-05-k3s-local-path-pvc.md
jack a67788de56 docs: 完善 03-05 本地存储文档与 local-path demo
- 落地路径查询优先 spec.local.path,兼容 hostPath
- local-path demo 增加 nodeSelector 固定节点

Made-with: Cursor
2026-03-24 06:21:10 +08:00

9.4 KiB
Raw Blame History

03-05-k3s 本地存储目录控制local-path-config / hostPath / local PV

这篇只讲一件事:在 K3s 里,如何控制“数据最终落到宿主机哪个目录”。

先说结论

  • 方法一(推荐)local-path-config:给 local-path 动态供给指定“基路径”。
    适合大多数场景PVC 仍可自动创建,但要通过 PV 反查落地目录。
  • 方法二 hostPath:在 Pod 里直接写宿主机目录。
    目录由你显式指定,不需要再查 provisioner 生成路径。
  • 方法三 local PV:可精确绑定“节点 + 目录”,但维护成本更高。
    本文只做介绍,不展开实操。

前置条件

  • K3s 已安装且 local-path-provisioner 正常运行
  • 能执行 kubectl
  • kubectl get storageclass 能看到 local-path(通常为 default

方法一:local-path-config 指定“基路径”(重点)

1) 原理

storageClassName: local-path 并不会在 PVC 里写死宿主机目录。
它表示交给 local-path-provisioner 动态创建 PVPV 的实际目录由 local-path-configconfig.json)里配置的基路径决定,然后在基路径下自动生成子目录。

2) 操作步骤

# 1. 备份当前配置
kubectl -n kube-system get configmap local-path-config -o yaml > /tmp/local-path-config.bak.yaml

# 2. 编辑 config.json把 paths 改成你希望的基路径)
kubectl -n kube-system edit configmap local-path-config

配置结构示意(请与现有 JSON 合并,不要盲目整段覆盖):

{
  "nodePathMap": [
    {
      "node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
      "paths": ["/storage/storage"]
    },
    {
      "node": "ylc61",
      "paths": ["/data/k3s-local-path"]
    }
  ]
}

说明:

  • DEFAULT_PATH_FOR_NON_LISTED_NODES 是兜底规则,不是实际节点名;凡是未单独列出的节点都走它。
  • ylc61 是单独覆盖规则;该节点会优先使用这里配置的基路径。
  • paths 是数组是因为支持“一个节点多个候选基路径”;示例里每个节点只写了一个路径。
  • 因此这段不是“一个节点两个路径”,而是“两个节点规则(默认 + 指定)”。
# 3. 重启 provisioner 使配置生效
kubectl -n kube-system rollout restart deploy/local-path-provisioner 2>/dev/null || true

3) 用 demo 验证PVC -> PV -> 节点 -> 落地目录)

Demo 清单:ansible/files/local-path-demo/local-path-pvc-demo.yaml

该 demo 已包含 nodeSelector 固定节点(示例为 ylc61),使用前请按你的节点主机名修改。

kubectl apply -f ansible/files/local-path-demo/local-path-pvc-demo.yaml
kubectl rollout status deploy/nginx-local-pvc-demo --timeout=180s

按下面顺序查真实落地路径:

# A. PVC 对应的 PV 名
PV=$(kubectl get pvc local-pvc-demo -o jsonpath='{.spec.volumeName}')
echo "$PV"

# B. PV 绑定到哪个节点
kubectl get pv "$PV" -o jsonpath='{.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0]}{"\n"}'

# C. PV 的宿主机物理路径local-path 在多数集群里是 local.path
kubectl get pv "$PV" -o jsonpath='{.spec.local.path}{"\n"}'

# D. 兼容差异:若上面为空,再尝试 hostPath.path 并用 yaml/describe 兜底
kubectl get pv "$PV" -o jsonpath='{.spec.hostPath.path}{"\n"}'
kubectl get pv "$PV" -o yaml | grep -E "path:|local:"
kubectl describe pv "$PV"

你之前看到的 path: /storage/storage/pvc-... 就是宿主机实际目录。
若是 /storage/storage/...,通常表示 K3s --data-dir=/storageprovisioner 再使用其 storage 子目录,属于正常现象。

4) 常见误区

  • persistence.pathvolumeMount.mountPath容器内路径,不是宿主机路径。
  • local-path 是动态供给,不是“每个 PVC 自定义绝对目录”。
  • WaitForFirstConsumerPod 未调度时 PVC Pending 属于正常现象。

5) 删除与回收(建议写进日常操作)

先删工作负载,再删 PVC

# 1) 先删 Deployment避免卷仍在挂载
kubectl delete deploy nginx-local-pvc-demo -n default --ignore-not-found

# 2) 再删 PVC
kubectl delete pvc local-pvc-demo -n default --ignore-not-found

# 3) 确认 PVC/PV 已回收
kubectl get pvc local-pvc-demo -n default
kubectl get pv | grep local-pvc-demo || true

检查 local-path 的回收策略(通常是 Delete

kubectl get sc local-path -o jsonpath='{.reclaimPolicy}{"\n"}'
  • Delete:删 PVC 后PV 和后端目录通常会被 provisioner 自动清理。
  • Retain:删 PVC 后PV/目录可能保留,需要手动处理。

手动删除宿主机目录(仅在异常残留或 Retain 场景使用):

# A. 找到 PVC 对应 PV
PV=$(kubectl get pvc local-pvc-demo -n default -o jsonpath='{.spec.volumeName}')

# B. 查目录和节点
NODE=$(kubectl get pv "$PV" -o jsonpath='{.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0]}')
PATH_ON_NODE=$(kubectl get pv "$PV" -o jsonpath='{.spec.hostPath.path}')
echo "node=$NODE path=$PATH_ON_NODE"

# C. 登录该节点手工删除(示例)
# ssh <你的节点登录> "sudo rm -rf '$PATH_ON_NODE'"

注意:手工删目录前务必确认 Pod/Deployment 已删除且不再挂载该路径。

方法二Pod 里直接用 hostPath(路径由你指定)

适用:单节点、实验环境,或者你明确要写死某个宿主机目录。

最小完整示例(可直接 kubectl apply -f,非空行逐行注释):

apiVersion: apps/v1 # Deployment 所属 API 版本
kind: Deployment # 资源类型Deployment
metadata: # 资源元数据
  name: nginx-hostpath-demo # Deployment 名称
  namespace: default # 部署命名空间
spec: # Deployment 规格
  replicas: 1 # 副本数1hostPath 通常用于单副本)
  selector: # Pod 选择器
    matchLabels: # 必须与 template.labels 完全一致
      app: nginx-hostpath-demo # 选择 app=nginx-hostpath-demo 的 Pod
  template: # Pod 模板
    metadata: # Pod 元数据
      labels: # Pod 标签
        app: nginx-hostpath-demo # 给 Pod 打 app 标签
    spec: # Pod 规格
      nodeSelector: # 指定调度节点(本地目录在该节点上生效)
        kubernetes.io/hostname: ylc61 # 请改成你的目标节点主机名
      containers: # 容器列表
        - name: nginx # 容器名称
          image: nginx:1.27-alpine # 容器镜像
          ports: # 容器暴露端口
            - containerPort: 80 # Nginx 容器监听 80
          volumeMounts: # 容器内挂载列表
            - name: app-data # 引用下方 volumes 的同名卷
              mountPath: /usr/share/nginx/html # 挂载到容器内网站目录
      volumes: # Pod 卷定义
        - name: app-data # 卷名称(供 volumeMounts 引用)
          hostPath: # 使用宿主机目录卷
            path: /data/nginx-hostpath-demo # 你指定的宿主机物理目录
            type: DirectoryOrCreate # 目录不存在则自动创建
---
apiVersion: v1 # Service 所属 API 版本
kind: Service # 资源类型Service
metadata: # 资源元数据
  name: nginx-hostpath-demo # Service 名称
  namespace: default # Service 命名空间
spec: # Service 规格
  selector: # 选择后端 Pod 的标签
    app: nginx-hostpath-demo # 指向 app=nginx-hostpath-demo 的 Pod
  ports: # Service 端口映射
    - port: 80 # Service 暴露端口
      targetPort: 80 # 转发到容器 80 端口
  type: ClusterIP # 集群内访问类型(默认)

应用与最小验证:

# 1) 保存上面的 YAML 到 /tmp/nginx-hostpath-demo.yaml 后应用
kubectl apply -f /tmp/nginx-hostpath-demo.yaml

# 2) 等待就绪
kubectl rollout status deploy/nginx-hostpath-demo --timeout=180s

# 2.1) 确认 Pod 已调度到你指定的节点
kubectl get pod -l app=nginx-hostpath-demo -o wide

# 3) 写入一段测试内容到挂载目录
kubectl exec deploy/nginx-hostpath-demo -- sh -c 'echo hostpath-ok > /usr/share/nginx/html/index.html'

# 4) 通过 Service 在集群内访问验证
kubectl run curl-test --image=curlimages/curl:8.8.0 --restart=Never -it --rm -- \
  curl -s http://nginx-hostpath-demo.default.svc.cluster.local/

说明:

  • 这里的 /data/nginx-hostpath-demo 就是你指定的宿主机目录,不需要通过 PV 反查。
  • 目录只在调度到的那个节点有效Pod 换节点会找不到原数据。
  • 生产中大量使用 hostPath 风险较高(权限、安全、漂移),需额外治理。

方法三:local PV(仅介绍)

local PV 介于上述两者之间:

  • hostPath 更“标准化”(走 PV/PVC 模型,可声明容量与绑定策略)
  • local-path 更“可控”(可固定具体节点和目录)
  • 但需要你手工维护 PV 生命周期、节点关系、回收策略

典型适用:

  • 必须固定到某节点某目录
  • 又希望保留 PVC 申领流程,而不是直接在 Pod 里写 hostPath

不适用:

  • 追求“自动化、低运维成本”的通用应用持久化

如何选

  • 默认选 local-path-config:自动化最好,够用且稳。
  • 需要“清单里写死路径”时选 hostPath(仅限明确单节点场景)。
  • 需要“精确目录 + PVC 模型”时考虑 local PV

关联文档

  • 03-06-k3s-使用nfs存储.md(多节点共享目录)
  • 03-07-k3s-longhorn-持久化存储.md(重状态、快照、备份)