11 KiB
03-05-k3s 本地存储目录控制(local-path-config / hostPath / local PV)
这篇只讲一件事:在 K3s 里,如何控制“数据最终落到宿主机哪个目录”。
TL;DR
- 自动化验收:
./scripts/verify.sh run 03-05 - 关键前置:按本文「前置条件」准备环境变量/Secret/入口 IP
- 成功判据:达到本文「预期」且 playbook 断言通过
- 排障:见本文「排障」
先说结论
- 方法一(推荐)
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 动态创建 PV,PV 的实际目录由 local-path-config(config.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
本仓库实验室真源(四节点 10G+32G、K3s --data-dir=/storage 统一拓扑):ansible/files/03-05/local-path-config-lab.json —— 仅含 DEFAULT_PATH_FOR_NON_LISTED_NODES → /storage/storage。应用方式:
- Ansible:
ansible-playbook -i inventory.ini playbooks/verify/03-05.yml,或在group_vars/all.yml设longhorn_apply_local_path_lab: true后执行03-07.yml(见01-06、03-07)。 - 手工:备份后编辑 ConfigMap,将
config.json与真源 JSON 对齐,再rollout restartprovisioner。
配置结构示意(四节点统一基路径时只需 DEFAULT 一条;请与现有 JSON 合并,不要盲目整段覆盖):
{
"nodePathMap": [
{
"node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths": ["/storage/storage"]
}
]
}
说明:
DEFAULT_PATH_FOR_NON_LISTED_NODES是兜底规则,不是实际节点名;凡是未单独列出的节点都走它。- 仅当集群里仍存在「部分节点系统盘小、部分节点有大盘」等混合规格时,才需要再增加按节点名的条目(例如为某主机名单独指定另一基路径);实验室四节点同拓扑时不要再为
ylc61等单独写小盘路径,以免与00-04基线冲突。 paths是数组是因为支持“一个节点多个候选基路径”;上例每个规则只写了一个路径。
# 3. 重启 provisioner 使配置生效
kubectl -n kube-system rollout restart deploy/local-path-provisioner 2>/dev/null || true
3) 用 demo 验证(PVC -> PV -> 节点 -> 落地目录)
Demo 清单:ansible/files/03-05/local-path-pvc-demo.yaml
该 demo 已包含
nodeSelector固定节点(示例为ylc61),使用前请按你的节点主机名修改。
kubectl apply -f ansible/files/03-05/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=/storage,provisioner 再使用其storage子目录,属于正常现象。
4) 常见误区
persistence.path、volumeMount.mountPath是容器内路径,不是宿主机路径。local-path是动态供给,不是“每个 PVC 自定义绝对目录”。WaitForFirstConsumer下,Pod 未调度时 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(路径由你指定)
适用:单节点、实验环境,或者你明确要写死某个宿主机目录。
最小完整示例真源:ansible/files/03-05/nginx-hostpath-demo.yaml
(下方保留带注释版本便于学习;执行时请优先使用真源文件):
apiVersion: apps/v1 # Deployment 所属 API 版本
kind: Deployment # 资源类型:Deployment
metadata: # 资源元数据
name: nginx-hostpath-demo # Deployment 名称
namespace: default # 部署命名空间
spec: # Deployment 规格
replicas: 1 # 副本数:1(hostPath 通常用于单副本)
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) 直接使用仓库真源应用
kubectl apply -f ansible/files/03-05/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(重状态、快照、备份)
排障
- 先看 playbook 输出:失败时先定位是 deploy/wait/http_check 哪一步。
- 集群侧总览:
kubectl get nodes -o wide、kubectl -n kube-system get pods -o wide。 - 事件与日志:
kubectl -n <ns> describe ...、kubectl -n <ns> logs ... --tail=200。