容器网络CNI实战:从零搭建网络插件
一、CNI概述
CNI(Container Network Interface)是容器网络的标准化接口,定义了容器网络配置和管理的规范。
1.1 CNI架构
┌─────────────────────┐ │ Container │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ CNI Plugin │ │ (bridge, macvlan, │ │ flannel, calico) │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Network │ │ (veth, bridge, │ │ route, iptables) │ └─────────────────────┘1.2 CNI规范
CNI插件需要支持以下操作:
- ADD:将容器添加到网络
- DEL:从网络中移除容器
- CHECK:检查网络状态
- VERSION:返回版本信息
二、CNI配置文件
2.1 配置文件结构
{ "cniVersion": "0.4.0", "name": "my-network", "type": "bridge", "bridge": "cni0", "isGateway": true, "ipMasq": true, "ipam": { "type": "host-local", "subnet": "10.244.0.0/16", "routes": [ { "dst": "0.0.0.0/0" } ] } }2.2 配置参数说明
| 参数 | 说明 | 必填 |
|---|---|---|
| cniVersion | CNI版本 | 是 |
| name | 网络名称 | 是 |
| type | 插件类型 | 是 |
| bridge | 网桥名称 | 否 |
| isGateway | 是否作为网关 | 否 |
| ipMasq | 是否启用IP伪装 | 否 |
| ipam | IP地址管理配置 | 是 |
三、Bridge插件实战
3.1 安装CNI工具
# 下载CNI插件 mkdir -p /opt/cni/bin cd /opt/cni/bin wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz tar -xzf cni-plugins-linux-amd64-v1.3.0.tgz3.2 创建配置文件
mkdir -p /etc/cni/net.d cat > /etc/cni/net.d/10-bridge.conf <<EOF { "cniVersion": "0.4.0", "name": "bridge-network", "type": "bridge", "bridge": "cni0", "isGateway": true, "ipMasq": true, "ipam": { "type": "host-local", "subnet": "10.100.0.0/16", "gateway": "10.100.0.1", "routes": [ { "dst": "0.0.0.0/0" } ] } } EOF3.3 手动调用CNI插件
# 创建网络命名空间 ip netns add test-namespace # 创建veth对 ip link add veth0 type veth peer name veth0-br # 将veth一端加入网桥 ip link set veth0-br master cni0 # 将veth另一端加入命名空间 ip link set veth0 netns test-namespace # 调用CNI ADD命令 cat > /tmp/cni-args.json <<EOF { "containerid": "test-container", "netns": "/var/run/netns/test-namespace", "ifname": "eth0", "args": { "ignoreUnknown": true } } EOF ./bridge < /tmp/cni-args.json3.4 Go语言调用CNI
package main import ( "fmt" "os" "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" ) func main() { netConf := []byte(`{ "cniVersion": "0.4.0", "name": "test-network", "type": "bridge", "ipam": { "type": "host-local", "subnet": "10.200.0.0/16" } }`) result, err := invoke.ExecPlugin( "/opt/cni/bin/bridge", netConf, []string{ "CNI_COMMAND=ADD", "CNI_CONTAINERID=test-container", "CNI_NETNS=/var/run/netns/test", "CNI_IFNAME=eth0", "CNI_PATH=/opt/cni/bin", }, ) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } fmt.Println(string(result)) }四、Flannel网络方案
4.1 Flannel架构
┌─────────────────┐ VXLAN ┌─────────────────┐ │ Node A │ ──────────────> │ Node B │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ Container │ │ │ │ Container │ │ │ └─────┬─────┘ │ │ └─────┬─────┘ │ │ │ │ │ │ │ │ ┌─────┴─────┐ │ │ ┌─────┴─────┐ │ │ │ flannel0 │ │ │ │ flannel0 │ │ │ └─────┬─────┘ │ │ └─────┬─────┘ │ │ │ │ │ │ │ │ ┌─────┴─────┐ │ │ ┌─────┴─────┐ │ │ │ eth0 │ │────────────────│ │ eth0 │ │ │ └───────────┘ │ │ └───────────┘ │ └─────────────────┘ └─────────────────┘4.2 安装Flannel
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml4.3 Flannel配置
apiVersion: v1 kind: ConfigMap metadata: name: kube-flannel-cfg namespace: kube-system data: cni-conf.json: | { "name": "cbr0", "cniVersion": "0.3.1", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] } net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan" } }五、Calico网络方案
5.1 Calico架构
┌─────────────────────────────────────────────────────────────┐ │ Felix Agent │ │ (管理路由、iptables规则) │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ BGP Router │ │ IPAM │ │ Policy Engine │ │ (路由分发) │ │ (IP分配) │ │ (网络策略) │ └─────────────────┘ └─────────────────┘ └─────────────────┘5.2 安装Calico
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml5.3 Calico网络策略
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: default spec: podSelector: {} policyTypes: - Ingress - Egress --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-http namespace: default spec: podSelector: matchLabels: app: web ingress: - from: - ipBlock: cidr: 10.0.0.0/8 ports: - protocol: TCP port: 80六、自定义CNI插件开发
6.1 插件结构
package main import ( "fmt" "os" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" ) func cmdAdd(args *skel.CmdArgs) error { // 解析配置 conf, err := parseConfig(args.StdinData) if err != nil { return fmt.Errorf("failed to parse config: %v", err) } // 创建网络接口 iface, err := setupInterface(args) if err != nil { return fmt.Errorf("failed to setup interface: %v", err) } // 配置IP地址 result, err := assignIP(conf, iface) if err != nil { return fmt.Errorf("failed to assign IP: %v", err) } return types.PrintResult(result, conf.CNIVersion) } func cmdDel(args *skel.CmdArgs) error { // 清理网络接口 return teardownInterface(args) } func main() { skel.PluginMain(cmdAdd, cmdDel, version.All) }6.2 网络配置解析
type NetConf struct { types.NetConf Bridge string `json:"bridge"` VlanID int `json:"vlanID"` MTU int `json:"mtu"` } func parseConfig(data []byte) (*NetConf, error) { conf := &NetConf{} if err := json.Unmarshal(data, conf); err != nil { return nil, fmt.Errorf("failed to unmarshal config: %v", err) } // 设置默认值 if conf.Bridge == "" { conf.Bridge = "cni0" } if conf.MTU == 0 { conf.MTU = 1500 } return conf, nil }6.3 接口创建
func setupInterface(args *skel.CmdArgs) (*current.Interface, error) { // 创建veth对 hostVeth, contVeth, err := netlink.VethAdd(args.Netns, args.IfName) if err != nil { return nil, fmt.Errorf("failed to create veth: %v", err) } // 将host端加入网桥 bridge, err := netlink.LinkByName(conf.Bridge) if err != nil { return nil, fmt.Errorf("failed to find bridge: %v", err) } if err := netlink.LinkSetMaster(hostVeth, bridge); err != nil { return nil, fmt.Errorf("failed to add to bridge: %v", err) } return ¤t.Interface{ Name: contVeth.Name, Mac: contVeth.HardwareAddr.String(), Sandbox: args.Netns, }, nil }七、网络诊断与调试
7.1 常用命令
# 查看网络命名空间 ip netns list # 进入命名空间执行命令 ip netns exec test-namespace ip addr # 查看路由表 ip route show # 查看iptables规则 iptables -t nat -L # 查看网桥信息 brctl show # 查看CNI配置 cat /etc/cni/net.d/*.conf7.2 网络连通性测试
# 测试Pod间通信 kubectl exec -it pod1 -- ping pod2-ip # 测试Pod到外部网络 kubectl exec -it pod1 -- ping 8.8.8.8 # 检查DNS解析 kubectl exec -it pod1 -- nslookup kubernetes.default八、性能优化
8.1 调整MTU
{ "cniVersion": "0.4.0", "name": "optimized-network", "type": "bridge", "mtu": 9001, "ipam": { "type": "host-local", "subnet": "10.244.0.0/16" } }8.2 使用高性能驱动
apiVersion: v1 kind: ConfigMap metadata: name: kube-flannel-cfg data: net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "host-gw" } }8.3 禁用不必要的功能
{ "cniVersion": "0.4.0", "name": "minimal-network", "type": "bridge", "ipMasq": false, "ipam": { "type": "host-local", "subnet": "10.200.0.0/16" } }九、最佳实践总结
9.1 插件选择建议
| 场景 | 推荐插件 | 原因 |
|---|---|---|
| 单节点 | bridge | 简单高效 |
| 多节点 | flannel | 易于部署 |
| 大规模集群 | calico | 高性能、灵活策略 |
| 网络隔离要求高 | calico | 强大的网络策略 |
9.2 配置建议
- 子网规划:根据集群规模合理分配CIDR
- MTU设置:跨节点通信建议使用Jumbo Frame
- IPAM选择:host-local适合小规模,dhcp适合大规模
- 安全策略:生产环境建议启用网络策略
9.3 监控指标
- 网络延迟:Pod间通信延迟
- 带宽利用率:各节点网络使用情况
- 丢包率:网络传输质量
- 连接数:容器网络连接状态
通过合理选择和配置CNI插件,可以构建高效、可靠的容器网络环境。