Docker 源码分析(六):Docker Daemon 网络

阅读数:7554 2014 年 12 月 30 日

1. 前言

Docker 作为一个开源的轻量级虚拟化容器引擎技术,已然给云计算领域带来了新的发展模式。Docker 借助容器技术彻底释放了轻量级虚拟化技术的威力,让容器的伸缩、应用的运行都变得前所未有的方便与高效。同时,Docker 借助强大的镜像技术,让应用的分发、部署与管理变得史无前例的便捷。然而,Docker 毕竟是一项较为新颖的技术,在 Docker 的世界中,用户并非一劳永逸,其中最为典型的便是 Docker 的网络问题。

毋庸置疑,对于 Docker 管理者和开发者而言,如何有效、高效的管理 Docker 容器之间的交互以及 Docker 容器的网络一直是一个巨大的挑战。目前,云计算领域中,绝大多数系统都采取分布式技术来设计并实现。然而,在原生态的 Docker 世界中,Docker 的网络却是不具备跨宿主机能力的,这也或多或少滞后了 Docker 在云计算领域的高速发展。

工业界中,Docker 的网络问题的解决势在必行,在此环境下,很多 IT 企业都开发了各自的新产品来帮助完善 Docker 的网络。这些企业中不乏像 Google 一样的互联网翘楚企业,同时也有不少初创企业率先出击,在最前沿不懈探索。这些新产品中有,Google 推出的容器管理和编排开源项目 Kubernetes,Zett.io 公司开发的通过虚拟网络连接跨宿主机容器的工具 Weave,CoreOS 团队针对 Kubernetes 设计的网络覆盖工具 Flannel,Docker 官方的工程师 Jérôme Petazzoni自己设计的SDN 网络解决方案 Pipework,以及 SocketPlane 项目等。

对于 Docker 管理者与开发者而言,Docker 的跨宿主机通信能力固然重要,但 Docker 自身的网络架构也同样重要。只有深入了解 Docker 自身的网络设计与实现,才能在这基础上扩展 Docker 的跨宿主机能力。

Docker 自身的网络主要包含两部分:Docker Daemon 的网络配置,Docker Container 的网络配置。本文主要分析 Docker Daemon 的网络。

2. Docker Daemon 网络分析内容安排

本文从源码的角度,分析 Docker Daemon 在启动过程中,为 Docker 配置的网络环境,章节安排如下:

(1) Docker Daemon 网络配置;

(2) 运行 Docker Daemon 网络初始化任务;

(3) 创建 Docker 网桥。

本文为《Docker 源码分析》系列第六篇——Docker Daemon 网络篇,第七篇将安排 Docker Container 网络篇。

3. Docker Daemon 网络配置

Docker 环境中,Docker 管理员完全有权限配置 Docker Daemon 运行过程中的网络模式。 关于 Docker 的网络模式,大家最熟知的应该就是“桥接”的模式。下图为桥接模式下,Docker 的网络环境拓扑图(包括 Docker Daemon 网络环境和 Docker Container 网络环境):

图 3.1 Docker 网络桥接示意图

然而,“桥接”是 Docker 网络模式中最为常用的模式。除此之外,Docker 还为用户提供了更多的可选项,下文将对此一一说来。

3.1 Docker Daemon 网络配置接口

Docker Daemon 每次启动的过程中,都会初始化自身的网络环境,这样的网络环境最终为 Docker Container 提供网络通信服务。

Docker 管理员配置 Docker 的网络环境,可以在 Docker Daemon 启动时,通过 Docker 提供的接口来完成。换言之,可以使用 docker 二进制可执行文件,运行 docker -d 并添加相应的 flag 参数来完成。

其中涉及的 flag 参数有 EnableIptables、EnableIpForward、BridgeIface、BridgeIP 以及 InterContainerCommunication。该五个参数的定义位于./docker/daemon/config.go,具体代码如下:

