HiHuo
首页
博客
手册
工具
首页
博客
手册
工具
  • 手撸容器系统

    • 完整手撸容器技术文档系列
    • 01-容器本质与基础概念
    • 02-Namespace隔离机制
    • 03-CGroup资源控制
    • 04-Capabilities与安全机制
    • 05-容器网络原理
    • 06-网络模式与实现
    • 07-CNI插件开发
    • 08-RootFS与文件系统隔离
    • 09-OverlayFS镜像分层
    • 10-命令行手撸容器
    • 11-Go实现最小容器
    • 12-Go实现完整容器
    • 13-容器生命周期管理
    • 14-调试技术与工具
    • 15-OCI规范与标准化
    • 16-进阶场景与优化
    • 常见问题与故障排查
    • 参考资料与延伸阅读

05-容器网络原理

学习目标

  • 深入理解 Network Namespace 的工作原理
  • 掌握 veth pair 虚拟网卡机制
  • 理解 Linux Bridge 的工作原理
  • 掌握 iptables NAT 配置
  • 能够追踪数据包的完整路径

前置知识

  • Linux 网络基础
  • TCP/IP 协议栈
  • 网络接口概念
  • iptables 基础

一、容器网络概述

1.1 容器网络挑战

容器网络需要解决以下问题:

graph TD
    A[容器网络需求] --> B[网络隔离]
    A --> C[网络连通]
    A --> D[网络管理]
    A --> E[性能优化]
    
    B --> B1[每个容器独立网络栈]
    B --> B2[独立的IP地址空间]
    B --> B3[独立的端口空间]
    
    C --> C1[容器间通信]
    C --> C2[容器与宿主机通信]
    C --> C3[容器与外部网络通信]
    
    D --> D1[IP地址分配]
    D --> D2[路由配置]
    D --> D3[防火墙规则]
    
    E --> E1[低延迟]
    E --> E2[高带宽]
    E --> E3[低CPU开销]

1.2 容器网络解决方案

graph LR
    A[容器网络] --> B[Network Namespace]
    A --> C[veth pair]
    A --> D[Linux Bridge]
    A --> E[iptables NAT]
    A --> F[路由表]
    
    B --> B1[网络隔离]
    C --> C1[虚拟网卡对]
    D --> D1[二层转发]
    E --> E1[地址转换]
    F --> F1[三层路由]

二、Network Namespace 详解

2.1 Network Namespace 原理

Network Namespace 是 Linux 内核提供的网络协议栈隔离机制:

graph TD
    A[Linux 内核] --> B[网络协议栈1]
    A --> C[网络协议栈2]
    A --> D[网络协议栈3]
    
    B --> B1[网络接口]
    B --> B2[IP地址]
    B --> B3[路由表]
    B --> B4[iptables规则]
    
    C --> C1[网络接口]
    C --> C2[IP地址]
    C --> C3[路由表]
    C --> C4[iptables规则]
    
    D --> D1[网络接口]
    D --> D2[IP地址]
    D --> D3[路由表]
    D --> D4[iptables规则]

2.2 Network Namespace 特性

特性说明示例
独立网络接口每个 namespace 有独立的网络接口列表ip link show
独立IP地址每个 namespace 有独立的IP地址空间ip addr show
独立路由表每个 namespace 有独立的路由表ip route show
独立端口空间每个 namespace 有独立的端口空间netstat -tlnp
独立防火墙规则每个 namespace 有独立的iptables规则iptables -L

2.3 Network Namespace 实战演示

2.3.1 创建和查看 Network Namespace

# 1. 创建 Network Namespace
sudo ip netns add test-ns

# 2. 查看所有 Network Namespace
ip netns list
# 输出: test-ns

# 3. 在 Network Namespace 中执行命令
sudo ip netns exec test-ns ip link show
# 输出: 只有 lo 接口

# 4. 启动 lo 接口
sudo ip netns exec test-ns ip link set lo up

# 5. 查看网络接口状态
sudo ip netns exec test-ns ip addr show
# 输出: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN

2.3.2 Network Namespace 隔离验证

