简述
网络模式决定了容器之间互联方式,地址资源的分配空间。处于统一网络的容器可以互通并且可以使用 hostname 访问。
使用的过程中需要根据 容器之间聚合的关系,是否需要隔离网络地址和转发的效率等方面来选择适合的网络模型。
dockerd 在启动的时候,会初始化以下三个不同 driver 模式的网络
NETWORK ID NAME DRIVER SCOPE
24e6af25485a bridge bridge local
3ad8ec491277 host host local
9608b5edcc32 none null local
因为第四种 joined-container
模式需要依托于一个运行时容器,所以并没有对应的初始网络。
同时由于 bridge 桥接模式需要一个 "网关", 所以还会有一个 docker0 虚拟网卡在 "宿主机" 上面。
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:7b:1d:8f:e4 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:7bff:fe1d:8fe4/64 scope link
valid_lft forever preferred_lft forever
之前使用的容器网络互连方式(Deprecated)
在容器启动时加上 --link <container_id|container_name[:alias]>
参数,可以使得新创建的容器在启动的时候自动添加环境变量和 hosts 参数(由于容器无论是哪一种网络模式,在宿主机上都是可以使用IP寻址的),从而实现互通。
这种方式适用于临时容器的连接,例如使用一个工具容器来 link 到现有的容器中进行网络测试。
早期的 k8s 容器互联中的容器名解析也试用了这种方式。
在 docker-compose 里面参数为 links: container_name[:alias],但是既然使用了 docker compose 就不建议使用这种方式了。
作为资源统一管理的 Docker 互联网络
可以使用 docker network 管理网络对象。建议为服务创建自己的网络。
在创建容器时,可以指定其属于的网络,或者说通过选择一个默认网络驱动来指定网络类型。
--network <bridge|host|none|container_name|custom>
其中,bridge|host|none 是网络资源里面默认的三种网络,container_name是joined-container类型,custom是预先创建的网络。
如果是 docker-compose 里面声明的网络,会自动创建。
bridge 桥接模式
bridge 模式是自动挡,创建的容器有自己的 Network Namespace,并且还会在目标网络中分配一个 IP。
桥接网络的网络地址是递增的,从默认初始的 docker0 的 172.17.0.1/16
开始,创建的桥接网络会以如下命名。
br-<NETWORK_ID> 172.18.0.1/16
br-<NETWORK_ID> 172.19.0.1/16
br-<NETWORK_ID> 172.20.0.1/16
如果创建的容器没有指定 network 类型,默认就是 docker0 为网关的桥接模式。
bridge 网络模式中,容器的网络初始化过程:
- 在主机上创建一对虚拟网卡 veth pair 设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。
- Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中,可以通过brctl show命令查看。
- 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。
veth pair 生成的一对网卡,一端是 容器的 eth0,一端是 docker0 网络段的地址。
为了实现容器与外部通信,宿主机上添加的 Iptable 规则
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.5:80
第一条规则会将源地址为 172.17.0.0/16 的包(也就是从 Docker 容器产生的包),并且不是从docker0网卡发出的,进行源地址转换,转换成主机网卡的地址。这么说可能不太好理解,举一个例子说明一下。假设主机有一块网卡为 eth0,IP地址为 10.10.101.105/24,网关为 10.10.101.254。从主机上一个IP为 172.17.0.2/16 的容器中ping百度(180.76.3.151)。IP 包首先从容器发往自己的默认网关 docker0,包到达 docker0 后,也就到达了主机上。然后会查询主机的路由表,发现包应该从主机的 eth0 发往主机的网关10.10.105.254/24。接着包会转发给eth0,并从 eth0 发出去(主机的ip_forward转发应该已经打开)。这条规则对包做 SNAT 转换,将源地址换为 eth0 的地址。这样,在外界看来,这个包就是从10.10.101.105上发出来的,Docker容器对外是不可见的。
第二条规则就是对主机 eth0 收到的目的端口为外部映射端口的 tcp 流量进行DNAT转换,将流量发往172.17.0.5:80,也就是我们创建的Docker容器的地址。所以,外界只需访问 10.10.101.105:80 就可以访问到容器中得服务。
host 主机模式
使用 --net 设定为 host 的容器,使用 ip addr 等会看到和宿主机一样的网卡情况。不会创建 Network Namespace 而是和宿主机公用同一个。
容器并没有向 bridge 模式那样有一个私有 IP 地址,而且端口都是和宿主机共用一个地址空间。所以 -p
等端口映射的操作不仅是无效的,还会得到运行失败和一句警告
WARNING: Published ports are discarded when using host network mode
因为 dockerfile 里面尝试使用的端口例如 80 如果在宿主机上已经 bind,例如 nginx 在 host 模式下面是无法启动的。
如果容器内的服务需要对本机其他服务进行操作,例如 Consul 或者 kubernates 等的本地服务之间的监听和通信,或者对性能有要求,就需要改为 host 模式。
none
none 模式是手动档。没有网络配置的网络模式,需要手动配置。有独立的 Network Namespace。
joined-container 联合容器
类似 host 模式,这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。
kubernetes 的 pod 里面,如果有多个容器,就是使用的这一模式。