Docker 1.9版本引入了一整套的docker network子命令和跨主机网络支持。用户可根据应用的拓扑结构创建虚拟网络并将容器接入对应的网络。
1.Docker网络架构
Docker在libnetwork中使用了CNM,架构图如下:
2.CNM
CNM有三个核心组件:
2.1.沙盒
一个沙盒包含容器网络栈信息。沙盒可以对容器的接口、路由和DNS设置等进行管理。沙盒的实现可以是Linux network namespace、FreeBSD Jail或者类似的机制。
2.2.断点
一个端点可以加入一个沙盒和一个网络。端点的实现可以是veth pair、Open vSwitch内部端口或相似的设备。
2.3.网络
一个网络是一组可以直接相互联通的端点。网络的实现可以是Linux bridge、VLAN等。
3.libnetwork内置驱动
libnetwork共支持五种内置驱动。
3.1.bridge驱动
Docker默认设置。libnetwork将创建的容器连接到Docker网桥。与外界通信使用NAT,增加了通信的复杂性。
3.2.host驱动
这种时候,libnetwork将不会为容器创建独立的network namespace。容器的进程处于宿主机的网络环境中,相当于Docker容器与宿主机共用一个network namespace,使用宿主机的网卡、IP、端口等信息。
3.3.overlay驱动
此驱动采用IETF标准的VXLAN方式。使用时需要额外的配置存储服务,如Consul、etcd或ZooKeeper,并在启动daemon时指定配置存储服务地址。
3.4.remote驱动
未做真正的网络服务实现,调用用户自行实现的网络驱动插件,使libnetwork实现了驱动的可插件化。
3.5.null驱动
使用这种驱动时,Docker容器拥有自己的network namespace,但并不为容器进行任何网络配置。除了network namespace自带的loopback网卡外,没有其他任何网卡、IP、路由等信息,需要用户为Docker容器添加网卡、配置IP等。
4.docker0网桥案例分析
案例如下图所示:
网桥等同于交换机,docker0网桥在daemon启动时自动创建,有三个相关启动参数:
(1)--bip=CIDR:设置docker0的IP地址和子网范围。
(2)--fix-cidr=CIDR:限制Docker容器获取IP得范围。默认为整个子网范围。
(3)--mtu=BYTES:指定最大传输单元。
也可将这些参数写入/etc/default/docker文件中的DOCKER_OPTS变量中,然后重启服务。
容器要访问外网服务的原理是宿主机进行iptables规则的设定。net表的POSTROUTING链有这么一条规则:-A POSTROUTONG -s 172.17.0.0/16 !-o docker0 -j MASQUERADE;也就是将Docker容器发出而不是从网卡发出的数据做SNAT,将IP包的源地址替换为相应网卡的地址。
外界想要访问Docker容器的服务怎么办?答案是端口映射。而端口映射的本质也是iptables规则。如运行5000:5000端口映射,则在宿主机可看到类似的两条iptables规则:
规则1-nat表:-A DOCKER ! docker0 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.17.0.4:5000
规则2-filter表:-A DOCKER -d 172.17.0.4/32 ! docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
Docker容器间通信也受iptables规则限制。首先,这些容器需属于同一个子网。然后,在Docker daemon启动时加参数配置“--icc=true”,此参数标志daemon会在FORWARD链的filter表中添加一条这样的规则“-A FORWARD -i docker0 -o docker0 -j ACCEPT”。或者在docker run命令中添加--link选项。
Docker容器与外界通信的过程中,还涉及多个网卡间数据包的转发(如从docker0到宿主机eth0网卡),这需要内核将ip-forward功能打开,即执行命令"echo 1 > /proc/sys/net/ipv4/ip_forward"。