版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhonglinzhang/article/details/84637343
源码路径: https://github.com/containernetworking/plugins
版本: v.0.10.0
flannel cni路径: plugins/plugins/meta/flannel/flannel.go
subnet.env文件
# cat /run/flannel/subnet.env
FLANNEL_NETWORK=172.30.0.0/16
FLANNEL_SUBNET=172.30.45.1/24
FLANNEL_MTU=1500
FLANNEL_IPMASQ=false
cni 配置文件
# cat /etc/cni/net.d/10-flannel.conf
{"name":"cbr0","type":"flannel","delegate": {"bridge": "docker0", "isDefaultGateway": true, "ipMasq": false}}
NetConf结构体
subnetFile文件默认为:/run/flannel/subnet.env
dataDIr默认为:/var/lib/cni/flannel
type NetConf struct {
types.NetConf
SubnetFile string `json:"subnetFile"`
DataDir string `json:"dataDir"`
Delegate map[string]interface{} `json:"delegate"`
}
1. cmdAdd函数
1.1 loadFlannelNetConf函数
将传入的参数解析到NetConf结构体,这里设置了默认的subnetFile,与dataDir
n, err := loadFlannelNetConf(args.StdinData)
if err != nil {
return err
}
1.2 loadFlannelSubnetEnv函数
从/run/flannel/subnet.env读取配置,包括:
- FLANNEL_NETWORK=172.30.0.0/16
- FLANNEL_SUBNET=172.30.46.1/24
- FLANNEL_MTU=1500
- FLANNEL_IPMASQ=false
fenv, err := loadFlannelSubnetEnv(n.SubnetFile)
if err != nil {
return err
}
1.3 subnet.env验证参数合法性
if n.Delegate == nil {
n.Delegate = make(map[string]interface{})
} else {
if hasKey(n.Delegate, "type") && !isString(n.Delegate["type"]) {
return fmt.Errorf("'delegate' dictionary, if present, must have (string) 'type' field")
}
if hasKey(n.Delegate, "name") {
return fmt.Errorf("'delegate' dictionary must not have 'name' field, it'll be set by flannel")
}
if hasKey(n.Delegate, "ipam") {
return fmt.Errorf("'delegate' dictionary must not have 'ipam' field, it'll be set by flannel")
}
}
1.4 doCmdAdd函数
对于未设置的参数初始化,如果未设置ipam则使用默认host-local
func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
n.Delegate["name"] = n.Name
if !hasKey(n.Delegate, "type") {
n.Delegate["type"] = "bridge"
}
if !hasKey(n.Delegate, "ipMasq") {
// if flannel is not doing ipmasq, we should
ipmasq := !*fenv.ipmasq
n.Delegate["ipMasq"] = ipmasq
}
if !hasKey(n.Delegate, "mtu") {
mtu := fenv.mtu
n.Delegate["mtu"] = mtu
}
if n.Delegate["type"].(string) == "bridge" {
if !hasKey(n.Delegate, "isGateway") {
n.Delegate["isGateway"] = true
}
}
if n.CNIVersion != "" {
n.Delegate["cniVersion"] = n.CNIVersion
}
n.Delegate["ipam"] = map[string]interface{}{
"type": "host-local",
"subnet": fenv.sn.String(),
"routes": []types.Route{
{
Dst: *fenv.nw,
},
},
}
return delegateAdd(args.ContainerID, n.DataDir, n.Delegate)
}
2. delegateAdd函数
saveScratchNetConf函数创建/var/lib/cni/flannel目录
调用DelegateAdd函数
func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error {
netconfBytes, err := json.Marshal(netconf)
if err != nil {
return fmt.Errorf("error serializing delegate netconf: %v", err)
}
// save the rendered netconf for cmdDel
if err = saveScratchNetConf(cid, dataDir, netconfBytes); err != nil {
return err
}
result, err := invoke.DelegateAdd(netconf["type"].(string), netconfBytes, nil)
if err != nil {
return err
}
return result.Print()
}
3. 根據設置的bridge,調用bridge cni
參照https://blog.csdn.net/zhonglinzhang/article/details/82733201
3.1 setupBridge函数
- 通过netlink.LinkAdd(br)创建网桥,相当于ip link add br-test type bridge
- 然后通过 netlink.LinkSetUp(br)启动网桥,相当于ip link set dev br-test up
3.2 setupVeth函数
- 调用netlink.LinkAdd(veth)创建veth,这个是一个管道,Linux的网卡对,在容器对应的namespace下创建好虚拟网络接口,相当于ip link add test-veth0 type veth peer name test-veth1
- 调用netlink.LinkSetUp(contVeth)启动容器端网卡,相当于ip link set dev test-veth0 up
- 调用netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd()))将host端加入namespace中,相当于ip link set $link netns $ns
- 调用netlink.LinkSetMaster(hostVeth, br)绑到bridge,相当于ip link set dev test-veth0 master br-test
与calico不同点:
calico 绑到eth0
flannel 绑到docker0 bridge上,在调用bridage cni创建一大堆网络相关