Docker单主机容器间通信

Docker的原生网络

也就是docker程序中自带的网络类型

查看Docker中的原生网络

[root@localhost ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
e1aa5d692686        bridge              bridge              local
082e568090f3        host                host                local
70d9c743f011        none                null                local

原生网络主要用于,容器和容器之间的网络通讯,以及容器与外部主机之间的通讯

Docker网络从覆盖范围分为两种:

  • 单主机的容器之间的通讯
  • 跨主机的容器之间的通讯

本文先来介绍单主机的容器之间的通讯,跨主机在后面会讲到

none网络

none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过--network=none指定使用 none 网络。

[root@localhost ~]# docker run -it --network none 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

可以看到使用none网络运行的容器中,只有一个lo网卡,用作本地回环,这个网络只能用作ping 127.0.0.1,不能够与外部任何主机或者容器进行通讯

none网络的应用场景

由于它不能与任何外部进行通讯,也就是被隔离,对于安全性较高的并且不需要网络的应用来说就非常适合使用none网络。

比如:某个容器的唯一作用是生成二维码或者随机码,每次的二维码和随机码是不一样的,就可以放到none网络中避免密码被窃取。

host网络

连接到host网络的容器共享了Docker主机的协议栈,容器的网络配置与主机的完全一样可以通过--network host指定使用host网络

[root@localhost ~]# docker run -it --rm --network host busybox /bin/sh
/ # 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
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:35:56:16 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.11/24 brd 192.168.1.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::4b2e:bbe5:64d2:e455/64 scope link 
       valid_lft forever preferred_lft forever
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue qlen 1000
    link/ether 52:54:00:fa:47:fa brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 qlen 1000
    link/ether 52:54:00:fa:47:fa brd ff:ff:ff:ff:ff:ff
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue 
    link/ether 02:42:31:92:1c:1e 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:31ff:fe92:1c1e/64 scope link 
       valid_lft forever preferred_lft forever
7: vetha3dd9d4@if6: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0 
    link/ether 5e:cb:94:6c:b8:a7 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5ccb:94ff:fe6c:b8a7/64 scope link 
       valid_lft forever preferred_lft forever

查看使用host网络的容器中的ip信息,与物理机的完全一样,包括IP地址。

host网络可以直接与外网通信,与主机相连

host网络适用场景

直接使用 Docker host网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker 主机上已经使用的端口就不能再用了。

Docker hos 的另一个用途是让容器可以直接配置主机网络。比如某些跨主机的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables

joined网络

joined 容器是另一种实现容器间通信的方式。

joined 容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信,可以通过--network container:已经运行的容器名/id

[root@localhost ~]# docker run -d --name test httpd
95552dc2c6add020aaa09de54f8cf76b3d7d64629d81908a04a2ed7e2856c846
[root@localhost ~]# docker exec -it test /bin/sh
# ip a
/bin/sh: 1: ip: not found

在上述例子中,test容器启动之后,想要知道这个容器的ip是多少,进入容器发现没有命令,几乎什么常用命令都没有,更没有关于网卡的配置

这时候需要使用另一个容器,且这个容器不能使用和以上例子中相同的镜像,否则还是没有任何命令,使用joined共享它的网络来达到查看ip的目的

--network container:test:表示启动此容器要使用容器test的网络配置

[root@localhost ~]# docker run -itd --network container:test --name test1 busybox
145d64a4d30c5e4f015c898ca177bc1382f239c6d0305e9472c2e104d0344098
[root@localhost ~]# docker exec -it test1 /bin/sh
/ # 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

此时查看到的ip172.17.0.2就是test容器的ip,它与test容器共享一个ip,也可以理解为他们在一个网络命名空间内

因为test容器是使用的httpd的镜像,所以容器内是有网页页面的,我们可以通过查看到的ip访问来看

[root@localhost ~]# curl 172.17.0.2
<html><body><h1>It works!</h1></body></html>

joined网络适用场景

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

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

bridge网络

之后要学习到的跨主机网络通信,都是在bridge网络的基础上来进行操作的。当启动容器不指定任何网络时,默认网络就是使用的bridge网络

docker在安装是会在主机默认创建一个网卡信息docker0网卡

[root@localhost ~]# ip a
...
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:31:92:1c:1e 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:31ff:fe92:1c1e/64 scope link 
       valid_lft forever preferred_lft forever

在docker0后面,有一个NO-CARRIER,表示它不用来传输任何的数据的,只有在没有任何容器运行时,才会出现这个NO-CARRIER,这个网卡也被叫做桥接网卡

查看用来控制桥接网卡的信息

[root@localhost ~]# brctl show
bridge name    bridge id        STP enabled    interfaces
docker0        8000.024231921c1e    no        
virbr0        8000.525400fa47fa    yes        virbr0-nic

docker0网卡的唯一作用是用来桥接的,根据以上信息中看到docker0的interfaces是没有信息的。这是因为它还没有为任何网络接口进行桥接

创建一个容器并使用桥接网络(默认就是桥接),也可以使用--network bridge

[root@localhost ~]# docker run -d --name testhttpd httpd
6fc2b659ef084fd88d7037586589a53bf7ae3f33b42e3be45b5e0cddf4cb3add
[root@localhost ~]# brctl show  # 再次查看时已经有了桥接的网络接口
bridge name    bridge id        STP enabled    interfaces
docker0        8000.024231921c1e    no        veth87cea4f
virbr0        8000.525400fa47fa    yes        virbr0-nic

因为是使用了httpd镜像运行的容器,所以需要使用joined网络来进行查看网卡信息

[root@localhost ~]# docker run -it --rm --network container:testhttpd busybox /bin/sh
/ # 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
12: eth0@if13: <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

可以看到网卡信息中是12: eth0@if13,表示接口12桥接在接口13上

同样在主机的网卡信息中也能看到13: veth89b705c@if12,而刚才使用brctl show查看到的桥接接口信息就是veth89b705cveth89b705c又桥接在docker0上,所以docker0和容器中的ip网段是一样的,可以通信

[root@localhost ~]# ip a
...
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:31:92:1c:1e 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:31ff:fe92:1c1e/64 scope link 
       valid_lft forever preferred_lft forever
13: veth89b705c@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether aa:72:2c:fa:3f:37 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::a872:2cff:fefa:3f37/64 scope link 
       valid_lft forever preferred_lft forever

查看bridge网卡的详细信息

[root@localhost ~]# docker network inspect bridge
[
    {
        "Name": "bridge",  # 网卡名
        "Id": "e1aa5d69268678d522830a9d4ac109cfe550b9b8533342924558710f2a8dace8",
        "Created": "2020-03-25T04:59:05.769955646+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {       # ip地址管理
            "Driver": "default",
            "Options": null,
            "Config": [
                {     # 物理机桥接网卡的网段和地址,并且作为网关存在
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {  # 容器id
            "60a082b599fb780ca4279af981369cc3a764a1816603448ed5a17dd2d456a664": {
                "Name": "testhttpd",   # 运行的容器名
                "EndpointID": "959aeef49b92f5f564d90c8bd8290b18d404b0582079c9aeba4a71cac881bd31",
                "MacAddress": "02:42:ac:11:00:02",  # 容器的网卡mac
                "IPv4Address": "172.17.0.2/16",  # 容器的ip
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

Docker单主机容器间通信

 

 

创建自定义网络

docker network create -d 网络类型 创建的网卡名称

-d/--driver:指定网络类型

创建自动分配网段的网卡

[root@localhost ~]# docker network create -d bridge my_net
6f4461e757b41d41bf720ae4ef35c5f4703145317eccaace669c62cf191d9a6c
[root@localhost ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
e1aa5d692686        bridge              bridge              local
082e568090f3        host                host                local
6f4461e757b4        my_net              bridge              local
70d9c743f011        none                null                local
[root@localhost ~]# brctl show 
bridge name    bridge id        STP enabled    interfaces
br-6f4461e757b4        8000.02422eb38b13    no        
docker0        8000.024231921c1e    no        veth89b705c
virbr0        8000.525400fa47fa    yes        virbr0-nic

可以看到bridge的信息中多了一块网卡br-6f4461e757b4,br就是bridge的意思,一串字符串是网卡id

查看my_net网卡的信息,可以看到这个网卡被自动分配的网段,当然也可以通过ip a直接看到

[root@localhost ~]# docker network inspect my_net
[
    {
        "Name": "my_net",  # 网卡名
        "Id": "6f4461e757b41d41bf720ae4ef35c5f4703145317eccaace669c62cf191d9a6c",
        "Created": "2020-03-30T19:10:45.014008523+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {   # 网卡网段自动分配为18
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

可以看到my_net地址为18网段,初始的桥接网卡是17,就可以发现自动分配的网段是网上递增的。

创建手动分配网段的网卡

docker network create -d bridge --subnet 网段/子网 --gateway 网关地址 网卡名称

--subnet:指定网段

--gateway:指定网关

[root@localhost ~]# docker network create -d bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
5c255bce71c3a5bebb0830b13963f600f1f4ee371b67ac4e6ef4eb2288f7e754

查看网卡信息,确定网段是否和手动分配的一样,这里使用ip a来查看

[root@localhost ~]# ip a
...
15: br-5c255bce71c3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:13:89:03:92 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.1/24 brd 172.22.16.255 scope global br-5c255bce71c3
       valid_lft forever preferred_lft forever

自定义网络的使用

使用--network指定新创建的网卡名称即可使用

[root@localhost ~]# docker run -itd --name net2 --network my_net2 busybox
cfa99b7cba2df64a9397f7b1b5b111174060b939fa4565eddba757ecbded06b5

进入容器进行验证,可以看到已经使用了自定义网络my_net2手动分配的ip网段

[root@localhost ~]# docker exec -it net2 /bin/sh
/ # 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
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:16:10:02 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.2/24 brd 172.22.16.255 scope global eth0
       valid_lft forever preferred_lft forever

手动指定容器的IP地址

docker run -itd --name net8 --network my_net2 --ip 72.22.16.8 busybox

--ip:指定分配给容器的ip(与my_net2同网段)

[root@localhost ~]# docker run -itd --name net8 --network my_net2 --ip 172.22.16.8 busybox
53afa008e9a2b86df9c5cfce05f9ec837ecd7c0d8f5867a9905461fe3ff9dbc8

进入容器验证

[root@localhost ~]# docker exec -it net8 /bin/sh
/ # 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
22: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:16:10:08 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.8/24 brd 172.22.16.255 scope global eth0
       valid_lft forever preferred_lft forever

容器间跨网段通信

刚才创建了一块桥接网卡net2,net2网段为172.22.16.0/24,docker0网段为172.17.0.0/16,如果他们俩要进行通信怎么办

进入使用net2网卡的容器中,ping网卡docker0的网关是可以通的,但是ping使用net网卡的容器就ping不通

[root@localhost ~]# docker exec -it net8 /bin/sh
/ # ping 172.18.0.1
PING 172.17.0.1 (172.17.0.1): 56 data bytes
64 bytes from 172.17.0.1: seq=0 ttl=64 time=0.140 ms
/ # ping 172.18.0.2
PING 172.17.0.2 (172.18.0.2): 56 data bytes
^C
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

在防火墙中可以看到有关网络的转发信息

[root@localhost ~]# iptables-save
# 查看关键信息
-A DOCKER-ISOLATION-STAGE-2 -o br-5c255bce71c3 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-6f4461e757b4 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
# 以上表示去往br-5c255bce71c3和br-6f4461e757b4这两块网卡的数据会被删除
# 正是我们刚才创建的两块bridge网卡的id号
# 不仅这两个网卡的被drop了,docker的也是一样,所以docker0不能和这两块网卡进行互相通信

如果想要去通讯,可以去把防火墙去关掉,但是上网的访问,还需要通过防火墙来转换ip,不能去关闭,或者清除防火墙。

运行一个docker0网卡的httpd容器

[root@localhost ~]# docker run -itd --name testhttpd httpd /bin/bash
e8747476713d1e8496f03dac071b04f5bf7d66a5baba5b8659b02e1087115ae7

使用docker network connect 自定义桥接网卡名 连接到的容器名/id

[root@localhost ~]# docker network connect my_net2 testhttpd 

因为httpd镜像中没有命令,使用joined的方法查看testhttpd的ip

[root@localhost ~]# docker run -it --rm --name test --network container:testhttpd 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
24: eth0@if25: <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
26: eth1@if27: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:16:10:03 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.3/24 brd 172.22.16.255 scope global eth1
       valid_lft forever preferred_lft forever

通过查看ip发现多了一块172.22.16.0/24网段的网卡,这个网段就是我们自己创建的net2网卡的网段,并且自动分配了ip

同样在bridge网卡信息中也能看到

[root@localhost ~]# brctl show
bridge name    bridge id        STP enabled    interfaces
br-5c255bce71c3        8000.024213890392    no        veth0d44d59
                            veth3b0daaa  # ip a可以看到这个信息
                            veth82c0237
br-6f4461e757b4        8000.02422eb38b13    no        
docker0        8000.024231921c1e    no        veth75947fa
virbr0        8000.525400fa47fa    yes        virbr0-nic

现在我们再来去访问,想要去ping通不同网段的那台主机,只能通过ping它新连接的那块网卡的新地址172.22.16.3

[root@localhost ~]# docker exec -it net8 /bin/sh
/ # ping 172.22.16.3
PING 172.22.16.3 (172.22.16.3): 56 data bytes
64 bytes from 172.22.16.3: seq=0 ttl=64 time=0.182 ms
^C
--- 172.22.16.3 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.182/0.182/0.182 ms

网段之间确实是没有办法通信,只能通过双网卡的方式来做到,间接的达到不同网段容器的通信

主机与外网时怎么通信的

主机进行通信时,依靠nat来进行处理数据包的地址封装,将私网地址转换成外网进行通信

nat转换又分为SNAT和DNAT

Docker单主机容器间通信

 

 

我们平常的上网就是使用了SNAT(源地址转换),我们作为client要访问外网的服务器主机HOST,将内网ip转换为路由器中的外网ip,由路由器的外网ip去帮我们拿取数据

而当外网中的中的server要来访问我们的client时,到达路由器时,会封装成路由器的内网ip,来到达client。这就是DNAT(目标地址转换)

外网与容器之间访问的原理

默认情况下运行一个容器都能够去访问到外网,实际本质不是外网,是可以访问除本网络以外的网络

[root@localhost ~]# docker run -it --rm --name test1 busybox
/ # ping www.baidu.com
PING www.baidu.com (61.135.169.121): 56 data bytes
64 bytes from 61.135.169.121: seq=0 ttl=127 time=3.789 ms
64 bytes from 61.135.169.121: seq=1 ttl=127 time=4.309 ms

既然使用的是nat路由来访问的外部网络,查看防火墙的SNAT策略

[root@localhost ~]# iptables -t nat -S
-A POSTROUTING -s 172.22.16.0/24 ! -o br-5c255bce71c3 -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-6f4461e757b4 -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

以其中docker0为例,这部分信息表示,如果docker0收到的源地址是172.17.0.0网段的数据,但目的并不是docker0的地址时,就交给MASQUERADE(伪装),而伪装就是将源地址转换为物理机的ip,也就是172.17.0.0网段的容器要访问其他网段时,去替换为主机ip

还是结合下面这张图来看

这里防火墙中的POSTROUTING,表示经过路由表之后检查数据包,PREROUTING是进入路由表之前检查数据包是否符合策略,而FORWORDING是查看路由表转发过程。相比来说FORWARDING比较好一点

Docker单主机容器间通信

 

 

容器访问外网

现在来看下图,开启三个终端来查看地址是如何转换的

TTY1监控网卡docker0的icmp协议,也即是ping命令的传输

TTY2监控网卡ens33

TTY3进入容器终端,ping外部网络,发送4个包即可

tcpdump -i docker0 -n icmp  # 然后会处于阻塞模式

TTY2

tcpdump -i docker0 -n icmp

TTY3

[root@localhost ~]# docker run -it --rm --name test busybox /bin/sh
/ # ping www.baidu.com
PING www.baidu.com (111.206.223.173): 56 data bytes
64 bytes from 111.206.223.173: seq=0 ttl=127 time=5.161 ms
64 bytes from 111.206.223.173: seq=1 ttl=127 time=4.952 ms
64 bytes from 111.206.223.173: seq=2 ttl=127 time=4.270 ms

此时其他两个终端你的监控网卡会弹出消息

TTY1,172.17.0.3 > 111.206.223.173docker0网卡将从容器中来的ip172.17.0.3转换为111.206.223.173

23:45:27.058529 IP 172.17.0.3 > 111.206.223.173: ICMP echo request, id 1536, seq 0, length 64
23:45:27.063566 IP 111.206.223.173 > 172.17.0.3: ICMP echo reply, id 1536, seq 0, length 64
23:45:28.059591 IP 172.17.0.3 > 111.206.223.173: ICMP echo request, id 1536, seq 1, length 64
23:45:28.064392 IP 111.206.223.173 > 172.17.0.3: ICMP echo reply, id 1536, seq 1, length 64
23:45:29.060885 IP 172.17.0.3 > 111.206.223.173: ICMP echo request, id 1536, seq 2, length 64
23:45:29.064974 IP 111.206.223.173 > 172.17.0.3: ICMP echo reply, id 1536, seq 2, length 64

TTY2,192.168.1.11 > 111.206.223.173ens33网卡将本机ip转为111.206.223.173

23:45:27.058568 IP 192.168.1.11 > 111.206.223.173: ICMP echo request, id 1536, seq 0, length 64
23:45:27.063519 IP 111.206.223.173 > 192.168.1.11: ICMP echo reply, id 1536, seq 0, length 64
23:45:28.059638 IP 192.168.1.11 > 111.206.223.173: ICMP echo request, id 1536, seq 1, length 64
23:45:28.064358 IP 111.206.223.173 > 192.168.1.11: ICMP echo reply, id 1536, seq 1, length 64
23:45:29.060908 IP 192.168.1.11 > 111.206.223.173: ICMP echo request, id 1536, seq 2, length 64
23:45:29.064926 IP 111.206.223.173 > 192.168.1.11: ICMP echo reply, id 1536, seq 2, length 64

在这个过程中,我们可以看下图中的上半部分,也就是黑色线条的部分,当busybox向baidu发送ping包时,源地址为172.17.0.0网段的主机,经过docker0发现目标是外网段的,就有NAT去处理,NAT会将源地址封装为192.168.1.0网段,也就是物理机ip,目标还是baidu。

Docker单主机容器间通信

 

 

外网访问容器

要通过端口映射来实现,docker0要将容器内访问的端口映射到物理机

这个端口要与httpd镜像中服务的端口一致

[root@localhost ~]# docker run -d -p 80 httpd
[root@localhost ~]# docker ps 
CONTAINER ID  IMAGE   COMMAND            CREATED        STATUS       PORTS                   NAMES
14bc71bb595e  httpd   "httpd-foreground" 2 minutes ago  Up 2 minutes 0.0.0.0:32769->80/tcp   distracted_wilbur

注意看以上输出信息中主机端口与容器端口的映射0.0.0.0:32769->80/tcp

主机端口为32768,容器中为80,这时候访问物理机的32769端口即可

[root@localhost ~]# curl 192.168.1.11:32769
<html><body><h1>It works!</h1></body></html>

查看容器对主机的端口映射

[root@localhost ~]# docker port distracted_wilbur # 容器名
80/tcp -> 0.0.0.0:32769

也可以指定主机使用什么端口来映射容器的80段口

[root@localhost ~]# docker run -d -p 8080:80 httpd
2c626edb9ab2c0295ebaa88a261eeeb3d2d0d77769b842bc01b97ecad1a40c32
[root@localhost ~]# curl 192.168.1.11:8080
<html><body><h1>It works!</h1></body></html>

这个过程中,我们看下图中的下半部分,也就是红色线的部分,当我们作为client去访问物理的映射端口8080时,会出现一个docker proxy的代理去找这个8080映射在哪个ip的哪个端口,然后通过docker0网卡,获取到容器的数据

Docker单主机容器间通信

 

上一篇:MySQL Orchestrator自动导换+VIP切换


下一篇:KVM网络实验