docker容器通信

前言

(1)Docker的本地网络实现其实就是利用了Linux上的网络命名空间和虚拟网络设备(特别是veth pair)。

(2)Docker中的网络接口默认都是虚拟的接口。虚拟接口的最大优势就是转发效率极高。这是因为Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发,即发送接口的发送缓存中的数据包将被直接复制到接收接口的接收缓存中,而无需通过外部物理网络设备进行交换。对于本地系统和容器内系统来看,虚拟接口跟一个正常的以太网卡相比并无区别,只是它速度要快得多。

(3)Docker容器网络就很好地利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做veth pair)。

在这里插入图片描述

一般情况下,Docker创建一个容器的时候,会具体执行如下操作:
1.创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;
2.本地主机一端的虚拟接口连接到默认的docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234;
3.容器一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见;
4.从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(例如172.17.42.1/16)。
完成这些之后,容器就可以使用它所能看到的eth0虚拟网卡来连接其他容器和访问外部网络。用户也可以通过docker network命令来手动管理网络。

建议使用自定义的网桥来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址
Docker提供了创建这些网络的默认网络驱动程序,你可以创建一个新的Bridge网络,Overlay或Macvlan网络
你还可以创建一个网络插件或远程网络进行完整的自定义和控制
你可以根据需要创建任意数量的网络,并且可以在任何给定时间将容器连接到这些网络中的零个或多个网络
此外,您可以连接并断开网络中的运行容器,而无需重新启动容器
当容器连接到多个网络时,其外部连接通过第一个非内部网络以词法顺序提供

1.容器如何访问外网是通过iptables的SNAT实现的

在这里插入图片描述

2.外网如何访问容器

端口映射,-p指定对应端口
外网访问容器用到了docker-proxy和iptables DNAT
宿主机访问本机容器使用的是iptables DNAT
外部主机访问容器或容器之间的访问是docker-proxy实现

在这里插入图片描述

3. 操作

在这里插入图片描述在这里插入图片描述在这里插入图片描述

[root@server1 ~]# docker run -it --name vm1 --network my_net1 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
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # [root@server1 ~]# 


[root@server1 ~]# docker run -it --name vm2 --network container:vm1 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
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # 

在这里插入图片描述在这里插入图片描述在这里插入图片描述
先以默认的网络运行一个容器vm1
然后使用link方法运行另外一个容器vm2

[root@server1 ~]# docker run -it --name vm1 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
8: eth0@if9: <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
/ # [root@server1 ~]# docker run  -it --name vm2 --link vm1:web busybox
/ # ping vm1
PING vm1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.087 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.063 ms
^C
--- vm1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.063/0.075/0.087 ms
/ # ping web
PING web (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.220 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.063 ms
^C
--- web ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.063/0.141/0.220 ms
/ # 

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

4. 总结

(1)容器访问控制

容器的访问控制主要通过Linux上的iptables防火墙软件来进行管理和实现。iptables是Linux系统流行的防火墙软件,在大部分发行版中都自带。

(2)容器访问外部网络

我们知道容器默认指定了网关为docker0网桥上的docker0内部接口。docker0内部接口同时也是宿主机的一个本地接口。因此,容器默认情况下是可以访问到宿主机本地的。更进一步,容器要想通过宿主机访问到外部网络,需要宿主机进行转发。

在这里插入图片描述如果为0,则没有开启转发,则需要手动打开:

sysctl -w net.ipv4.ip_forward=1

(3)外部访问容器实现

容器允许外部访问,可以在docker run时候通过-p或-P参数来启用。

不管用那种办法,其实也是在本地的iptable的nat表中添加相应的规则,将访问外部IP地址的网包进行目标地址DNAT,将目标地址修改为容器的IP地址。

以一个开放80端口的Web容器为例,使用-P时,会自动映射本地49000~49900范文内的端口随机端口到容器的80端口:

在这里插入图片描述可以看到,nat表中涉及两条链,PREROUTING链负责包到达网络接口时,改写其目的地址。其中规则将所有流量都扔到DOCKER链。而DOCKER链中将所有不是从docker0进来的网包(意味着不是本地主机产生),将目标端口为49153的,修改目标地址为172.17.0.2,目标端口修改为80。

(4)容器之间的通信

容器之间可通过 IP,Docker DNS Server 和joined 容器三种方式通信。

IP 通信

两个容器要能通信,必须要有属于同一个网络的网卡。满足这个条件后,容器就可以通过 IP 交互了。具体做法是在容器创建时通过 --network 指定相应的网络,或者通过 docker network connect 将现有容器加入到指定网络。

Docker DNS Server

通过 IP 访问容器虽然满足了通信的需求,但还是不够灵活。因为我们在部署应用之前可能无法确定 IP,部署之后再指定要访问的 IP 会比较麻烦。对于这个问题,可以通过 docker 自带的 DNS 服务解决。

从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过"容器名"通信。方法很简单,只要在启动时用 --name 为容器命名就可以了。

下面启动两个容器 box1 和 box2,并且在上面定义的网络模式bridge2中:

docker run -it --network=bridge2 --name box1 busybox

docker run -it --network=bridge2 --name box2 busybox

box2和 box1可以互ping通

使用 docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的。

joined 容器

joined 容器是另一种实现容器间通信的方式。它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信。例:

先创建一个http容器,名字为 box1

docker run -it --name box1 http

然后创建 busybox 容器并通过 --network=container:box1 指定 jointed 容器为 box1:

docker run -it --network=container:box1 busybox

busybox 和 box1 的网卡 mac 地址与 IP 完全一样,它们共享了相同的网络栈。busybox 可以直接用 127.0.0.1 访问 box1 的 http 服务。

joined 容器非常适合以下场景:

不同容器中的程序希望通过 loopback 高效快速地通信,比如 web server 与 app server。

希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。

发布了260 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yrx420909/article/details/105428909