目录
每一个docker容器拥有自己的网络命名空间,那么容器之间是如何通信的呢?
docker容器有4种通信方式:
-
bridge模式(默认)
-
host模式
-
container模式
-
none模式
1. bridge桥接模式
(1) 原理
物理设备的通信需要:
- 交换机:维护、学习MAC地址,做寻址、转发
- 网线两端的网口:两个网口组成一对,一端接入交换机,一端接入PC设备
- 传输介质:网线
[root@k8s-master ~]# yum install -y bridge-utils
[root@k8s-master ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02428c27984c no veth0db5e12
veth8369b2f
vethb632024
与物理设备类似,docker桥接模式也是通过这种原理实现的:
容器的通信也同样需要:
- 虚拟网桥docker0:
- 网卡对:veth0 veth1,一端接入虚拟网桥,一端接入容器内
- 传输介质:虚拟的,通过网卡对传输
docker 创建一个容器的时候,会执行如下操作:
-
创建一对虚拟接口/网卡,也就是veth pair
-
将网卡对的本地主机一端接到docker网桥上,并具有一个唯一的名字,如 veth0db5e12(主机上的网卡)
-
将网卡对的容器一端接到容器内部,并修改名字为 eth0(这个网卡只在容器内可见)
-
从网桥的可用地址段中(也就是与该bridge对应的network)获取一个空闲地址分配给容器的eth0
-
配置默认路由到网桥
这样就模拟了二层交换机,创建好了通信桥梁。
(2) 上手操作一下
#清空
docker rm -f `docker ps -aq`
#查看
docker ps
brctl show
创建两个测试容器test1,test2:
#运行容器test1
docker run -d --name test1 nginx:alpine
#查看docker0上的网卡变化
brctl show
#查看网卡信息
ip a |grep veth
# 查看容器中的网卡
docker exec -ti test1 ifconfig
#运行容器test2
docker run -d --name test2 nginx:alpine
#进入容器
docker exec -ti test2 sh
#curl到test1容器
sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
apk add curl
curl 172.18.0.2
(3) 容器的通信过程
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0
- eth0 网卡
是这个容器里的默认路由设备
- 第1条路由规则
所有对 172.18.0.0/16 这个网段的请求,都会被交给 eth0 来处理,这条路由规则的网关(Gateway)是 0.0.0.0。这就意味着这是一条直连规则,即:凡是匹配到这条规则的 IP 包,应该经过本机的 eth0 网卡,通过二层网络(数据链路层)直接发往目的主机。
- test2发送ARP广播请求
test2容器通过 eth0 网卡发送一个 ARP 广播,来通过目的 IP 地址(172.18.0.2)查找对应的 MAC 地址。
- 网桥转发ARP广播请求
eth0 的另一端插宿主机的 docker0 网桥上,通过另一端vethx,把请求交给网桥处理,由网桥转发ARP的广播请求。
- test1对ARP做出响应
docker0将ARP请求广播出去,test1收到之后返回响应,这样docker0就维护了一份MAC地址映射表,后续对于test2的eth0对test1的请求,都由docker0网桥负责转发。
查看MAC映射表:
[root@k8s-master ~]# brctl showmacs docker0
port no mac addr is local? ageing timer
2 72:b5:69:87:9c:20 yes 0.00
2 72:b5:69:87:9c:20 yes 0.00
1 f2:da:d9:3d:99:4a yes 0.00
1 f2:da:d9:3d:99:4a yes 0.00
查看容器的网卡eth0与主机的哪个虚拟网卡相对应:
#查看容器内网卡eth0的索引序号
[root@k8s-master ~]# docker exec -it test1 cat /sys/class/net/eth0/ifindex
48
#查看所有网卡对
[root@k8s-master ~]# ip a|grep @if
49: veth32652ea@if48: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
51: veth96900b0@if50: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
2. Host模式
(1) 使用
docker不会为容器创建网络命名空间,容器直接共享宿主机的网络空间。那么在容器内的所有网络操作,都是走的宿主机网络,容器也没有自己独立的IP地址。
这种方式由于不需要进行NAT转换,网络性能较好。
启动一个容器,并使用 --net host 参数指定 host 模式:
docker run --net host -d --name mysql mysql:5.7
可以查看主机网络接口与容器的网络接口,结果应该是一致的:
#查看主机网络接口
ifconfig
#查看容器网络接口
docker exec -it mysql ifconfig
(2) 场景
适用于需要容器与宿主机共享网络资源的场景。
例如,网络监控、日志收集等,需要直接访问宿主机的网络接口和IP地址。
3. Container模式
(1) 使用
指定新创建的容器和已经存在的某个容器共享一个网络命名空间,而不是和宿主机共享。
新创建的容器不会创建自己的网卡,配置自己的 IP,而是和指定的容器共享 IP、端口。两个容器的进程可以通过 lo 网卡设备通信。
这种模式可以直接访问 localhost 来访问 namespace下的其他容器,可以降低容器间的通信难度,同时节约网络资源。
启动一个容器,并使用 --net container:{target container} 参数指定 container 模式:
docker run -d --name test1 nginx:alpine
docker run -d --name test2 --net container:test1 nginx:alpine
可以查看两个容器的网络接口,结果应该是一致的:
docker exec -it test1 ifconfig
docker exec -it test2 ifconfig
在test1容器内可以使用localhost访问test2的接口:
wget -O- localhost
(2) 场景
适用于需要多个容器之间共享网络配置的场景。
比如 k8s 的一个 pod 内不同容器间的通信方式,使用的就是这种模式。k8s 会在 pod 中创建一个名为 pause 的默认容器,pause 会创建一个命名空间,其他容器加入到 puase 容器中共享 pause 的网络,多个容器之间通过 localhost 相互通信。
4. None模式
docker 会将容器与宿主机隔离开来,但容器不提供任何网络能力。在这种模式下,容器内部没有网卡、IP地址、路由等信息,只有一个回环网络(loopback)接口。这意味着容器不能访问外部网络,也不能被外部网络访问。
这种模式会用于一些特殊场景,比如需要在容器内部运行一些独立的、与网络无关的应用程序,或者需要进行一些网络调试。