通读Flannel
代码以及如何编写backend
进行网络开发
Flannel代码组织
代码private和public结构1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102├── backend
│ ├── alivpc
│ ├── alloc
│ ├── awsvpc
│ ├── common.go
│ ├── extension
│ ├── gce
│ ├── hostgw
│ ├── hostgw_ocean
│ ├── kscvpc
│ ├── kscvpc_ocean
│ ├── manager.go
│ ├── udp
│ └── vxlan
├── bill-of-materials.json
├── bill-of-materials.override.json
├── CONTRIBUTING.md
├── DCO
├── dist
│ ├── bill-of-materials.sh
│ ├── extension-hostgw
│ ├── extension-vxlan
│ ├── fake-node.yaml
│ ├── functional-test.sh
│ ├── iptables-amd64
│ ├── iptables-arm
│ ├── iptables-arm64
│ ├── iptables-ppc64le
│ ├── iptables-s390x
│ ├── license-check.sh
│ ├── mk-docker-opts.sh
│ ├── mk-docker-opts_tests.sh
│ └── sample_subnet.env
├── Dockerfile.amd64
├── Dockerfile.arm
├── Dockerfile.arm64
├── Dockerfile.ppc64le
├── Dockerfile.s390x
├── Documentation
│ ├── alicloud-vpc-backend.md
│ ├── aws-vpc-backend.md
│ ├── backends.md
│ ├── building.md
│ ├── configuration.md
│ ├── extension.md
│ ├── gce-backend.md
│ ├── img
│ ├── integrations.md
│ ├── k8s-manifests
│ ├── kube-flannel-aliyun.yml
│ ├── kube-flannel.yml
│ ├── Kubernetes.md
│ ├── minikube.yml
│ ├── production-users.md
│ ├── reporting_bugs.md
│ ├── reservations.md
│ ├── running.md
│ └── troubleshooting.md
├── glide.lock
├── glide.yaml
├── LICENSE
├── license-check.sh
├── logos
│ ├── flannel-glyph-color.png
│ ├── flannel-glyph-color.svg
│ ├── flannel-glyph-white.png
│ ├── flannel-glyph-white.svg
│ ├── flannel-horizontal-bw.png
│ ├── flannel-horizontal-bw.svg
│ ├── flannel-horizontal-color.png
│ ├── flannel-horizontal-color.svg
│ ├── flannel-horizontal-white.png
│ └── flannel-horizontal-white.svg
├── main.go
├── MAINTAINERS
├── Makefile
├── network
│ ├── iptables.go
│ └── iptables_test.go
├── NOTICE
├── packet-01.png
├── pkg
│ └── ip
├── README.md
├── subnet
│ ├── config.go
│ ├── config_test.go
│ ├── etcdv2
│ ├── kube
│ ├── subnet.go
│ └── watch.go
├── tools
│ └── token.go
├── vendor
│ ├── cloud.google.com
│ ├── github.com
│ ├── golang.org
│ ├── google.golang.org
│ ├── gopkg.in
│ └── k8s.io
└── version
└── version.go
比较重要的就是backend
、network
pkg
subnet
tools
等目录,其中main.go
为入口
main.go
在导入各个
backend
时,会调用各个backend
的init
函数,主要用来注册backend
到全局的constructors
中1
2
3func init() {
backend.Register("host-gw", New)
}注册
backend
的type
和new
构造函数main.go
中init
中解析cmd
参数main.go#L111- 对解析出的参数进行校验
- 根据参数判断主要操作的网卡main.go#L198
- 生成一个
subnetmanager
,主要是封装了subnet
存入etcdv2
以及一些事件event的生成和维护main.go#L241 - 一些信号的捕获处理
- 健康检查服务启动
- 获取网络配置,这些网络配置是通过
subnetmanager
来获取的,保存在etcd
里 - 构造
backendmanager
main.go#L280 - 根据网络配置的
backendtype
从全局的constructors
获取相应的backend
构造函数并执行,生成想要的backend
实现 执行
backend
的RegisterNetwork
方法,这个方法返回了一个实现了backend.Network
的网络1
2
3
4
5type Network interface {
Lease() *subnet.Lease
MTU() int
Run(ctx context.Context)
}需要实现
interface
里三个方法,具体实现后面分析- 是否需要
NAT
,来设置iptables
规则main.go#L298 - 写入
subnet
文件,持久化flannel
子网信息到本地 - 执行
backend.Network
的Run
,启动服务监听subnet event
,并根据类型进行设置,比如添加子网、删除子网时做些工作
backend实现
backend
的interface
定义1
2
3
4type Backend interface {
// Called when the backend should create or begin managing a new network
RegisterNetwork(ctx context.Context, wg *sync.WaitGroup, config *subnet.Config) (Network, error)
}
只需要实现RegisterNetwork
方法,但是它返回的是一个Network interface
,所以还需要实现Network
接口
其中host-gw
的实现
host-gw
1 | // backend需要实现的RegisterNetwork |
主要就是申请了一个lease
(租约),并赋值给实现的NetWork
申请lease
主要的调用链1
AcquireLease-->tryAcquireLease
tryAcquireLease
更多是为了找到一个适合的subnet
(这些信息保存在etcdv2
)里,然后组成network
实例
然后就是核心逻辑network
的Run
方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33func (n *network) Run(ctx context.Context) {
wg := sync.WaitGroup{}
log.Info("Watching for new subnet leases")
evts := make(chan []subnet.Event)
wg.Add(1)
go func() {
subnet.WatchLeases(ctx, n.sm, n.lease, evts)
wg.Done()
}()
// Store a list of routes, initialized to capacity of 10.
n.rl = make([]netlink.Route, 0, 10)
wg.Add(1)
// Start a goroutine which periodically checks that the right routes are created
go func() {
n.routeCheck(ctx)
wg.Done()
}()
defer wg.Wait()
for {
select {
case evtBatch := <-evts:
n.handleSubnetEvents(evtBatch)
case <-ctx.Done():
return
}
}
}
WatchLeases
通过监听etcd
里的subnet
变化,返回event
切片,然后写入evts
管道handleSubnetEvents
消费evts
管道里的消息,根据evt.Type
执行不同的操作也就是addToRouteList
和removeFromRouteList
来增删子网路由
这些路由都是在宿主机上的
host-gw-ocean
自研的backend
,主要是把路由写到交换机上
它的流程与host-gw
相同
但是他的核心逻辑addRoute
在RegisterNetwork
方法里,主要的功能是http
调用交换机接口,把子网信息和网关写到交换机上,由交换机广播出去
但是delRoute
在Run
里,这里估计调用不了defer
,因为Run
里面有个for
循环不断监听子网变化evt
为什么没写在handleSubnetEvents
,我理解应该是因为其他机器子网的添加,不需要维护交换机,由所在node
上的flannel
进城维护子网即可