flag.BoolVar(&config.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable Docker's addition of iptables rules")
flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")

以下介绍这 5 个 flag 的作用:

  • EnableIptables:确保 Docker 对于宿主机上的 iptables 规则拥有添加权限;
  • EnableIpForward:确保 net.ipv4.ip_forward 可以使用,使得多网络接口设备模式下,数据报可以在网络设备之间转发;
  • BridgeIP:在 Docker Daemon 启动过程中,为网络环境中的网桥配置 CIDR 网络地址;
  • BridgeIface:为 Docker 网络环境指定具体的通信网桥,若 BridgeIface 的值为”none”,则说明不需要为 Docker Container 创建网桥服务,关闭 Docker Container 的网络能力;
  • InterContainerCommunication:确保 Docker 容器之间可以完成通信。

除了 Docker 会使用到的 5 个 flag 参数之外,Docker 在创建网络环境时,还使用一个 DefaultIP 变量,如下:

opts.IPVar(&config.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")

该变量的作用是:当绑定容器的端口时,将 DefaultIp 作为默认使用的 IP 地址。

具备了以上 Docker Daemon 的网络背景知识,以下着重举例分析使用 BridgeIP 和 BridgeIface,在启动 Docker Daemon 时进行网络配置:

启动 Docker Daemon 使用命令

用途注释

docker -d

启动 Docker Daemon,使用默认网桥 docker0,不指定 CIDR 网络地址

docker -d -b=”xxx”

启动 Docker Daemon,使用

网桥 xxx,不指定 CIDR 网络地址

docker -d --bip=”172.17.42.1”

启动 Docker Daemon,使用默认网桥 docker0,使用指定 CIDR 网络地址”172.17.42.1”

docker -d --bridge=”xxx” --bip=”10.0.42.1”

报错,出现兼容性问题,不能同时指定”BridgeIP”和”BridgeIface”

docker -d --bridge=”none”

启动 Docker Daemon,

不创建 Docker 网络环境

深入理解 BridgeIface 与 BridgeIP,并熟练使用相应的 flag 参数,即做到了如何配置 Docker Daemon 的网络环境。需要特别注意的是,Docker Daemon 的网络与 Docker Container 的网络存在很大的区别。Docker Daemon 为 Docker Container 创建网络的大环境,Docker Container 的网络需要 Docker Daemon 的网络提供支持,但不唯一。举一个形象的例子,Docker Daemon 可以创建 docker0 网桥,为之后 Docker Container 的桥接模式提供支持,然而 Docker Container 仍然可以根据用户需求创建自身网络,其中 Docker Container 的网络可以是桥接模式的网络,同时也可以直接共享使用宿主机的网络接口,另外还有其他模式,会在《Docker 源码分析》系列的第七篇——Docker Container 网络篇中详细介绍。

3.2 Docker Daemon 网络初始化

正如上一节所言,Docker 管理员可以通过与网络相关的 flag 参数 BridgeIface 与 BridgeIP,来为 Docker Daemon 创建网路环境。最简单的,Docker 管理员通过执行”docker -d”就已经完成了运行 Docker Daemon,而 Docker Daemon 在启动的时候,根据以上两个 flag 参数的值,创建相应的网络环境。

Docker Daemon 网络初始化流程图如下:

图 3.2 Docker Daemon 网络初始化流程图

Docker Daemon 网络初始化的流程总体而言,主要是根据解析 flag 参数来决定到底建立哪种类型的网络环境。从流程图中可知,Docker Daemon 创建网络环境时有两个分支,不难发现分支代表的分别是:为 Docker 创建一个网络驱动、以及对 Docker 的网络不做任何的操作。

以下参照 Docker Daemon 网络初始化流程图具体分析实现步骤。

3.2.1 启动 Docker Daemon 传递 flag 参数

用户启动 Docker Daemon,并在命令行中选择性的传入所需要的 flag 参数。

3.2.2 解析网络 flag 参数

flag 包对命令行中的 flag 参数进行解析,其中和 Docker Daemon 网络配置相关的 flag 参数有 5 个,分别是:EnableIptables、EnableIpForward、BridgeIP、BridgeIface 以及 InterContanierCommunication,各个 flag 参数的作用上文已有介绍。

3.2.3 预处理 flag 参数

预处理与网络配置相关的 flag 参数信息,包括检测配置信息的兼容性、以及判断是否创建 Docker 网络环境。

首先检验是否会出现彼此不兼容的配置信息,源码位于./docker/daemon/daemon.go#L679-L685

这部分的兼容信息有两种。第一种是 BridgeIP 和 BridgeIface 配置信息的兼容性,具体表现为用户启动 Docker Daemon 时,若同时指定了 BridgeIP 和 BridgIface 的值,则出现兼容问题。原因为这两者属于互斥对,换言之,若用户指定了新建网桥的设备名,那么该网桥已经存在,无需指定网桥的 IP 地址 BridgeIP;若用户指定了新建网桥的网络 IP 地址 BridgeIP,那么该网桥肯定还没有新建成功,则 Docker Daemon 在新建网桥时使用默认网桥名“docker0”。具体如下:

// Check for mutually incompatible config options
if config.BridgeIface != "" && config.BridgeIP != "" {
	return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.")
}

第二种是 EnableIptables 和 InterContainerCommunication 配置的兼容性,具体是指不能同时指定这两个 flag 参数为 false。原因很简单,如果指定 InterContainerCommunication 为 false,则说明 Docker Daemon 不允许创建的 Docker 容器之间互相进行通信。但是为了达到以上目的,Docker 正是使用 iptables 过滤规则。因此,再次设定 EnableIptables 为 false,关闭 iptables 的使用,即出现了自相矛盾的结果。代码如下:

if !config.EnableIptables && !config.InterContainerCommunication {
	return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.")
	}

检验完系统配置信息的兼容性问题,Docker Daemon 接着会判断是否需要为 Docker Daemon 配置网络环境。判断的依据为 BridgeIface 的值是否与 DisableNetworkBridge 的值相等,DisableNetworkBridge 在./docker/daemon/config.go#L13中被定义为 const 量,值为字符串”none”。因此,若 BridgeIface 为”none”,则 DisableNetwork 为 true,最终 Docker Daemon 不会创建网络环境;若 BridgeIface 不为”none”,则 DisableNetwork 为 false,最终 Docker Daemon 需要创建网络环境(桥接模式)。

3.2.4 确定 Docker 网络模式

Docker 网络模式由配置信息 DisableNetwork 决定。由于在上一环节已经得出 DisableNetwork 的值,故这一环节可以确定 Docker 网络模式。该部分的源码实现位于./docker/daemon/daemon.go#L792-L805,如下:

if !config.DisableNetwork {
	job := eng.Job("init_networkdriver")

	job.SetenvBool("EnableIptables", config.EnableIptables)
	job.SetenvBool("InterContainerCommunication", config.InterContainerCommunication)
	job.SetenvBool("EnableIpForward", config.EnableIpForward)
	job.Setenv("BridgeIface", config.BridgeIface)
	job.Setenv("BridgeIP", config.BridgeIP)
	job.Setenv("DefaultBindingIP", config.DefaultIp.String())

	if err := job.Run(); err != nil {
		return nil, err
	}
}

若 DisableNetwork 为 false,则说明需要创建网络环境,具体的模式为创建 Docker 网桥模式。创建网络环境的步骤为:

(1) 创建名为”init_networkdriver”的 job;

(2) 为该 job 配置环境变量,设置的环境变量有 EnableIptables、InterContainerCommunication、EnableIpForward、BridgeIface、BridgeIP 以及 DefaultBindingIP;

(3) 运行 job。

运行”init_network”即为创建 Docker 网桥,这部分内容将会在下一节详细分析。

若 DisableNetwork 为 true。则说明不需要创建网络环境,网络模式属于 none 模式。

以上便是 Docker Daemon 网络初始化的所有流程。

3.3 创建 Docker 网桥

Docker 的网络往往是 Docker 开发者最常提起的话题。而 Docker 网络中最常使用的模式为 bridge 桥接模式。本小节将详细分析创建 Docker 网桥的创建流程。

创建 Docker 网桥的实现通过”init_network”这个 job 的运行来完成。”init_network”的实现为 InitDriver 函数,位于./docker/daemon/networkdriver/bridge/driver.go#L79,运行流程如下:

图 3.3 Docker Daemon 创建网桥流程图

3.3.1 提取环境变量

在 InitDriver 函数的实现过程中,Docker 首先提取”init_networkdriver”这个 job 的环境变量。这样的环境变量共有 6 个,各自的作用在上文已经详细说明。具体的实现代码为:

var (
	network        *net.IPNet
	enableIPTables = job.GetenvBool("EnableIptables")
	icc            = job.GetenvBool("InterContainerCommunication")
	ipForward      = job.GetenvBool("EnableIpForward")
	bridgeIP       = job.Getenv("BridgeIP")
)

if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
	defaultBindingIP = net.ParseIP(defaultIP)
}

bridgeIface = job.Getenv("BridgeIface")

3.3.2 确定 Docker 网桥设备名

提取 job 的环境变量之后,Docker 随即确定最终使用网桥设备的名称。为此,Docker 首先创建了一个名为 usingDefaultBridge 的 bool 变量,含义为是否使用默认的网桥设备,默认值为 false。接着,若环境变量中 bridgeIface 的值为空,则说明用户启动 Docker 时,没有指定特定的网桥设备名,因此 Docker 首先将 usingDefaultBridge 置为 true,然后使用默认的网桥设备名 DefaultNetworkBridge,即 docker0;若 bridgeIface 的值不为空,则判断条件不成立,继续往下执行。这部分的代码实现为:

usingDefaultBridge := false
if bridgeIface == "" {
	usingDefaultBridge = true
	bridgeIface = DefaultNetworkBridge
}

3.3.3 查找 bridgeIface 网桥设备

确定 Docker 网桥设备名 bridgeIface 之后,Docker 首先通过 bridgeIface 设备名在宿主机上查找该设备是否真实存在。若存在,则返回该网桥设备的 IP 地址,若不存在,则返回 nil。实现代码位于./docker/daemon/networkdriver/bridge/driver.go#L99,如下:

addr, err := networkdriver.GetIfaceAddr(bridgeIface)

GetIfaceAddr 的实现位于./docker/daemon/networkdriver/utils.go,实现步骤为:首先通过 Golang 中 net 包的 InterfaceByName 方法获取名为 bridgeIface 的网桥设备,会得出以下结果:

  • 若名为 bridgeIface 的网桥设备不存在,直接返回 error;
  • 若名为 bridgeIface 的网桥设备存在,返回该网桥设备的 IP 地址。

需要强调的是:GetIfaceAddr 函数返回 error,说明当前宿主机上不存在名为 bridgeIface 的网桥设备。而这样的结果会有两种不同的情况:第一,用户指定了 bridgeIface,那么 usingDefaultBridge 为 false,而该 bridgeIface 网桥设备在宿主机上不存在;第二,用户没有指定 bridgeIface,那么 usingDefaultBridge 为 true,bridgeIface 名为 docker0,而 docker0 网桥在宿主机上也不存在。

当然,若 GetIfaceAddr 函数返回的是一个 IP 地址,则说明当前宿主机上存在名为 bridgeIface 的网桥设备。这样的结果同样会有两种不同的情况:第一,用户指定了 bridgeIface,那么 usingDefaultBridge 为 false,而该 bridgeIface 网桥设备在宿主机上已经存在;第二,用户没有指定 bridgeIface,那么 usingDefaultBridge 为 true,bridgeIface 名为 docker0,而 docker0 网桥在宿主机上也已经存在。第二种情况一般是:用户在宿主机上第一次启动 Docker Daemon 时,创建了默认网桥设备 docker0,而后 docker0 网桥设备一直存在于宿主机上,故之后在不指定网桥设备的情况下,重启 Docker Daemon,会出现 docker0 已经存在的情况。

以下两小节将分别从 bridgeIface 已创建与 bridgeIface 未创建两种不同的情况分析。

3.3.4 bridgeIface 已创建的情况

Docker Daemon 所在宿主机上 bridgeIface 的网桥设备存在时,Docker Daemon 仍然需要验证用户在配置信息中是否为网桥设备指定了 IP 地址。

用户启动 Docker Daemon 时,假如没有指定 bridgeIP 参数信息,则 Docker Daemon 使用名为 bridgeIface 的原有的 IP 地址。

当用户指定了 bridgeIP 参数信息时,则需要验证:指定的 bridgeIP 参数信息与 bridgeIface 网桥设备原有的 IP 地址信息是否匹配。若两者匹配,则验证通过,继续往下执行;若两者不匹配,则验证不通过,抛出错误,显示“bridgeIP 与已有网桥配置信息不匹配”。该部分内容位于./docker/daemon/networkdriver/bridge/driver.go#L119-L129,代码如下:

network = addr.(*net.IPNet)
// validate that the bridge ip matches the ip specified by BridgeIP
if bridgeIP != "" {
	bip, _, err := net.ParseCIDR(bridgeIP)
	if err != nil {
		return job.Error(err)
	}
	if !network.IP.Equal(bip) {
		return job.Errorf("bridge ip (%s) does not match existing bridge configuration %s", network.IP, bip)
	}
}

3.3.5 bridgeIface 未创建的情况

Docker Daemon 所在宿主机上 bridgeIface 的网桥设备未创建时,上文已经介绍将存在两种情况:

l 用户指定的 bridgeIface 未创建;

l 用户未指定 bridgeIface,而 docker0 暂未创建。

当用户指定的 bridgeIface 不存在于宿主机时,即没有使用 Docker 的默认网桥设备名 docker0,Docker 打印日志信息“指定网桥设备未找到”,并返回网桥未找到的错误信息。代码实现如下:

if !usingDefaultBridge {
	job.Logf("bridge not found: %s", bridgeIface)
	return job.Error(err)
}

当使用的默认网桥设备名,而 docker0 网桥设备还未创建时,Docker Daemon 则立即实现创建网桥的操作,并返回该 docker0 网桥设备的 IP 地址。代码如下:

// If the iface is not found, try to create it
job.Logf("creating new bridge for %s", bridgeIface)
if err := createBridge(bridgeIP); err != nil {
	return job.Error(err)
}

job.Logf("getting iface addr")
addr, err = networkdriver.GetIfaceAddr(bridgeIface)
if err != nil {
	return job.Error(err)
}
network = addr.(*net.IPNet)

创建 Docker Daemon 网桥设备 docker0 的实现,全部由 createBridge(bridgeIP) 来实现,createBridge 的实现位于./docker/daemon/networkdriver/bridge/driver.go#L245

createBridge 函数实现过程的主要步骤为:

(1) 确定网桥设备 docker0 的 IP 地址;

(2) 通过 createBridgeIface 函数创建 docker0 网桥设备,并为网桥设备分配随机的 MAC 地址;

(3) 将第一步中已经确定的 IP 地址,添加给新创建的 docker0 网桥设备;

(4) 启动 docker0 网桥设备。

以下详细分析 4 个步骤的具体实现。

首先 Docker Daemon 确定 docker0 的 IP 地址,实现方式为判断用户是否指定 bridgeIP。若用户未指定 bridgeIP,则从 Docker 预先准备的 IP 网段列表 addrs 中查找合适的网段。具体的代码实现位于./docker/daemon/networkdriver/bridge/driver.go#L257-L278,如下:

if len(bridgeIP) != 0 {
	_, _, err := net.ParseCIDR(bridgeIP)
	if err != nil {
		return err
	}
	ifaceAddr = bridgeIP
} else {
	for _, addr := range addrs {
		_, dockerNetwork, err := net.ParseCIDR(addr)
		if err != nil {
			return err
		}
		if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil {
			if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil {
				ifaceAddr = addr
				break
			} else {
				log.Debugf("%s %s", addr, err)
			}
		}
	}
}

其中为网桥设备准备的候选网段地址 addrs 为:

addrs = []string{
		"172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23
		"10.0.42.1/16",   // Don't even try using the entire /8, that's too intrusive
		"10.1.42.1/16",
		"10.42.42.1/16",
		"172.16.42.1/24",
		"172.16.43.1/24",
		"172.16.44.1/24",
		"10.0.42.1/24",
		"10.0.43.1/24",
		"192.168.42.1/24",
		"192.168.43.1/24",
		"192.168.44.1/24",
}

通过以上的流程的执行,可以确定找到一个可用的 IP 网段地址,为 ifaceAddr;若没有找到,则返回错误日志,表明没有合适的 IP 地址赋予 docker0 网桥设备。

第二个步骤通过 createBridgeIface 函数创建 docker0 网桥设备。createBridgeIface 函数的实现如下:

func createBridgeIface(name string) error {
	kv, err := kernel.GetKernelVersion()
	// only set the bridge's mac address if the kernel version is > 3.3
	// before that it was not supported
	setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3)
	log.Debugf("setting bridge mac address = %v", setBridgeMacAddr)
	return netlink.CreateBridge(name, setBridgeMacAddr)
}

