故障现象
单个服务很快,但组合起来速度就慢。
具体表现:
- 页面加载从2秒延迟到5秒
- 接口响应延迟明显
- 偶尔出现超时错误
- 数据库查询正常
- 应用资源正常(CPU、内存等)
- 带宽充足
问题定位
1.抓包分析
进入api网关容器(本例中使用nginx容器作为入口网关),对相应端口的数据进行抓包分析。
# 抓包命令
docker exec nginx tcpdump -i eth0 -nn -tttt 'tcp port 8080' -w /tmp/capture.pcap
#tcpdump: 使用网络抓包工具 tcpdump
#-i eth0: 指定监听的网络接口为 eth0
#-nn: 不解析主机名和端口号(直接显示IP和数字端口)
#-tttt: 使用完整格式的时间戳(年月日时分秒)
#'tcp port 8080': 过滤条件,只捕获 TCP 协议且目标或源端口为 8080 的流量
#-w /tmp/capture.pcap: 将捕获的数据包写入到容器内的 /tmp/capture.pcap 文件,而不是显示在终端
# Wireshark分析结果
时间线:
00:00.000 请求到达nginx
00:12.000 nginx → service-a (12ms延迟)
00:18.000 service-a收到 (6ms)
00:25.000 service-a → service-b (7ms)
00:45.000 service-b收到 (20ms !!!)
00:78.000 service-b处理完成 (33ms)
00:98.000 service-a收到响应 (20ms !!!)
00:105.000 nginx收到 (7ms)
00:130.000 返回给用户 (25ms)
总计:130ms
2.发现问题
容器间通信延迟异常高,尤其是 service-a 和 service-b 之间。
3.分析问题
为什么容器间通信会有20ms延迟?
- 同一主机的容器通信应该<1ms
- 我们的却有20-30ms
原因:
- Docker默认使用Bridge网络模式
- 数据要经过:容器 → veth → bridge → NAT → veth → 容器
- 每一层都有损耗
网络路径:
容器A → veth0 → docker0网桥 → iptables规则(300+条)
→ NAT转换 → veth1 → 容器B
损耗来源:
- veth虚拟网卡:3-5ms
- 网桥转发:2-3ms
- iptables遍历:5-8ms
- NAT转换:1-2ms
- conntrack查表:1-2ms
总损耗:12-20ms
docker网络模式对比
1.bridge模式(默认)
# 测试
docker run -d --name app1 alpine sleep 3600
docker run -d --name app2 alpine sleep 3600
docker exec app1 apk add iputils
docker exec app1 ping -c 100 app2
# 结果
rtt min/avg/max/mdev = 0.082/0.125/0.245/0.031 ms
特点:
- 延迟:0.08-0.25ms(容器间)
- 吞吐量:950Mbps
- 优点:隔离性好
- 缺点:性能一般
2.Host模式
docker run -d --name app-host --network host alpine sleep 3600
docker exec app-host ping -c 100 127.0.0.1
# 结果
rtt min/avg/max/mdev = 0.015/0.022/0.045/0.008 ms特点:
- 延迟:0.015-0.045ms
- 吞吐量:10Gbps
- 优点:性能最好
- 缺点:端口冲突、安全性差
3.Overlay模式(跨主机)
docker network create -d overlay testnet
docker run -d --name app1 --network testnet alpine sleep 3600
# 在另一台主机
docker run -d --name app2 --network testnet alpine sleep 3600
# 测试
docker exec app1 ping -c 100 app2
# 结果
rtt min/avg/max/mdev = 0.245/0.512/1.245/0.156 ms特点:
- 延迟:0.5-30ms(取决于网络)
- 吞吐量:700Mbps(VXLAN损耗)
- 优点:跨主机、K8s支持
- 缺点:性能较差、CPU占用高
4.Macvlan模式
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
-o parent=eth0 macnet
docker run -d --name app1 --network macnet --ip 192.168.1.100 alpine
docker exec app1 ping -c 100 192.168.1.101
# 结果
rtt min/avg/max/mdev = 0.052/0.085/0.156/0.022 ms
特点:
- 延迟:0.05-0.15ms
- 吞吐量:950Mbps
- 优点:性能好、独立IP
缺点:配置复杂
性能对比
| 模式 | 延迟(ms) | 吞吐量 | 隔离性 | 易用性 | 适用场景 |
|---|---|---|---|---|---|
| Bridge | 0.08-0.25 | 950Mbps | 好 | 高 | 开发测试 |
| Host | 0.02-0.05 | 10Gbps | 差 | 高 | 单体应用 |
| Overlay | 0.5-30 | 700Mbps | 好 | 中 | k8s集群 |
| Macvlan | 0.05-0.15 | 950Mbps | 好 | 低 | 独立IP |
结论:
- 性能:Host > Macvlan > Bridge > Overlay
- 易用:Bridge = Host > Overlay > Macvlan
- 生产推荐:看场景选择
优化方案
方案1,内核参数调优
cat >> /etc/sysctl.conf <<EOF
# TCP优化
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_slow_start_after_idle = 0
# 缓冲区
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
# 连接跟踪
net.netfilter.nf_conntrack_max = 262144
# 队列
net.core.netdev_max_backlog = 5000
net.core.somaxconn = 4096
EOF
sysctl -p测试结果:
- 延迟:30ms
- 吞吐量:942Mbps
- CPU:45%
优化后:
- 延迟:26.5ms (-12%)
- 吞吐量:1.05Gbps (+11%)
- CPU:42% (-7%)
评价: 效果一般,但零成本,推荐做。
方案2,简化iptables规则
# 发现问题
iptables -L -n | wc -l
# 输出:347行规则!
# 每个数据包都要遍历这347条规则
# 优化:禁用Docker的iptables管理
cat > /etc/docker/daemon.json <<EOF
{
"iptables": false,
"ip-forward": true
}
EOF
systemctl restart docker
# 手动添加最小规则集
iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT
iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
# 现在只有50条规则测试结果:
- iptables规则:347条
- 延迟:26.5ms
优化后:
- iptables规则:50条
- 延迟:22.2ms (-16%)
评价: 效果明显,但要小心配置错误导致网络不通。
方案3:使用自定义网络
# Docker默认bridge性能差
# 自定义网络更好
docker network create \
--driver bridge \
--opt com.docker.network.bridge.name=br-prod \
--opt com.docker.network.driver.mtu=1500 \
prod-network
# 使用自定义网络
docker run -d --name app --network prod-network myapp
# 可以直接用服务名通信
docker exec app1 ping app2 # 无需IP测试结果:
- DNS解析:5-10ms
- 延迟:0.125ms
自定义network:
- DNS解析:1-2ms
- 延迟:0.098ms (-22%)
评价: 简单有效,强烈推荐。
方案4:增加conntrack表(效果:解决抖动)
高并发时连接跟踪表会满
# 导致丢包和延迟抖动
echo 262144 > /proc/sys/net/netfilter/nf_conntrack_max
# 永久生效
cat >> /etc/sysctl.conf <<EOF
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_buckets = 65536
EOF
sysctl -p测试结果(10000并发):
- 丢包率:15%
- 延迟:30-80ms(抖动大)
优化后:
- 丢包率:0%
- 延迟:22-24ms(稳定)
评价: 高并发场景必做。
方案5:使用ipvlan
# ipvlan绕过网桥,直接三层路由
docker network create -d ipvlan \
--subnet=192.168.100.0/24 \
-o parent=eth0 \
-o ipvlan_mode=l2 \
ipvlan-net
docker run -d --name app --network ipvlan-net --ip 192.168.100.10 myapp测试结果:
ipvlan模式:
rtt min/avg/max/mdev = 0.045/0.068/0.115/0.018 ms
改善:-46%
评价: 效果很好,但需要管理IP地址。
评论 (0)