Skip to content

第三阶段:数据管理与网络


3.1 数据持久化

3.1.1 为什么需要数据持久化?

Docker 容器的文件系统是临时的——容器删除后,内部的所有数据都会丢失。

容器生命周期与数据关系:

┌──────────────────────────────────┐
│  Container                       │
│  ┌──────────────────────────┐    │
│  │  可写层(容器层)          │    │ ← 容器删除后,这一层数据全部丢失!
│  ├══════════════════════════┤    │
│  │  镜像层(只读)            │    │
│  └──────────────────────────┘    │
└──────────────────────────────────┘

问题场景:
  - 数据库数据(MySQL、PostgreSQL)
  - 上传的文件(用户头像、文档)
  - 应用日志
  - 配置文件

解决方案 → Docker 提供三种挂载方式

3.1.2 三种挂载方式总览

┌──────────────────────────────────────────────────────────────┐
│                          Container                           │
│                                                              │
│    ┌──────────┐     ┌──────────┐     ┌──────────┐           │
│    │ /data    │     │ /config  │     │ /tmp/cache│           │
│    └────┬─────┘     └────┬─────┘     └────┬─────┘           │
└─────────┼────────────────┼────────────────┼──────────────────┘
          │                │                │
          ▼                ▼                ▼
   ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
   │   Volume     │ │  Bind Mount  │ │  tmpfs Mount │
   │              │ │              │ │              │
   │ Docker 管理的 │ │ 宿主机指定    │ │ 仅存于内存    │
   │ /var/lib/    │ │ 目录/文件     │ │ 不写入磁盘    │
   │  docker/     │ │              │ │              │
   │  volumes/    │ │ /home/user/  │ │ (RAM)        │
   │              │ │  project/    │ │              │
   └──────────────┘ └──────────────┘ └──────────────┘
