Featured image of post Docker 进阶与运维:网络模式、Volume 与镜像加速实战

Docker 进阶与运维:网络模式、Volume 与镜像加速实战

2019 年中视角:深入理解 Docker 四大网络模式、自定义网桥、存储卷生命周期,以及 Docker Hub / ghcr / k8s.gcr.io / Quay 镜像代理

装好 Docker 引擎只是入门。真正决定一台主机能不能稳定跑生产容器的,是网络、存储和镜像拉取这三件事。这一篇围绕"网络模式 + 存储卷 + 镜像代理 + 常见排障"四个高频主题,把零散的运维笔记整理成可复用的参考。

阅读对象:已经能跑 docker run 的开发者、想把 Docker 用于生产环境或本地自建集群的运维同学 覆盖范围:bridge / host / none / container / 自定义网络 + Volume 生命周期 + Docker Hub / ghcr / GCR / k8s / Quay 代理 + 故障排查

一、为什么需要关注网络与存储

跑一个 hello-world 很简单,但一旦要把 Docker 真正"用起来",就会撞到这些问题:

  • 容器之间怎么互访?同一个 Pod 里的两个 sidecar 容器能直接 localhost 通信吗?
  • 数据库的数据在容器里,删容器数据就没了——怎么持久化?
  • 怎么让外部用户访问容器内的服务?
  • 不同机器上的容器怎么互联?
  • 国内服务器拉 Docker Hub 镜像慢到秒级超时,K8s 节点拉 CoreDNS 镜像也卡怎么办?
  • 私有仓库(ghcr.io / gcr.io / quay.io)在国内怎么拉?

这些问题的答案,藏在网络模式、Volume、镜像代理三个核心机制里。

When to use

  • 单机多容器互访 → bridge / 自定义网络
  • 容器需要"绑定宿主机端口"的高性能场景 → host 网络
  • 强隔离、不能联网的安全沙箱 → none 网络
  • 数据库、需要长期保存的日志 → Volume(命名卷)/ bind mount
  • 跨主机容器互联(Swarm / k8s)→ overlay 网络(本文不展开)
  • 国内服务器拉镜像 → 镜像代理(dockerproxy / ghcr.nju.edu.cn 等)

二、容器网络基础:docker0 网桥与 veth pair

Docker 启动时会在宿主机上创建一个叫 docker0虚拟网桥(默认子网 172.17.0.0/16)。所有以 bridge 模式启动的容器都会接到这张网桥上,通过这张网桥 + iptables NAT 表与宿主机通信

可以用 ifconfig(或 ip a)看到 docker0

1
2
3
4
5
$ ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:xx:xx:xx:xx
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          ...

核心机制(veth pair)

  • 容器内看到的 eth0 并不是真正的网卡,而是一对 veth pair 设备的一端
  • 另一端放在宿主机,挂到 docker0 网桥上
  • 数据从一端进,另一端出,等同于一根虚拟网线

可以用 brctl show(来自 bridge-utils 包)查看网桥上挂了哪些 veth:

1
2
3
$ brctl show docker0
bridge name    bridge id        STP enabled    interfaces
docker0        8000.0242xxxxxx  no             vethxxxxxx

重要结论docker0 网桥对外是不可见的,外部网络无法通过 Container-IP 直接访问容器。要让外部访问,必须做端口映射-p 参数)。

三、四种网络模式详解

Docker 在安装时会自动创建三个网络:bridge(默认)、nonehost

1
2
3
4
5
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
xxxxx          bridge    bridge    local
xxxxx          host      host      local
xxxxx          none      null      local

创建容器时通过 --net / --network 指定:

模式指定方式核心特点典型场景
bridge--net=bridge(默认)独立 netns,分配 IP,通过 docker0 + NAT 通信90% 场景
host--net=host共享宿主机 netns,无独立 IP高性能网络服务、监控 agent
none--net=none仅有 lo 回环,无任何网络强隔离、离线批处理
container--net=container:NAME共享另一个容器的 netnssidecar 模式紧密协作

3.1 bridge 模式(默认)

1
2
3
4
5
# 随机端口映射(大写 P)
docker run -itd --name test1 -P nginx

# 指定端口映射(小写 p)
docker run -itd --name test2 -p 44541:80 nginx

访问路径

1
外部网络 → 宿主机 IP:宿主机端口 → iptables DNAT → 容器 IP:容器端口