以上代码通过宿主机 Linux 内核信息,确定是否支持设定网桥设备的 MAC 地址。若 Linux 内核版本大于 3.3,则支持配置 MAC 地址,否则则不支持。而 Docker 在不小于 3.8 的内核版本上运行才稳定,故可以认为内核支持配置 MAC 地址。最后通过 netlink 的 CreateBridge 函数实现创建 docker0 网桥。

Netlink 是 Linux 中一种较为特殊的 socket 通信方式,提供了用户应用间和内核进行双向数据传输的途径。在这种模式下,用户态可以使用标准的 socket API 来使用 netlink 强大的功能,而内核态需要使用专门的内核 API 才能使用 netlink。

Libcontainer 的 netlink 包中 CreateBridge 实现了创建实际的网桥设备,具体使用系统调用的代码如下:

syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDBR, uintptr(unsafe.Pointer(nameBytePtr)))

创建完网桥设备之后,为 docker0 网桥设备配置 MAC 地址,实现函数为 setBridgeMacAddress。

第三个步骤是为创建 docker0 网桥设备绑定 IP 地址。上一步骤仅完成了创建名为 docker0 的网桥设备,之后仍需要为 docker0 网桥设备绑定 IP 地址。具体代码实现为:

if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
		return fmt.Errorf("Unable to add private network: %s", err)
}