类型存储位置Docker 管理适用场景
Volume/var/lib/docker/volumes/✅ 是数据库、持久化数据(推荐
Bind Mount宿主机任意路径❌ 否开发时挂载源码、配置文件
tmpfs Mount内存(RAM)临时敏感数据、高速缓存

3.1.3 Volume(卷)— 推荐的持久化方式

Volume 是 Docker 官方推荐的数据持久化方式,由 Docker 引擎管理,与宿主机文件系统解耦。

Volume 管理命令

bash
# ==================== 创建 Volume ====================
docker volume create my-data                    # 创建命名卷
docker volume create --driver local my-data     # 指定驱动(默认 local)

# 创建带标签的 Volume
docker volume create --label project=myapp --label env=prod my-data

# ==================== 查看 Volume ====================
docker volume ls                                # 列出所有卷
docker volume ls -q                             # 只显示卷名
docker volume ls --filter label=project=myapp   # 按标签过滤
docker volume ls --filter dangling=true         # 查看未被使用的卷

# ==================== 查看 Volume 详情 ====================
docker volume inspect my-data
# 输出示例:
# [
#     {
#         "CreatedAt": "2026-03-13T10:00:00Z",
#         "Driver": "local",
#         "Labels": {},
#         "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
#         "Name": "my-data",
#         "Options": {},
#         "Scope": "local"
#     }
# ]

# ==================== 删除 Volume ====================
docker volume rm my-data                        # 删除指定卷
docker volume prune                             # 删除所有未使用的卷
docker volume prune --filter label=env=test     # 按标签过滤删除

Volume 使用方式

bash
# ===== 方式一:-v 参数(简写形式) =====
# 格式:-v <卷名>:<容器内路径>[:选项]

# 使用命名卷
docker run -d --name db \
  -v my-data:/var/lib/mysql \
  mysql:8.0

# 使用匿名卷(Docker 自动生成卷名)
docker run -d --name db \
  -v /var/lib/mysql \
  mysql:8.0

# 只读挂载
docker run -d --name web \
  -v my-config:/etc/nginx/conf.d:ro \
  nginx

# ===== 方式二:--mount 参数(推荐,更清晰) =====
# 格式:--mount type=volume,source=<卷名>,target=<容器路径>[,选项]

docker run -d --name db \
  --mount type=volume,source=my-data,target=/var/lib/mysql \
  mysql:8.0

# 只读
docker run -d --name web \
  --mount type=volume,source=my-config,target=/etc/nginx/conf.d,readonly \
  nginx

-v vs --mount 的关键区别

bash
# ⚠️ 最大的区别:当 source 不存在时的行为

# -v:如果卷不存在,会自动创建(可能导致意外行为)
docker run -v nonexistent-vol:/data nginx   # 自动创建 nonexistent-vol ✅

# --mount:如果卷不存在,会报错退出(更安全)
docker run --mount type=volume,source=nonexistent-vol,target=/data nginx
# Error: volume nonexistent-vol not found ❌

# 建议:生产环境用 --mount,更安全;开发时 -v 更方便

Volume 的特性

bash
# 1. 多个容器可以共享同一个 Volume
docker run -d --name writer -v shared-data:/data ubuntu bash -c "while true; do date >> /data/log.txt; sleep 1; done"
docker run -d --name reader -v shared-data:/data:ro ubuntu tail -f /data/log.txt

# 2. Volume 独立于容器生命周期
docker rm -f writer reader    # 删除容器
docker volume ls               # Volume 依然存在
docker run --rm -v shared-data:/data alpine cat /data/log.txt  # 数据还在

# 3. Volume 内容初始化
# 如果容器内目标路径已有文件,首次挂载时会将容器内的文件复制到 Volume 中
docker run -d --name nginx-vol -v nginx-html:/usr/share/nginx/html nginx
docker run --rm -v nginx-html:/data alpine ls /data
# → 50x.html  index.html  (从 nginx 镜像中复制过来的)

3.1.4 Bind Mount(绑定挂载)

Bind Mount 将宿主机上的指定目录或文件直接挂载到容器内。

bash
# ===== -v 方式 =====
# 格式:-v <宿主机绝对路径>:<容器路径>[:选项]

# 挂载目录
docker run -d --name web \
  -v /home/user/website:/usr/share/nginx/html \
  -p 8080:80 \
  nginx

# 挂载单个文件
docker run -d --name web \
  -v /home/user/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx

# ===== --mount 方式(推荐) =====
docker run -d --name web \
  --mount type=bind,source=/home/user/website,target=/usr/share/nginx/html \
  -p 8080:80 \
  nginx

# 只读
docker run -d --name web \
  --mount type=bind,source=/home/user/nginx.conf,target=/etc/nginx/nginx.conf,readonly \
  nginx

Bind Mount 与 Volume 的关键区别

bash
# ⚠️ Bind Mount 的"覆盖"行为

# 如果宿主机目录是空的,容器内的目标路径的原有文件会被"隐藏"(不是删除)
mkdir -p /tmp/empty-dir
docker run --rm -v /tmp/empty-dir:/usr/share/nginx/html nginx ls /usr/share/nginx/html
# → (空) — 原来的 index.html 被遮盖了

# 而 Volume 的行为是:如果 Volume 是空的,会将容器内的文件复制到 Volume
docker run --rm -v test-vol:/usr/share/nginx/html nginx ls /usr/share/nginx/html
# → 50x.html  index.html  (自动复制过来)
对比VolumeBind Mount
管理方式Docker 管理用户自行管理
存储位置/var/lib/docker/volumes/宿主机任意路径
初始化行为自动复制容器内文件覆盖(隐藏)容器内文件
备份难度需要命令操作直接操作宿主机文件
跨平台✅ 一致⚠️ 路径与 OS 相关
性能(Linux)接近原生接近原生
性能(Mac/Win)较好⚠️ 较慢(文件系统转译)
推荐场景数据库、持久化存储开发时挂载源码

开发时热重载(最常见的 Bind Mount 用法)

bash
# 将源码挂载进容器,修改宿主机文件后容器内立即生效

# Python 开发(配合 flask --reload)
docker run -d --name dev-app \
  -v $(pwd)/src:/app/src \
  -p 5000:5000 \
  -e FLASK_ENV=development \
  my-flask-app flask run --reload --host 0.0.0.0

# Node.js 开发(配合 nodemon)
docker run -d --name dev-app \
  -v $(pwd)/src:/app/src \
  -v $(pwd)/package.json:/app/package.json \
  -p 3000:3000 \
  my-node-app npx nodemon src/index.js

# 前端开发(Vite HMR)
docker run -d --name dev-frontend \
  -v $(pwd)/src:/app/src \
  -p 5173:5173 \
  my-vite-app npm run dev -- --host 0.0.0.0

3.1.5 tmpfs Mount(临时内存挂载)

tmpfs Mount 将数据存储在宿主机内存中,不写入磁盘。容器停止后数据消失。

bash
# 使用 --tmpfs
docker run -d --name secure-app \
  --tmpfs /tmp:rw,size=100m,mode=1777 \
  my-app

# 使用 --mount(更多控制)
docker run -d --name secure-app \
  --mount type=tmpfs,target=/tmp,tmpfs-size=104857600,tmpfs-mode=1777 \
  my-app

# 参数说明:
# size=100m    : 限制大小 100MB
# mode=1777    : 设置权限(sticky bit)

适用场景:

bash
# 1. 存储临时敏感数据(如解密后的密钥)
docker run -d \
  --mount type=tmpfs,target=/run/secrets \
  my-app

# 2. 高速临时缓存(如编译中间产物)
docker run -d \
  --tmpfs /tmp/build:rw,size=500m \
  my-build-app

# 3. 避免写放大(频繁写入的临时文件)
docker run -d \
  --tmpfs /var/log:rw,size=50m \
  my-app

3.1.6 Volume vs Bind Mount 使用场景决策

需要持久化数据吗?

├─ 否 → 用 tmpfs Mount(纯内存,停止即消失)

├─ 是 → 需要直接操作宿主机文件吗?
│    │
│    ├─ 是 → 用 Bind Mount
│    │   ├─ 开发时挂载源码
│    │   ├─ 挂载宿主机配置文件
│    │   └─ 需要宿主机直接读写的日志
│    │
│    └─ 否 → 用 Volume(推荐)
│        ├─ 数据库数据(MySQL、PostgreSQL、MongoDB)
│        ├─ 应用持久化数据
│        ├─ 多容器共享数据
│        └─ 生产环境

3.1.7 数据卷备份与恢复

Volume 的数据存储在 Docker 内部管理的路径下,不能直接操作。需要通过容器来做备份。

bash
# ==================== 备份 Volume ====================

# 原理:启动一个临时容器,同时挂载需要备份的 Volume 和宿主机目录,
#       在容器内将 Volume 数据打包到宿主机目录

# 假设要备份名为 db-data 的 Volume
docker run --rm \
  -v db-data:/source:ro \
  -v $(pwd)/backup:/backup \
  alpine \
  tar czf /backup/db-data-$(date +%Y%m%d_%H%M%S).tar.gz -C /source .

# 解释:
# -v db-data:/source:ro      → 将要备份的 Volume 挂载为只读
# -v $(pwd)/backup:/backup   → 将宿主机 backup 目录挂载
# tar czf ...                → 打包 Volume 内容到宿主机

# 查看备份文件
ls -lh backup/
# -rw-r--r-- 1 root root 15M Mar 13 10:00 db-data-20260313_100000.tar.gz

# ==================== 恢复 Volume ====================

# 创建新的 Volume
docker volume create db-data-restored

# 恢复数据
docker run --rm \
  -v db-data-restored:/target \
  -v $(pwd)/backup:/backup:ro \
  alpine \
  sh -c "tar xzf /backup/db-data-20260313_100000.tar.gz -C /target"

# 验证恢复
docker run --rm -v db-data-restored:/data alpine ls -la /data

# ==================== 迁移 Volume 到另一台机器 ====================

# 方法一:备份 → 传输 → 恢复
# 源机器:
docker run --rm -v my-vol:/source:ro -v $(pwd):/backup alpine \
  tar czf /backup/vol-backup.tar.gz -C /source .

scp vol-backup.tar.gz user@target-host:/tmp/

# 目标机器:
docker volume create my-vol
docker run --rm -v my-vol:/target -v /tmp:/backup:ro alpine \
  sh -c "tar xzf /backup/vol-backup.tar.gz -C /target"

# 方法二:直接复制 Volume 底层数据(需要 root 权限)
sudo cp -r /var/lib/docker/volumes/my-vol /path/to/backup/
# ⚠️ 需要先停止使用该 Volume 的容器

自动化备份脚本

bash
#!/bin/bash
# backup-volumes.sh — 自动备份所有命名 Volume

BACKUP_DIR="/backup/docker-volumes"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

# 遍历所有命名 Volume(排除匿名卷)
for vol in $(docker volume ls -q --filter dangling=false); do
    echo "Backing up volume: $vol"
    docker run --rm \
        -v "$vol":/source:ro \
        -v "$BACKUP_DIR":/backup \
        alpine \
        tar czf "/backup/${vol}_${DATE}.tar.gz" -C /source .
    echo "  → ${BACKUP_DIR}/${vol}_${DATE}.tar.gz"
done

# 清理 30 天前的备份
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete

echo "Backup completed at $(date)"
bash
# 设置 cron 定时任务(每天凌晨 2 点备份)
crontab -e
# 添加:
# 0 2 * * * /path/to/backup-volumes.sh >> /var/log/docker-backup.log 2>&1

3.2 Docker 网络

3.2.1 Docker 网络模式概览

Docker 提供了多种网络驱动,适用于不同场景:

┌─────────────────────────────────────────────────────────────────────┐
│                           Host Network                              │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Host OS (eth0: 192.168.1.100)                              │   │
│   │                                                             │   │
│   │  ┌─── bridge (docker0: 172.17.0.1) ──────────────────────┐  │   │
│   │  │                                                       │  │   │
│   │  │  ┌────────────┐  ┌────────────┐  ┌────────────┐     │  │   │
│   │  │  │ Container1 │  │ Container2 │  │ Container3 │     │  │   │
│   │  │  │172.17.0.2  │  │172.17.0.3  │  │172.17.0.4  │     │  │   │
│   │  │  └────────────┘  └────────────┘  └────────────┘     │  │   │
│   │  │                                                       │  │   │
│   │  └── Bridge Network ─────────────────────────────────────┘  │   │
│   │                                                             │   │
│   │  ┌─── my-net (172.18.0.1) ───────────────────────────────┐  │   │
│   │  │                                                       │  │   │
│   │  │  ┌────────────┐  ┌────────────┐                      │  │   │
│   │  │  │ App        │  │ Redis      │                      │  │   │
│   │  │  │172.18.0.2  │  │172.18.0.3  │                      │  │   │
│   │  │  └────────────┘  └────────────┘                      │  │   │
│   │  │                                                       │  │   │
│   │  └── Custom Bridge Network ──────────────────────────────┘  │   │
│   │                                                             │   │
│   │  ┌────────────┐     ← host 模式:直接用宿主机网络栈         │   │
│   │  │ Container4 │        无网络隔离,共享 host IP 和端口      │   │
│   │  └────────────┘                                             │   │
│   │                                                             │   │
│   └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘
网络模式说明隔离性适用场景
bridge(默认)容器通过虚拟网桥互联单机多容器通信(最常用
host容器直接使用宿主机网络高性能网络、避免 NAT 开销
none禁用所有网络完全隔离安全场景、离线处理任务
overlay跨主机容器通信Docker Swarm / 多节点集群
macvlan容器拥有独立 MAC 地址需要容器像物理机一样接入网络

3.2.2 Bridge 网络详解

Bridge 是 Docker 的默认网络模式。安装 Docker 后会自动创建 docker0 网桥。

bash
# 查看默认网桥
ip addr show docker0
# docker0: <BROADCAST,MULTICAST,UP,LOWER_UP>
#     inet 172.17.0.1/16 brd 172.17.255.255

# 查看 Docker 网络
docker network ls
# NETWORK ID     NAME      DRIVER    SCOPE
# abc123def456   bridge    bridge    local
# 789ghi012jkl   host      host      local
# mno345pqr678   none      null      local

# 查看 bridge 网络详情
docker network inspect bridge

默认 bridge 网络的局限性

bash
# 在默认 bridge 网络中,容器间只能通过 IP 通信,不支持容器名 DNS 解析

docker run -d --name app1 nginx
docker run -d --name app2 nginx

# 获取 app1 的 IP
docker inspect --format '{{.NetworkSettings.IPAddress}}' app1
# 172.17.0.2

# 从 app2 ping app1 — 用 IP 可以,用容器名不行
docker exec app2 ping 172.17.0.2     # ✅ 可以
docker exec app2 ping app1           # ❌ ping: bad address 'app1'

# 清理
docker rm -f app1 app2

⚠️ 不推荐使用默认 bridge 网络,因为它不支持 DNS 解析,只能靠 IP 通信,而容器 IP 是动态分配的。

3.2.3 自定义 Bridge 网络(推荐)

自定义 Bridge 网络相比默认 bridge,提供了 DNS 解析、更好的隔离性和灵活的配置

bash
# ==================== 创建自定义网络 ====================

# 基本创建
docker network create my-network

# 指定子网和网关
docker network create \
  --driver bridge \
  --subnet 172.20.0.0/16 \
  --gateway 172.20.0.1 \
  --ip-range 172.20.1.0/24 \
  my-network

# 带标签
docker network create \
  --label project=myapp \
  my-network

# ==================== 网络管理命令 ====================
docker network ls                          # 列出所有网络
docker network inspect my-network          # 查看网络详情
docker network rm my-network               # 删除网络
docker network prune                       # 清理未使用的网络

# ==================== 容器连接到网络 ====================

# 方式一:创建容器时指定网络
docker run -d --name app --network my-network nginx

# 方式二:将已有容器连接到网络
docker network connect my-network existing-container

# 断开网络
docker network disconnect my-network existing-container

# 一个容器可以同时连接多个网络
docker network connect another-network app

自定义网络的 DNS 解析

bash
# 创建自定义网络
docker network create app-net

# 启动两个容器
docker run -d --name web --network app-net nginx
docker run -d --name api --network app-net nginx

# 在自定义网络中,容器可以通过名称互相访问 ✅
docker exec web ping -c 3 api
# PING api (172.19.0.3): 56 data bytes
# 64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.089 ms

docker exec api ping -c 3 web
# PING web (172.19.0.2): 56 data bytes
# 64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.092 ms

# 清理
docker rm -f web api
docker network rm app-net

网络别名(Network Alias)

bash
# 为容器设置网络别名,一个服务可以有多个名字
docker network create app-net

docker run -d \
  --name mysql-primary \
  --network app-net \
  --network-alias db \
  --network-alias mysql \
  mysql:8.0

# 其他容器可以通过以下任一名字访问:
# - mysql-primary(容器名)
# - db(别名)
# - mysql(别名)

docker run --rm --network app-net alpine ping -c 1 db
docker run --rm --network app-net alpine ping -c 1 mysql
docker run --rm --network app-net alpine ping -c 1 mysql-primary
# 三个都能解析到同一个 IP ✅

# 多个容器可以共享同一个别名(简单的负载均衡)
docker run -d --name mysql-replica1 --network app-net --network-alias db mysql:8.0
docker run -d --name mysql-replica2 --network app-net --network-alias db mysql:8.0

# DNS 查询 db 时会轮询返回不同的 IP
docker run --rm --network app-net alpine nslookup db
# Name:      db
# Address 1: 172.19.0.2 mysql-primary
# Address 2: 172.19.0.3 mysql-replica1
# Address 3: 172.19.0.4 mysql-replica2

3.2.4 Host 网络模式

bash
# host 模式:容器直接使用宿主机的网络栈,没有网络隔离
docker run -d --name web-host --network host nginx

# 此时 nginx 直接监听宿主机的 80 端口
curl http://localhost:80    # ✅ 直接访问,无需 -p 端口映射

# 查看网络——容器没有独立 IP
docker inspect --format '{{.NetworkSettings.IPAddress}}' web-host
# (空)

# 在容器内看到的是宿主机的网络接口
docker exec web-host ip addr
# 显示的是宿主机的 eth0、lo 等

docker rm -f web-host

Host 网络适用场景:

  • 需要极致网络性能(避免 NAT 转发开销)
  • 容器需要访问宿主机的所有端口
  • 网络监控/嗅探类应用

注意事项:

  • ⚠️ 端口冲突风险:容器直接占用宿主机端口
  • ⚠️ 无网络隔离,安全性较低
  • ⚠️ -p 端口映射参数会被忽略
  • ⚠️ 仅 Linux 支持,macOS/Windows 上无效

3.2.5 None 网络模式

bash
# none 模式:完全禁用网络
docker run -d --name isolated --network none alpine sleep infinity

# 容器内没有任何网络接口(除了 lo)
docker exec isolated ip addr
# 1: lo: <LOOPBACK,UP,LOWER_UP>
#     inet 127.0.0.1/8 scope host lo

docker exec isolated ping 8.8.8.8
# ping: bad address '8.8.8.8'  — 无法访问外网

docker rm -f isolated

适用场景:

  • 纯计算任务(不需要网络)
  • 安全敏感的数据处理
  • 需要完全隔离的沙箱环境

3.2.6 端口映射详解

bash
# ==================== -p 参数语法 ====================

# 完整格式:-p [宿主机IP:]宿主机端口:容器端口[/协议]

# 基础映射
docker run -d -p 8080:80 nginx           # 宿主机 8080 → 容器 80

# 映射到指定 IP(安全:只允许本机访问)
docker run -d -p 127.0.0.1:8080:80 nginx # 仅 localhost 可访问

# 随机端口
docker run -d -p 80 nginx                # 宿主机随机端口 → 容器 80

# 映射 UDP 端口
docker run -d -p 53:53/udp dns-server    # UDP 协议

# 同时映射 TCP 和 UDP
docker run -d -p 53:53/tcp -p 53:53/udp dns-server

# 多个端口映射
docker run -d \
  -p 80:80 \
  -p 443:443 \
  -p 8080:8080 \
  nginx

# 端口范围映射
docker run -d -p 8000-8010:8000-8010 my-app

# ==================== 查看端口映射 ====================
docker port my-container
# 80/tcp -> 0.0.0.0:8080
# 443/tcp -> 0.0.0.0:8443

# ==================== 大写 -P:自动映射所有 EXPOSE 端口 ====================
docker run -d -P nginx
docker port <container_id>
# 80/tcp -> 0.0.0.0:32768   ← 自动分配的随机端口

端口映射工作原理

请求流程:

客户端 → 宿主机:8080 → iptables NAT → docker0 网桥 → 容器:80

┌─────────┐     ┌──────────────────┐     ┌────────────┐
│  客户端  │────▶│  宿主机          │────▶│  容器       │
│         │     │  :8080           │     │  :80       │
│         │     │                  │     │            │
│         │     │  iptables DNAT   │     │  nginx     │
│         │     │  172.17.0.2:80   │     │            │
└─────────┘     └──────────────────┘     └────────────┘

# 查看 Docker 创建的 iptables 规则
sudo iptables -t nat -L -n | grep -i docker

3.2.7 容器 DNS 解析机制

bash
# ===== 自定义网络中的 DNS 解析 =====

# Docker 内置了一个 DNS 服务器(127.0.0.11),
# 自动为同一自定义网络中的容器提供名称解析

docker network create test-net

docker run -d --name svc-a --network test-net alpine sleep infinity
docker run -d --name svc-b --network test-net alpine sleep infinity

# 查看 DNS 配置
docker exec svc-a cat /etc/resolv.conf
# nameserver 127.0.0.11
# options ndots:0

# DNS 解析过程
docker exec svc-a nslookup svc-b
# Server:    127.0.0.11      ← Docker 内置 DNS
# Address:   127.0.0.11:53
#
# Name:      svc-b
# Address 1: 172.20.0.3 svc-b.test-net   ← 解析成功

# ===== DNS 解析顺序 =====
# 1. 查找容器名
# 2. 查找网络别名
# 3. 查找 --dns 指定的外部 DNS
# 4. 使用宿主机的 DNS

# 自定义 DNS 服务器
docker run -d \
  --name my-app \
  --network test-net \
  --dns 8.8.8.8 \
  --dns-search example.com \
  alpine sleep infinity

docker rm -f svc-a svc-b
docker network rm test-net

3.2.8 网络隔离与安全

bash
# ===== 不同网络之间的容器默认不能通信 =====

docker network create net-frontend
docker network create net-backend

docker run -d --name frontend --network net-frontend alpine sleep infinity
docker run -d --name backend --network net-backend alpine sleep infinity

# 跨网络不可达
docker exec frontend ping -c 1 -W 1 backend
# ping: bad address 'backend'   ← DNS 都解析不了

# ===== 让容器跨网络通信 — 连接到多个网络 =====

# 让 backend 也加入 net-frontend
docker network connect net-frontend backend

# 现在可以通信了
docker exec frontend ping -c 1 backend
# ✅ 64 bytes from 172.18.0.3: ...

# ===== 典型的网络隔离架构 =====
#
#   Internet → [Frontend Network] → App → [Backend Network] → DB
#
#   - 前端容器只在 Frontend Network
#   - App 容器同时在 Frontend 和 Backend Network(中间人角色)
#   - DB 容器只在 Backend Network
#   - DB 无法被外部直接访问 ✅ 安全

docker rm -f frontend backend
docker network rm net-frontend net-backend

3.3 实践 Demo

Demo 1:MySQL + Volume 数据持久化

验证:删除容器后数据不丢失。

bash
# ===== Step 1: 创建 Volume =====
docker volume create mysql-data

# ===== Step 2: 启动 MySQL 容器 =====
docker run -d \
  --name mysql-server \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -e MYSQL_DATABASE=testdb \
  -e MYSQL_USER=testuser \
  -e MYSQL_PASSWORD=test123 \
  -v mysql-data:/var/lib/mysql \
  -p 3306:3306 \
  mysql:8.0

# 等待 MySQL 完全启动
echo "等待 MySQL 启动..."
sleep 15

# ===== Step 3: 写入测试数据 =====
docker exec -it mysql-server mysql -uroot -proot123 -e "
  USE testdb;
  CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  );
  INSERT INTO users (name, email) VALUES
    ('Alice', 'alice@example.com'),
    ('Bob', 'bob@example.com'),
    ('Charlie', 'charlie@example.com');
  SELECT * FROM users;
"

# 输出:
# +----+---------+---------------------+---------------------+
# | id | name    | email               | created_at          |
# +----+---------+---------------------+---------------------+
# |  1 | Alice   | alice@example.com   | 2026-03-13 10:00:00 |
# |  2 | Bob     | bob@example.com     | 2026-03-13 10:00:00 |
# |  3 | Charlie | charlie@example.com | 2026-03-13 10:00:00 |
# +----+---------+---------------------+---------------------+

# ===== Step 4: 删除容器 =====
docker stop mysql-server
docker rm mysql-server
echo "容器已删除"

# Volume 还在
docker volume ls | grep mysql-data
# local     mysql-data  ✅

# ===== Step 5: 用相同的 Volume 启动新容器 =====
docker run -d \
  --name mysql-server-new \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -v mysql-data:/var/lib/mysql \
  -p 3306:3306 \
  mysql:8.0

sleep 10

# ===== Step 6: 验证数据是否还在 =====
docker exec -it mysql-server-new mysql -uroot -proot123 -e "
  USE testdb;
  SELECT * FROM users;
"

# 输出:数据完好无损!✅
# +----+---------+---------------------+---------------------+
# | id | name    | email               | created_at          |
# +----+---------+---------------------+---------------------+
# |  1 | Alice   | alice@example.com   | 2026-03-13 10:00:00 |
# |  2 | Bob     | bob@example.com     | 2026-03-13 10:00:00 |
# |  3 | Charlie | charlie@example.com | 2026-03-13 10:00:00 |
# +----+---------+---------------------+---------------------+

# ===== 清理 =====
docker stop mysql-server-new && docker rm mysql-server-new
docker volume rm mysql-data

Demo 2:App + Redis 自定义网络通信

使用自定义网络实现 Python 应用与 Redis 通过容器名互相访问。

bash
# ===== Step 1: 创建自定义网络 =====
docker network create app-network

# ===== Step 2: 启动 Redis 容器 =====
docker run -d \
  --name redis \
  --network app-network \
  -v redis-data:/data \
  redis:7-alpine \
  redis-server --appendonly yes   # 开启 AOF 持久化

# ===== Step 3: 验证 Redis 正常运行 =====
docker exec redis redis-cli ping
# PONG

# ===== Step 4: 创建 Python 应用 =====
mkdir -p /tmp/redis-app && cat > /tmp/redis-app/app.py << 'PYEOF'
import redis
import time
import socket
import os

# 通过容器名 "redis" 连接(DNS 自动解析)
r = redis.Redis(host='redis', port=6379, decode_responses=True)

print(f"=== Redis App Demo ===")
print(f"Hostname: {socket.gethostname()}")
print(f"Connected to Redis: {r.ping()}")

# 写入数据
r.set('app:name', 'Docker Network Demo')
r.set('app:version', '1.0')
r.incr('app:visits')

# 写入列表
r.rpush('app:logs', f'[{time.strftime("%Y-%m-%d %H:%M:%S")}] App started on {socket.gethostname()}')

# 读取数据
print(f"\nStored data:")
print(f"  app:name    = {r.get('app:name')}")
print(f"  app:version = {r.get('app:version')}")
print(f"  app:visits  = {r.get('app:visits')}")
print(f"  app:logs    = {r.lrange('app:logs', 0, -1)}")

# 查看 Redis 连接信息
info = r.info('server')
print(f"\nRedis Server: v{info['redis_version']} at redis:6379")
print(f"Redis uptime: {info['uptime_in_seconds']}s")

print("\n✅ App <-> Redis communication via container name works!")
PYEOF

cat > /tmp/redis-app/requirements.txt << 'EOF'
redis>=5.0
EOF

cat > /tmp/redis-app/Dockerfile << 'EOF'
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]
EOF

