首页 / Docker 入门教程 / 开发挂载:Bind Mounts

Docker 入门教程

开发挂载:Bind Mounts

本教程共 25 篇 · 第 17 篇 · 更新于 2026-07-26 · 约 8 分钟阅读

DockerDocker 入门教程Bind Mount绑定挂载开发环境热重载容器存储

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 快得多。

Warning

Windows 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
Warning

Windows 下用 -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,数据库数据用数据卷。

Note

Docker Desktop 还有个「同步文件共享」(Synchronized File Share)功能,针对大型代码库优化 bind mount 性能。普通 bind mount 在文件特别多时会很慢,这个功能用同步缓存加速。如果你的项目文件上万个,构建和文件访问慢得难以忍受,可以试试。

小结

这一章的要点:

  1. bind mount 把宿主机指定目录直接挂进容器,路径你完全掌控。
  2. 开发场景用它挂源码,改代码不用重建镜像,配合 nodemon 等工具实现热重载。
  3. :ro 做只读挂载,挂配置和密钥时更安全。
  4. --mount-v 行为更严格,路径不存在会报错,生产推荐。
  5. 挂载是覆盖关系,宿主机内容会盖住容器原目录,和数据卷的「填充」行为不同。
  6. 跨平台注意路径格式,Windows 优先用 --mount

下一章我们讲容器网络—怎么让外部访问到容器里的服务,端口映射是怎么回事。