DOCKER源码-创建网络
Daemon结构体
daemon/daemon.go
type Daemon struct {
// ... 省略多行 ...
EventsService *events.Events
netController libnetwork.NetworkController
volumes *volumesservice.VolumesService
discoveryWatcher discovery.Reloader
// ... 省略多行 ...
}
Daemon中的netController字段负责网络相关操作,它是一个接口,其接口的定义和实现都在vendor/github.com/docker/libnetwork/controller.go
文件中。
controller结构体
controller结构体实现了libnetwork.NetworkController接口,Daemon对象的对网络设备的增删改查都是调用controller对象的相关函数,其中创建网络的关键函数如下:
vendor/github.com/docker/libnetwork/controller.go
func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) {
// ... 省略多行 ...
network := &network{
name: name,
networkType: networkType,
generic: map[string]interface{}{netlabel.GenericData: make(map[string]string)},
ipamType: defaultIpam,
id: id,
created: time.Now(),
ctrlr: c,
persist: true,
drvOnce: &sync.Once{},
loadBalancerMode: loadBalancerModeDefault,
}
// ... 省略多行 ...
err = c.addNetwork(network)
// ... 省略多行 ...
return network, nil
}
根据驱动创建网络
addNetwork()函数中先根据network对象的网络类型获取相应的驱动,然后调用该驱动对象的d.CreateNetwork()
函数:
vendor/github.com/docker/libnetwork/controller.go
func (c *controller) addNetwork(n *network) error {
d, err := n.driver(true)
if err != nil {
return err
}
// Create the network
if err := d.CreateNetwork(n.id, n.generic, n, n.getIPData(4), n.getIPData(6)); err != nil {
return err
}
n.startResolver()
return nil
}
关键函数
以bridge网络驱动为例,展开其CreateNetwork()函数,它主要对一些关键的配置信息做检查,比如验证IPv4和IPv6的地址是否有误:
vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go
func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
// ... 省略多行 ...
if err = d.createNetwork(config); err != nil {
return err
}
return d.storeUpdate(config)
}
分步骤创建网络设备
vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go
func (d *driver) createNetwork(config *networkConfiguration) (err error) {
// ... 省略多行 ...
for _, step := range []struct {
Condition bool
Fn setupStep
}{
// 如果需要,在桥上启用IPv6。我们甚至对以前存在的桥这样做,
// 因为它可能是在这里从以前的安装IPv6还不受支持,需要分配一个IPv6链接本地地址。
{config.EnableIPv6, setupBridgeIPv6},
// 我们确保在现有设备的情况下,网桥具有预期的dipv4和IPv6地址。
{bridgeAlreadyExists && !config.InhibitIPv4, setupVerifyAndReconcile},
// 启用IPv6转发
{enableIPv6Forwarding, setupIPv6Forwarding},
// 设置环回地址路由
{!d.config.EnableUserlandProxy, setupLoopbackAddressesRouting},
// 设置 IPTables.
{d.config.EnableIPTables, network.setupIPTables},
// 我们希望跟踪firewalld配置,以便在启动/重新加载时能够正确应用规则
{d.config.EnableIPTables, network.setupFirewalld},
// 设置IPv4的默认网关
{config.DefaultGatewayIPv4 != nil, setupGatewayIPv4},
// 设置IPv6的默认网关
{config.DefaultGatewayIPv6 != nil, setupGatewayIPv6},
// 添加网络间通信规则
{d.config.EnableIPTables, setupNetworkIsolationRules},
// 如果ICC关闭且启用了IPTables,则配置桥接网络过滤
{!config.EnableICC && d.config.EnableIPTables, setupBridgeNetFiltering},
} {
if step.Condition {
bridgeSetup.queueStep(step.Fn)
}
}
return bridgeSetup.apply()
}
上面for循环中定义了10个函数,每个函数负责一项配置,我为每一步加上了中文注释,这些步骤被放在bridgeSetup对象中,最后调用bridgeSetup.apply()
执行里面的每一个函数。