# ===== Step 5: 构建并运行 App =====
docker build -t redis-app /tmp/redis-app

docker run --rm \
  --name app \
  --network app-network \
  redis-app

# 输出:
# === Redis App Demo ===
# Hostname: a1b2c3d4e5f6
# Connected to Redis: True
#
# Stored data:
#   app:name    = Docker Network Demo
#   app:version = 1.0
#   app:visits  = 1
#   app:logs    = ['[2026-03-13 10:00:00] App started on a1b2c3d4e5f6']
#
# Redis Server: v7.2.4 at redis:6379
# Redis uptime: 30s
#
# ✅ App <-> Redis communication via container name works!

# ===== Step 6: 再运行一次,验证数据累加 =====
docker run --rm --name app --network app-network redis-app
# app:visits  = 2  ← 累加了
# app:logs    = ['...App started on xxx', '...App started on yyy']  ← 两条记录

# ===== 清理 =====
docker rm -f redis
docker network rm app-network
docker volume rm redis-data
rm -rf /tmp/redis-app

Demo 3:Nginx 反向代理 + 多服务网络架构

模拟一个典型的微服务网络架构:Nginx → App → Database(三层网络隔离)。

bash
# ===== Step 1: 创建两个网络 =====
docker network create frontend-net    # 前端网络
docker network create backend-net     # 后端网络

