开发挂载:Bind Mounts
本教程共 25 篇 · 第 17 篇 · 更新于 2026-07-26 · 约 8 分钟阅读
17. 开发挂载:Bind Mounts
本节目标:搞懂 bind mount 和数据卷的区别,学会把宿主机目录挂进容器做开发,掌握只读挂载的用法。
上一章的数据卷适合存数据,但你不知道数据具体存在宿主机哪里—Docker 替你管着。这种「黑盒」存数据很好,但有个场景不合适:开发时改代码。
你想让容器里的代码跟着宿主机编辑器实时变化,改一行立刻生效,不用重新构建镜像。这就需要你完全掌控挂载的宿主机路径。bind mount(绑定挂载)就是干这个的。
bind mount 和数据卷的区别
先把两者对比清楚,这俩新人最容易混。
| 对比项 | 数据卷(Volume) | 绑定挂载(Bind Mount) |
|---|---|---|
| 宿主机路径 | Docker 选择,你不用管 | 你指定,完全掌控 |
| 挂载写法 | -v 卷名:/容器路径 | -v /宿主机路径:/容器路径 |
| 新卷初始内容 | 用容器原目录内容填充 | 直接覆盖容器原目录 |
| 适用场景 | 持久化数据(数据库等) | 开发挂载源码、配置文件 |
| 跨平台 | 好 | 受路径格式影响 |
最直观的区别在写法:数据卷第一段是「卷名」(一个标识符),bind mount 第一段是「宿主机绝对路径」(以 / 或盘符开头)。Docker 看第一段是路径还是名字来区分类型。
把源码挂进容器
来看开发场景最典型的用法:把宿主机的项目目录挂进容器,让代码改动实时生效。
假设你在开发一个 Node 应用,项目在 /path/to/my-app。用 bind mount 挂进去:
docker run -dp 3000:3000 \
-w /app -v "$(pwd):/app" \
node:22-alpine \
sh -c "yarn install && yarn run dev"
拆开看这条命令:
-dp 3000:3000:后台运行,端口映射。-w /app:设定容器的工作目录为/app。-v "$(pwd):/app":把宿主机当前目录挂到容器的/app。$(pwd)输出当前绝对路径,因为 bind mount 要求绝对路径。node:22-alpine:基础镜像,带 Node 运行时。sh -c "yarn install && yarn run dev":装依赖后启动开发模式(通常配 nodemon 监听文件变化)。
这样挂载后,你在宿主机用编辑器改 src/index.js,容器里的 /app/src/index.js 同步变化。nodemon 监听到文件改动,自动重启应用。刷新浏览器,改动立刻生效。
Tip这就是 bind mount 在开发中的杀手锏:改代码不用重新构建镜像。宿主机编辑器改文件 -> 容器里文件同步变 -> 进程监听到变化自动重启。整套开发循环丝滑无感,比每次
docker build快得多。
WarningWindows PowerShell 里
$(pwd)用法不同,要用反引号续行。另外 Windows 路径格式(如D:\project)在 bind mount 时要注意转义,推荐用--mount语法更稳。具体见后面「路径格式的坑」一节。
只读挂载
默认挂载是可读写的,容器能改宿主机文件。有时候你不希望容器改宿主机文件—比如挂一份配置进去,只让容器读不让改。
加 :ro(read-only)后缀:
docker run -d --name nginx \
-v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx
这样容器只能读这个配置文件,写不了。好处是防止容器里的程序意外修改宿主机文件,更安全。
对应的 --mount 写法加 readonly 参数:
docker run -d --name nginx \
--mount type=bind,source=/path/to/nginx.conf,target=/etc/nginx/nginx.conf,readonly \
nginx
只读挂载的典型场景:
- 挂配置文件给服务(nginx.conf、redis.conf)。
- 挂静态资源给 Web 服务器,只读不写。
- 挂密钥文件,防止容器篡改。
读写挂载则是 :rw,这是默认行为,一般不用显式写。
-v 和 —mount 的差异
bind mount 也支持 -v 和 --mount 两种写法,但有细微差别。
-v 简洁,但有个行为差异:如果宿主机路径不存在,-v 会自动创建一个空目录,不报错。这有时候是个坑—你本来想挂一个存在的目录,路径写错了,Docker 默默建了个空目录,容器里对应位置就空了,排查半天。
--mount 更严格:如果宿主机路径不存在,直接报错,不会偷偷创建。生产环境推荐用 --mount,少踩坑。
# -v 写法
docker run -d -v /home/me/public_html:/usr/local/apache2/htdocs/ httpd:2.4
# --mount 写法
docker run -d --mount type=bind,source=/home/me/public_html,target=/usr/local/apache2/htdocs/ httpd:2.4
Note官方推荐用
--mount,理由就是它行为更可预测。-v的「自动创建」特性在脚本里可能掩盖错误。但日常手敲命令,-v更短,看你习惯。
路径格式的坑
bind mount 要求宿主机路径是绝对路径,不同系统格式不一样,这是跨平台最容易出问题的地方。
Linux / macOS:用正斜杠,-v /home/me/app:/app,相对路径用 "$(pwd):/app"。
Windows PowerShell:盘符路径,注意反斜杠和续行符的差异。PowerShell 里推荐用 --mount,避免 -v 的转义麻烦:
docker run -d -p 8080:80 `
--mount type=bind,source=D:\project\public_html,target=/usr/local/apache2/htdocs/ `
httpd:2.4
WarningWindows 下用
-v "D:\project:/app"这种写法,盘符冒号和路径分隔容易和-v的分隔冒号混淆。最稳的办法是用--mount,键值对形式没有歧义。另外 Docker Desktop 默认只允许挂载某些目录(在 Settings -> Resources -> File Sharing 里配置),挂载范围外的路径会失败。
bind mount 会覆盖容器内容
这是 bind mount 一个重要特性,理解错了会出怪事。
挂载点和容器原目录是「覆盖」关系,不是合并。挂载后,容器原目录的内容会被宿主机目录的内容「盖住」—容器看到的是宿主机目录的内容,看不到镜像里原本的文件。
举个例子,nginx 镜像里 /usr/share/nginx/html 本来有默认欢迎页。你挂个空目录上去:
docker run -d -p 8080:80 -v /my-empty-dir:/usr/share/nginx/html nginx
访问 8080 端口,会发现 403 或空白—默认欢迎页被你的空目录盖掉了。这是正常行为,不是 bug。
对比数据卷:数据卷第一次挂载到一个非空目录时,会把容器原目录的内容复制到卷里(填充),不会盖空。bind mount 没有这个机制,纯覆盖。
Tip如果你想「挂载但保留容器原有内容」,用数据卷而不是 bind mount。或者先把容器原内容拷到宿主机目录,再挂载。
什么时候用 bind mount,什么时候用数据卷
记住一个判断原则:
- 数据由容器产生、需要持久化、不关心存在宿主机哪 -> 数据卷。比如数据库数据、应用日志。
- 数据在宿主机、需要容器直接访问、关心具体路径 -> bind mount。比如源码、配置文件、密钥。
开发场景几乎都用 bind mount:挂源码、挂配置、挂 .env 文件。生产场景多用数据卷:数据库、日志、上传文件。混着用也很常见—同一个容器,源码用 bind mount,数据库数据用数据卷。
NoteDocker Desktop 还有个「同步文件共享」(Synchronized File Share)功能,针对大型代码库优化 bind mount 性能。普通 bind mount 在文件特别多时会很慢,这个功能用同步缓存加速。如果你的项目文件上万个,构建和文件访问慢得难以忍受,可以试试。
小结
这一章的要点:
- bind mount 把宿主机指定目录直接挂进容器,路径你完全掌控。
- 开发场景用它挂源码,改代码不用重建镜像,配合 nodemon 等工具实现热重载。
:ro做只读挂载,挂配置和密钥时更安全。--mount比-v行为更严格,路径不存在会报错,生产推荐。- 挂载是覆盖关系,宿主机内容会盖住容器原目录,和数据卷的「填充」行为不同。
- 跨平台注意路径格式,Windows 优先用
--mount。
下一章我们讲容器网络—怎么让外部访问到容器里的服务,端口映射是怎么回事。