你的应用在本地开发环境跑得飞快,上线后用户投诉”卡”——这几乎是每个工程师都经历过的场景。本地环境的网络条件太”完美”了:零丢包、亚毫秒延迟、千兆带宽。而真实世界的网络充满了延迟抖动、偶发丢包、带宽波动和链路故障。如果你不在测试阶段模拟这些条件,生产环境就是你的测试环境。
本文覆盖三个层次的网络模拟与测试:tc netem
提供的单机网络条件模拟、Mininet
提供的虚拟网络拓扑、以及面向分布式系统的网络混沌工程。目标是让你具备在任何阶段复现和验证网络行为的能力。
一、tc netem 基础
1.1 netem 是什么
netem(Network Emulator)是 Linux 内核
tc(Traffic
Control)子系统的一个排队规则(qdisc),用于模拟广域网的各种网络条件。它工作在内核网络栈的出口路径上,可以对经过指定网卡的数据包施加延迟、丢包、重排序、重复和损坏等效果。
# netem 在 tc 中的位置
# tc 的三层模型:qdisc → class → filter
# netem 是一种 classless qdisc
# 直接挂载到网卡的出口(egress)
# 检查当前 qdisc 配置
tc qdisc show dev eth0
# 默认输出:
# qdisc noqueue 0: root refcnt 2
# 或
# qdisc fq_codel 0: root refcnt 2 limit 10240p ...1.2 基本延迟模拟
# 添加固定延迟
# 所有经过 eth0 发出的包延迟 100ms
tc qdisc add dev eth0 root netem delay 100ms
# 验证效果
ping -c 5 10.0.0.1
# PING 10.0.0.1: 64 bytes, icmp_seq=1, time=100.2ms
# PING 10.0.0.1: 64 bytes, icmp_seq=2, time=100.1ms
# 带抖动的延迟
# 延迟 100ms ± 20ms(均匀分布)
tc qdisc change dev eth0 root netem delay 100ms 20ms
# 带相关性的延迟抖动
# 延迟 100ms ± 20ms,25% 的相关性
# 相关性意味着连续包的延迟值是相关的,更接近真实网络行为
tc qdisc change dev eth0 root netem delay 100ms 20ms 25%
# 使用正态分布的延迟
# 延迟 100ms ± 20ms,正态分布
tc qdisc change dev eth0 root netem delay 100ms 20ms distribution normal
# 支持的分布类型:
# normal — 正态分布(最常用)
# pareto — Pareto 分布(适合模拟长尾延迟)
# paretonormal — Pareto-Normal 混合分布
# 自定义 — 使用 /usr/lib/tc/ 下的分布表文件1.3 丢包模拟
# 随机丢包——1% 的包被丢弃
tc qdisc change dev eth0 root netem loss 1%
# 带相关性的丢包——突发性丢包
# 1% 丢包率,25% 相关性
# 高相关性意味着丢包倾向于连续发生(突发性丢包)
tc qdisc change dev eth0 root netem loss 1% 25%
# Gilbert-Elliott 模型——更真实的丢包模式
# 参数:p(好→坏的概率)、r(坏→好的概率)
# 1-h(好状态的丢包率)、1-k(坏状态的丢包率)
tc qdisc change dev eth0 root netem loss gemodel 1% 10% 100% 0%
# 含义:1% 概率进入坏状态,10% 概率恢复
# 好状态不丢包(100%),坏状态全丢(0%)
# ECN 标记代替丢包
# 对支持 ECN 的流量标记 CE 而非丢弃
tc qdisc change dev eth0 root netem loss 1% ecn1.4 包重排序
# 包重排序——25% 的包被立即发送(跳过延迟队列)
# 需要配合延迟使用才有效果
tc qdisc change dev eth0 root netem delay 100ms reorder 25% 50%
# 25% 的包立即发送,50% 的相关性
# 间隔重排序——每 N 个包中有一个被重排序
tc qdisc change dev eth0 root netem delay 100ms reorder 100% gap 5
# 每 5 个包中第 1 个被立即发送,其余延迟 100ms
# 效果:1,5,2,3,4,6,10,7,8,9,...1.5 包重复与损坏
# 包重复——1% 的包被复制
tc qdisc change dev eth0 root netem duplicate 1%
# 包损坏——在包的随机位置引入比特错误
tc qdisc change dev eth0 root netem corrupt 0.1%
# 0.1% 的包会有一个随机位被翻转
# 触发校验和失败,模拟链路层错误
# 包乱序——将包重新排列
tc qdisc change dev eth0 root netem delay 100ms reorder 25%二、tc netem 高级用法
2.1 组合多种网络条件
# 真实世界的网络条件是多种因素的叠加
# netem 支持在一条规则中组合多种效果
# 模拟 3G 移动网络
tc qdisc add dev eth0 root netem \
delay 200ms 50ms distribution normal \
loss 2% 25% \
duplicate 0.5% \
reorder 5% 50%
# 200ms 基础延迟 ± 50ms 正态分布
# 2% 丢包率,25% 相关性(突发丢包)
# 0.5% 包重复
# 5% 包重排序
# 模拟卫星链路
tc qdisc add dev eth0 root netem \
delay 600ms 50ms distribution normal \
loss 0.5% \
rate 5mbit
# 模拟跨太平洋链路(中国 ↔ 美国)
tc qdisc add dev eth0 root netem \
delay 150ms 30ms distribution normal \
loss 0.1% 50%2.2 带宽限制
# netem 本身不直接限制带宽,需要配合 tbf 或 htb
# 方法 1:netem + tbf 组合
# 先加 netem(延迟/丢包),再链式添加 tbf(限速)
tc qdisc add dev eth0 root handle 1: netem delay 100ms loss 1%
tc qdisc add dev eth0 parent 1:1 handle 2: tbf \
rate 1mbit burst 32kbit latency 400ms
# 方法 2:netem 内置 rate 限速
tc qdisc add dev eth0 root netem \
delay 100ms \
rate 1mbit \
loss 1%
# 注意:netem 的 rate 参数比 tbf 简单但不够精确
# 对精确的带宽控制场景,推荐 netem + tbf 组合
# 方法 3:htb + netem 分层控制
# 适合对不同流量类型施加不同条件
tc qdisc add dev eth0 root handle 1: htb default 10
tc class add dev eth0 parent 1: classid 1:10 htb rate 10mbit
tc qdisc add dev eth0 parent 1:10 netem delay 50ms loss 0.5%2.3 使用 filter 选择性模拟
# 只对特定流量施加网络条件
# 需要使用 tc filter 配合 classful qdisc
# 只对目的端口 80 的流量添加延迟
tc qdisc add dev eth0 root handle 1: prio bands 3
tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 200ms
tc qdisc add dev eth0 parent 1:2 handle 20: pfifo_fast
tc qdisc add dev eth0 parent 1:3 handle 30: pfifo_fast
# 匹配 TCP 目的端口 80
tc filter add dev eth0 parent 1:0 protocol ip u32 \
match ip dport 80 0xffff flowid 1:1
# 只对特定 IP 段的流量添加丢包
tc filter add dev eth0 parent 1:0 protocol ip u32 \
match ip dst 10.0.0.0/24 flowid 1:1
# 只对特定源 IP 模拟网络条件
tc filter add dev eth0 parent 1:0 protocol ip u32 \
match ip src 192.168.1.100/32 flowid 1:12.4 入口流量模拟
# tc netem 默认只能控制出口(egress)流量
# 要模拟入口(ingress)延迟,需要使用 IFB 设备
# 创建 IFB 设备
modprobe ifb
ip link set dev ifb0 up
# 将入口流量重定向到 IFB 设备
tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: protocol ip u32 \
match u32 0 0 action mirred egress redirect dev ifb0
# 在 IFB 设备上设置 netem
tc qdisc add dev ifb0 root netem delay 100ms loss 1%
# 验证——ping 现在是双向延迟
ping -c 5 10.0.0.1
# 往返延迟 = 出口延迟 + 入口延迟
# 清理
tc qdisc del dev eth0 handle ffff: ingress
tc qdisc del dev ifb0 root
ip link set dev ifb0 down2.5 时变网络条件
#!/bin/bash
# simulate-variable-network.sh
# 模拟随时间变化的网络条件
IFACE="eth0"
# 初始化
tc qdisc add dev $IFACE root netem delay 50ms
echo "开始模拟时变网络条件..."
# 阶段 1:正常(30 秒)
echo "[$(date +%H:%M:%S)] 阶段 1:正常网络 (50ms, 0% loss)"
tc qdisc change dev $IFACE root netem delay 50ms
sleep 30
# 阶段 2:轻度拥塞(30 秒)
echo "[$(date +%H:%M:%S)] 阶段 2:轻度拥塞 (150ms, 1% loss)"
tc qdisc change dev $IFACE root netem delay 150ms 30ms loss 1%
sleep 30
# 阶段 3:严重拥塞(30 秒)
echo "[$(date +%H:%M:%S)] 阶段 3:严重拥塞 (500ms, 5% loss)"
tc qdisc change dev $IFACE root netem delay 500ms 100ms loss 5% 50%
sleep 30
# 阶段 4:恢复(30 秒)
echo "[$(date +%H:%M:%S)] 阶段 4:恢复 (80ms, 0.5% loss)"
tc qdisc change dev $IFACE root netem delay 80ms 20ms loss 0.5%
sleep 30
# 清理
echo "[$(date +%H:%M:%S)] 模拟结束,清理规则"
tc qdisc del dev $IFACE root三、Mininet 虚拟网络
3.1 Mininet 架构
Mininet 利用 Linux 内核的 Network Namespace、veth pair 和 Open vSwitch 创建轻量级虚拟网络拓扑。与 GNS3/EVE-NG 等基于虚拟机的模拟器不同,Mininet 在一台主机上就能创建包含数百个节点的拓扑,启动时间在秒级。
┌──────────────────────────────────────────┐
│ 主机内核 │
│ ┌──────────────────────────────────────┐ │
│ │ OVS Bridge │ │
│ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │
│ │ │port 1│ │port 2│ │port 3│ │ │
│ │ └──┬───┘ └──┬───┘ └──┬───┘ │ │
│ └─────┼────────┼────────┼──────────┘ │
│ │veth │veth │veth │
│ ┌─────┴─┐ ┌───┴───┐ ┌──┴────┐ │
│ │ NS: h1│ │ NS: h2│ │ NS: h3│ │
│ │10.0.0.1│ │10.0.0.2│ │10.0.0.3│ │
│ └───────┘ └───────┘ └───────┘ │
└──────────────────────────────────────────┘
3.2 Mininet 基本使用
# 安装 Mininet
apt-get install mininet
# 或从源码安装(推荐,版本更新)
git clone https://github.com/mininet/mininet.git
cd mininet
./util/install.sh -a
# 创建最简拓扑——2 个主机 + 1 个交换机
sudo mn --topo single,2
# 进入 Mininet CLI
# 常用 CLI 命令
mininet> nodes # 列出所有节点
mininet> links # 列出所有链路
mininet> net # 显示网络信息
mininet> dump # 显示节点详细信息
# 在主机中执行命令
mininet> h1 ping -c 3 h2 # h1 ping h2
mininet> h1 ifconfig # 查看 h1 的网卡配置
mininet> h1 iperf -s & # 在 h1 启动 iperf 服务端
mininet> h2 iperf -c 10.0.0.1 # 从 h2 测试带宽
# 打开 xterm 终端
mininet> xterm h1 h2 # 打开 h1 和 h2 的终端窗口
# 测试全连通性
mininet> pingall # 所有主机互 ping3.3 自定义拓扑
#!/usr/bin/env python3
# custom_topo.py
# 自定义 Mininet 拓扑——模拟数据中心 Fat-Tree
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import OVSSwitch, Controller
from mininet.link import TCLink
from mininet.cli import CLI
from mininet.log import setLogLevel
class DataCenterTopo(Topo):
"""
简化的数据中心拓扑:
- 2 个核心交换机
- 4 个接入交换机
- 8 个主机(每个接入交换机下 2 个)
"""
def build(self):
# 核心交换机
core1 = self.addSwitch('s1')
core2 = self.addSwitch('s2')
# 接入交换机
for i in range(1, 5):
access = self.addSwitch(f's{i+2}')
# 核心到接入的链路——1Gbps,1ms 延迟
self.addLink(core1, access, bw=1000, delay='1ms')
self.addLink(core2, access, bw=1000, delay='1ms')
# 每个接入交换机连接 2 个主机
for j in range(1, 3):
host_id = (i - 1) * 2 + j
host = self.addHost(
f'h{host_id}',
ip=f'10.0.{i}.{j}/24'
)
# 主机到接入的链路——100Mbps,0.5ms 延迟
self.addLink(host, access, bw=100, delay='0.5ms')
def run():
setLogLevel('info')
topo = DataCenterTopo()
net = Mininet(
topo=topo,
switch=OVSSwitch,
link=TCLink,
controller=Controller
)
net.start()
print("=== 数据中心拓扑已启动 ===")
print("核心交换机: s1, s2")
print("接入交换机: s3, s4, s5, s6")
print("主机: h1-h8")
# 运行连通性测试
net.pingAll()
# 进入交互 CLI
CLI(net)
net.stop()
if __name__ == '__main__':
run()3.4 Mininet + tc netem
#!/usr/bin/env python3
# wan_simulation.py
# 使用 Mininet 模拟广域网连接
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.link import TCLink
from mininet.cli import CLI
from mininet.log import setLogLevel
class WANTopo(Topo):
"""
模拟两个数据中心之间的广域网连接
DC1 ─── WAN ─── DC2
h1,h2 (高延迟) h3,h4
"""
def build(self):
# DC1 交换机和主机
s1 = self.addSwitch('s1')
h1 = self.addHost('h1', ip='10.1.0.1/24')
h2 = self.addHost('h2', ip='10.1.0.2/24')
self.addLink(h1, s1, bw=1000, delay='0.1ms')
self.addLink(h2, s1, bw=1000, delay='0.1ms')
# DC2 交换机和主机
s2 = self.addSwitch('s2')
h3 = self.addHost('h3', ip='10.2.0.1/24')
h4 = self.addHost('h4', ip='10.2.0.2/24')
self.addLink(h3, s2, bw=1000, delay='0.1ms')
self.addLink(h4, s2, bw=1000, delay='0.1ms')
# WAN 链路——高延迟、低带宽、有丢包
self.addLink(s1, s2,
bw=100, # 100Mbps
delay='50ms', # 50ms 单向延迟
loss=0.1, # 0.1% 丢包
jitter='10ms' # 10ms 抖动
)
def run():
setLogLevel('info')
net = Mininet(topo=WANTopo(), link=TCLink)
net.start()
# 配置跨 DC 路由
for h in ['h1', 'h2']:
net.get(h).cmd('ip route add 10.2.0.0/24 via 10.1.0.254')
for h in ['h3', 'h4']:
net.get(h).cmd('ip route add 10.1.0.0/24 via 10.2.0.254')
print("=== WAN 模拟拓扑已启动 ===")
print("DC1: h1(10.1.0.1), h2(10.1.0.2)")
print("DC2: h3(10.2.0.1), h4(10.2.0.2)")
print("WAN: 100Mbps, 50ms delay, 0.1% loss")
CLI(net)
net.stop()
if __name__ == '__main__':
run()四、网络混沌工程
4.1 混沌工程的网络维度
混沌工程(Chaos Engineering)的核心思想是在可控的条件下主动引入故障,验证系统的韧性。网络层是混沌工程最重要的维度之一,因为分布式系统的大多数故障都与网络有关:延迟飙升、丢包、分区、DNS 故障、证书过期。
# 网络混沌的常见场景:
# 1. 延迟注入——服务间调用突然变慢
# 验证目标:超时设置是否合理?重试策略是否有效?
# 2. 丢包注入——部分请求失败
# 验证目标:重试机制是否工作?是否有正确的降级策略?
# 3. 网络分区——服务间完全不可达
# 验证目标:系统能否检测分区?能否自动恢复?
# 4. DNS 故障——域名解析失败
# 验证目标:DNS 缓存是否有效?是否有 DNS 故障的降级路径?
# 5. 带宽限制——网络带宽突然降低
# 验证目标:系统在低带宽下的行为?是否有背压机制?
# 6. 连接重置——TCP 连接被意外关闭
# 验证目标:连接池是否能自动恢复?重连逻辑是否正确?4.2 Toxiproxy
Toxiproxy 是 Shopify 开源的 TCP 代理工具,专门用于模拟网络故障。它工作在应用层,作为中间代理拦截 TCP 连接。
# 安装 Toxiproxy
wget https://github.com/Shopify/toxiproxy/releases/download/v2.9.0/toxiproxy-server-linux-amd64
chmod +x toxiproxy-server-linux-amd64
./toxiproxy-server-linux-amd64 &
# 安装 CLI
wget https://github.com/Shopify/toxiproxy/releases/download/v2.9.0/toxiproxy-cli-linux-amd64
chmod +x toxiproxy-cli-linux-amd64
# 创建代理——代理 Redis 连接
./toxiproxy-cli create redis-proxy \
--listen 0.0.0.0:26379 \
--upstream localhost:6379
# 创建代理——代理 MySQL 连接
./toxiproxy-cli create mysql-proxy \
--listen 0.0.0.0:33306 \
--upstream localhost:3306
# 查看所有代理
./toxiproxy-cli list
# 添加毒素——延迟
./toxiproxy-cli toxic add redis-proxy \
--type latency \
--attribute latency=1000 \
--attribute jitter=500
# 所有经过代理的请求延迟 1000ms ± 500ms
# 添加毒素——丢包(带宽限制)
./toxiproxy-cli toxic add redis-proxy \
--type bandwidth \
--attribute rate=100
# 限制带宽到 100KB/s
# 添加毒素——连接超时
./toxiproxy-cli toxic add redis-proxy \
--type timeout \
--attribute timeout=5000
# 5 秒后关闭连接
# 模拟网络分区——完全中断
./toxiproxy-cli toggle redis-proxy
# 代理停止转发,所有连接被拒绝
# 恢复
./toxiproxy-cli toggle redis-proxy4.3 Toxiproxy 编程接口
// Go 测试中使用 Toxiproxy
package main
import (
"testing"
"time"
toxiproxy "github.com/Shopify/toxiproxy/v2/client"
)
func TestRedisWithLatency(t *testing.T) {
client := toxiproxy.NewClient("localhost:8474")
// 创建代理
proxy, err := client.CreateProxy("redis", "localhost:26379", "localhost:6379")
if err != nil {
t.Fatal(err)
}
defer proxy.Delete()
// 添加 500ms 延迟
proxy.AddToxic("latency", "latency", "downstream", 1.0, toxiproxy.Attributes{
"latency": 500,
"jitter": 100,
})
// 测试应用在高延迟下的行为
start := time.Now()
// ... 执行 Redis 操作 ...
elapsed := time.Since(start)
// 验证超时设置是否合理
if elapsed > 2*time.Second {
t.Errorf("Redis operation too slow under latency: %v", elapsed)
}
// 模拟网络分区
proxy.AddToxic("partition", "timeout", "downstream", 1.0, toxiproxy.Attributes{
"timeout": 0,
})
// 验证降级策略
// ... 执行操作,验证返回缓存结果或默认值 ...
}4.4 Comcast
Comcast 是一个轻量级的网络模拟工具,直接调用
tc/iptables
实现网络条件模拟,不需要代理。
# 安装 Comcast
go install github.com/tylertreat/comcast@latest
# 模拟延迟
comcast --device eth0 \
--latency 250 \
--target-bw 1000 \
--default-bw 1000000 \
--packet-loss 10% \
--target-addr 10.0.0.0/24 \
--target-port 80,443
# 参数说明:
# --device 目标网卡
# --latency 延迟(ms)
# --target-bw 目标带宽(kbit)
# --packet-loss 丢包率
# --target-addr 目标地址(只对特定地址生效)
# --target-port 目标端口
# 停止模拟
comcast --device eth0 --stop4.5 工具对比
| 特性 | tc netem | Toxiproxy | Comcast | Pumba |
|---|---|---|---|---|
| 层级 | 内核(L3) | 应用(L4/L7) | 内核(L3) | 容器 |
| 延迟 | ✓ | ✓ | ✓ | ✓ |
| 丢包 | ✓ | ✗(有 bandwidth) | ✓ | ✓ |
| 分区 | iptables 配合 | ✓(toggle) | ✗ | ✓ |
| 选择性 | tc filter | 按代理 | 按 IP/端口 | 按容器 |
| API | CLI | REST API | CLI | CLI |
| 容器支持 | 需要特权 | 天然支持 | 需要特权 | 专为容器 |
| 生产安全 | 需谨慎 | 安全 | 需谨慎 | 安全 |
五、CI/CD 中的网络测试
5.1 测试策略
# CI 中的网络条件测试架构:
#
# ┌─────────────────────────────────┐
# │ CI Pipeline │
# │ ┌───────────────────────────┐ │
# │ │ Unit Tests (no network) │ │
# │ └────────────┬──────────────┘ │
# │ ↓ │
# │ ┌───────────────────────────┐ │
# │ │ Integration Tests │ │
# │ │ (ideal network) │ │
# │ └────────────┬──────────────┘ │
# │ ↓ │
# │ ┌───────────────────────────┐ │
# │ │ Network Resilience Tests │ │
# │ │ (degraded conditions) │ │
# │ └────────────┬──────────────┘ │
# │ ↓ │
# │ ┌───────────────────────────┐ │
# │ │ Chaos Tests (optional) │ │
# │ │ (failure injection) │ │
# │ └───────────────────────────┘ │
# └─────────────────────────────────┘5.2 Docker Compose 网络测试
# docker-compose.test.yml
# 使用 Docker 网络进行集成测试
version: "3.8"
services:
app:
build: .
networks:
- test-net
environment:
REDIS_HOST: redis-proxy
REDIS_PORT: 6379
DB_HOST: db-proxy
DB_PORT: 3306
depends_on:
- toxiproxy
redis:
image: redis:7
networks:
- test-net
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: test
networks:
- test-net
toxiproxy:
image: ghcr.io/shopify/toxiproxy:2.9.0
networks:
- test-net
ports:
- "8474:8474" # API 端口
- "26379:26379" # Redis 代理端口
- "33306:33306" # MySQL 代理端口
test-runner:
build:
context: .
dockerfile: Dockerfile.test
networks:
- test-net
environment:
TOXIPROXY_URL: http://toxiproxy:8474
APP_URL: http://app:8080
depends_on:
- app
- toxiproxy
networks:
test-net:
driver: bridge5.3 GitHub Actions 集成
# .github/workflows/network-resilience.yml
name: Network Resilience Tests
on: [push, pull_request]
jobs:
network-tests:
runs-on: ubuntu-latest
services:
redis:
image: redis:7
ports:
- 6379:6379
toxiproxy:
image: ghcr.io/shopify/toxiproxy:2.9.0
ports:
- 8474:8474
- 26379:26379
steps:
- uses: actions/checkout@v4
- name: Setup Toxiproxy
run: |
# 等待 Toxiproxy 启动
sleep 3
# 创建 Redis 代理
curl -X POST http://localhost:8474/proxies \
-H "Content-Type: application/json" \
-d '{
"name": "redis",
"listen": "0.0.0.0:26379",
"upstream": "redis:6379"
}'
- name: Run normal tests
run: |
REDIS_HOST=localhost REDIS_PORT=26379 \
go test ./... -tags=integration
- name: Run latency tests
run: |
# 添加 200ms 延迟
curl -X POST http://localhost:8474/proxies/redis/toxics \
-H "Content-Type: application/json" \
-d '{
"name": "latency",
"type": "latency",
"attributes": {"latency": 200}
}'
# 运行测试
REDIS_HOST=localhost REDIS_PORT=26379 \
go test ./... -tags=latency -timeout 60s
- name: Run partition tests
run: |
# 模拟网络分区
curl -X POST http://localhost:8474/proxies/redis/toxics \
-H "Content-Type: application/json" \
-d '{
"name": "partition",
"type": "timeout",
"attributes": {"timeout": 0}
}'
# 验证降级行为
REDIS_HOST=localhost REDIS_PORT=26379 \
go test ./... -tags=partition -timeout 60s六、Kubernetes 网络混沌
6.1 Chaos Mesh 网络故障
# Chaos Mesh——CNCF 项目,原生支持 Kubernetes 网络混沌
# 安装 Chaos Mesh
# helm install chaos-mesh chaos-mesh/chaos-mesh -n chaos-mesh --create-namespace
# 网络延迟实验
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: network-delay
namespace: default
spec:
action: delay
mode: all
selector:
namespaces:
- default
labelSelectors:
app: my-service
delay:
latency: "200ms"
jitter: "50ms"
correlation: "25"
duration: "5m"
scheduler:
cron: "@every 1h" # 每小时执行一次# 网络分区实验——模拟 Pod 间网络不可达
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: network-partition
spec:
action: partition
mode: all
selector:
labelSelectors:
app: frontend
direction: both
target:
mode: all
selector:
labelSelectors:
app: backend
duration: "2m"6.2 Litmus Chaos 网络实验
# Litmus Chaos——另一个 CNCF 混沌工程项目
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: pod-network-loss
spec:
appinfo:
appns: default
applabel: app=nginx
chaosServiceAccount: litmus-admin
experiments:
- name: pod-network-loss
spec:
components:
env:
- name: NETWORK_INTERFACE
value: eth0
- name: NETWORK_PACKET_LOSS_PERCENTAGE
value: "50"
- name: TOTAL_CHAOS_DURATION
value: "120"
- name: TARGET_PODS
value: "nginx-pod-1"七、网络模拟最佳实践
7.1 模拟策略
# 网络条件参考值——来自真实网络测量
# 数据中心内部(同可用区)
# RTT: 0.1-0.5ms, 丢包: ~0%, 带宽: 10-100Gbps
# 数据中心之间(同区域,跨可用区)
# RTT: 0.5-2ms, 丢包: ~0%, 带宽: 1-10Gbps
# 跨区域(如北京-上海)
# RTT: 15-30ms, 丢包: 0-0.1%, 带宽: 1-10Gbps
# 跨大洲(如中国-美国)
# RTT: 150-250ms, 丢包: 0.1-1%, 带宽: 100Mbps-1Gbps
# 移动网络 4G
# RTT: 30-100ms, 丢包: 0.5-2%, 带宽: 10-50Mbps
# 移动网络 3G
# RTT: 100-500ms, 丢包: 1-5%, 带宽: 1-5Mbps
# 卫星网络
# RTT: 500-700ms, 丢包: 0.5-2%, 带宽: 1-50Mbps
# WiFi(拥挤环境)
# RTT: 5-50ms, 丢包: 1-5%, 带宽: 10-100Mbps7.2 常见陷阱
# 陷阱 1:只测试单向延迟
# 错误:只在出口添加 netem
# 正确:使用 IFB 同时模拟入口和出口延迟
# 或者认识到 ping RTT = 双向,单向 netem delay = 单向
# 陷阱 2:忘记清理规则
# 危险:测试结束后 netem 规则仍然生效
# 解决:always 使用 trap 或 defer 清理
trap "tc qdisc del dev eth0 root 2>/dev/null" EXIT
# 陷阱 3:netem 影响所有流量
# 问题:SSH 连接也被延迟了,无法远程管理
# 解决:使用 tc filter 排除管理流量
# 或使用独立的管理网卡
# 陷阱 4:容器中使用 netem 需要特权
# 问题:普通容器无法执行 tc 命令
# 解决:添加 NET_ADMIN capability
# docker run --cap-add NET_ADMIN ...
# 陷阱 5:延迟值不现实
# 问题:模拟 5 秒延迟(现实中不存在)
# 解决:参考真实网络测量数据7.3 自动化测试脚本
#!/bin/bash
# network-resilience-test.sh
# 自动化网络韧性测试
set -euo pipefail
APP_URL="http://localhost:8080"
IFACE="eth0"
RESULTS_DIR="./test-results"
mkdir -p "$RESULTS_DIR"
# 清理函数
cleanup() {
echo "清理网络规则..."
tc qdisc del dev $IFACE root 2>/dev/null || true
}
trap cleanup EXIT
# 测试函数——发送请求并记录结果
run_test() {
local test_name="$1"
local condition="$2"
local output_file="$RESULTS_DIR/$test_name.json"
echo "=== 测试: $test_name ==="
echo "条件: $condition"
# 发送 100 个请求,记录延迟
results=$(for i in $(seq 1 100); do
curl -s -o /dev/null -w "%{time_total}" "$APP_URL/health"
echo
done)
# 计算统计数据
echo "$results" | awk '{
sum += $1; sumsq += $1*$1; count++
if (NR == 1 || $1 < min) min = $1
if (NR == 1 || $1 > max) max = $1
} END {
avg = sum/count
stddev = sqrt(sumsq/count - avg*avg)
printf " P50: %.3f P99: %.3f Avg: %.3f Max: %.3f\n", avg, max, avg, max
}'
}
# 基准测试——无网络干扰
run_test "baseline" "无干扰"
# 测试 1:轻度延迟
tc qdisc add dev $IFACE root netem delay 50ms 10ms
run_test "mild-latency" "50ms ± 10ms delay"
tc qdisc del dev $IFACE root
# 测试 2:高延迟
tc qdisc add dev $IFACE root netem delay 200ms 50ms
run_test "high-latency" "200ms ± 50ms delay"
tc qdisc del dev $IFACE root
# 测试 3:丢包
tc qdisc add dev $IFACE root netem loss 5%
run_test "packet-loss" "5% packet loss"
tc qdisc del dev $IFACE root
# 测试 4:组合条件
tc qdisc add dev $IFACE root netem delay 100ms 20ms loss 2%
run_test "combined" "100ms delay + 2% loss"
tc qdisc del dev $IFACE root
echo "=== 测试完成 ==="
echo "结果保存在: $RESULTS_DIR/"八、GNS3 与 EVE-NG
8.1 重量级网络模拟器
# Mininet 适合软件定义网络研究和开发测试
# 当需要模拟真实网络设备(Cisco/Juniper/Arista)时,需要更重的模拟器
# GNS3(Graphical Network Simulator-3)
# - 支持真实网络设备镜像(IOS/IOS-XR/JunOS)
# - 支持 QEMU/VirtualBox/Docker 后端
# - 图形化拓扑设计
# - 适合网络认证学习(CCNA/CCNP)
# EVE-NG(Emulated Virtual Environment - Next Generation)
# - Web 界面管理
# - 支持更多厂商镜像
# - 多用户协作
# - 适合企业网络设计和培训
# 对比选型:
# | 场景 | 推荐工具 |
# |-------------|-------------|
# | SDN 研究 | Mininet |
# | 应用测试 | tc netem |
# | 网络认证学习 | GNS3 |
# | 企业培训 | EVE-NG |
# | 混沌工程 | Toxiproxy |
# | K8s 网络测试 | Chaos Mesh |8.2 NS-3 网络模拟器
# NS-3 是学术界最常用的离散事件网络模拟器
# 适合协议研究和性能建模,不适合应用测试
# 安装 NS-3
git clone https://gitlab.com/nsnam/ns-3-dev.git
cd ns-3-dev
./ns3 configure --enable-examples
./ns3 build
# NS-3 vs Mininet:
# NS-3:纯模拟,不使用真实内核网络栈
# Mininet:使用真实 Linux 内核,运行真实应用
# NS-3 适合:协议设计、理论研究、大规模拓扑
# Mininet 适合:SDN 开发、应用测试、教学参考文献
- Hemminger, S., “Network Emulation with NetEm,” Linux Foundation, 2005.
- Lantz, B., Heller, B., McKeown, N., “A Network in a Laptop: Rapid Prototyping for Software-Defined Networking,” ACM HotNets, 2010.
- Basiri, A. et al., “Chaos Engineering,” IEEE Software, 2016.
- Shopify, “Toxiproxy: A TCP proxy to simulate network conditions,” github.com/Shopify/toxiproxy.
- Chaos Mesh Authors, “Chaos Mesh Documentation,” chaos-mesh.org.
- Linux man page, “tc-netem(8),” man7.org.
上一篇: 可编程数据平面与 P4:软件定义转发 下一篇: 网络技术展望:SmartNIC、CXL 与内核旁路的未来
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【网络工程】带宽管理与流量整形:tc、QoS 与拥塞管理
带宽管理是网络工程的核心能力。本文从 Linux tc 的 qdisc/class/filter 三层模型出发,系统讲解 HTB 分层令牌桶、TBF 令牌桶过滤器、FQ/fq_codel 公平队列、tc-bpf 可编程流量控制、netem 网络模拟,以及 QoS 策略设计和容量规划的工程实践。
网络工程索引
汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。
【网络工程】QUIC 生态与工程部署:从实验到生产
QUIC 已经不是实验性协议——HTTP/3 标准化后,CDN、浏览器和主流服务端框架都在推进 QUIC 支持。本文从工程视角对比主流 QUIC 库的成熟度和性能特征,讲解 CDN/负载均衡器的 QUIC 适配方案、从 TCP 迁移到 QUIC 的渐进路径、QUIC 调试工具链,以及生产环境的部署陷阱和性能调优实践。
【网络工程】eBPF 可编程网络:从包过滤到流量工程
eBPF 正在重新定义网络工程——从传统的 iptables/netfilter 规则堆砌,到可编程、可观测、高性能的网络数据平面。本文系统讲解 eBPF 网络程序类型(XDP/TC/Socket)、Map 数据结构、Cilium 的 eBPF 数据平面实现,以及 eBPF 在负载均衡、可观测性和网络安全中的工程实践。