# ===== Step 2: 启动 Database(仅在 backend-net) =====
docker run -d \
  --name db \
  --network backend-net \
  -e POSTGRES_PASSWORD=pg123 \
  -e POSTGRES_DB=appdb \
  -v pg-data:/var/lib/postgresql/data \
  postgres:16-alpine

# ===== Step 3: 创建一个简单的 Web API =====
mkdir -p /tmp/web-api && cat > /tmp/web-api/app.py << 'PYEOF'
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import socket
import os

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/health':
            response = {"status": "healthy"}
        elif self.path == '/api/info':
            response = {
                "service": "web-api",
                "hostname": socket.gethostname(),
                "db_host": os.getenv("DB_HOST", "unknown"),
                "message": "Hello from the API service!"
            }
        else:
            response = {"path": self.path, "service": "web-api"}

        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        self.wfile.write(json.dumps(response, indent=2).encode())

    def log_message(self, format, *args):
        print(f"[API] {args[0]}")

server = HTTPServer(('0.0.0.0', 8000), Handler)
print(f"API server running on port 8000")
server.serve_forever()
PYEOF

cat > /tmp/web-api/Dockerfile << 'EOF'
FROM python:3.12-slim
WORKDIR /app
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]
EOF

docker build -t web-api /tmp/web-api

