容器网络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 配置文件结构
json
{
"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工具
bash
# 下载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.tgz
3.2 创建配置文件
bash
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" }
]
}
}
EOF
3.3 手动调用CNI插件
bash
# 创建网络命名空间
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.json
3.4 Go语言调用CNI
go
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
yaml
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
4.3 Flannel配置
yaml
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
yaml
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
5.3 Calico网络策略
yaml
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 插件结构
go
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 网络配置解析
go
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 接口创建
go
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 常用命令
bash
# 查看网络命名空间
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/*.conf
7.2 网络连通性测试
bash
# 测试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
json
{
"cniVersion": "0.4.0",
"name": "optimized-network",
"type": "bridge",
"mtu": 9001,
"ipam": {
"type": "host-local",
"subnet": "10.244.0.0/16"
}
}
8.2 使用高性能驱动
yaml
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 禁用不必要的功能
json
{
"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插件,可以构建高效、可靠的容器网络环境。