Docker 容器通信详解

目录

    • 一、容器间通信
      • 1.1 IP
      • 1.2 Docker DNS Server
      • 1.3 Joined
    • 二、外部与容器通信
      • 2.1 动态端口映射
      • 2.2 静态端口映射


这里分两个部分来讲解,分别为:容器间通信外部(宿主机外的网络)与容器的通信

一、容器间通信

1.1 IP

通过 IP 的形式来通信。试想一下,两个容器之间是相互隔离的,因此是无法互相 ping 通的,那如果运行的这两个容器使用的是同一个自定义的网络模式,那是否可以连接呢?答案是可以的。我们来实际测试一下。

前提条件:

  • 自定义网络名:net_b
  • 自定义网络IP段:192.168.4.0/244

创建 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 地址。

Docker 容器通信详解_第1张图片

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 容器的资源。

小结:

  • 结论1:同一个自定义网络("Driver": "bridge")下的所有容器可互相通信。
  • 结论2:自定义网络("Driver": "bridge") 想与普通 Bridge 网络模式进行通信,可在双方任意一方加入对方的网络模式即可。

1.2 Docker DNS Server

上面提到网络驱动 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")模式

1.3 Joined

这种模式类似 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 转换。这也是容器可以与外部通信的原因。

Docker 容器通信详解_第2张图片

容器能与外界通信,那外部是如何与容器内部通信呢?其实道理是一样的,也是通过 NAT 技术,在结合端口映射的方式实现外部与 Host 下的容器通信。

2.1 动态端口映射

[root@qcloud ~]# docker run -itd --name=httpd -p 80 httpd
[root@qcloud ~]# docker port httpd
80/tcp -> 0.0.0.0:1024

该方式会随机生成一个动态端口,且是用于浏览器访问的端口号。

2.2 静态端口映射

[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 进程来处理访问容器的流量。

Docker 容器通信详解_第3张图片

<点击跳转至开头>

你可能感兴趣的:(DevOps,docker,网络,运维)