# ===== Step 4: 启动 App(同时连接两个网络) =====
docker run -d \
  --name api \
  --network backend-net \
  -e DB_HOST=db \
  web-api

# 将 api 也连接到 frontend-net
docker network connect frontend-net api

# ===== Step 5: 创建 Nginx 反向代理配置 =====
mkdir -p /tmp/nginx-proxy
cat > /tmp/nginx-proxy/default.conf << 'EOF'
upstream api_backend {
    server api:8000;
}

server {
    listen 80;

    location / {
        return 200 '{"message": "Welcome! Use /api/* for API endpoints"}\n';
        add_header Content-Type application/json;
    }

    location /api/ {
        proxy_pass http://api_backend/api/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /health {
        proxy_pass http://api_backend/health;
    }
}
EOF

# ===== Step 6: 启动 Nginx(仅在 frontend-net) =====
docker run -d \
  --name nginx \
  --network frontend-net \
  -v /tmp/nginx-proxy/default.conf:/etc/nginx/conf.d/default.conf:ro \
  -p 80:80 \
  nginx:1.25-alpine

# ===== Step 7: 测试 =====

# 通过 Nginx 访问 API
curl http://localhost/api/info
# {
#   "service": "web-api",
#   "hostname": "xxxx",
#   "db_host": "db",
#   "message": "Hello from the API service!"
# }