NetworkLinkAddIP 的实现同样位于 libcontainer 中的 netlink 包,主要的功能为:通过 netlink 机制为一个网络接口设备绑定一个 IP 地址。

第四个步骤是启动 docker0 网桥设备。具体实现代码为:

if err := netlink.NetworkLinkUp(iface); err != nil {
		return fmt.Errorf("Unable to start network bridge: %s", err)
	}

NetworkLinkUp 的实现同样位于 libcontainer 中的 netlink 包,功能为启动 docker 网桥设备。

至此,docker0 网桥历经确定 IP、创建、绑定 IP、启动四个环节,createBridge 关于 docker0 网桥设备的工作全部完成。

3.3.6 获取网桥设备的网络地址

创建完网桥设备之后,网桥设备必然会存在一个网络地址。网桥网络地址的作用为:Docker Daemon 在创建 Docker Container 时,使用该网络地址为 Docker Container 分配 IP 地址。

Docker 使用代码 network = addr.(*net.IPNet) 获取网桥设备的网络地址。

3.3.7 配置 Docker Daemon 的 iptables

创建完网桥之后,Docker Daemon 为容器以及宿主机配置 iptables,包括为 container 之间所需要的 link 操作提供支持,为 host 主机上所有的对外对内流量制定传输规则等。该部分详情可以参看《Docker 源码分析(四):Docker Daemon 之 NewDaemon 实现》。代码位于./docker/daemon/networkdriver/bridge/driver/driver.go#L133,如下:

// Configure iptables for link support
if enableIPTables {
		if err := setupIPTables(addr, icc); err != nil {
			return job.Error(err)
		}
}

// We can always try removing the iptables
if err := iptables.RemoveExistingChain("DOCKER"); err != nil {
		return job.Error(err)
}

if enableIPTables {
		chain, err := iptables.NewChain("DOCKER", bridgeIface)
		if err != nil {
			return job.Error(err)
		}
		portmapper.SetIptablesChain(chain)
}

3.3.8 配置网络设备间数据报转发功能

在 Linux 系统上,数据包转发功能是被默认禁止的。数据包转发,就是当 host 主机存在多个网络设备时,如果其中一个接收到数据包,并需要将其转发给另外的网络设备。通过修改 /proc/sys/net/ipv4/ip_forward 的值,将其置为 1,则可以保证系统内数据包可以实现转发功能,代码如下:

if ipForward {
		// Enable IPv4 forwarding
		if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {
			job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err)
		}
}

3.3.9 注册网络 Handler

创建 Docker Daemon 网络环境的最后一个步骤是:注册 4 个与网络相关的 Handler。这 4 个 Handler 分别是 allocate_interface、release_interface、allocate_port 和 link,作用分别是为 Docker Container 分配网络设备,回收 Docker Container 网络设备、为 Docker Container 分配端口资源、以及为 Docker Container 间执行 link 操作。

