Docker网络模式详解
一、概述
1.1 背景介绍
容器网络是Docker使用中最容易出问题的部分。容器之间怎么通信、容器怎么访问外网、外部怎么访问容器内的服务——这三个问题搞不清楚,排查网络故障就是抓瞎。
Docker网络基于Linux内核的Network Namespace、veth pair、iptables和bridge实现。每个容器有自己独立的网络命名空间,通过veth pair连接到宿主机的网桥(docker0),再通过iptables NAT规则访问外网。理解这个数据包流向,网络问题排查就有了方向。
Docker提供了bridge、host、none、container、overlay、macvlan六种网络模式,每种模式的隔离级别、性能、适用场景都不同。单机环境用bridge和host就够了,跨主机通信需要overlay或macvlan。
1.2 技术特点
bridge模式:默认模式,通过docker0网桥和veth pair实现容器间通信,通过iptables NAT实现外网访问。隔离性好,有约5%-10%的网络性能损耗
host模式:容器直接使用宿主机网络栈,没有NAT开销,网络性能和宿主机一致。但失去了网络隔离性
none模式:容器没有网络接口(只有lo),完全隔离。适合不需要网络的批处理任务或安全敏感场景
container模式:多个容器共享同一个网络命名空间,通过localhost通信。Kubernetes的Pod网络就是基于这个原理
overlay模式:基于VXLAN的跨主机容器网络,Docker Swarm和部分K8s网络插件使用
macvlan模式:容器直接获得物理网络的IP地址,像一台独立的物理机。适合需要直接接入物理网络的场景
1.3 适用场景
bridge模式:大多数单机容器部署场景,开发测试环境
host模式:对网络性能要求高的应用(Nginx反向代理、高频交易系统),需要监听大量端口的应用
none模式:安全隔离要求高的计算任务,不需要网络的数据处理容器
container模式:Sidecar模式(日志收集、监控代理),需要共享网络的紧耦合容器
overlay模式:Docker Swarm集群中的跨主机服务通信
macvlan模式:需要容器拥有独立MAC地址和IP的场景,传统网络架构迁移
1.4 环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| Docker Engine | 20.10+(推荐24.0+) | 基本网络功能所有版本都支持 |
| Linux内核 | 3.10+(推荐5.4+) | overlay需要4.0+内核的VXLAN支持 |
| iptables | 1.4+ | bridge模式的NAT依赖iptables |
| bridge-utils | 任意版本 | 调试用,brctl命令查看网桥信息 |
| iproute2 | 任意版本 | 调试用,ip命令查看网络配置 |
| tcpdump | 任意版本 | 抓包分析用 |
二、详细步骤
2.1 准备工作
2.1.1 系统检查
# 检查内核网络模块 lsmod | grep -E"bridge|vxlan|macvlan|overlay" # 检查IP转发是否开启 sysctl net.ipv4.ip_forward # 必须为1 # 检查iptables iptables -L -n iptables -t nat -L -n # 检查docker0网桥 ip addr show docker0 brctl show docker0 # 安装网络调试工具 sudo apt install -y bridge-utils tcpdump iproute2 net-tools # Debian/Ubuntu sudo yum install -y bridge-utils tcpdump iproute net-tools # CentOS/RHEL
2.1.2 查看当前Docker网络
# 查看所有Docker网络
docker network ls
# 默认会有三个网络:
# bridge - 默认bridge网络
# host - host网络
# none - 无网络
# 查看bridge网络详情
docker network inspect bridge
# 查看网络中的容器
docker network inspect bridge --format='{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{"
"}}{{end}}'
2.2 核心配置
2.2.1 Bridge模式详解
Bridge是Docker默认的网络模式。Docker启动时会创建一个docker0虚拟网桥,每个容器通过veth pair连接到docker0,容器之间通过网桥二层转发通信,访问外网通过iptables MASQUERADE做源地址转换。
数据包流向:
容器eth0 → veth pair → docker0网桥 → iptables NAT → 宿主机eth0 → 外网
# 创建自定义bridge网络(推荐,比默认bridge功能更多) docker network create --driver bridge --subnet 172.20.0.0/24 --gateway 172.20.0.1 --opt"com.docker.network.bridge.name"="br-mynet" --opt"com.docker.network.bridge.enable_icc"="true" --opt"com.docker.network.bridge.enable_ip_masquerade"="true" mynet # 查看创建的网桥 brctl show ip addr show br-mynet # 在自定义网络中运行容器 docker run -d --name web1 --network mynet nginx:1.24-alpine docker run -d --name web2 --network mynet nginx:1.24-alpine # 自定义网络支持容器名DNS解析(默认bridge不支持) dockerexecweb1 ping -c 3 web2 # 能ping通,通过Docker内置DNS解析容器名 # 指定容器IP docker run -d --name web3 --network mynet --ip 172.20.0.100 nginx:1.24-alpine # 查看容器网络配置 dockerexecweb1 ip addr dockerexecweb1 ip route dockerexecweb1 cat /etc/resolv.conf
默认bridge和自定义bridge的区别:
| 特性 | 默认bridge | 自定义bridge |
|---|---|---|
| 容器名DNS解析 | 不支持(只能用IP) | 支持(推荐) |
| 容器间隔离 | 同网络内全部互通 | 同网络内互通,不同网络隔离 |
| 热插拔 | 不支持 | 支持(docker network connect/disconnect) |
| 自定义子网 | 不方便 | 创建时指定 |
注意:生产环境不要用默认bridge网络,用自定义bridge。默认bridge不支持容器名DNS解析,容器重启后IP可能变化,用IP通信会断。
2.2.2 Host模式详解
Host模式下容器直接使用宿主机的网络命名空间,没有网络隔离,也没有NAT转换开销。容器内看到的网络接口和宿主机完全一样。
# 使用host网络运行容器 docker run -d --name nginx-host --network host nginx:1.24-alpine # 不需要-p端口映射,Nginx直接监听宿主机的80端口 curl http://localhost:80 # 查看容器网络(和宿主机一样) dockerexecnginx-host ip addr # 输出和宿主机的 ip addr 完全一致 # 查看端口占用 ss -tlnp | grep 80 # 能看到nginx进程直接监听在宿主机上
Host模式的性能对比:
# 用iperf3测试bridge和host模式的网络吞吐量差异 # 启动iperf3服务端(bridge模式) docker run -d --name iperf-bridge -p 5201:5201 networkstatic/iperf3 -s # 启动iperf3服务端(host模式) docker run -d --name iperf-host --network host networkstatic/iperf3 -s # 测试bridge模式吞吐量 docker run --rm networkstatic/iperf3 -c <宿主机IP> -p 5201 # 典型结果:约30-40 Gbps(取决于硬件) # 测试host模式吞吐量 docker run --rm --network host networkstatic/iperf3 -c 127.0.0.1 -p 5201 # 典型结果:约45-50 Gbps # host模式比bridge模式吞吐量高约15%-25%
警告:host模式下容器端口直接占用宿主机端口,多个容器不能监听同一端口。而且容器能看到宿主机所有网络接口和连接,安全隔离性为零。
2.2.3 None模式详解
None模式下容器只有lo回环接口,没有任何外部网络连接。适合不需要网络的计算任务,或者需要完全自定义网络的场景。
# 使用none网络运行容器 docker run -d --name isolated --network none alpine sleep 3600 # 查看容器网络接口(只有lo) dockerexecisolated ip addr # 输出: # 1: lo:mtu 65536 # inet 127.0.0.1/8 scope host lo # 验证无法访问外网 dockerexecisolated ping -c 1 8.8.8.8 # ping: sendto: Network is unreachable # 适用场景:数据加密/解密处理 docker run --rm --network none -v /data/input:/input:ro -v /data/output:/output crypto-tool encrypt /input/data.bin /output/data.enc
2.2.4 Container模式详解
Container模式让一个容器共享另一个容器的网络命名空间。两个容器通过localhost通信,共享IP地址和端口空间。Kubernetes的Pod就是基于这个原理——Pod内所有容器共享同一个网络命名空间。
# 先启动一个基础容器 docker run -d --name base-container -p 8080:80 nginx:1.24-alpine # 启动第二个容器,共享base-container的网络 docker run -d --name sidecar --network container:base-container alpine sleep 3600 # sidecar可以通过localhost访问nginx dockerexecsidecar wget -qO- http://localhost:80 # 返回Nginx默认页面 # 两个容器的网络接口完全一样 dockerexecbase-container ip addr dockerexecsidecar ip addr # 输出一致 # 典型应用:日志收集sidecar docker run -d --name app -p 8080:8080 myapp:1.0 docker run -d --namelog-collector --network container:app -v /data/logs:/logs fluentd:v1.16 # log-collector通过localhost收集app的日志
2.2.5 Overlay模式详解
Overlay网络基于VXLAN隧道实现跨主机容器通信。数据包在源主机封装VXLAN头部,通过UDP 4789端口发送到目标主机,目标主机解封装后转发给目标容器。
# 初始化Docker Swarm(overlay网络需要Swarm模式) docker swarm init --advertise-addr 192.168.1.10 # 在其他节点加入Swarm # docker swarm join --token192.168.1.10:2377 # 创建overlay网络 docker network create --driver overlay --subnet 10.10.0.0/24 --gateway 10.10.0.1 --attachable my-overlay # --attachable 允许非Swarm服务的独立容器也能加入这个网络 # 在overlay网络中部署服务 docker service create --name web --network my-overlay --replicas 3 -p 80:80 nginx:1.24-alpine # 验证跨主机通信 docker service ps web # 三个副本分布在不同节点上,通过overlay网络互通
注意:overlay网络有约10%-15%的性能损耗(VXLAN封装/解封装开销)。对延迟敏感的应用(如Redis集群),建议用macvlan或host模式。
2.2.6 Macvlan模式详解
Macvlan让容器直接获得物理网络的IP地址,每个容器有独立的MAC地址,在网络上表现得像一台独立的物理机。不经过NAT,网络性能接近原生。
# 创建macvlan网络 # 需要知道宿主机的物理网卡名和所在网段 docker network create --driver macvlan --subnet 192.168.1.0/24 --gateway 192.168.1.1 --opt parent=eth0 my-macvlan # 运行容器(指定IP) docker run -d --name db --network my-macvlan --ip 192.168.1.200 mysql:8.0.35 # 容器直接拥有192.168.1.200这个IP # 同网段的其他机器可以直接访问192.168.1.200:3306 # 查看容器MAC地址 dockerexecdb ip link show eth0 # 每个容器有独立的MAC地址
注意:macvlan模式下,容器和宿主机之间默认无法通信(这是macvlan的设计限制)。如果需要宿主机访问容器,需要在宿主机上创建macvlan子接口:
# 在宿主机上创建macvlan子接口 ip link add macvlan-host link eth0typemacvlan mode bridge ip addr add 192.168.1.201/24 dev macvlan-host ip linksetmacvlan-host up # 现在宿主机可以通过192.168.1.201访问macvlan网络中的容器
2.2.7 容器网络互联
# 将容器连接到多个网络 docker network create frontend --subnet 172.20.0.0/24 docker network create backend --subnet 172.21.0.0/24 # Nginx连接到frontend docker run -d --name nginx --network frontend -p 80:80 nginx:1.24-alpine # App连接到frontend和backend docker run -d --name app --network frontend myapp:1.0 docker network connect backend app # MySQL只连接到backend docker run -d --name mysql --network backend mysql:8.0.35 # 网络拓扑: # 外部 → Nginx(frontend) → App(frontend+backend) → MySQL(backend) # Nginx无法直接访问MySQL(不在同一网络) # App可以同时访问Nginx和MySQL # 断开容器的网络连接 docker network disconnect frontend app
2.3 启动和验证
2.3.1 端口映射详解
# 映射到宿主机所有接口 docker run -d -p 8080:80 nginx:1.24-alpine # 等价于 -p 0.0.0.080 # 映射到指定接口(安全,只监听内网) docker run -d -p 192.168.1.1080 nginx:1.24-alpine # 映射到随机端口 docker run -d -p 80 nginx:1.24-alpine docker port# 查看分配的随机端口 # UDP端口映射 docker run -d -p 53:53/udp dns-server:1.0 # 映射多个端口 docker run -d -p 80:80 -p 443:443 nginx:1.24-alpine # 映射端口范围 docker run -d -p 8000-8010:8000-8010 myapp:1.0 # 查看端口映射对应的iptables规则 sudo iptables -t nat -L DOCKER -n
2.3.2 功能验证
# 验证bridge网络容器间通信 docker network create testnet docker run -d --name server --network testnet nginx:1.24-alpine docker run --rm --network testnet alpine wget -qO- http://server:80 # 预期返回Nginx默认页面 # 验证DNS解析 docker run --rm --network testnet alpine nslookup server # 预期解析到server容器的IP # 验证外网访问 docker run --rm alpine ping -c 3 8.8.8.8 docker run --rm alpine wget -qO- http://ifconfig.me # 预期返回宿主机的公网IP # 验证端口映射 docker run -d --name web -p 8080:80 nginx:1.24-alpine curl -I http://localhost:8080 # 预期返回 HTTP/1.1 200 OK # 清理 docker rm -f server web docker network rm testnet
三、示例代码和配置
3.1 完整配置示例
3.1.1 生产环境网络规划配置
// 文件路径:/etc/docker/daemon.json
// 网络相关配置
{
"bip":"172.17.0.1/24",
"default-address-pools": [
{
"base":"172.20.0.0/16",
"size":24
},
{
"base":"172.21.0.0/16",
"size":24
}
],
"dns": ["223.5.5.5","8.8.8.8"],
"dns-search": ["example.com"],
"ip-forward":true,
"iptables":true,
"ip-masq":true,
"userland-proxy":false,
"fixed-cidr":"172.17.0.0/25"
}
参数说明:
bip:docker0网桥的IP和子网,默认172.17.0.1/16。生产环境改成/24,避免分配太大的网段
default-address-pools:自定义网络的地址池。docker network create时从这里分配子网。配两个池做冗余
userland-proxy:设为false用iptables做端口映射。默认的docker-proxy是用户态进程,每个端口映射都fork一个进程,高并发下CPU开销大
fixed-cidr:限制容器IP分配范围,172.17.0.0/25表示只分配172.17.0.1-172.17.0.126
3.1.2 网络排查脚本
#!/bin/bash
# 文件名:docker-network-diag.sh
# Docker网络诊断脚本
CONTAINER_NAME=${1:-""}
if[ -z"$CONTAINER_NAME"];then
echo"Usage:$0"
exit1
fi
echo"========== 容器基本信息 =========="
docker inspect --format='容器ID: {{.Id}}'$CONTAINER_NAME
docker inspect --format='状态: {{.State.Status}}'$CONTAINER_NAME
docker inspect --format='PID: {{.State.Pid}}'$CONTAINER_NAME
echo""
echo"========== 网络配置 =========="
docker inspect --format='{{range $net, $config := .NetworkSettings.Networks}}网络: {{$net}} IP: {{$config.IPAddress}} 网关: {{$config.Gateway}} MAC: {{$config.MacAddress}}{{"
"}}{{end}}'$CONTAINER_NAME
echo""
echo"========== 端口映射 =========="
docker port$CONTAINER_NAME2>/dev/null ||echo"无端口映射"
echo""
echo"========== DNS配置 =========="
dockerexec$CONTAINER_NAMEcat /etc/resolv.conf 2>/dev/null
echo""
echo"========== 路由表 =========="
dockerexec$CONTAINER_NAMEip route 2>/dev/null
echo""
echo"========== 网络接口 =========="
dockerexec$CONTAINER_NAMEip addr 2>/dev/null
echo""
echo"========== 连接测试 =========="
echo"--- 外网连通性 ---"
dockerexec$CONTAINER_NAMEping -c 2 -W 3 8.8.8.8 2>/dev/null &&echo"外网: OK"||echo"外网: FAIL"
echo"--- DNS解析 ---"
dockerexec$CONTAINER_NAMEnslookup www.baidu.com 2>/dev/null &&echo"DNS: OK"||echo"DNS: FAIL"
echo""
echo"========== 宿主机iptables NAT规则 =========="
sudo iptables -t nat -L DOCKER -n 2>/dev/null | head -20
echo""
echo"========== 宿主机veth接口 =========="
PID=$(docker inspect --format='{{.State.Pid}}'$CONTAINER_NAME)
if["$PID"!="0"];then
VETH_INDEX=$(sudo nsenter -t$PID-n ip link show eth0 2>/dev/null | head -1 | awk -F:'{print $1}'| awk -F@'{print $2}'| tr -d'if')
if[ -n"$VETH_INDEX"];then
ip link show | grep"^${VETH_INDEX}:"| awk'{print "veth接口: "$2}'
fi
fi
3.2 实际应用案例
案例一:微服务网络隔离架构
场景描述:一个典型的Web应用包含Nginx、App、MySQL、Redis四个服务。通过Docker网络实现前后端隔离——Nginx和App在前端网络,App和数据库在后端网络,Nginx无法直接访问数据库。
实现代码:
#!/bin/bash # 创建隔离网络 docker network create frontend --subnet 172.20.0.0/24 docker network create backend --subnet 172.21.0.0/24 # 启动MySQL(只在backend网络) docker run -d --name mysql --network backend --ip 172.21.0.10 --restart=unless-stopped --memory=2g -e MYSQL_ROOT_PASSWORD='DbP@ss123!' -e MYSQL_DATABASE=myapp -v /data/mysql/data:/var/lib/mysql mysql:8.0.35 # 启动Redis(只在backend网络) docker run -d --name redis --network backend --ip 172.21.0.11 --restart=unless-stopped --memory=1g redis:7.2-alpine # 启动App(连接frontend和backend) docker run -d --name app --network frontend --restart=unless-stopped --memory=1g -e DB_HOST=172.21.0.10 -e REDIS_HOST=172.21.0.11 myapp:1.0 # 将App也连接到backend网络 docker network connect backend app # 启动Nginx(只在frontend网络) docker run -d --name nginx --network frontend --restart=unless-stopped -p 80:80 -p 443:443 -v /data/nginx/conf.d:/etc/nginx/conf.d:ro nginx:1.24-alpine # 验证网络隔离 echo"--- Nginx访问App(应该成功)---" dockerexecnginx wget -qO- --timeout=3 http://app:8080/health echo"--- Nginx访问MySQL(应该失败)---" dockerexecnginx ping -c 1 -W 2 172.21.0.10 ||echo"隔离生效:Nginx无法访问MySQL" echo"--- App访问MySQL(应该成功)---" dockerexecapp ping -c 1 -W 2 172.21.0.10 &&echo"App可以访问MySQL"
运行结果:
--- Nginx访问App(应该成功)---
{"status":"UP"}
--- Nginx访问MySQL(应该失败)---
PING 172.21.0.10: 1 data bytes
ping: sendto: Network is unreachable
隔离生效:Nginx无法访问MySQL
--- App访问MySQL(应该成功)---
PING 172.21.0.10: 1 data bytes
64 bytes from 172.21.0.10: seq=0 ttl=64 time=0.089 ms
App可以访问MySQL
案例二:使用Macvlan让容器直接接入物理网络
场景描述:公司有一套传统的监控系统,通过SNMP轮询固定IP获取设备状态。现在要把监控Agent容器化,但监控系统不支持NAT后的地址,需要容器拥有物理网络的真实IP。
实现步骤:
# 1. 确认宿主机网络信息 ip addr show eth0 # 假设:IP=192.168.1.100/24, 网关=192.168.1.1 # 2. 开启网卡混杂模式(macvlan需要) sudo ip linkseteth0 promisc on # 3. 创建macvlan网络 docker network create --driver macvlan --subnet 192.168.1.0/24 --gateway 192.168.1.1 --ip-range 192.168.1.200/29 --opt parent=eth0 physical-net # --ip-range 限制Docker只分配192.168.1.200-192.168.1.207 # 避免和DHCP分配的地址冲突 # 4. 运行监控Agent容器 docker run -d --name monitor-agent --network physical-net --ip 192.168.1.200 --restart=unless-stopped monitor-agent:1.0 # 5. 验证:从其他物理机直接访问容器IP # 在192.168.1.50这台机器上: ping 192.168.1.200 # 能ping通,容器就像一台独立的物理机 # 6. 解决宿主机无法访问容器的问题 sudo ip link add macvlan-shim link eth0typemacvlan mode bridge sudo ip addr add 192.168.1.201/32 dev macvlan-shim sudo ip linksetmacvlan-shim up sudo ip route add 192.168.1.200/32 dev macvlan-shim
四、最佳实践和注意事项
4.1 最佳实践
4.1.1 性能优化
关闭userland-proxy:daemon.json中设置"userland-proxy": false,用iptables替代docker-proxy做端口映射。docker-proxy是用户态进程,每个端口映射fork一个进程,100个端口映射就是100个进程。iptables在内核态处理,零进程开销,高并发下吞吐量提升约20%:
{
"userland-proxy":false
}
高性能场景用host网络:Nginx反向代理、HAProxy负载均衡这类网络密集型应用,bridge模式的NAT转换有5%-10%的性能损耗。切换到host模式后,Nginx的QPS从12000提升到14000(测试环境4核8GB):
docker run -d --name nginx --network host -v /data/nginx/nginx.conf:/etc/nginx/nginx.conf:ro nginx:1.24-alpine
调大conntrack表:bridge模式依赖iptables的连接跟踪(conntrack),默认nf_conntrack_max=65536,高并发场景下会满,导致新连接被丢弃。生产环境建议调到100万:
sysctl -w net.netfilter.nf_conntrack_max=1048576 sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=600 # 写入 /etc/sysctl.d/docker-network.conf 持久化
4.1.2 安全加固
网络隔离:不同安全级别的服务放在不同的Docker网络中。数据库和缓存放在backend网络,Web服务放在frontend网络,只有应用层同时连接两个网络。这样即使Web容器被攻破,攻击者也无法直接访问数据库:
docker network create --internal backend-secure # --internal 禁止该网络访问外网,数据库不需要外网访问
限制容器间通信(ICC):默认同一bridge网络内的容器可以互相通信。如果不需要容器间通信,关闭ICC:
docker network create --opt"com.docker.network.bridge.enable_icc"="false"isolated-net # ICC关闭后,容器间只能通过端口映射通信
端口映射绑定内网IP:不要把端口映射到0.0.0.0,绑定到内网IP。我见过把MySQL 3306映射到0.0.0.0,结果被外网扫描到暴力破解的:
# 危险:监听所有接口 docker run -d -p 3306:3306 mysql:8.0.35 # 安全:只监听内网 docker run -d -p 192.168.1.103306 mysql:8.0.35
4.1.3 高可用配置
DNS轮询负载均衡:自定义bridge网络支持同名容器的DNS轮询。多个容器用相同的网络别名,Docker DNS会轮询返回不同IP:
docker network create mynet docker run -d --name app1 --network mynet --network-alias app myapp:1.0 docker run -d --name app2 --network mynet --network-alias app myapp:1.0 docker run -d --name app3 --network mynet --network-alias app myapp:1.0 # 访问 app 这个名字会轮询到app1/app2/app3
overlay网络跨主机高可用:Docker Swarm的overlay网络自动处理节点故障,服务副本会在健康节点上重新调度
网络故障自愈:配置容器restart策略,网络闪断导致容器异常退出时自动重启
4.2 注意事项
4.2.1 配置注意事项
警告:Docker网络地址段不能和宿主机所在网段、公司内网网段冲突。我遇到过Docker默认的172.17.0.0/16和公司办公网段冲突,导致开发机无法访问172.17开头的内网服务器。修改daemon.json的bip和default-address-pools避免冲突。
注意iptables规则持久化:Docker重启会重建iptables规则,但如果手动修改了iptables规则(比如加了防火墙规则),Docker重启后可能覆盖。建议用firewalld的docker zone管理防火墙规则
注意IPv6支持:Docker默认不启用IPv6。如果需要IPv6,在daemon.json中配置"ipv6": true和"fixed-cidr-v6": "fd00::/80"
注意容器重启后IP变化:默认bridge网络不保证容器IP不变。用自定义网络+容器名DNS解析,或者用--ip指定固定IP
4.2.2 常见错误
| 错误现象 | 原因分析 | 解决方案 |
|---|---|---|
| 容器无法访问外网 | ip_forward未开启或iptables NAT规则丢失 | sysctl -w net.ipv4.ip_forward=1 ,重启Docker重建规则 |
| 端口映射不生效 | 防火墙阻断或端口被占用 | 检查firewalld/iptables规则,ss -tlnp检查端口占用 |
| 容器间ping不通 | 不在同一Docker网络 | docker network connect 将容器加入同一网络 |
| DNS解析失败 | 使用了默认bridge网络(不支持DNS) | 改用自定义bridge网络 |
| conntrack表满导致丢包 | nf_conntrack_max太小 | 调大到1048576,dmesg中搜索"nf_conntrack: table full" |
| macvlan容器和宿主机不通 | macvlan的设计限制 | 在宿主机创建macvlan子接口 |
4.2.3 兼容性问题
版本兼容:overlay网络需要Docker 1.12+和Swarm模式。独立容器加入overlay需要--attachable参数(Docker 17.06+)
平台兼容:macvlan在虚拟机环境中可能不工作(取决于虚拟化平台是否允许混杂模式)。AWS/阿里云等云平台通常不支持macvlan
内核兼容:VXLAN需要内核4.0+,ipvlan需要内核4.2+。CentOS 7默认内核3.10不支持这些特性,需要升级内核
五、故障排查和监控
5.1 故障排查
5.1.1 日志查看
# 查看Docker网络相关日志 sudo journalctl -u docker.service | grep -i -E"network|bridge|iptables" # 查看容器网络事件 docker events --filter'type=network' # 查看iptables规则(NAT表) sudo iptables -t nat -L -n -v # 查看iptables规则(filter表) sudo iptables -L DOCKER -n -v sudo iptables -L DOCKER-ISOLATION-STAGE-1 -n -v # 查看conntrack连接跟踪表 sudo conntrack -L | head -20 sudo conntrack -C # 当前连接数
5.1.2 常见问题排查
问题一:容器无法访问外网
# 第一步:检查IP转发 sysctl net.ipv4.ip_forward # 如果为0,开启: sudo sysctl -w net.ipv4.ip_forward=1 # 第二步:检查iptables MASQUERADE规则 sudo iptables -t nat -L POSTROUTING -n | grep MASQUERADE # 应该有类似:MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 # 第三步:检查容器DNS dockerexeccat /etc/resolv.conf # nameserver应该指向Docker内置DNS 127.0.0.11 # 第四步:分步测试 dockerexec ping -c 1 172.17.0.1 # 网关 dockerexec ping -c 1 8.8.8.8 # 外网IP dockerexec nslookup www.baidu.com # DNS解析
解决方案:
ip_forward为0:开启并写入sysctl.conf持久化
MASQUERADE规则缺失:systemctl restart docker重建
DNS不通:在daemon.json中配置"dns": ["223.5.5.5", "8.8.8.8"]
问题二:端口映射后外部无法访问
# 检查端口映射是否生效 docker port# 检查宿主机端口是否在监听 ss -tlnp | grep # 检查防火墙规则 sudo firewall-cmd --list-all sudo iptables -L INPUT -n | grep # 检查Docker的iptables规则 sudo iptables -t nat -L DOCKER -n | grep sudo iptables -L DOCKER -n | grep # 检查是否有docker-proxy进程(如果userland-proxy=true) ps aux | grep docker-proxy
解决方案:
防火墙阻断:firewall-cmd --add-port=8080/tcp --permanent && firewall-cmd --reload
iptables规则丢失:重启Docker或手动重建
端口被其他进程占用:ss -tlnp | grep
问题三:容器间网络延迟高
症状:同一宿主机上的容器间通信延迟从0.1ms突然升高到5-10ms
排查:
# 检查网桥状态 brctl show brctl showstp docker0 # 检查veth接口是否有错误 ip -s link show | grep -A 5 veth # 检查conntrack表是否接近满 sudo sysctl net.netfilter.nf_conntrack_count sudo sysctl net.netfilter.nf_conntrack_max # 如果count接近max,说明conntrack表快满了 # 抓包分析 sudo tcpdump -i docker0 -nn -c 100
解决:conntrack表满导致丢包重传,调大nf_conntrack_max;veth接口错误计数增长说明网络栈有问题,检查内核日志
5.1.3 调试模式
# 使用nsenter进入容器网络命名空间(不需要容器内有调试工具)
PID=$(docker inspect --format='{{.State.Pid}}')
sudo nsenter -t$PID-n ip addr
sudo nsenter -t$PID-n ss -tlnp
sudo nsenter -t$PID-n iptables -L -n
sudo nsenter -t$PID-n tcpdump -i eth0 -nn -c 50
# 在docker0网桥上抓包
sudo tcpdump -i docker0 -nn -w /tmp/docker0.pcap
# 在veth接口上抓包(找到容器对应的veth)
VETH=$(ip link show | grep"$(docker inspect --format='{{.State.Pid}}' )"| awk'{print $2}'| tr -d':')
sudo tcpdump -i$VETH-nn -c 50
# 跟踪iptables规则匹配(调试NAT问题)
sudo iptables -t nat -L -n -v --line-numbers
# 观察各规则的pkts和bytes计数器变化
# 使用nicolaka/netshoot调试容器(自带各种网络工具)
docker run --rm -it --network container: nicolaka/netshoot
# 进入后可以用 tcpdump, iperf3, nslookup, curl, ss 等工具
5.2 性能监控
5.2.1 关键指标监控
# 查看容器网络IO
docker stats --no-stream --format"table {{.Name}} {{.NetIO}}"
# 查看网桥流量
cat /sys/class/net/docker0/statistics/rx_bytes
cat /sys/class/net/docker0/statistics/tx_bytes
# 查看conntrack使用率
echo"$(cat /proc/sys/net/netfilter/nf_conntrack_count)/$(cat /proc/sys/net/netfilter/nf_conntrack_max)"| bc -l
# 查看veth接口错误计数
ip -s link show | grep -A 6 veth
# 查看iptables规则计数
sudo iptables -t nat -L -n -v | grep DOCKER
5.2.2 监控指标说明
| 指标名称 | 正常范围 | 告警阈值 | 说明 |
|---|---|---|---|
| conntrack使用率 | <60% | >80% | 超过80%开始丢包 |
| 容器网络收发错误 | 0 | >0 | 有错误说明网络栈异常 |
| docker0网桥流量 | 视业务而定 | 突增50%以上 | 流量突增可能是攻击或异常 |
| DNS解析延迟 | <5ms | >50ms | Docker内置DNS通常很快 |
| 端口映射连接数 | 视业务而定 | 接近conntrack_max | 连接数过多需要扩容 |
| veth接口丢包率 | 0% | >0.01% | 丢包说明网络拥塞或配置问题 |
5.2.3 Prometheus监控配置
# Prometheus告警规则:docker-network-alerts.yml
groups:
-name:docker_network_alerts
rules:
-alert:ConntrackTableNearlyFull
expr:node_nf_conntrack_entries/node_nf_conntrack_entries_limit>0.8
for:5m
labels:
severity:critical
annotations:
summary:"conntrack表使用率超过80%"
description:"当前使用率{{ $value | humanizePercentage }},即将导致新连接被丢弃"
-alert:ContainerNetworkErrors
expr:rate(container_network_receive_errors_total{name!=""}[5m])>0
for:2m
labels:
severity:warning
annotations:
summary:"容器{{ $labels.name }}网络接收错误"
description:"错误率{{ $value }}/s"
-alert:HighNetworkTraffic
expr:rate(container_network_receive_bytes_total{name!=""}[5m])>100000000
for:5m
labels:
severity:warning
annotations:
summary:"容器{{ $labels.name }}网络流量异常"
description:"接收流量{{ $value | humanize }}B/s,超过100MB/s"
5.3 备份与恢复
5.3.1 备份策略
#!/bin/bash
# Docker网络配置备份脚本
BACKUP_DIR="/backup/docker-network/$(date +%Y%m%d)"
mkdir -p${BACKUP_DIR}
# 备份所有自定义网络配置
fornetin$(docker network ls --filter'type=custom'-q);do
NET_NAME=$(docker network inspect --format='{{.Name}}'$net)
docker network inspect$net>${BACKUP_DIR}/${NET_NAME}.json
done
# 备份iptables规则
sudo iptables-save >${BACKUP_DIR}/iptables-rules.txt
sudo ip6tables-save >${BACKUP_DIR}/ip6tables-rules.txt
# 备份sysctl网络参数
sysctl -a 2>/dev/null | grep -E"net.(ipv4|bridge|netfilter)">${BACKUP_DIR}/sysctl-network.txt
# 备份daemon.json
cp /etc/docker/daemon.json${BACKUP_DIR}/
echo"Network backup completed:${BACKUP_DIR}"
5.3.2 恢复流程
恢复daemon.json:cp daemon.json /etc/docker/ && systemctl restart docker
恢复sysctl参数:cp sysctl-network.txt /etc/sysctl.d/docker-network.conf && sysctl --system
重建自定义网络:根据备份的JSON文件中的subnet、gateway等参数重新创建
验证网络连通性:启动测试容器验证各网络的连通性和隔离性
六、总结
6.1 技术要点回顾
bridge模式:默认模式,通过docker0网桥+veth pair+iptables NAT实现。生产环境用自定义bridge,支持容器名DNS解析
host模式:直接使用宿主机网络栈,零NAT开销,适合网络密集型应用。代价是失去网络隔离
网络隔离:通过多个自定义网络实现服务间隔离,前端网络和后端网络分离,最小化攻击面
性能调优:关闭userland-proxy、调大conntrack表、高性能场景用host模式
排查思路:ip_forward → iptables规则 → DNS解析 → conntrack表,按这个顺序排查覆盖90%的网络问题
6.2 进阶学习方向
容器网络接口(CNI):Kubernetes使用CNI标准管理容器网络,理解CNI插件机制是进入K8s网络的基础
学习资源:CNI规范文档、Flannel/Calico源码
实践建议:手动配置CNI插件,理解网络创建和删除的生命周期
eBPF网络:Cilium等新一代容器网络方案用eBPF替代iptables,性能更好,可观测性更强
学习资源:Cilium官方文档
实践建议:在测试环境部署Cilium,对比iptables方案的性能差异
Service Mesh:Istio/Linkerd在容器网络之上提供流量管理、熔断、链路追踪等能力
学习资源:Istio官方教程
6.3 参考资料
Docker网络官方文档- 各网络驱动的详细说明
Linux网络命名空间- 理解容器网络隔离的内核基础
iptables教程- 理解Docker的NAT规则
nicolaka/netshoot- 容器网络调试工具集
附录
A. 命令速查表
# 网络管理 docker network ls # 查看所有网络 docker network create --subnet X mynet # 创建自定义网络 docker network rm mynet # 删除网络 docker network inspect mynet # 查看网络详情 docker network connect mynet container # 容器加入网络 docker network disconnect mynet container # 容器离开网络 docker network prune # 清理未使用的网络 # 容器网络操作 docker run --network host ... # host模式运行 docker run --network none ... # 无网络运行 docker run --network container:other ... # 共享网络运行 docker run -p 8080:80 ... # 端口映射 docker run --ip 172.20.0.100 --network mynet # 指定IP docker port container # 查看端口映射 # 网络调试 dockerexeccontainer ip addr # 查看容器网络 dockerexeccontainer ping target # 连通性测试 dockerexeccontainer nslookup name # DNS测试 sudo tcpdump -i docker0 -nn # 网桥抓包 sudo nsenter -t PID -n ip addr # 进入网络命名空间
B. 网络模式对比表
| 特性 | bridge | host | none | container | overlay | macvlan |
|---|---|---|---|---|---|---|
| 网络隔离 | 有 | 无 | 完全隔离 | 共享 | 有 | 有 |
| 性能损耗 | 5-10% | 0% | N/A | 0% | 10-15% | <2% |
| 端口映射 | 需要-p | 不需要 | N/A | 不需要 | 需要-p | 不需要 |
| 跨主机通信 | 不支持 | 不支持 | 不支持 | 不支持 | 支持 | 支持(同网段) |
| DNS解析 | 自定义网络支持 | 用宿主机 | 无 | 共享 | 支持 | 无 |
| 适用场景 | 通用 | 高性能 | 安全隔离 | Sidecar | Swarm集群 | 物理网络接入 |
C. 术语表
| 术语 | 英文 | 解释 |
|---|---|---|
| 网络命名空间 | Network Namespace | Linux内核特性,为进程提供独立的网络栈(接口、路由表、iptables规则) |
| veth pair | Virtual Ethernet Pair | 虚拟以太网对,一端在容器内(eth0),一端在宿主机(vethXXX),数据从一端进另一端出 |
| 网桥 | Bridge | 二层网络设备,连接多个网络接口,实现同网段内的数据转发。docker0就是一个Linux网桥 |
| NAT | Network Address Translation | 网络地址转换,Docker用MASQUERADE规则将容器IP转换为宿主机IP访问外网 |
| VXLAN | Virtual Extensible LAN | 虚拟可扩展局域网,overlay网络的底层隧道协议,用UDP 4789端口封装二层帧 |
| conntrack | Connection Tracking | iptables的连接跟踪机制,记录每个网络连接的状态,NAT依赖此机制 |
| ICC | Inter-Container Communication | 容器间通信,可以在网络级别开启或关闭 |
| macvlan | MAC VLAN | 在一个物理网卡上创建多个虚拟网卡,每个有独立的MAC地址和IP |
-
网络
+关注
关注
14文章
8378浏览量
95696 -
容器
+关注
关注
0文章
541浏览量
23057 -
Docker
+关注
关注
0文章
540浏览量
14467
原文标题:从网络隔离到服务互通:Docker 网络模式全解析
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
Docker容器网络模式全解析
评论