curl http://localhost/health
# {"status": "healthy"}

# ===== Step 8: 验证网络隔离 =====

# Nginx 可以访问 API ✅
docker exec nginx ping -c 1 api
# 64 bytes from 172.18.0.3: ...

# Nginx 不能直接访问 DB ❌(不在同一网络)
docker exec nginx ping -c 1 -W 1 db
# ping: bad address 'db'  ← 完全隔离

# API 可以访问 DB ✅(在同一 backend-net)
docker exec api ping -c 1 db
# 64 bytes from 172.19.0.2: ...

echo "
┌─────────────────────────────────────────────────┐
│                 网络架构验证                      │
│                                                 │
│   Internet                                      │
│      │                                          │
│      ▼                                          │
│   ┌──────────────────────────────┐              │
│   │  frontend-net                │              │
│   │  ┌───────┐    ┌─────┐       │              │
│   │  │ Nginx │───▶│ API │       │              │
│   │  │ :80   │    │:8000│       │              │
│   │  └───────┘    └──┬──┘       │              │
│   └──────────────────┼──────────┘              │
│                      │                          │
│   ┌──────────────────┼──────────┐              │
│   │  backend-net     │          │              │
│   │              ┌───▼───┐      │              │
│   │              │  DB   │      │              │
│   │              │ :5432 │      │              │
│   │              └───────┘      │              │
│   └─────────────────────────────┘              │
│                                                 │
│   Nginx → DB: ❌ 隔离(安全)                    │
│   API → DB:   ✅ 可达                           │
│   Nginx → API: ✅ 可达                          │
└─────────────────────────────────────────────────┘
"

# ===== 清理 =====
docker rm -f nginx api db
docker network rm frontend-net backend-net
docker volume rm pg-data
rm -rf /tmp/web-api /tmp/nginx-proxy

Demo 4:Bind Mount 开发热重载

bash
# ===== 创建一个简单的 Node.js 项目 =====
mkdir -p /tmp/hot-reload-demo/src

cat > /tmp/hot-reload-demo/package.json << 'EOF'
{
  "name": "hot-reload-demo",
  "version": "1.0.0",
  "scripts": {
    "dev": "node --watch src/index.js"
  }
}
EOF

cat > /tmp/hot-reload-demo/src/index.js << 'EOF'
const http = require('http');
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({
    message: 'Hello! Try editing this file...',
    time: new Date().toISOString()
  }));
});
server.listen(3000, '0.0.0.0', () => {
  console.log('Server running at http://localhost:3000');
});
EOF

# ===== 用 Bind Mount 启动容器 =====
docker run -d \
  --name dev-server \
  -v /tmp/hot-reload-demo/src:/app/src \
  -v /tmp/hot-reload-demo/package.json:/app/package.json \
  -w /app \
  -p 3000:3000 \
  node:20-alpine \
  npm run dev

# 测试初始响应
sleep 2
curl http://localhost:3000
# {"message":"Hello! Try editing this file...","time":"..."}

# ===== 在宿主机上修改文件(不重启容器!) =====
cat > /tmp/hot-reload-demo/src/index.js << 'EOF'
const http = require('http');
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({
    message: '🎉 File updated! Hot reload works!',
    version: '2.0',
    time: new Date().toISOString()
  }));
});
server.listen(3000, '0.0.0.0', () => {
  console.log('Server v2 running at http://localhost:3000');
});
EOF

# 等待 node --watch 检测到变化并重启
sleep 2

# 测试更新后的响应
curl http://localhost:3000
# {"message":"🎉 File updated! Hot reload works!","version":"2.0","time":"..."}
# → 无需重建镜像、无需重启容器 ✅

# 查看日志
docker logs dev-server
# Server running at http://localhost:3000
# Restarting 'src/index.js'      ← 自动检测到文件变化
# Server v2 running at http://localhost:3000

# ===== 清理 =====
docker rm -f dev-server
rm -rf /tmp/hot-reload-demo

Demo 5:Volume 备份与恢复完整演示

bash
# ===== Step 1: 创建一个包含数据的 Volume =====
docker volume create important-data

# 写入一些测试数据
docker run --rm -v important-data:/data alpine sh -c '
  mkdir -p /data/docs /data/config
  echo "Important document content" > /data/docs/report.txt
  echo "Config settings here" > /data/config/app.conf
  echo "Database dump simulation" > /data/backup.sql
  echo "=== Files in volume ==="
  find /data -type f -exec echo "  {}" \;
'

# ===== Step 2: 备份 Volume =====
mkdir -p /tmp/vol-backup

docker run --rm \
  -v important-data:/source:ro \
  -v /tmp/vol-backup:/backup \
  alpine \
  tar czf /backup/important-data-backup.tar.gz -C /source .

ls -lh /tmp/vol-backup/
# -rw-r--r-- 1 root root  350 ... important-data-backup.tar.gz

# ===== Step 3: 模拟数据丢失 =====
docker volume rm important-data
echo "Volume 已删除!"

docker volume ls | grep important-data
# (无输出 — 真的没了)

# ===== Step 4: 恢复 Volume =====
docker volume create important-data

