主题
第三阶段:数据管理与网络
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 \
nginxBind 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 (自动复制过来)| 对比 | Volume | Bind 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.03.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-app3.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>&13.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-replica23.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-hostHost 网络适用场景:
- 需要极致网络性能(避免 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 docker3.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-net3.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-backend3.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-dataDemo 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-appDemo 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-proxyDemo 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-demoDemo 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-backupDemo 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-host3.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 nginxQ2: 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-volQ3: 容器之间 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 :8080Q5: -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 nginxQ6: 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-imageQ7: 多个容器挂载同一个 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.logQ8: 容器重启后 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 redisQ9: 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-netQ10: 如何查看容器之间的网络连通性?
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 的三层网络隔离架构是生产环境的常见模式,理解它对后续微服务部署非常有帮助。