可以用 iptables -t nat -vnL 看到 Docker 自动添加的 DNAT 规则:

1
2
3
4
$ iptables -t nat -vnL
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source     destination
    0     0 DNAT       tcp  --  !docker0 *  0.0.0.0/0    0.0.0.0/0     tcp dpt:44541 to:172.17.0.2:80

等价类比:bridge 模式 ≈ VMware 里的 NAT 模式——容器有独立 IP、能上网,但需要端口映射才能被外部访问。

3.2 host 模式

容器不创建自己的网卡,直接使用宿主机的 IP 和端口。

1
docker run -itd --name test-host --net=host nginx

优势

  • 没有 NAT 转换,性能最佳
  • 适合需要"处理大量端口"的服务(如 nginx 反代、Kafka、Prometheus exporter)

代价

  • 容器与宿主机共用网络栈,端口冲突会直接报错
  • -p / --publish 参数被忽略,会出现 WARNING: Published ports are discarded when using host network mode

注意:host 网络驱动只支持 Linux 宿主机。Docker Desktop for Mac / Windows / Windows Server 上的 Docker 不支持 host 模式。

在 Swarm 集群里也可以使用 host 网络:

1
docker service create --network host nginx

控制流量(swarm manager 相关)仍走 overlay 网络,但 swarm 服务容器本身使用 Docker 守护进程的主机网络和端口发送数据——这意味着如果一个服务容器绑定到 80 端口,那么在给定的集群节点上只能运行一个该服务容器。

3.3 none 模式

容器有独立的 Network Namespace,但 Docker 不会给它配置任何网络

  • 没有 eth0、没有 IP、没有路由
  • 只有 lo 回环网卡
  • 完全不能上网

典型场景:安全沙箱离线批处理任务(如敏感数据脱敏、纯计算作业)。

3.4 container 模式

新创建的容器共享指定容器的 netns,而不是和宿主机共享。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 查看已有容器的 PID
$ docker inspect -f '{{.State.Pid}}' 5ace523962fc
14685

# 验证 net namespace
$ ls -l /proc/14685/ns
... net -> net:[4026532649]   # 就是这个 net namespace ID

# 创建新容器并共享
$ docker run -itd --name test3 --net=container:5ace523962fc centos bash

# 验证新容器的 net namespace 一致
$ docker inspect -f '{{.State.Pid}}' 2b7af7c8842d
15832
$ ls -l /proc/15832/ns
... net -> net:[4026532649]   # 跟前面一样

特性

  • 两个容器共享 IP、端口范围
  • 网络之外(文件系统、进程、用户)仍然隔离
  • 两个容器的进程可以通过 lo 通信

典型场景:sidecar 模式——主容器和 sidecar 容器(如日志收集器、监控 agent、Service Mesh proxy)共用 netns,方便在 localhost 上互调。

四、自定义网络:指定 IP / 子网

直接用默认 bridge 模式,不能给容器指定固定 IP

1
2
3
# 报错:只能在自定义网络指定 IP
$ docker run -itd --name test4 --network bridge --ip 172.17.0.5 centos:7 /bin/bash
Error response from daemon: User specified IP address is supported only when connecting to networks with user configured subnets.

正确做法:先自定义网络,再指定 IP 启动。

1
2
3
4
5
6
7
8
# 1. 创建自定义网络(指定子网 + 网卡名)
docker network create \
  --subnet=172.18.0.0/16 \
  --opt "com.docker.network.bridge.name"="docker1" \
  mynetwork

# 2. 用指定 IP 启动容器
docker run -itd --name test5 --net mynetwork --ip 172.18.0.10 centos /bin/bash

参数说明

  • --subnet:定义子网范围
  • --opt "com.docker.network.bridge.name"="docker1":让 ifconfig -a 看到的是 docker1,而不是 br-110eb56a0b22 这种随机名
  • mynetwork:网络名,docker network ls 里的 NAME

Why 用自定义网络

  • 容器 DNS 自动解析(容器名 → IP),默认 bridge 不支持
  • 可以在创建时规划 IP 段
  • 适合需要稳定网络拓扑的复杂部署

配套的清理命令:当自定义网络不再需要时:

1
2
3
4
5
6
# 临时把容器从指定网络断开(排查网络问题很有用)
docker network disconnect -f bridge <container_name>

# 禁用并删除 docker0 网桥
ifconfig docker0 down
brctl delbr docker0

