技术博客

Docker 网络与存储基础:bridge 网络原理、volume vs bind mount 详解

深入讲解 Docker 网络和存储的核心基础:bridge 网络的工作原理(veth pair、docker0 网桥、iptables NAT)、容器 DNS 解析与服务发现、host/none/bridge 三种网络模式对比,以及数据持久化方案——Named Volume 与 Bind Mount 的本质区别、适用场景与生产选型建议。

Docker网络bridgevolumebind 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、自定义网络与共享存储等进阶主题。