docker run --rm \
  -v important-data:/target \
  -v /tmp/vol-backup:/backup:ro \
  alpine \
  tar xzf /backup/important-data-backup.tar.gz -C /target

# ===== Step 5: 验证恢复结果 =====
docker run --rm -v important-data:/data alpine sh -c '
  echo "=== Restored files ==="
  find /data -type f -exec echo "  {}" \;
  echo ""
  echo "=== File contents ==="
  cat /data/docs/report.txt
  cat /data/config/app.conf
'
# 输出:
# === Restored files ===
#   /data/docs/report.txt
#   /data/config/app.conf
#   /data/backup.sql
# === File contents ===
# Important document content
# Config settings here
# → 数据完整恢复 ✅

# ===== 清理 =====
docker volume rm important-data
rm -rf /tmp/vol-backup

Demo 6:Host 网络 vs Bridge 网络性能对比

bash
# ===== 使用简单的 HTTP 服务器对比延迟 =====

# 方式一:Bridge 网络(默认,有 NAT 转发)
docker run -d --name bench-bridge -p 8081:80 nginx:alpine

# 方式二:Host 网络(无 NAT)
docker run -d --name bench-host --network host nginx:alpine
# Nginx 直接监听宿主机 80 端口

# 安装测试工具
# apt-get install -y apache2-utils  # 或使用 docker 运行

# Bridge 网络测试
docker run --rm httpd:alpine ab -n 10000 -c 100 http://host.docker.internal:8081/
# 或直接在宿主机执行:
# ab -n 10000 -c 100 http://localhost:8081/

# Host 网络测试
# ab -n 10000 -c 100 http://localhost:80/

# 对比指标:
# - Requests per second(每秒请求数)
# - Time per request(平均请求耗时)
# - Transfer rate(传输速率)
# 通常 Host 网络会比 Bridge 快 5-15%

# ===== 清理 =====
docker rm -f bench-bridge bench-host

3.4 常见问题 QA

Q1: Volume 挂载后容器内看到的是空目录

场景:使用 Bind Mount 挂载宿主机目录后,容器内原有文件消失了。

bash
# 复现:
docker run --rm -v /tmp/my-empty-dir:/usr/share/nginx/html nginx ls /usr/share/nginx/html
# (空) — 原来的 index.html 不见了!

# 原因:
# Bind Mount 会"遮盖"容器内的原有文件(类似 Linux mount 的行为)
# 宿主机目录是空的 → 容器看到的就是空的

# 解决方案:
# 方案一:使用 Volume 而不是 Bind Mount(Volume 首次挂载会自动复制容器内文件)
docker volume create nginx-html
docker run --rm -v nginx-html:/usr/share/nginx/html nginx ls /usr/share/nginx/html
# 50x.html  index.html  ← 文件还在 ✅

# 方案二:先把文件复制出来,再挂载
docker create --name tmp-nginx nginx
docker cp tmp-nginx:/usr/share/nginx/html/ /tmp/my-html/
docker rm tmp-nginx
docker run -v /tmp/my-html:/usr/share/nginx/html nginx

Q2: Volume 中的文件权限问题 — Permission denied

bash
# 场景:容器以非 root 用户运行时,无法写入 Volume

# 复现:
docker volume create test-vol
docker run --rm -u 1000:1000 -v test-vol:/data alpine touch /data/test.txt
# touch: /data/test.txt: Permission denied

# 原因:Volume 默认创建为 root:root 权限

# 解决方案一:在 Dockerfile 中预设权限
# Dockerfile:
# RUN mkdir /data && chown 1000:1000 /data
# VOLUME /data
# USER 1000

# 解决方案二:使用初始化容器设置权限
docker run --rm -v test-vol:/data alpine chown 1000:1000 /data
docker run --rm -u 1000:1000 -v test-vol:/data alpine touch /data/test.txt
# ✅ 成功

# 解决方案三:在 entrypoint 中修复权限
# entrypoint.sh:
# #!/bin/bash
# chown -R appuser:appuser /data
# exec gosu appuser "$@"

docker volume rm test-vol

Q3: 容器之间 ping 不通

bash
# ===== 排查步骤 =====

# 1. 确认两个容器在同一网络
docker inspect --format '{{json .NetworkSettings.Networks}}' container1 | jq
docker inspect --format '{{json .NetworkSettings.Networks}}' container2 | jq
# 检查 NetworkID 是否相同

# 2. 如果在默认 bridge 网络 → 不支持 DNS,只能用 IP
docker inspect --format '{{.NetworkSettings.IPAddress}}' container1
# 用 IP 去 ping

# 3. 解决:使用自定义网络
docker network create my-net
docker network connect my-net container1
docker network connect my-net container2
docker exec container1 ping container2   # ✅

# 4. 如果容器内没有 ping 命令(精简镜像)
docker exec container1 wget -qO- http://container2:80/
# 或
docker exec container1 nc -zv container2 80
# 或
docker exec container1 python -c "import socket; print(socket.getaddrinfo('container2', 80))"

Q4: 端口映射不生效 — 外部无法访问

bash
# ===== 排查清单 =====

# 1. 确认容器正在运行
docker ps | grep my-container

# 2. 查看端口映射
docker port my-container
# 如果输出为空,说明没有端口映射

# 3. 确认应用监听的是 0.0.0.0 而不是 127.0.0.1
docker exec my-container ss -tlnp
# 或
docker exec my-container netstat -tlnp

# 常见错误:
# 应用只监听 127.0.0.1:3000 → 外部无法通过端口映射访问
# 修复:让应用监听 0.0.0.0:3000
#   - Python Flask: app.run(host='0.0.0.0')
#   - Node.js:      server.listen(3000, '0.0.0.0')
#   - Go:           http.ListenAndServe(":3000", nil)

# 4. 检查防火墙
sudo iptables -L -n | grep 8080
sudo ufw status   # 或 firewalld

# 5. 确认没有其他进程占用端口
sudo lsof -i :8080

Q5: -v 使用相对路径导致意外行为

bash
# ⚠️ -v 对 Volume 和 Bind Mount 的判断依据是路径格式

# 以 / 或 ./ 开头 → Bind Mount
docker run -v /home/user/data:/data nginx          # Bind Mount ✅
docker run -v ./data:/data nginx                   # Bind Mount ✅(Docker Compose 支持)

# 不以 / 开头 → Volume(可能不是你想要的!)
docker run -v data:/data nginx                     # Volume(名为 "data" 的 Volume)
docker run -v mydata:/data nginx                   # Volume(名为 "mydata")

# 常见误解:
docker run -v data:/data nginx
# 很多人以为这会挂载当前目录下的 data/ 文件夹
# 实际上它创建/使用了一个名为 "data" 的 Docker Volume!

