Docker挂载目录:别等线上出问题了才想起来看
我们会从几种挂载方式讲起,然后看两个最容易踩的坑,最后再回过头聊聊这套机制是怎么工作的。顺序不是特别重要,你可以直接跳到需要的地方。
挂载的几种手段
日常最常用的还是 -v,简单直接。跑一个 nginx 容器把主机上的 /data/logs 挂到容器的 /app/logs:
docker run -d -v /data/logs:/app/logs nginx
这个操作做完,/data/logs 和容器里的 /app/logs 就绑定了,一边有变化另一边马上能看到。日志持久化、开发时挂代码目录基本都是这么干。
如果希望控制得更细一点——比如只读挂载——可以用 --mount 参数。这玩意儿写起来啰嗦一点,但胜在意图明确:
docker run -d --mount type=bind,source=/config,target=/etc/config,readonly nginx
这里 type=bind 表示直接绑定主机目录,source 是主机路径,target 是容器内路径,后面还能跟读写权限。像挂配置文件的时候设成 readonly 能避免容器内部误改。
团队协作或者服务多起来之后,写一堆 docker run 命令不现实,这时候就用 Docker Compose。在 docker-compose.yml 里定义 volumes:
services:
web:
image: nginx
volumes:
- /data/html:/usr/share/nginx/html
这样一条命令起来,挂载关系就和声明的一样。后面加服务、改路径也方便维护。
还有一个是 Dockerfile 里的 VOLUME 指令,它只是声明了容器内的挂载点,不指定主机目录。像这样:
FROM nginx
VOLUME /usr/share/nginx/html
构建镜像之后启动时依然要用 -v 把主机目录和它关联起来:
docker build -t my-nginx .
docker run -d -v /主机/html:/usr/share/nginx/html my-nginx
有些人会误以为 Dockerfile 里写了 VOLUME 就能自动把主机目录挂进去,其实不是,它更多是用来告诉使用者“这个目录推荐挂载出去”。
不同场景选哪种
日志持久化最简单的就是 -v 或者 Compose 里写,启动直接指过去。比如:
docker run -d -v /data/logs:/var/log/nginx nginx
配置文件管理需要注意的地方多些,后面专门讲。
数据存储比如 MySQL,直接挂数据目录:
docker run -d --name mysql -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql
也可以创建命名卷(docker volume create)来挂,好处是多个容器可以共享同一个卷名,迁移备份也方便。
开发的时候做代码同步,把当前目录映射进去就行:

docker run -it -v $(pwd):/app -w /app node npm start
修改本地文件,容器里即时生效,不用反复构建镜像。
两个容易出问题的地方
第一个是配置文件挂载。
如果你想把一个配置文件从主机挂到容器里覆盖默认配置,千万先确保主机上的文件已经存在。如果主机上那个路径是空的,容器启动时对应目录的内容会被“清空”,也就是说原本容器内的配置文件全部消失,变成空目录。这会直接导致服务起不来或者配置丢失,线上部署时这个坑特别疼。正确做法:先在主机创建好 /config/nginx.conf,再执行:
docker run -d -v /config/nginx.conf:/etc/nginx/nginx.conf nginx
文件对文件的挂载没问题。目录对目录时,如果主机目录为空,容器内对应目录也就空了,这个规则要记住。
第二个是权限。主机和容器内的用户 UID/GID 不一致会导致 Permission Denied。比如主机上的 /data 属于 uid 1000,而容器内 nginx 进程以 www-data(uid 33)运行,往挂载目录写东西就会失败。
解决方法要么调整目录权限,要么用 user 参数指定运行用户,视情况来。
另外有一点,容器跑起来之后想改挂载目录,其实可以操作,但流程很重:需要停掉容器,甚至停掉 Docker 服务,然后去改配置里的 config.v2.json 和 hostconfig.json。一般建议直接销毁旧容器重新 run,除非真的不敢动数据又没法迁。日常使用基本用不上。
挂载到底是怎么工作的
这部分用到了 Linux 的命名空间和控制组。命名空间让容器拥有独立的文件系统视图,我们把主机目录挂进去,其实就是让容器的文件系统命名空间里多了一个和主机路径绑定的挂载点。
控制组则用来限制资源,顺便保证挂载点的权限和所有权匹配。
两条核心规则:挂载前以主机目录为准。
用户可以轻松申请到SSL证书,即使是没有技术经验的小白用户也能快速上手。来此加密的操作界面简洁明了,申请流程直观易懂,大大降低了申请证书的难度,让更多用户能够快速实现网站加密。
如果主机目录里没东西,容器对应路径也空;如果主机目录有内容,容器启动后就能看到那些内容。挂载之后两边实时同步,容器里新建的文件马上出现在主机,反过来也是一样。知道了这个也就理解了为啥配置文件一定要预先建好。
几个常用的组合示例
挂载日志目录:
docker run -d -v /var/log/myapp:/var/log/nginx nginx
挂载 MySQL 数据目录:
docker run -d --name mysql -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql
用 Compose 跑 WordPress:
services:
wordpress:
image: wordpress
volumes:
- /data/wordpress:/var/www/html
ports:
- "8080:80"
开发环境同步代码并启动:
docker run -it -v $(pwd):/code -w /code node:latest npm start
这些覆盖了大部分日常场景,后面踩坑了再回来补。总之,挂载方法本身不复杂,麻烦往往出在权限和空目录这些细节上。
把这些搞清楚,线上能少很多莫名其妙的故障。
更多推荐

所有评论(0)