# 在宿主机上查看网络接口
ip link show
# 输出: 包含 eth0, lo 等接口

# 在 test-ns 中查看网络接口
sudo ip netns exec test-ns ip link show
# 输出: 只有 lo 接口

# 在宿主机上查看路由表
ip route show
# 输出: 包含默认路由等

# 在 test-ns 中查看路由表
sudo ip netns exec test-ns ip route show
# 输出: 空(没有路由)

三、veth pair 虚拟网卡

3.1 veth pair 原理

veth pair 是 Linux 内核提供的虚拟以太网设备对:

graph LR
    A[veth0] <--> B[veth1]
    A --> C[宿主机网络栈]
    B --> D[容器网络栈]
    
    E[数据包] --> A
    A --> F[转发到 veth1]
    F --> B
    B --> G[容器接收]

3.2 veth pair 特性

  • 成对出现:veth0 和 veth1 总是成对创建
  • 数据转发:从一端进入的数据会从另一端出来
  • 双向通信:支持双向数据传输
  • 命名空间隔离:两端可以分别放在不同的 Network Namespace 中

3.3 veth pair 实战演示

3.3.1 创建 veth pair

# 1. 创建 veth pair
sudo ip link add veth0 type veth peer name veth1

# 2. 查看创建的 veth pair
ip link show | grep veth
# 输出: 
# 4: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN
# 5: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN

# 3. 启动 veth0
sudo ip link set veth0 up

# 4. 将 veth1 移到 test-ns
sudo ip link set veth1 netns test-ns

# 5. 在 test-ns 中启动 veth1
sudo ip netns exec test-ns ip link set veth1 up

3.3.2 配置 IP 地址

# 1. 给 veth0 配置 IP 地址
sudo ip addr add 192.168.1.1/24 dev veth0

# 2. 给 veth1 配置 IP 地址
sudo ip netns exec test-ns ip addr add 192.168.1.2/24 dev veth1

# 3. 验证配置
ip addr show veth0
# 输出: inet 192.168.1.1/24 scope global veth0

sudo ip netns exec test-ns ip addr show veth1
# 输出: inet 192.168.1.2/24 scope global veth1

3.3.3 测试连通性

# 1. 从宿主机 ping 容器
ping -c 3 192.168.1.2
# 输出: 64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.123 ms

# 2. 从容器 ping 宿主机
sudo ip netns exec test-ns ping -c 3 192.168.1.1
# 输出: 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.098 ms

四、Linux Bridge 详解

4.1 Linux Bridge 原理

Linux Bridge 是内核提供的二层虚拟交换机:

graph TD
    A[Linux Bridge] --> B[学习表]
    A --> C[转发规则]
    
    D[veth0] --> A
    E[veth1] --> A
    F[veth2] --> A
    G[eth0] --> A
    
    A --> H[根据MAC地址转发]
    H --> I[广播/单播/组播]

4.2 Bridge 特性

  • MAC 学习:自动学习 MAC 地址与端口的对应关系
  • 广播转发:向所有端口转发广播帧
  • 单播转发:根据 MAC 地址表转发单播帧
  • VLAN 支持:支持 VLAN 隔离
  • STP 支持:支持生成树协议

4.3 Bridge 实战演示

4.3.1 创建和配置 Bridge

# 1. 创建 Bridge
sudo ip link add name br0 type bridge

# 2. 启动 Bridge
sudo ip link set br0 up

# 3. 给 Bridge 配置 IP 地址
sudo ip addr add 192.168.100.1/24 dev br0

# 4. 查看 Bridge 状态
ip link show br0
# 输出: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP

4.3.2 连接 veth 到 Bridge

# 1. 创建多个 veth pair
sudo ip link add veth-host1 type veth peer name veth-cont1
sudo ip link add veth-host2 type veth peer name veth-cont2

# 2. 将 veth-host 端连接到 Bridge
sudo ip link set veth-host1 master br0
sudo ip link set veth-host2 master br0

# 3. 启动 veth-host 端
sudo ip link set veth-host1 up
sudo ip link set veth-host2 up

