土法炼钢兴趣小组的算法知识备份

【网络工程】网络模拟与测试:netem、Mininet 与混沌工程

文章导航

分类入口
network
标签入口
#netem#mininet#chaos-engineering#network-testing#tc#simulation

目录

你的应用在本地开发环境跑得飞快,上线后用户投诉”卡”——这几乎是每个工程师都经历过的场景。本地环境的网络条件太”完美”了:零丢包、亚毫秒延迟、千兆带宽。而真实世界的网络充满了延迟抖动、偶发丢包、带宽波动和链路故障。如果你不在测试阶段模拟这些条件,生产环境就是你的测试环境。

本文覆盖三个层次的网络模拟与测试: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% ecn

1.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:1

2.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 down

2.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        # 所有主机互 ping

3.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-proxy

4.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 --stop

4.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: bridge

5.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-100Mbps

7.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 开发、应用测试、教学

参考文献

  1. Hemminger, S., “Network Emulation with NetEm,” Linux Foundation, 2005.
  2. Lantz, B., Heller, B., McKeown, N., “A Network in a Laptop: Rapid Prototyping for Software-Defined Networking,” ACM HotNets, 2010.
  3. Basiri, A. et al., “Chaos Engineering,” IEEE Software, 2016.
  4. Shopify, “Toxiproxy: A TCP proxy to simulate network conditions,” github.com/Shopify/toxiproxy.
  5. Chaos Mesh Authors, “Chaos Mesh Documentation,” chaos-mesh.org.
  6. Linux man page, “tc-netem(8),” man7.org.

上一篇: 可编程数据平面与 P4:软件定义转发 下一篇: 网络技术展望:SmartNIC、CXL 与内核旁路的未来

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-08-02 · network

【网络工程】带宽管理与流量整形:tc、QoS 与拥塞管理

带宽管理是网络工程的核心能力。本文从 Linux tc 的 qdisc/class/filter 三层模型出发,系统讲解 HTB 分层令牌桶、TBF 令牌桶过滤器、FQ/fq_codel 公平队列、tc-bpf 可编程流量控制、netem 网络模拟,以及 QoS 策略设计和容量规划的工程实践。

2026-04-22 · network

网络工程索引

汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。

2025-08-04 · network

【网络工程】QUIC 生态与工程部署:从实验到生产

QUIC 已经不是实验性协议——HTTP/3 标准化后,CDN、浏览器和主流服务端框架都在推进 QUIC 支持。本文从工程视角对比主流 QUIC 库的成熟度和性能特征,讲解 CDN/负载均衡器的 QUIC 适配方案、从 TCP 迁移到 QUIC 的渐进路径、QUIC 调试工具链,以及生产环境的部署陷阱和性能调优实践。

2025-08-05 · network

【网络工程】eBPF 可编程网络:从包过滤到流量工程

eBPF 正在重新定义网络工程——从传统的 iptables/netfilter 规则堆砌,到可编程、可观测、高性能的网络数据平面。本文系统讲解 eBPF 网络程序类型(XDP/TC/Socket)、Map 数据结构、Cilium 的 eBPF 数据平面实现,以及 eBPF 在负载均衡、可观测性和网络安全中的工程实践。


By .