Docker 容器挂载目录排查实战:从 inspect 命令到数据落盘路径的完整追踪
Docker 容器挂载目录排查实战:从 inspect 命令到数据落盘路径的完整追踪
摘要:容器化部署中,“数据到底存哪了”是运维和开发反复遇到的问题——数据备份找不到宿主机路径、磁盘打满定位不到元凶、容器重建后数据丢失,多半都和挂载配置有关。本文梳理一套完整的挂载排查命令链,覆盖单容器查询、批量扫描、volume 与 bind 的差异、以及挂载未生效的常见原因,适用于 Docker/K8s 环境的日常运维。
一、场景引入
接手一套存量容器环境,典型疑问有三类:
- 这个数据库容器的数据实际落在宿主机哪个路径?
- 磁盘某个目录被谁占用,能不能清?
- 明明启动时写了
-v,容器里却看不到文件,挂载去哪了?
这类问题的共同入口是docker inspect,但直接看原始 JSON 输出容易迷失在几十个字段里。下面按场景拆命令。
二、单容器挂载查询:一条命令拿全信息
最直接的命令:
docker inspect --format '{{json .Mounts}}' 容器名 | jq
没装 jq 就去掉 | jq。典型输出:
[
{
"Type": "bind",
"Source": "/home/user/data",
"Destination": "/var/lib/postgresql/data",
"Mode": "rw",
"RW": true
}
]
想要更易读的逐行排版,用 range 遍历:
docker inspect --format '{{range .Mounts}}{{.Type}} {{.Source}} -> {{.Destination}} RW={{.RW}}{{println}}{{end}}' 容器名
每行一个挂载点,类型、源路径、目标路径、读写权限一目了然。
字段含义速查:
| 字段 | 含义 |
|---|---|
| Type | bind(目录绑定)/ volume(命名卷)/ tmpfs(内存) |
| Source | 宿主机上的真实路径 |
| Destination | 容器内的挂载路径 |
| RW | true 可读写,false 只读 |
| Name | 仅 volume 类型有,是卷的名字 |
三、bind 与 volume 的关键差异:Source 路径要区分对待
这是排查时最容易混淆的点,两类挂载的宿主机落盘位置完全不同。
bind 挂载:Source 就是 docker run -v /host/path:/container/path 里写的那个宿主机路径,所见即所得,直接进这个目录操作。
volume 挂载:Source 形如:
/var/lib/docker/volumes/<卷名>/_data
注意末尾的 _data 子目录——真实数据落盘在这里,少看这一层就会找错地方。也可以用专用命令直接查:
docker volume inspect 卷名 | grep Mountpoint
tmpfs 挂载:Source 为空,纯内存存储,容器停止数据即消失,宿主机上根本没有对应路径。排查“数据怎么没了”时先确认是不是 tmpfs。
四、批量扫描:磁盘排查场景的利器
磁盘打满要定位是哪个容器在写入,一条命令扫完所有运行中容器:
docker ps -q | xargs docker inspect --format '{{.Name}} {{range .Mounts}}{{.Source}} -> {{.Destination}} {{end}}'
输出形如:
/timescaledb /data/pgdata -> /var/lib/postgresql/data
/backend /app/logs -> /var/log/app
/redis -> /data
redis 那行没有 Source 说明是 volume,卷名要去 volume 列表里查。根据 Source 路径反查 du -sh 就能定位占用大户。
顺带判断容器是不是 compose 起的:
docker inspect 容器名 --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'
有输出就是 compose 部署,路径即 docker-compose.yml 所在目录,改挂载要改 yml 后 docker compose up -d 重建;没输出就是 docker run 起的,改挂载只能删容器重建(Docker 不支持在线修改挂载)。
五、挂载“失效”的三种常见原因
现象是“启动时写了 -v 但容器里看不到文件”,按下面顺序排查。
原因一:容器内已有内容,目录非空导致覆盖问题
bind 挂载一个空目录到容器内的非空目录时,Docker 不会把容器内原有内容复制出来,表现为“原数据消失了”。数据其实还在镜像层里,但被挂载点遮住了。典型如数据库镜像首次启动前的挂载,建议先 docker run --rm 镜像名 ls 目标路径 确认镜像内默认内容,再做挂载方案设计。
原因二:SELinux/AppArmor 拦截
CentOS/RHEL 系统上若启用了 SELinux,bind 挂载可能出现 Permission denied。挂载路径末尾加 :z 或 :Z 标签:
-v /host/data:/container/data:z
或者临时 setenforce 0 验证是否是它的锅。
原因三:路径不存在,Docker 静默创建了空目录
写 -v /data/pgdata:/var/lib/postgresql/data 时,如果宿主机 /data/pgdata 不存在,Docker 会自动创建这个空目录再挂载,不报任何错。首启时数据库把这个空目录当数据目录初始化,看起来“正常”,但备份时你备份的是另一个路径,就出问题了。启动前先 mkdir -p 并确认路径正确。
六、验证挂载是否真的生效
配置改完别急着宣布完成,双向读写验证一下:
# 宿主机写文件
echo test > /宿主机路径/mount_test.txt
# 容器里读
docker exec 容器名 cat /容器内路径/mount_test.txt
# 反向:容器里写
docker exec 容器名 sh -c 'echo from_container > /容器内路径/mount_test2.txt'
ls /宿主机路径/mount_test2.txt
双向都通才是真正挂载成功。
七、一个老字段的坑:别去看 HostConfig.Binds
网上不少老文章教你看 docker inspect 里的 HostConfig.Binds 字段。这个字段在 Docker 20.10+ 上可能为空或信息不完整——以 .Mounts 为准,它始终反映运行时真实挂载状态,是官方推荐的数据源。
八、总结
容器挂载排查的完整路径可以收敛为四步:
| 步骤 | 命令 | 目的 |
|---|---|---|
| 1. 查挂载 | docker inspect --format '{{json .Mounts}}' |
获取类型与路径 |
| 2. 定落盘 | 区分 bind 的 Source 与 volume 的 _data |
找到宿主机真实路径 |
| 3. 批量扫 | docker ps -q | xargs docker inspect |
磁盘占用定位 |
| 4. 验生效 | 双向读写测试 | 确认配置真实生效 |
| 把这套流程封装成团队脚本(输入容器名,输出挂载清单和落盘路径),比每次翻文档拼命令可靠得多。 |
- 点赞
- 收藏
- 关注作者
评论(0)