# 4. 将 veth-cont 端移到不同的 namespace
sudo ip link set veth-cont1 netns test-ns
sudo ip netns add test-ns2
sudo ip link set veth-cont2 netns test-ns2

# 5. 在 namespace 中配置 veth-cont 端
sudo ip netns exec test-ns ip link set veth-cont1 up
sudo ip netns exec test-ns ip addr add 192.168.100.2/24 dev veth-cont1

sudo ip netns exec test-ns2 ip link set veth-cont2 up
sudo ip netns exec test-ns2 ip addr add 192.168.100.3/24 dev veth-cont2

4.3.3 测试容器间通信

# 1. 从 test-ns ping test-ns2
sudo ip netns exec test-ns ping -c 3 192.168.100.3
# 输出: 64 bytes from 192.168.100.3: icmp_seq=1 ttl=64 time=0.123 ms

# 2. 从 test-ns2 ping test-ns
sudo ip netns exec test-ns2 ping -c 3 192.168.100.2
# 输出: 64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=0.098 ms

# 3. 查看 Bridge 的 MAC 学习表
bridge fdb show br br0
# 输出: 显示 MAC 地址与端口的对应关系

五、iptables NAT 配置

5.1 NAT 原理

NAT (Network Address Translation) 用于实现地址转换:

graph TD
    A[容器网络] --> B[192.168.100.0/24]
    B --> C[SNAT 转换]
    C --> D[宿主机IP]
    D --> E[外部网络]
    
    F[外部网络] --> G[宿主机IP]
    G --> H[DNAT 转换]
    H --> I[容器IP]
    I --> J[容器网络]

5.2 NAT 类型

类型全称作用使用场景
SNATSource NAT源地址转换容器访问外网
DNATDestination NAT目标地址转换外网访问容器
MASQUERADE动态SNAT自动获取源IP动态IP环境

5.3 NAT 实战演示

5.3.1 配置 SNAT 出网

# 1. 启用 IP 转发
sudo sysctl -w net.ipv4.ip_forward=1

# 2. 配置 SNAT 规则
sudo iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE

# 3. 查看 NAT 规则
sudo iptables -t nat -L POSTROUTING
# 输出: 
# Chain POSTROUTING (policy ACCEPT)
# target     prot opt source               destination         
# MASQUERADE  all  --  192.168.100.0/24    anywhere

# 4. 在容器中测试出网
sudo ip netns exec test-ns ping -c 3 8.8.8.8
# 输出: 64 bytes from 8.8.8.8: icmp_seq=1 ttl=64 time=12.345 ms

5.3.2 配置 DNAT 入网

# 1. 配置 DNAT 规则(将宿主机8080端口映射到容器80端口)
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.100.2:80

# 2. 配置 FORWARD 规则
sudo iptables -A FORWARD -p tcp -d 192.168.100.2 --dport 80 -j ACCEPT

# 3. 查看 NAT 规则
sudo iptables -t nat -L PREROUTING
# 输出:
# Chain PREROUTING (policy ACCEPT)
# target     prot opt source               destination         
# DNAT       tcp  --  anywhere             anywhere            tcp dpt:8080 to:192.168.100.2:80

六、数据包路径追踪

6.1 完整数据包路径

sequenceDiagram
    participant C as 容器
    participant V as veth pair
    participant B as Bridge
    participant I as iptables
    participant H as 宿主机网卡
    participant E as 外部网络
    
    C->>V: 发送数据包
    V->>B: 转发到 Bridge
    B->>I: 经过 iptables
    I->>H: SNAT 转换
    H->>E: 发送到外部网络
    
    E->>H: 返回数据包
    H->>I: 经过 iptables
    I->>B: DNAT 转换
    B->>V: 转发到 veth
    V->>C: 返回给容器

6.2 数据包处理流程

6.2.1 出网流程

  1. 容器发送:容器进程发送数据包
  2. veth 转发:veth pair 将数据包转发到 Bridge
  3. Bridge 学习:Bridge 学习 MAC 地址
  4. iptables 处理:经过 iptables 规则处理
  5. SNAT 转换:将源 IP 转换为宿主机 IP
  6. 路由转发:通过宿主机网卡发送到外部网络