# 建议:
# - Bind Mount 始终使用绝对路径或 $(pwd)
docker run -v $(pwd)/data:/data nginx              # ✅ 明确
# - 使用 --mount 更清晰
docker run --mount type=bind,source=$(pwd)/data,target=/data nginx

Q6: Docker 网络中容器的 DNS 解析很慢

bash
# 原因:DNS 查询超时或使用了不合适的 DNS 服务器

# 排查:
docker exec my-container cat /etc/resolv.conf

# 解决方案一:指定高效的 DNS 服务器
docker run --dns 8.8.8.8 --dns 8.8.4.4 my-image

# 解决方案二:在 daemon.json 中全局配置
# /etc/docker/daemon.json
{
  "dns": ["8.8.8.8", "8.8.4.4"]
}

# 解决方案三:减少 DNS 查询超时
# resolv.conf 中的 options ndots 值过大会导致多次无效查询
docker run --dns-opt "ndots:1" --dns-opt "timeout:1" my-image

Q7: 多个容器挂载同一个 Volume,数据会冲突吗?

bash
# 取决于应用类型:

# ✅ 安全场景:一个写、多个读
docker run -d --name writer -v shared:/data my-writer
docker run -d --name reader1 -v shared:/data:ro my-reader
docker run -d --name reader2 -v shared:/data:ro my-reader

# ⚠️ 危险场景:多个容器同时写入同一文件
# 可能导致数据损坏(特别是数据库文件)
# MySQL、PostgreSQL 等数据库绝对不能多个实例共享同一个数据目录!

# ✅ 安全的多写场景:写入不同文件
docker run -d --name app1 -v logs:/logs my-app  # 写 /logs/app1.log
docker run -d --name app2 -v logs:/logs my-app  # 写 /logs/app2.log

Q8: 容器重启后 IP 地址变了,导致通信失败

bash
# 原因:Docker 的 IP 地址是动态分配的,重启后可能会变

# ❌ 错误做法:硬编码容器 IP
# 在 app 配置中写 redis_host = "172.17.0.3"

# ✅ 正确做法一:使用自定义网络 + 容器名(推荐)
docker network create my-net
docker run -d --name redis --network my-net redis
# 在 app 中用 redis_host = "redis"

# ✅ 正确做法二:指定固定 IP(不推荐,维护困难)
docker network create --subnet 172.30.0.0/16 my-net
docker run -d --name redis --network my-net --ip 172.30.0.100 redis

Q9: Overlay 网络是什么?什么时候需要?

bash
# Overlay 网络用于跨主机的容器通信(Docker Swarm 或 Kubernetes)
# 如果你只在单机上运行容器,不需要 Overlay

# 单机场景(本阶段):使用 bridge 网络即可
# 多机场景:
#   - Docker Swarm → 使用 overlay 网络
#   - Kubernetes → 使用 CNI 插件(如 Calico、Flannel)

# Overlay 网络创建示例(需要先初始化 Swarm):
# docker swarm init
# docker network create --driver overlay my-overlay-net

Q10: 如何查看容器之间的网络连通性?

bash
# ===== 完整的网络诊断工具箱 =====

# 1. 查看容器网络配置
docker inspect --format '
  Name: {{.Name}}
  Networks: {{range $net, $config := .NetworkSettings.Networks}}
    {{$net}}: {{$config.IPAddress}} (Gateway: {{$config.Gateway}})
  {{end}}' my-container

# 2. 查看网络中的所有容器
docker network inspect my-network --format '{{range .Containers}}{{.Name}} → {{.IPv4Address}}{{"\n"}}{{end}}'

# 3. 使用 nicolaka/netshoot 网络调试容器(瑞士军刀)
docker run -it --rm --network my-network nicolaka/netshoot

# 在 netshoot 容器内可以使用:
# ping, curl, wget, nslookup, dig, traceroute, mtr,
# tcpdump, iperf, netstat, ss, ip, nc, nmap...

# 4. 抓包分析
docker run -it --rm --network my-network nicolaka/netshoot \
  tcpdump -i eth0 -n port 80

# 5. 快速检测端口连通性
docker run --rm --network my-network alpine \
  sh -c 'nc -zv redis 6379 && echo "✅ Redis OK" || echo "❌ Redis FAIL"'

3.5 网络与存储命令速查表

┌──────────────────────────────────────────────────────────────────────┐
│                    Volume 命令速查                                   │
├─────────────────────┬────────────────────────────────────────────────┤
│ docker volume create│ 创建卷                                        │
│ docker volume ls    │ 列出所有卷                                     │
│ docker volume inspect│查看卷详情                                     │
│ docker volume rm    │ 删除卷                                        │
│ docker volume prune │ 清理未使用的卷                                  │
├─────────────────────┼────────────────────────────────────────────────┤
│                     │                                                │
│  挂载方式            │  -v vol:/path          命名卷                  │
│                     │  -v /host:/path        Bind Mount             │
│                     │  -v /path              匿名卷                  │
│                     │  --mount type=...      显式挂载(推荐)         │
│                     │  --tmpfs /path         内存挂载                 │
│                     │  :ro                   只读                    │
│                     │                                                │
└─────────────────────┴────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────┐
│                    Network 命令速查                                  │
├─────────────────────┬────────────────────────────────────────────────┤
│ docker network create│创建网络                                       │
│ docker network ls    │列出所有网络                                    │
│ docker network inspect│查看网络详情                                   │
│ docker network rm    │删除网络                                       │
│ docker network prune │清理未使用的网络                                 │
│ docker network connect│将容器连接到网络                               │
│ docker network disconnect│将容器从网络断开                            │
├─────────────────────┼────────────────────────────────────────────────┤
│                     │                                                │
│  运行时指定          │  --network <name>      指定网络                 │
│                     │  --network host        使用宿主机网络           │
│                     │  --network none        禁用网络                 │
│                     │  --network-alias       设置网络别名             │
│                     │  --dns 8.8.8.8         指定 DNS                │
│                     │  -p 8080:80            端口映射                 │
│                     │  -P                    自动映射 EXPOSE 端口     │
│                     │                                                │
└─────────────────────┴────────────────────────────────────────────────┘

📝 学习建议:本阶段有两个核心要点——Volume 持久化自定义网络通信。建议优先完成 Demo 1(MySQL 数据持久化)和 Demo 2(App + Redis 网络通信),这两个是后续 Docker Compose 阶段的基础。Demo 3 的三层网络隔离架构是生产环境的常见模式,理解它对后续微服务部署非常有帮助。