Docker 网络深入理解

版权声明: https://blog.csdn.net/wo18237095579/article/details/83899023

bridge 网络

容器之间的互通

  这里首先启动两个容器,一个 name 为:test1,另一个 name 为:test2。如下:

[root@Docker1 ~]# docker run --name test1 -d busybox /bin/sh -c "while true; do sleep 60; done"
a7e7ecb9de2a8ef47d29630455fd6587602f9fa25dc7db25c70867aa15ebb5eb
[root@Docker1 ~]# docker run --name test2 -d busybox /bin/sh -c "while true; do sleep 60; done"
4da18e7bebd304c78237e1a38a07fca84b6c229344f98792c6c0a2629a68b1a8
[root@Docker1 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
4da18e7bebd3        busybox             "/bin/sh -c 'while t…"   5 seconds ago       Up 4 seconds                            test2
a7e7ecb9de2a        busybox             "/bin/sh -c 'while t…"   9 seconds ago       Up 8 seconds                            test1

  在容器 test1 中执行命令,查看网卡信息:

[root@Docker1 ~]# docker exec test1 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
38: eth0@if39: <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

  在容器 test2 中执行命令,查看网卡信息:

[root@Docker1 ~]# docker exec test2 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
40: eth0@if41: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

  查看宿主机(装有 Docker 服务的机器)查看宿主机的网卡信息:

[root@Docker1 ~]# docker exec test1 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
38: eth0@if39: <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@Docker1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
    inet 10.211.55.9/24 brd 10.211.55.255 scope global noprefixroute dynamic eth0
       valid_lft 1149sec preferred_lft 1149sec
    inet6 fdb2:2c26:f4e4:0:2c63:a924:4f67:12b/64 scope global noprefixroute dynamic
       valid_lft 2588295sec preferred_lft 601095sec
    inet6 fe80::139f:3030:9bc7:ec26/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:80:a4:a8:0e 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:80ff:fea4:a80e/64 scope link
       valid_lft forever preferred_lft forever
39: veth0075e8a@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 52:f3:a1:46:4d:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::50f3:a1ff:fe46:4da6/64 scope link
       valid_lft forever preferred_lft forever
41: vethd027e2f@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 2e:be:16:55:ea:61 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::2cbe:16ff:fe55:ea61/64 scope link
       valid_lft forever preferred_lft forever

  在没有启动两个容器之前,宿主机的网卡信息中是不存在最后边两个 veth0075e8avethd027e2f 的信息的。也可以看到每个容器的网卡信息:eth0,各自的ip 172.17.0.2/16172.17.0.3/16,它们的 Network Namesapce 是分开的。也是可以相互通信的,如下:

[root@Docker1 ~]# docker exec test1 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.101 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.211 ms
64 bytes from 172.17.0.3: seq=3 ttl=64 time=0.166 ms

[root@Docker1 ~]# docker exec test2 ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.068 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.162 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.166 ms

  结论显得很简单,下面就从 Liunx 的 Network Namespace 说说是如何做到通信的。

Linux 的 Network Namespace

  先查看下本机拥有的 Network Namespace(以下简称 netns):

[root@Docker1 ~]# ip netns list

  我当前的宿主机是没有 netns 的,这里先创建一个 test1test2

# 删除命令:ip netns delete test1
[root@Docker1 ~]# ip netns add test1
[root@Docker1 ~]# ip netns add test2
[root@Docker1 ~]# ip netns list
test2
test1

  上一小节里面利用 Docker 创建了两个容器,并能查看其中的 netns 的信息,那么这里手动创建的 netns 如何查看?如下:

[root@Docker1 ~]# ip netns exec test1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  上面的命令意思:在 netns 名称为 test1 的上面执行命令 ip a。可以看到,只有一个本地会换地址。没有任何地址,状态为 DOWN。当然,你也可以执行其他的命令,如:ip link

[root@Docker1 ~]# ip netns exec test1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  这里的 lo 网卡状态是 DOWN,这里把它给 UP 起来。

[root@Docker1 ~]# ip netns exec test1 ip link set dev lo up
[root@Docker1 ~]# ip netns exec test1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  这里执行了 UP 操作之后它的状态依旧不是 UP,而是 UNKNOWN,是因为网络连接是多方面的,单单只是这个 netns 的 Veth UP 是没有用的,没人跟它连接。也就是说,单个网卡没办法 UP 起来。

  现在的情况是这样的:

独立 netns

  这里呢,想要做的是让这俩个独立的 netns 连接起来,能够通信。如果想让两台计算机连接起来,我们知道,需要网线,让它们可以进行通信。那么在 Linux 的 netns 中,有一个概念叫做 Veth,我们可以创建两个 Veth,并分别分给 test1 和 test2,这样他们就可以连接起来,如下图:

连接

  如下操作,首先我查看当前宿主机的只存在五个 link,然后添加了两个类型为 vethlink,再次查看,发现存在两个个 link,veth-test1 AND veth-test2。他们有个特点,没有 ip,状态也都是 DOWN。

[root@Docker1 ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:80:a4:a8:0e brd ff:ff:ff:ff:ff:ff
39: veth0075e8a@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 52:f3:a1:46:4d:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
41: vethd027e2f@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 2e:be:16:55:ea:61 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@Docker1 ~]# ip link add veth-test1 type veth peer name veth-test2
[root@Docker1 ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:80:a4:a8:0e brd ff:ff:ff:ff:ff:ff
39: veth0075e8a@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 52:f3:a1:46:4d:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
41: vethd027e2f@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 2e:be:16:55:ea:61 brd ff:ff:ff:ff:ff:ff link-netnsid 1
42: veth-test2@veth-test1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 3a:4a:97:ec:ee:5a brd ff:ff:ff:ff:ff:ff
43: veth-test1@veth-test2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 32:62:53:b3:f1:46 brd ff:ff:ff:ff:ff:ff

  现在把新增的 Veth veth-test1 添加到 netns test1 中去(netns test2 同样操作),这时候在宿主机中就只剩下五个,并且,查看 netns test1 和 test2 的 link 可以发现,在宿主机"消失"的 Veth 都已经跑到它们里面去了,如下:

[root@Docker1 ~]# ip link set veth-test1 netns test1
[root@Docker1 ~]# ip link set veth-test2 netns test2
[root@Docker1 ~]# ip netns exec test1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
43: veth-test1@if42: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 32:62:53:b3:f1:46 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@Docker1 ~]# ip netns exec test2 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
42: veth-test2@if43: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 3a:4a:97:ec:ee:5a brd ff:ff:ff:ff:ff:ff link-netnsid 0
[root@Docker1 ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:80:a4:a8:0e brd ff:ff:ff:ff:ff:ff
39: veth0075e8a@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 52:f3:a1:46:4d:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
41: vethd027e2f@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 2e:be:16:55:ea:61 brd ff:ff:ff:ff:ff:ff link-netnsid 1

  这样我们就实现了上面图中的,通过 Veth 进行连接通信的第一步了。可以看到,已经分配到 netns 中的 Veth 仅仅只有 Mac 地址,并不存在 IP 地址,而且状态也是 DOWN 的。

  下面我们手动添加上 IP 地址:

[root@Docker1 ~]# ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1
[root@Docker1 ~]# ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2
[root@Docker1 ~]# ip netns exec test1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
43: veth-test1@if42: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 32:62:53:b3:f1:46 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@Docker1 ~]# ip netns exec test2 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
42: veth-test2@if43: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 3a:4a:97:ec:ee:5a brd ff:ff:ff:ff:ff:ff link-netnsid 0

  添加上 IP 地址还不算完,还要把对应的 Veth 给 UP 了。如下:

[root@Docker1 ~]# ip netns exec test1 ip link set dev veth-test1 up
[root@Docker1 ~]# ip netns exec test2 ip link set dev veth-test2 up
[root@Docker1 ~]# ip netns exec test1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
43: veth-test1@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 32:62:53:b3:f1:46 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 192.168.1.1/24 scope global veth-test1
       valid_lft forever preferred_lft forever
    inet6 fe80::3062:53ff:feb3:f146/64 scope link
       valid_lft forever preferred_lft forever
[root@Docker1 ~]# ip netns exec test2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
42: veth-test2@if43: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 3a:4a:97:ec:ee:5a brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.2/24 scope global veth-test2
       valid_lft forever preferred_lft forever
    inet6 fe80::384a:97ff:feec:ee5a/64 scope link
       valid_lft forever preferred_lft forever

  此时发现已经存在 IP 地址,并且状态也已经是 UP 了,那么我们从 netns 中 ping 一下试试。

[root@Docker1 ~]# ip netns exec test1 ping 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.070 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.038 ms
^C
--- 192.168.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.038/0.058/0.070/0.014 ms
[root@Docker1 ~]# ip netns exec test2 ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.033 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.072 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.045 ms
^C
--- 192.168.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 0.033/0.050/0.072/0.016 ms

  到这里,已经把下图的这种通信方式给实现了。这里是使用 Linux 的 netns 做的一个实验,最终达到通信的目的。和文章一开始通过 docker run 运行起来的两个 busybox 容器可以相互通信的道理是基本一样的。

连接

bridge

  前面两小节是为了铺垫,主要普及一下 netns 的一些基本原理。这里再开说之前,先抛出两个疑问。

  • 两个容器之间是怎么通信的?
  • 容器又是怎么访问互联网的?

  先看一下 Docker 具有哪些网络:

[root@Docker1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
664f65044a2f        bridge              bridge              local
2596b17410cc        host                host                local
3accb13c9e4c        none                null                local

  这里要说的是 bridge,它也是 Docker 中最重要的网络了。一开始启动了两个 busybox 的容器,这里为了方便,先停掉一个:

[root@Docker1 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
4da18e7bebd3        busybox             "/bin/sh -c 'while t…"   2 hours ago         Up 2 hours                              test2
a7e7ecb9de2a        busybox             "/bin/sh -c 'while t…"   2 hours ago         Up 2 hours                              test1
[root@Docker1 ~]# docker stop test2

  从上文已经知道,每一个容器都是新增一个 netns,也知道单个的 Veth 是没办法正常使用的,那么容器里的 Veth 是连接到哪里了呢?连接到 Docker 的 bridge 上了,**注意:这里说的是 NAME = bridge 的的网络上,因为自己也可以定义类型为 bridge 的网络,名称你可以叫 demo1、demo2 等。**可以通过下面的命令查看到:

[root@Docker1 ~]# docker network inspect 664f65044a2f
[
    {
        "Name": "bridge",
        "Id": "664f65044a2ff1f59413d02db2ec032cf6c0e1d759a0ca497d02c4c8dd4ef619",
        "Created": "2018-11-08T14:58:57.971424978+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "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": {
            "a7e7ecb9de2a8ef47d29630455fd6587602f9fa25dc7db25c70867aa15ebb5eb": {
                "Name": "test1",
                "EndpointID": "c1c530262d07673bbe69b7780e23d2e83b13bea7882da1a2e65d8076e63692f2",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "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": {}
    }
]

  仔细看 Containers 项,会发现有 test1 的信息。说明 test1 这个 Container 是连接到了 NETWORK ID664f65044a2f 的网络上面,而 NETWORK ID664f65044a2f 的网络就是 NAME = bridge 的网络了。这里就有个问题,这个 bridge 是什么?在哪里?

[root@Docker1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
    inet 10.211.55.9/24 brd 10.211.55.255 scope global noprefixroute dynamic eth0
       valid_lft 1506sec preferred_lft 1506sec
    inet6 fdb2:2c26:f4e4:0:2c63:a924:4f67:12b/64 scope global noprefixroute dynamic
       valid_lft 2581512sec preferred_lft 594312sec
    inet6 fe80::139f:3030:9bc7:ec26/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:80:a4:a8:0e 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:80ff:fea4:a80e/64 scope link
       valid_lft forever preferred_lft forever
39: veth0075e8a@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 52:f3:a1:46:4d:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::50f3:a1ff:fe46:4da6/64 scope link
       valid_lft forever preferred_lft forever

  上面的命令可以看到,在宿主机中有一个接口 docker0,这就是刚才所说的 bridge 了,那我们 Container 中的 Veth 有是怎么连接上去的呢?注意看 docker0 下面的 veth0075e8a@if38,它也是一个 Veth,一个属于我们宿主机所在 netns 的 Veth,上面已经知道,一对 Veth 可以构成通信环境,查看一下 Container 中的 Veth 信息:

[root@Docker1 ~]# docker exec test1 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
38: eth0@if39: <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

  那么这里构成通信环境的两个 Veth 就是宿主机的 veth0075e8a 接口和 Container 的 eth0 接口,最终连接到宿主机上面,具体的就是连接到 docker0 上面。这里可以通过一个工具来帮我们验证一下为什么说 veth0075e8a 最终是连接到 docker0 上面,brctl,没有的话先通过 yum 进行安装,yum -y install bridge-utils

[root@Docker1 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.024280a4a80e	no		veth0075e8a

  这里可以看到,veth0075e8a 确实是连接到了 docker0 上。我们可以把原来那个停掉的 Container test2 给启动起来:

[root@Docker1 ~]# docker start test2
test2

[root@Docker1 ~]# docker network inspect 664f65044a2f
[
    {
        "Name": "bridge",
        "Id": "664f65044a2ff1f59413d02db2ec032cf6c0e1d759a0ca497d02c4c8dd4ef619",
        "Created": "2018-11-08T14:58:57.971424978+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "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": {
            "4da18e7bebd304c78237e1a38a07fca84b6c229344f98792c6c0a2629a68b1a8": {
                "Name": "test2",
                "EndpointID": "500c6ef02b4c8cd61ee00461513bbb80fe6ea3985db239f93cde09a2a85a2447",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "a7e7ecb9de2a8ef47d29630455fd6587602f9fa25dc7db25c70867aa15ebb5eb": {
                "Name": "test1",
                "EndpointID": "c1c530262d07673bbe69b7780e23d2e83b13bea7882da1a2e65d8076e63692f2",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "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 bridge 网络的信息,可以看到 Containers 项的网络信息。在宿主机查看网络信息发现:

[root@Docker1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
    inet 10.211.55.9/24 brd 10.211.55.255 scope global noprefixroute dynamic eth0
       valid_lft 1403sec preferred_lft 1403sec
    inet6 fdb2:2c26:f4e4:0:2c63:a924:4f67:12b/64 scope global noprefixroute dynamic
       valid_lft 2580649sec preferred_lft 593449sec
    inet6 fe80::139f:3030:9bc7:ec26/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:80:a4:a8:0e 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:80ff:fea4:a80e/64 scope link
       valid_lft forever preferred_lft forever
39: veth0075e8a@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 52:f3:a1:46:4d:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::50f3:a1ff:fe46:4da6/64 scope link
       valid_lft forever preferred_lft forever
45: veth93f79d1@if44: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 2e:19:19:09:d2:29 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::2c19:19ff:fe09:d229/64 scope link
       valid_lft forever preferred_lft forever

  发现多了一个 Veth veth93f79d1,这个就是和 Container test2 进行通信的 Veth 了。接下来使用工具 brctl 查看:

[root@Docker1 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.024280a4a80e	no		veth0075e8a
							veth93f79d1

  到这里,可以用一张图来表示一下:

容器之间通信

  这张图就很类似于我们现实中的网络,两台电脑通信,他们可以都连接到一个交换机/路由器上面,然后就可以进行通信了。到这里,已经解决了这小节提出的第一个问题两个容器之间是怎么通信的。

  再解释第二个问题之前,先来看一张图:

容器访问外网

  这里先抛开容器,作为宿主机的 Liunx 它本身是可以访问外网的,通过 eth0 接口进行访问。容器可以访问到 Docker0,这里使用的全部都是在 netns 中间进行通信的 Veth,而内部 IP 想要访问到外网,并且外网能够对内网的 IP 进行响应就需要进行 NAT 网络地址转换,包装一层信息,这个工作是由 iptables 完成的。这样,就可以达到访问外网的目的了。到这里第二个问题容器又是怎么访问互联网的也搞定了。

none 网络

  none 网络比较简单,这里已经把上节中的 Container test1 和 test2 删除掉了,重新启动一个 test1 Container 并指定网络为 none。

[root@Docker1 ~]# docker run --name test1 -d --network none busybox /bin/sh -c "while true; do sleep 60; done"
8df85a4e16a6cee04faf2cb480dd3ea044d94332fdc5fe25d19d986e8c790810

  进入容器查看网络信息,发现只有一个本地回环地址,那么就说明这个属于 none 网络的 Container 是独立的,无法与其他网络进行通信的。

[root@Docker1 ~]# docker exec -it 8df85a4e16a6cee04faf2cb480dd3ea044d94332fdc5fe25d19d986e8c790810 /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

  也可以查看一下 none 的网络信息:

[root@Docker1 ~]# docker network inspect none
[
    {
        "Name": "none",
        "Id": "3accb13c9e4c2c957c67fe6bc53065a2f32117c2f00949834c51f35e6f5fb22c",
        "Created": "2018-09-01T16:26:21.207367782+08:00",
        "Scope": "local",
        "Driver": "null",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": []
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "8df85a4e16a6cee04faf2cb480dd3ea044d94332fdc5fe25d19d986e8c790810": {
                "Name": "test1",
                "EndpointID": "6665b31036f8068b17956093045d303de19ad78c58c2b55f4bb2d4a1776a7819",
                "MacAddress": "",
                "IPv4Address": "",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

host 网络

  同样的,删除掉上一届的 test1 Container。重新启动一个连接到 host 网络的 Container。

[root@Docker1 ~]# docker run --name test1 -d --network host busybox /bin/sh -c "while true; do sleep 60; done"
361b2f79a22a5ad1696cb2e54110c191ae88b5e8caf9fb0b164c1de99df62ca5

  查看 host 网络:

[root@Docker1 ~]# docker network inspect 2596b17410cc
[
    {
        "Name": "host",
        "Id": "2596b17410ccaa4191ed459e3d4f8f8fc06919cfa0433e50ce8d79ed6e079fdf",
        "Created": "2018-09-01T16:26:21.213958845+08:00",
        "Scope": "local",
        "Driver": "host",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": []
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "361b2f79a22a5ad1696cb2e54110c191ae88b5e8caf9fb0b164c1de99df62ca5": {
                "Name": "test1",
                "EndpointID": "fa49dd211aa42b6b9a7aa39a326617ccfe259dddd287abae7b70d78eabc6634b",
                "MacAddress": "",
                "IPv4Address": "",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

  这里看到的 Container 网络信息也是空的,不要着急,先进入 Container 查看下网络信息:

[root@Docker1 ~]# 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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
    inet 10.211.55.9/24 brd 10.211.55.255 scope global dynamic eth0
       valid_lft 1249sec preferred_lft 1249sec
    inet6 fdb2:2c26:f4e4:0:2c63:a924:4f67:12b/64 scope global dynamic
       valid_lft 2575090sec preferred_lft 587890sec
    inet6 fe80::139f:3030:9bc7:ec26/64 scope link
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
    link/ether 02:42:80:a4:a8:0e 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:80ff:fea4:a80e/64 scope link
       valid_lft forever preferred_lft forever
55: veth2aa69e1@if54: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
    link/ether 2a:ec:a8:72:1c:49 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::28ec:a8ff:fe72:1c49/64 scope link
       valid_lft forever preferred_lft forever

  退出容器,查看下宿主机的网络信息:

/ # exit
[root@Docker1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:1c:42:9c:52:cd brd ff:ff:ff:ff:ff:ff
    inet 10.211.55.9/24 brd 10.211.55.255 scope global noprefixroute dynamic eth0
       valid_lft 1221sec preferred_lft 1221sec
    inet6 fdb2:2c26:f4e4:0:2c63:a924:4f67:12b/64 scope global noprefixroute dynamic
       valid_lft 2575063sec preferred_lft 587863sec
    inet6 fe80::139f:3030:9bc7:ec26/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:80:a4:a8:0e 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:80ff:fea4:a80e/64 scope link
       valid_lft forever preferred_lft forever
55: veth2aa69e1@if54: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 2a:ec:a8:72:1c:49 brd ff:ff:ff:ff:ff:ff link-netnsid 4
    inet6 fe80::28ec:a8ff:fe72:1c49/64 scope link
       valid_lft forever preferred_lft forever

  对的,没错,是一样的。共同用统一网络信息,这种情况下有一个坏处,那就是你不能启动两个同样的容易,端口号会冲突,这是因为现在使用的都是宿主机的网络环境。比如 Nginx Image,启动第一个的时候没事,正常运行,启动第二个的时候就会报错了。如下:

# 前台运行启动
[root@Docker1 ~]# docker run --name web1 --network host nginx

# 切换个窗口,再启动一次一个 name = web2 的
[root@Docker1 ~]# docker run --name web2 --network host nginx
2018/11/08 20:14:09 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
2018/11/08 20:14:09 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
2018/11/08 20:14:09 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
2018/11/08 20:14:09 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
2018/11/08 20:14:09 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
2018/11/08 20:14:09 [emerg] 1#1: still could not bind()
nginx: [emerg] still could not bind()

猜你喜欢

转载自blog.csdn.net/wo18237095579/article/details/83899023