6.2.2 入网流程

  1. 外部接收:宿主机网卡接收数据包
  2. iptables 处理:经过 iptables 规则处理
  3. DNAT 转换:将目标 IP 转换为容器 IP
  4. Bridge 转发:Bridge 根据 MAC 地址转发
  5. veth 转发:veth pair 将数据包转发到容器
  6. 容器接收:容器进程接收数据包

6.3 调试工具

6.3.1 使用 tcpdump 抓包

# 1. 在宿主机上抓包
sudo tcpdump -i br0 -n

# 2. 在容器中抓包
sudo ip netns exec test-ns tcpdump -i veth-cont1 -n

# 3. 抓取特定协议
sudo tcpdump -i br0 -n icmp

# 4. 抓取特定端口
sudo tcpdump -i br0 -n port 80

6.3.2 使用 iptables 日志

# 1. 添加日志规则
sudo iptables -A FORWARD -j LOG --log-prefix "FORWARD: "

# 2. 查看日志
sudo tail -f /var/log/kern.log | grep FORWARD

# 3. 删除日志规则
sudo iptables -D FORWARD -j LOG --log-prefix "FORWARD: "

️ 七、综合实战:搭建完整容器网络

7.1 自动化脚本

#!/bin/bash
# 搭建完整的容器网络环境

echo "=== 搭建容器网络环境 ==="

# 1. 创建 Network Namespace
sudo ip netns add container1
sudo ip netns add container2

# 2. 创建 Bridge
sudo ip link add name br0 type bridge
sudo ip link set br0 up
sudo ip addr add 192.168.100.1/24 dev br0

# 3. 创建 veth pair
sudo ip link add veth-host1 type veth peer name veth-cont1
sudo ip link add veth-host2 type veth peer name veth-cont2

# 4. 连接 veth 到 Bridge
sudo ip link set veth-host1 master br0
sudo ip link set veth-host2 master br0
sudo ip link set veth-host1 up
sudo ip link set veth-host2 up

# 5. 配置容器网络
sudo ip link set veth-cont1 netns container1
sudo ip link set veth-cont2 netns container2

sudo ip netns exec container1 ip link set veth-cont1 up
sudo ip netns exec container1 ip addr add 192.168.100.2/24 dev veth-cont1
sudo ip netns exec container1 ip route add default via 192.168.100.1

sudo ip netns exec container2 ip link set veth-cont2 up
sudo ip netns exec container2 ip addr add 192.168.100.3/24 dev veth-cont2
sudo ip netns exec container2 ip route add default via 192.168.100.1

# 6. 配置 NAT
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE

# 7. 测试连通性
echo "=== 测试容器间通信 ==="
sudo ip netns exec container1 ping -c 3 192.168.100.3

echo "=== 测试出网 ==="
sudo ip netns exec container1 ping -c 3 8.8.8.8

echo "=== 网络环境搭建完成 ==="
echo "容器1: 192.168.100.2"
echo "容器2: 192.168.100.3"
echo "Bridge: 192.168.100.1"

7.2 Go 代码实现

package main

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

func createNetworkNamespace(name string) error {
    // 创建 Network Namespace
    cmd := exec.Command("ip", "netns", "add", name)
    return cmd.Run()
}

func createBridge(name, cidr string) error {
    // 创建 Bridge
    if err := exec.Command("ip", "link", "add", "name", name, "type", "bridge").Run(); err != nil {
        return err
    }
    
    // 启动 Bridge
    if err := exec.Command("ip", "link", "set", name, "up").Run(); err != nil {
        return err
    }
    
    // 配置 IP 地址
    if err := exec.Command("ip", "addr", "add", cidr, "dev", name).Run(); err != nil {
        return err
    }
    
    return nil
}

func createVethPair(hostIf, contIf string) error {
    // 创建 veth pair
    return exec.Command("ip", "link", "add", hostIf, "type", "veth", "peer", "name", contIf).Run()
}

