267 lines
11 KiB
Markdown
267 lines
11 KiB
Markdown
# 03-05-k3s 本地存储目录控制(local-path-config / hostPath / local PV)
|
||
|
||
> 这篇只讲一件事:在 K3s 里,如何控制“数据最终落到宿主机哪个目录”。
|
||
|
||
|
||
## TL;DR
|
||
|
||
- **自动化验收**:`./ansible/bin/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) 操作步骤
|
||
|
||
```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-lab.json`](../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-05`、`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-pvc-demo.yaml`](../ansible/files/03-05/local-path-pvc-demo.yaml)
|
||
|
||
> 该 demo 已包含 `nodeSelector` 固定节点(示例为 `ylc61`),使用前请按你的节点主机名修改。
|
||
|
||
```bash
|
||
kubectl apply -f ansible/files/03-05/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`(路径由你指定)
|
||
|
||
适用:单节点、实验环境,或者你明确要写死某个宿主机目录。
|
||
|
||
最小完整示例真源:[`ansible/files/03-05/nginx-hostpath-demo.yaml`](../ansible/files/03-05/nginx-hostpath-demo.yaml)
|
||
(下方保留带注释版本便于学习;执行时请优先使用真源文件):
|
||
|
||
```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 # 集群内访问类型(默认)
|
||
```
|
||
|
||
应用与最小验证:
|
||
|
||
```bash
|
||
# 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`。
|