Docker 网络
Docker 的网络实现其实就是利用了 Linux 上的网络名称空间和虚拟网络设备(特别是 veth pair)。
Linux 网络命名空间:https://www.jianshu.com/p/369e50201bce
Linux虚拟网络设备之veth: https://segmentfault.com/a/1190000009251098
监控和调整Linux网络堆栈:接收数据:https://blog.packagecloud.io/eng/2016/06/22/monitoring-tuning-linux-networking-stack-receiving-data/
Linux网络-数据包的发送过程:https://segmentfault.com/a/1190000008926093
Docker网络核心原理 http://blog.51cto.com/ganbing/2087598
基本原理
首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
Docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势之一是转发效率较高。Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包被直接复制到接收接口的接收缓存中。
对于本地系统和容器内系统看来就像是一个正常的以太网卡,只是它不需要真正同外部网络设备通信,速度要快很多。
Docker 容器网络就利用了这项技术。它在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做 veth pair
)。
veth设备的特点
- veth和其它的网络设备都一样,一端连接的是内核协议栈。
- veth设备是成对出现的,另一端两个设备彼此相连
- 一个设备收到协议栈的数据发送请求后,会将数据发送到另一个设备上去。
参考自 http://blog.51cto.com/ganbing/2087598 https://segmentfault.com/a/1190000009251098
使用docker run命令创建一个执行shell(/bin/bash)的Docker容器,假设容器名称为con1。
在con1容器中可以看到它有两个网卡lo和eth0。lo设备不必多说,是容器的回环网卡;eth0即为容器与外界通信的网卡,eth0的ip 为 172.17.0.2/16,和宿主机上的网桥docker0在同一个网段。
查看con1的路由表,可以发现con1的默认网关正是宿主机的docker0网卡,通过测试, con1可以顺利访问外网和宿主机网络,因此表明con1的eth0网卡与宿主机的docker0网卡是相互连通的。
这时再来查看(ifconfig)宿主机的网络设备,会发现有一块以"veth"开头的网卡,如veth60b16bd,我们可以大胆猜测这块网卡肯定是veth设备了,而veth pair总是成对出现的。veth pair通常用来连接两个network namespace,
那么另一个应该是Docker容器con1中的eth0了。之前已经判断con1容器的eth0和宿主机的docker0是相连的,那么veth60b16bd也应该是与docker0相连的,不难想到,docker0就不只是一个简单的网卡设备了,而是一个网桥。
真实情况正是如此,下图即为Docker默认网络模式(bridge模式)下的网络环境拓扑图,创建了docker0网桥,并以eth pair连接各容器的网络,容器中的数据通过docker0网桥转发到eth0网卡上。
这里的网桥概念等同于交换机,为连在其上的设备转发数据帧。网桥上的veth网卡设备相当于交换机上的端口,可以将多个容器或虚拟机连接在上面,这些端口工作在二层,所以是不需要配置IP信息的。
图中docker0网桥就为连在其上的容器转发数据帧,使得同一台宿主机上的Docker容器之间可以相互通信。
大家应该注意到docker0既然是二层设备,它上面怎么设置了IP呢?docker0是普通的linux网桥,它是可以在上面配置IP的,可以认为其内部有一个可以用于配置IP信息的网卡接口
(如同每一个Open vSwitch网桥都有一个同名的内部接口一样)。在Docker的桥接网络模式中,docker0的IP地址作为连于之上的容器的默认网关地址存在。
在Linux中,可以使用brctl命令查看和管理网桥(需要安装bridge-utils软件包),比如查看本机上的Linux网桥以及其上的端口:
# yum install bridge-utils
# brctl show //四个虚拟接口
bridge name bridge id STP enabled interfaces docker0 8000.02428f0e6a12 no veth6a95f3bveth6d97324veth7314b3evethb2e752fvethdb62bce
更多关于brctl命令的功能和用法,大家通过man brctl或brctl --help查阅。
docker0网桥是在Docker daemon启动时自动创建的,其IP默认为172.17.0.1/16,之后创建的Docker容器都会在docker0子网的范围内选取一个未占用的IP使用,并连接到docker0网桥上。
除了使用docker0网桥外,还可以使用自己创建的网桥,比如创建一个名为br0的网桥,配置IP:
# ip link show //veth7314b3e和if8是一对
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether 00:0c:29:ee:53:1e brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 02:42:8f:0e:6a:12 brd ff:ff:ff:ff:ff:ff
9: veth7314b3e@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 42:a0:eb:96:15:cf brd ff:ff:ff:ff:ff:ff link-netnsid 0
11: vethb2e752f@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 5a:8e:fa:1c:7f:8d brd ff:ff:ff:ff:ff:ff link-netnsid 1
13: veth6d97324@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 4a:2f:07:20:53:15 brd ff:ff:ff:ff:ff:ff link-netnsid 2
17: vethdb62bce@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 8a:1a:78:56:07:d8 brd ff:ff:ff:ff:ff:ff link-netnsid 3
19: veth6a95f3b@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether aa:02:b6:a3:44:87 brd ff:ff:ff:ff:ff:ff link-netnsid 4
iptables规则
Docker安装完成后,将默认在宿主机系统上增加一些iptables规则,以用于Docker容器和容器之间以及和外界的通信,可以使用iptables-save命令查看。其中nat表中的POSTROUTING链有这么一条规则:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
参数说明:
-s :源地址172.17.0.0/16
-o:指定数据报文流出接口为docker0
-j :动作为MASQUERADE(地址伪装)
上面这条规则关系着Docker容器和外界的通信,含义是:将源地址为172.17.0.0/16的数据包(即Docker容器发出的数据),当不是从docker0网卡发出时做SNAT。
这样一来,从Docker容器访问外网的流量,在外部看来就是从宿主机上发出的,外部感觉不到Docker容器的存在。
那么,外界想到访问Docker容器的服务时该怎么办呢?我们启动一个简单的web服务容器,观察iptables规则有何变化。
OVS:全称是Open VSwitch,就是一个虚拟交换机,用于虚拟机VM环境。在虚拟化环境中,一个虚拟交换机主要有两个作用:传递虚拟机VM之间的通信,以及实现VM和外界网络的通信。
OVS主要由以下4层组成:
虚拟网卡:位于虚拟机内,在虚拟机创建后,会挂载在OVS上,每个虚拟网卡都有不同的MAC地址。
虚拟交换层:通过2个ovs虚拟交换机完成通信。OVS除提供基本网络交换外,还提供一些高级功能,如NetFlow等。
bond层:bond是由linux提供的将多个物理网卡绑定在一起的技术,另一种说法叫trunk。Bond模式主要有:负载平衡、主备模式。目前主要使用的是主备模式,在主备模式下,有一个网卡是主用状态,其它网卡均是备用状态。这一层是可选的,如果没有这一层,物理网卡可以直接挂在ovs上。
物理网卡:真正执行收发包的物理设备,一般都会挂载在trunk上,常见的物理网卡有Intel 82599 10G网卡、Intel 82576 1G网卡、Intel I350 1G网卡、SIGMA mellanox 10G网卡。
按照这样的解释,应该就比较清楚了。
OVS与传统的硬件交换机工作原理也没什么区别,就是基于MAC地址实现报文的交换。
host https://blog.csdn.net/wangguohe/article/details/81535942
容器不会获得一个独立的network namespace,而是与宿主机共用一个。
在容器中使用ifconfig查看网络发现显示的是宿主机的网络
host
父进程在创建子进程时,如果不使用```CLONE_NEWNET```这个参数标志,那么创建出的子进程会与父 进程共享同一个网络namespace。
Docker就是采用了这个简单的原理,在创建进程启动容器的过程中,没有传入CLONE_NEWNET
参数标志,实现Docker Container与宿主机共享同一个网络环境,即实现host网络模式。
优势:
- 可以直接使用宿主机的IP地址与外界进行通信,若宿主机的eth0是一个公有IP,那么容器也拥有这个公有IP。
- 同时容器内服务的端口也可以使用宿主机的端口,无需额外进行NAT转换。
缺陷:
- 最明显的是Docker Container网络环境隔离性的弱化,即容器不再拥有隔离、独立的网络栈。
- 使用host模式的Docker Container虽然可以让容器内部的服务和传统情况无差别、无改造的使用,但是由于网络隔离性的弱化,该容器会与宿主机共享竞争网络栈的使用;
- 容器内部将不再拥有所有的端口资源,原因是部分端口资源已经被宿主机本身的服务占用,还有部分端口已经用以bridge网络模式容器的端口映射。
none
获取独立的network namespace,但不为容器进行任何网络配置,之后用户可以自己进行配置,容器内部只能使用loopback网络设备,不会再有其他的网络资源。
创建docker网络
# docker network ls
NETWORK ID NAME DRIVER SCOPE
5ebae7e509fa bridge bridge local //默认网络
b255b50dc21e host host local
db26f5263d07 none null local
# docker container run --help
--network string Connect a container to a network (default "default") //string指明要使用哪个网络,默认是bridge
# docker network inspect bridge //查看bridge的信息
# docker container inspect web1 //查看容器web1的详细信息