这里分两个部分来讲解,分别为:容器间通信
和外部(宿主机外的网络)与容器的通信
。
通过 IP 的形式来通信。试想一下,两个容器之间是相互隔离的,因此是无法互相 ping 通的,那如果运行的这两个容器使用的是同一个自定义的网络模式
,那是否可以连接呢?答案是可以的。我们来实际测试一下。
前提条件:
创建 A 容器:
[root@qcloud ~]# docker run -it --name test_a --network=net_b busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:04:05
inet addr:192.168.4.5 Bcast:192.168.4.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:9 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:726 (726.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
ping 一下 B 容器:
/ # ping 192.168.4.6
PING 192.168.4.6 (192.168.4.6): 56 data bytes
64 bytes from 192.168.4.6: seq=0 ttl=64 time=0.177 ms
64 bytes from 192.168.4.6: seq=1 ttl=64 time=0.132 ms
64 bytes from 192.168.4.6: seq=2 ttl=64 time=0.116 ms
创建 B 容器:
[root@qcloud ~]# docker run -it --name test_b --network=net_b busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:04:06
inet addr:192.168.4.6 Bcast:192.168.4.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:516 (516.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
ping 一下 A 容器:
/ # ping 192.168.4.5
PING 192.168.4.5 (192.168.4.5): 56 data bytes
64 bytes from 192.168.4.5: seq=0 ttl=64 time=0.117 ms
64 bytes from 192.168.4.5: seq=1 ttl=64 time=0.126 ms
64 bytes from 192.168.4.5: seq=2 ttl=64 time=0.125 ms
从结果看,A、B 容器可互相 通信。
创建 C 容器:
如果该容器为普通 Bridge 网络模式
的容器(如:Nginx),那么容器 A 或容器 B 是否能访问 Nginx 容器呢?答案是不能访问
。如何解决不可访问的问题?
如果 Host 上对每个网络都有一条路由,且 Host 打开了路由转发(net.ipv4.ip_forward = 1),那不同网桥上的网络就可互相通信。此时,我在 C 容器上加入一块 A、B 容器的虚拟网卡设备,来实现与 A、B 容器的通信。
[root@qcloud ~]# docker network connect net_b nginx
Nginx 容器里没有查看 IP 地址的命令,可通过 docker inspect nginx
来查看,如下图,Nginx 容器已经分配了 net_b 网络的一个 IP 地址。
Nginx 容器的 index.html 我已经提前更改为:hello zhurs 111
A 容器 ping 一下 C 容器:
/ # ping 192.168.4.4
PING 192.168.4.4 (192.168.4.4): 56 data bytes
64 bytes from 192.168.4.4: seq=0 ttl=64 time=0.214 ms
64 bytes from 192.168.4.4: seq=1 ttl=64 time=0.129 ms
64 bytes from 192.168.4.4: seq=2 ttl=64 time=0.225 ms
A 容器访问 C 容器的 Nginx 内容:
/ # wget 192.168.4.4
Connecting to 192.168.4.4 (192.168.4.4:80)
saving to 'index.html'
index.html 100% |**************************************************************************************************************************| 16 0:00:00 ETA
'index.html' saved
/ # cat index.html
hello zhurs 111
从结果看,A 容器可正常访问 C 容器的资源。
小结:
自定义网络("Driver": "bridge")
下的所有容器可互相通信。自定义网络("Driver": "bridge")
想与普通 Bridge 网络模式
进行通信,可在双方任意一方加入对方的网络模式即可。上面提到网络驱动 Driver 为 bridge 的自定义网络模式下的容器可通过 IP 进行通信,但在实际应用场景中,IP 可能并不稳定(除非你自定义 IP),因此,我们可以通过容器名
的方式进行通信。
创建容器名为 dns1:
[root@qcloud ~]# docker run -it --network=net_b --name=dns1 busybox
创建容器名为 dns2:
[root@qcloud ~]# docker run -it --network=net_b --name=dns2 busybox
/ # ping dns1
PING dns1 (192.168.4.5): 56 data bytes
64 bytes from 192.168.4.5: seq=0 ttl=64 time=0.107 ms
64 bytes from 192.168.4.5: seq=1 ttl=64 time=0.121 ms
...
可见,通过域名可正常通信,该通信方式等效于 IP 通信方式,无非就是一个通过 IP 方式,另一个通过容器名的方式。
但是需要注意,通过容器名通信的前提是:网络模式必须为自定义网络("Driver": "bridge")
模式
这种模式类似 k8s 中 pod 的多容器情况,在 k8s 中,一个 pod 可有一个或多个容器,一般我们多出的那些容器主要起辅助作用,比如一些日志监控等。Docker 的 Joined 通信类型也类似,该模式的作用是:它可使两个或多个容器共享一个网络栈(网卡、配置信息等),因此 Joined 模式下的所有容器可通过 127.0.0.1
直接通信,你可以将他们想象为是一个整体。
如何实现?
在运行容器时指定要 joined 的目标容器,成功后将共享目标容器的网络栈。
[root@qcloud ~]# docker run -it --name=join-test --network=container:nginx busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
388: eth1@if389: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:c0:a8:04:04 brd ff:ff:ff:ff:ff:ff
inet 192.168.4.4/24 brd 192.168.4.255 scope global eth1
valid_lft forever preferred_lft forever
216: eth0@if217: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
查看 IP 信息,完全共享被 joined 目标容器的网络。
其实这是通过 Host 的 iptables 机制来实现的,如下标红的几个示例,当收到 172.19.0.0/16 网段的外出包,就把它交给 MASQUERADE 处理,MASQUERADE 则将外出包的源地址转换为 Host 的地址发送出去,实现了网络的 NAT 转换。这也是容器可以与外部通信的原因。
容器能与外界通信,那外部是如何与容器内部通信呢?其实道理是一样的,也是通过 NAT 技术,在结合端口映射的方式实现外部与 Host 下的容器通信。
[root@qcloud ~]# docker run -itd --name=httpd -p 80 httpd
[root@qcloud ~]# docker port httpd
80/tcp -> 0.0.0.0:1024
该方式会随机生成一个动态端口,且是用于浏览器访问的端口号。
[root@qcloud ~]# docker run -itd --name=httpd-1 -p 8686:80 httpd
[root@qcloud ~]# docker port httpd-1
80/tcp -> 0.0.0.0:8686
该方式会指定一个静态端口,且是用于浏览器访问的端口号。静态端口映射用的居多。
小结:
没映射一个端口,Host 都会启动一个 docker-proxy 进程来处理访问容器的流量。
<点击跳转至开头>