func connectVethToBridge(hostIf, bridgeName string) error {
    // 连接 veth 到 Bridge
    if err := exec.Command("ip", "link", "set", hostIf, "master", bridgeName).Run(); err != nil {
        return err
    }
    
    // 启动 veth
    return exec.Command("ip", "link", "set", hostIf, "up").Run()
}

func configureContainerNetwork(nsName, contIf, ip, gateway string) error {
    // 将 veth 移到 namespace
    if err := exec.Command("ip", "link", "set", contIf, "netns", nsName).Run(); err != nil {
        return err
    }
    
    // 在 namespace 中配置网络
    commands := [][]string{
        {"ip", "netns", "exec", nsName, "ip", "link", "set", contIf, "up"},
        {"ip", "netns", "exec", nsName, "ip", "addr", "add", ip, "dev", contIf},
        {"ip", "netns", "exec", nsName, "ip", "route", "add", "default", "via", gateway},
    }
    
    for _, cmd := range commands {
        if err := exec.Command(cmd[0], cmd[1:]...).Run(); err != nil {
            return err
        }
    }
    
    return nil
}

func configureNAT(subnet string) error {
    // 启用 IP 转发
    if err := exec.Command("sysctl", "-w", "net.ipv4.ip_forward=1").Run(); err != nil {
        return err
    }
    
    // 配置 SNAT
    return exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-s", subnet, "-j", "MASQUERADE").Run()
}

func main() {
    // 创建网络环境
    if err := createNetworkNamespace("container1"); err != nil {
        fmt.Printf("创建 namespace 失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := createNetworkNamespace("container2"); err != nil {
        fmt.Printf("创建 namespace 失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := createBridge("br0", "192.168.100.1/24"); err != nil {
        fmt.Printf("创建 Bridge 失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := createVethPair("veth-host1", "veth-cont1"); err != nil {
        fmt.Printf("创建 veth pair 失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := createVethPair("veth-host2", "veth-cont2"); err != nil {
        fmt.Printf("创建 veth pair 失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := connectVethToBridge("veth-host1", "br0"); err != nil {
        fmt.Printf("连接 veth 到 Bridge 失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := connectVethToBridge("veth-host2", "br0"); err != nil {
        fmt.Printf("连接 veth 到 Bridge 失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := configureContainerNetwork("container1", "veth-cont1", "192.168.100.2/24", "192.168.100.1"); err != nil {
        fmt.Printf("配置容器网络失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := configureContainerNetwork("container2", "veth-cont2", "192.168.100.3/24", "192.168.100.1"); err != nil {
        fmt.Printf("配置容器网络失败: %v\n", err)
        os.Exit(1)
    }
    
    if err := configureNAT("192.168.100.0/24"); err != nil {
        fmt.Printf("配置 NAT 失败: %v\n", err)
        os.Exit(1)
    }
    
    fmt.Println("容器网络环境创建成功!")
    fmt.Println("容器1: 192.168.100.2")
    fmt.Println("容器2: 192.168.100.3")
    fmt.Println("Bridge: 192.168.100.1")
}

八、验证检查清单

基础理解

  • [ ] 理解 Network Namespace 的作用和原理
  • [ ] 掌握 veth pair 的工作机制
  • [ ] 理解 Linux Bridge 的工作原理
  • [ ] 掌握 iptables NAT 配置

实践能力

  • [ ] 能够创建和配置 Network Namespace
  • [ ] 能够创建和配置 veth pair
  • [ ] 能够创建和配置 Linux Bridge
  • [ ] 能够配置 iptables NAT 规则
  • [ ] 能够搭建完整的容器网络环境

调试技能

  • [ ] 掌握网络调试工具的使用
  • [ ] 能够追踪数据包路径
  • [ ] 能够排查网络连通性问题
  • [ ] 理解网络性能优化方法

相关链接

  • 02-Namespace隔离机制 - 进程隔离技术
  • 06-网络模式与实现 - 网络模式详解
  • 07-CNI插件开发 - 网络插件开发

下一步:让我们学习不同的网络模式与实现,这是容器网络的高级应用!

Prev
04-Capabilities与安全机制
Next
06-网络模式与实现