Files
Deploy-Laboratory/docs/03-05-k3s-local-path-pvc.md
jack 8c43761962 feat: 按 doc_id 重组 ansible/files 与验证框架
- ansible/files 改为与文档 XX-YY 对齐的目录结构,更新相关 playbook 路径
- 新增 scripts/verify.sh 与 ansible/playbooks/verify/*.yml,移除单体 verify-matrix.yml
- 补充 docs/00-02 矩阵状态、00-05 验证框架与流程、00-04 环境与 ylc65 工作机说明
- 增加 k3s 存储准备、Longhorn、local-path 等 playbook 与辅助脚本

Made-with: Cursor
2026-03-26 07:01:14 +08:00

253 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-config``config.json`)里配置的基路径决定,然后在基路径下自动生成子目录。
### 2) 操作步骤
```bash
# 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/local-path-config-lab.json`](../ansible/files/03-05-local-path-config/local-path-config-lab.json) —— 仅含 **`DEFAULT_PATH_FOR_NON_LISTED_NODES``/storage/storage`**。应用方式:
- Ansible`ansible-playbook -i inventory.ini playbooks/apply-local-path-config-lab.yml`,或在 `group_vars/all.yml``longhorn_apply_local_path_lab: true` 后执行 `longhorn-install.yml`(见 `01-06``03-07`)。
- 手工:备份后编辑 ConfigMap`config.json` 与真源 JSON 对齐,再 `rollout restart` provisioner。
配置结构示意(**四节点统一基路径**时只需 `DEFAULT` 一条;请与现有 JSON 合并,不要盲目整段覆盖):
```json
{
"nodePathMap": [
{
"node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths": ["/storage/storage"]
}
]
}
```
说明:
- `DEFAULT_PATH_FOR_NON_LISTED_NODES` 是兜底规则,不是实际节点名;凡是未单独列出的节点都走它。
- **仅当**集群里仍存在「部分节点系统盘小、部分节点有大盘」等**混合规格**时,才需要再增加按节点名的条目(例如为某主机名单独指定另一基路径);实验室四节点同拓扑时**不要**再为 `ylc61` 等单独写小盘路径,以免与 `00-04` 基线冲突。
- `paths` 是数组是因为支持“一个节点多个候选基路径”;上例每个规则只写了一个路径。
```bash
# 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-demo/local-path-pvc-demo.yaml`](../ansible/files/03-05-local-path-demo/local-path-pvc-demo.yaml)
> 该 demo 已包含 `nodeSelector` 固定节点(示例为 `ylc61`),使用前请按你的节点主机名修改。
```bash
kubectl apply -f ansible/files/03-05-local-path-demo/local-path-pvc-demo.yaml
kubectl rollout status deploy/nginx-local-pvc-demo --timeout=180s
```
按下面顺序查真实落地路径:
```bash
# 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
```bash
# 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`
```bash
kubectl get sc local-path -o jsonpath='{.reclaimPolicy}{"\n"}'
```
- `Delete`:删 PVC 后PV 和后端目录通常会被 provisioner 自动清理。
- `Retain`:删 PVC 后PV/目录可能保留,需要手动处理。
手动删除宿主机目录(仅在异常残留或 `Retain` 场景使用):
```bash
# 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`,非空行逐行注释):
```yaml
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 # 集群内访问类型(默认)
```
应用与最小验证:
```bash
# 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`(重状态、快照、备份)