至此,Docker Daemon 的网络环境初始化工作全部完成。

4 总结

在工业界,Docker 的网络问题备受关注。Docker 的网络环境可以分为 Docker Daemon 网络和 Docker Container 网络。本文从 Docker Daemon 的网络入手,分析了大家熟知的 Docker 桥接模式。

Docker 的容器技术以及镜像技术,已经给 Docker 实践者带来了诸多效益。然而 Docker 网络的发展依然具有很大的潜力。下一篇 Docker Container 网络篇,将会带来更为灵活的 Docker 网络配置。

5 作者介绍

孙宏亮,DaoCloud初创团队成员,软件工程师,浙江大学 VLIS 实验室应届研究生。读研期间活跃在 PaaS 和 Docker 开源社区,对 Cloud Foundry 有深入研究和丰富实践,擅长底层平台代码分析,对分布式平台的架构有一定经验,撰写了大量有深度的技术博客。2014 年末以合伙人身份加入 DaoCloud 团队,致力于传播以 Docker 为主的容器的技术,推动互联网应用的容器化步伐。邮箱:allen.sun@daocloud.io

6 参考文献

http://www.cnblogs.com/iceocean/articles/1594195.html

http://docs.studygolang.com/pkg/net/


感谢郭蕾对本文的策划和审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论