注意:手工 brctl delbr docker0 之后,下次 daemon 启动(未指定 -b 参数)会自动重建 docker0。要真正控制默认网桥,需要在 daemon.json 里写:

1
2
3
4
{
  "iptables": false,
  "bridge": ""
}

五、存储卷(Volume):让数据活过容器生命周期

容器的文件系统是临时的——docker rm 时容器层全部销毁。任何需要持久化的数据,都必须挂载到 Volume 或 bind mount。

5.1 Volume 操作四件套

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 创建
docker volume create mydata

# 查看
docker volume ls

# 删除单个
docker volume rm mydata

# 清理(删所有未挂载的 volume —— 危险)
docker volume prune

5.2 两种挂载方式

1
2
3
4
5
# 1. 命名卷(推荐,Docker 自动管理)
docker run -d -v mydata:/var/lib/mysql mysql:5.7

# 2. bind mount(直接挂主机目录,运维最常用)
docker run -d -v /host/path:/container/path nginx
维度VolumeBind Mount
路径Docker 管理(默认 /var/lib/docker/volumes/任意宿主机路径
迁移docker volume 命令一键迁移手动 rsync
权限Docker 自动设手动 chmod
适用数据库、共享数据配置文件、日志、特殊路径

5.3 关键避坑

  1. docker volume prune 会删所有未挂载的卷——生产环境跑这个命令之前必须 docker volume ls 确认
  2. bind mount 时容器内 UID 与宿主机不一致会导致权限问题——用 user: "1000:1000" 显式指定
  3. Volume 在多容器间共享非常合适(如配置中心、日志聚合),但不能用 tmpfs 替代——重启数据就没了

5.4 配合清理

1
2
3
4
5
# 只删除未使用的网络
docker network prune -f

# 删除 12 小时前创建的网络
docker network prune -a --filter "until=12h"

六、镜像代理与加速

国内服务器拉 Docker Hub 镜像默认会非常慢甚至超时。除了 daemon.json 里配 registry-mirrors,还可以直接用镜像代理拉特定 registry 的镜像。

6.1 Docker Hub 镜像代理

场景官方命令代理命令
用户镜像docker pull stilleshan/frpc:latestdocker pull dockerproxy.com/stilleshan/frpc:latest
根镜像(library)docker pull nginx:latestdocker pull dockerproxy.net/library/nginx:latest

dockerproxy.comdockerproxy.net 是两个常用的镜像代理。根镜像(library/xxx)必须显式写 library/——这是和用户镜像最大的区别。

6.2 GitHub Container Registry (ghcr.io)

2019 年 9 月 GitHub 即将开放公开测试的 ghcr.io,配合 GitHub Actions 的 docker push 流程,越来越多的开源项目(包括很多 K8s 生态组件)开始把镜像推到 ghcr 而非 Docker Hub。

1
2
3
4
5
# 官方
docker pull ghcr.io/username/image:tag

# 代理
docker pull ghcr.dockerproxy.com/username/image:tag

南京大学的 ghcr 加速ghcr.nju.edu.cn 是高校里对 ghcr 比较稳定的镜像(南大信管系维护)。速度通常比走 dockerproxy 更快,适合学术 / 教学环境。

6.3 Google Container Registry (gcr.io) 与 k8s.gcr.io

K8s 自身组件(corednskube-proxyetcd 等)都托管在 k8s.gcr.io(2018 年起)或新地址 registry.k8s.io(2019 年开始迁移)。国内拉这些镜像几乎必走代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# GCR 官方
docker pull gcr.io/username/image:tag

# GCR 代理
docker pull gcr.dockerproxy.com/username/image:tag

# K8s 镜像(CNCF 仓库,从 k8s 1.11 开始 CoreDNS 取代 kube-dns 成为默认 DNS)
docker pull k8s.gcr.io/coredns:1.6.5
docker pull registry.k8s.io/coredns:1.6.5

# 代理
docker pull k8s.dockerproxy.com/coredns:1.6.5

CoreDNS 1.6.5 是 2019 年中比较常用的稳定版本——CNI 插件、KubeDNS 替换等场景都会用到。

6.4 Quay.io

Red Hat 系镜像(coreos/etcd 旧版本、Operator Framework 等)大多托管在 Quay:

1
2
3
4
5
# 官方
docker pull quay.io/username/image:tag

# 代理
docker pull quay.dockerproxy.com/username/image:tag

6.5 加速效果对比

1
2
3
# 测速(删本地缓存后用 time 计时)
docker rmi node:latest
time docker pull node:latest
  • 直连 Docker Hub:~1m14s(国内典型)
  • 用镜像代理:~5-15s(视镜像大小)

注意:公开镜像代理可能因合规或运营原因失效。生产环境建议自建 mirror(用 registry 镜像 + 反代),或者用云厂商(阿里云 ACR、DaoCloud)的企业级加速服务。

七、常见问题与排障

7.1 WARNING: IPv4 forwarding is disabled

容器网络不通,dockerd 启动时打这条警告。生产环境装完 Docker 必须先修这个

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 临时启用
sysctl -w net.ipv4.ip_forward=1

# 永久(任选其一)
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
# 或
echo "net.ipv4.ip_forward=1" >> /usr/lib/sysctl.d/00-system.conf

# 生效
sysctl -p

# 验证
sysctl net.ipv4.ip_forward
# 期望输出:net.ipv4.ip_forward = 1

7.2 unknown or invalid runtime name: docker-runc

从老版本(Docker 18.06 之前)升级到 Docker 18.09+ 后,runtime 名从 docker-runc 改为 runc,老容器的 hostconfig.json 还引用老名字,导致容器起不来。

1
2
3
4
5
# 批量替换
grep -rl 'docker-runc' /var/lib/docker/containers/ | xargs sed -i 's/docker-runc/runc/g'

systemctl stop docker
systemctl start docker

7.3 live-restore:让容器在 daemon 重启时不停机

升级 Docker daemon 或 daemon 崩溃时,默认会停掉所有正在运行的容器。开启 live-restore 可让容器继续运行:

1
2
3
{
  "live-restore": true
}
1
2
3
systemctl daemon-reload
sudo systemctl reload docker.service    # 注意是 reload 不是 restart
docker info | grep -i live              # 验证:Live Restore Enabled: true

之后 systemctl restart docker 时,容器不会停。

多机同步:在 K8s 集群里,可以用脚本批量同步 daemon.json

1
2
3
4
5
for NODE in master2 master3 worker1 worker3 worker4 worker5 worker6 worker7 worker8 worker9; do
  echo $NODE
  scp /etc/docker/daemon.json $NODE:/etc/docker/
  ssh root@$NODE "systemctl daemon-reload && systemctl reload docker && systemctl restart docker"
done

7.4 network disconnect:容器临时断网

1
2
# 临时把容器从指定网络断开
docker network disconnect -f bridge <container_name>

排查网络问题时很有用——能快速判断"是容器问题还是网络问题"。

八、核心要点 / 常见坑

把这一篇的要点压缩成 8 条:

  1. bridge 模式是默认(90% 场景),host 模式换性能换隔离,none 模式是沙箱
  2. 端口映射必须用 -p——bridge 模式下不映射外部访问不了
  3. 容器共享 netnscontainer 模式,sidecar 利器
  4. 指定固定 IP必须用自定义网络,默认 bridge 不支持
  5. 数据持久化用 Volume,docker volume prune 是危险命令
  6. bind mount vs Volume:配置/日志用 bind mount,数据库用 Volume
  7. 国内拉镜像必须用代理或 mirror——dockerproxy 系列、ghcr.nju.edu.cn、阿里云、DaoCloud 都要会
  8. live-restore 是 daemon 升级不停机的关键配置

九、小结

  • 网络模式:4 种核心模式(bridge / host / none / container)+ 自定义网络,理解 veth pair 是关键
  • 存储卷:Volume 管数据生命周期,bind mount 管配置文件,两手都要硬
  • 镜像代理dockerproxy 系列覆盖 Docker Hub / GCR / k8s.gcr.io / Quay,ghcr.nju.edu.cn 补足 ghcr
  • 排障思路:先看 docker info 状态、再看 journalctl -u docker 日志、最后看 iptables 和 netns

下一步:单机 Docker 玩熟之后,下一步就是 Swarm / Kubernetes 的多机编排——那时网络(overlay / flannel / calico)、存储(Rexray / CSI)、调度(scheduler)都会变成新的核心议题。在 2019 年的 K8s 生态里,CoreDNS 已经取代 kube-dns 成为默认 DNSghcr.io 即将开放公开测试,镜像分发的格局正在重写——提前理解这些变化能少踩很多坑。

参考资料

使用 Hugo 构建
主题 StackJimmy 设计