Docker 网络与存储基础:bridge 网络原理、volume vs bind mount 详解
深入讲解 Docker 网络和存储的核心基础:bridge 网络的工作原理(veth pair、docker0 网桥、iptables NAT)、容器 DNS 解析与服务发现、host/none/bridge 三种网络模式对比,以及数据持久化方案——Named Volume 与 Bind Mount 的本质区别、适用场景与生产选型建议。
网络和存储是 Docker 容器化应用的两大基础设施。网络决定容器之间如何通信,存储决定数据如何持久化。本文讲解最常用的网络模式和存储方案,帮你在生产中做出正确的选择。
一、Docker 网络基础
三种网络模式
# 查看 Docker 默认网络
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# 3b2c1a0... bridge bridge local ← 默认网络
# 8f4d2e1... host host local
# 1a2b3c4... none null local
三种模式对比:
bridge(默认):
容器有独立的网络 namespace
通过 docker0 虚拟网桥连接
默认 IP 段:172.17.0.0/16
容器间可通信,外部通过端口映射访问
→ 大多数应用使用此模式
host:
容器直接使用宿主机网络
没有网络隔离,性能最好
容器端口 = 宿主机端口,无需 -p 映射
→ 适合对网络性能要求极高的场景(监控组件、高频服务)
none:
容器没有网络(只有 lo 回环)
完全网络隔离
→ 适合离线数据处理任务
bridge 网络工作原理
bridge 网络的实现原理(Linux 底层):
宿主机网络结构:
┌─────────────────────────────────────────────┐
│ 宿主机 │
│ │
│ eth0 (192.168.1.10) → 物理网卡 │
│ ↕ iptables NAT │
│ docker0 (172.17.0.1) → 虚拟网桥 │
│ ↕ ↕ │
│ veth0a veth1a → 虚拟网卡对 │
│ ↕ ↕ │
│ ┌─────────┐ ┌─────────┐ │
│ │容器A │ │容器B │ │
│ │eth0 │ │eth0 │ │
│ │172.17.0.2│ │172.17.0.3│ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────┘
通信路径:
容器A → 容器B:172.17.0.2 → 172.17.0.3(走 docker0 网桥,不出宿主机)
容器A → 外网:172.17.0.2 → docker0 → iptables MASQUERADE → eth0 → 外网
外网 → 容器A:外网 → eth0 → iptables DNAT → docker0 → 容器A
# 验证网络结构
# 查看 docker0 网桥
ip addr show docker0
# docker0: <BROADCAST,MULTICAST,UP,LOWER_UP>
# inet 172.17.0.1/16 brd 172.17.255.255
# 查看 veth pair(每个容器对应一对)
ip link show | grep veth
# veth3a4b5c6: ... ← 宿主机端的 veth
# 查看 iptables NAT 规则(Docker 自动添加)
sudo iptables -t nat -L DOCKER -n
# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80
二、自定义 bridge 网络
默认 bridge 网络有一个重要限制:容器只能通过 IP 访问,不能通过名称。自定义网络解决了这个问题。
# 创建自定义网络
docker network create mynet
# 指定网段
docker network create \
--driver bridge \
--subnet 192.168.100.0/24 \
--gateway 192.168.100.1 \
mynet
# 在自定义网络中启动容器
docker run -d --name mysql --network mynet mysql:8.0
docker run -d --name redis --network mynet redis:7.2
docker run -d --name api --network mynet myapi:v1
# 自定义网络中,可以直接用容器名访问!
# api 容器内:
docker exec -it api bash
ping mysql # ✅ 直接通!自动 DNS 解析
curl mysql:3306 # ✅ 不需要知道 IP
默认 bridge vs 自定义 bridge 对比:
功能 默认 bridge 自定义 bridge
────────────────────────────────────────────────────
容器间通过名称通信 ❌ ✅ 自动 DNS
自动服务发现 ❌ ✅
网络隔离(多项目) ❌(共享) ✅(每个网络独立)
动态连接/断开 ❌ ✅
# 网络管理命令
docker network ls # 列出所有网络
docker network inspect mynet # 查看网络详情
docker network connect mynet myapp # 将容器连接到网络
docker network disconnect mynet myapp # 断开
docker network rm mynet # 删除网络
docker network prune # 删除所有未使用网络
三、host 网络模式
# 使用 host 网络(无端口映射,容器直接用宿主机端口)
docker run -d --network host nginx
# nginx 直接监听宿主机的 80 端口
curl http://localhost # ✅ 直接访问,不需要 -p
# 使用场景:
# - 监控组件(node-exporter、cAdvisor),需要访问宿主机网络
# - 高性能场景(省去 NAT 转换开销,带宽提升约 10-15%)
# - 需要绑定特定宿主机 IP 的服务
# 注意:host 网络在 Docker Desktop(Mac/Windows)上不生效
# host 网络只在 Linux 宿主机上真正有效
四、容器 DNS 与服务发现
# 容器内的 DNS 配置
docker exec myapp cat /etc/resolv.conf
# nameserver 127.0.0.11 ← Docker 内嵌 DNS Server
# options ndots:0
# Docker 内嵌 DNS(127.0.0.11)解析优先级:
# 1. 自定义网络中的容器名(如 mysql → 172.18.0.2)
# 2. 宿主机 /etc/hosts 中的记录
# 3. 外部 DNS(宿主机配置的 DNS)
# 自定义 DNS
docker run -d \
--dns=8.8.8.8 \
--dns-search=company.internal \
myapp
# 添加自定义 hosts 记录
docker run -d \
--add-host=db.internal:192.168.1.100 \
myapp
# 容器内 ping db.internal → 192.168.1.100
五、Docker 存储基础
容器的可写层(Container Layer)在容器删除后消失,需要数据持久化时要使用外部存储。
Docker 存储方案:
1. Named Volume(命名卷)
Docker 管理存储位置
默认在 /var/lib/docker/volumes/<name>/_data
适合:数据库数据、应用数据、需要 Docker 管理的数据
2. Bind Mount(绑定挂载)
挂载宿主机的指定目录
适合:配置文件、开发时代码挂载、日志目录
3. tmpfs Mount(内存挂载)
数据存在内存中,容器停止后消失
适合:敏感信息、临时缓存
4. 容器可写层(默认)
随容器生命周期,不建议存储重要数据
六、Named Volume(命名卷)
# 创建命名卷
docker volume create mysql_data
docker volume create nginx_logs
# 查看卷
docker volume ls
docker volume inspect mysql_data
# "Mountpoint": "/var/lib/docker/volumes/mysql_data/_data"
# 使用卷(-v 格式)
docker run -d \
-v mysql_data:/var/lib/mysql \
--name mysql-prod \
mysql:8.0
# 使用卷(--mount 格式,更清晰)
docker run -d \
--mount type=volume,source=mysql_data,target=/var/lib/mysql \
--name mysql-prod \
mysql:8.0
# 多容器共享卷
docker run -d --name web1 -v shared_data:/app/data myapp:v1
docker run -d --name web2 -v shared_data:/app/data myapp:v1
# web1 和 web2 共享 /app/data 目录
# 删除卷
docker volume rm mysql_data # 删除指定卷(必须先停止使用它的容器)
docker volume prune # 删除所有未使用的卷
# 备份卷数据
docker run --rm \
-v mysql_data:/source \
-v $(pwd):/backup \
ubuntu tar czf /backup/mysql_data_backup.tar.gz -C /source .
# 恢复卷数据
docker run --rm \
-v mysql_data:/target \
-v $(pwd):/backup \
ubuntu tar xzf /backup/mysql_data_backup.tar.gz -C /target
七、Bind Mount(绑定挂载)
# 挂载宿主机目录(-v 格式)
docker run -d \
-v /data/nginx/html:/usr/share/nginx/html \
-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
nginx
# 只读挂载(容器内不能修改)
docker run -d \
-v /data/nginx/conf:/etc/nginx/conf.d:ro \
nginx
# --mount 格式(推荐,参数更清晰)
docker run -d \
--mount type=bind,source=/data/nginx/html,target=/usr/share/nginx/html \
--mount type=bind,source=/etc/nginx/nginx.conf,target=/etc/nginx/nginx.conf,readonly \
nginx
# 开发场景:挂载代码目录(代码修改立即生效)
docker run -d \
-v $(pwd):/app \
-p 5000:5000 \
flask-app:dev
八、Volume vs Bind Mount 选型
场景 推荐方案 理由
─────────────────────────────────────────────────────────
MySQL/PostgreSQL 数据 Named Volume Docker 管理,可备份,可迁移
Redis 持久化数据 Named Volume 同上
应用配置文件(nginx.conf) Bind Mount 需要在宿主机直接编辑
日志目录(收集日志) Bind Mount 宿主机日志收集工具需要直接访问
开发时代码热更新 Bind Mount 本地编辑,容器内即时生效
CI 构建缓存(.m2、node_modules) Named Volume 跨容器复用,不依赖宿主机路径
具体选择标准:
用 Named Volume 当:
✅ 数据只由容器使用(数据库、队列)
✅ 需要在不同宿主机间迁移(docker volume 可导出)
✅ 不关心数据的宿主机路径
✅ 生产环境数据存储
用 Bind Mount 当:
✅ 需要从宿主机直接读写(配置文件、日志、开发代码)
✅ 数据路径有明确规范(/data/app、/var/log/myapp)
✅ 需要现有目录(宿主机已有 /data/mysql/...)
✅ 与其他非 Docker 进程共享数据
九、tmpfs 挂载
# 内存挂载(数据不落盘,容器停止即消失)
docker run -d \
--mount type=tmpfs,target=/tmp,tmpfs-size=256m \
myapp
# 用途:
# 1. 存放敏感信息(密钥、临时 token),不写磁盘
# 2. 高频读写的临时数据(缓存)
# 3. 测试场景(每次干净启动)
docker run -d \
--tmpfs /tmp:rw,noexec,nosuid,size=128m \
myapp
实战:MySQL + Nginx 持久化配置
# 准备目录和配置
mkdir -p /data/mysql /data/nginx/html /data/nginx/conf
cat > /data/nginx/conf/nginx.conf << 'EOF'
events { worker_connections 1024; }
http {
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
}
}
EOF
echo "<h1>Hello Docker</h1>" > /data/nginx/html/index.html
# 创建自定义网络
docker network create app-net
# 启动 MySQL(数据用 Named Volume)
docker run -d \
--name mysql \
--network app-net \
-v mysql_data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret123 \
-e MYSQL_DATABASE=myapp \
--restart=unless-stopped \
mysql:8.0
# 启动 Nginx(配置用 Bind Mount,数据用 Volume)
docker run -d \
--name nginx \
--network app-net \
-v /data/nginx/html:/usr/share/nginx/html:ro \
-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
-p 80:80 \
--restart=unless-stopped \
nginx
# Nginx 容器内可以访问 MySQL(通过容器名)
docker exec nginx curl mysql:3306 # tcp 连接
小结
网络选型: 生产用自定义 bridge 网络(容器名解析 + 网络隔离),监控组件用 host 网络(性能 + 直接访问宿主机信息)。
存储选型: 数据库等核心数据用 Named Volume(Docker 管理、可备份),配置文件和日志用 Bind Mount(宿主机直接访问)。
到这里,Docker 零基础入门的 6 篇已经覆盖了所有核心基础:概念 → 安装 → 镜像管理 → 容器操作 → Dockerfile → 网络存储。下一批(进阶系列)将深入 Dockerfile 多阶段构建、镜像瘦身、docker-compose、自定义网络与共享存储等进阶主题。
