美文网首页Docker
四、Docker网络揭秘

四、Docker网络揭秘

作者: Suny____ | 来源:发表于2020-03-12 21:52 被阅读0次

Docker 之所以功能这么强大,其实就是充分利用了Linux Kernel的特性:NameSpace、CGroups、UnionFileSystem。通过这些特性实现了资源隔离、限制与分层等。本文这次就来揭晓Docker中的容器是如何做到网络互通的。

1、通信

两台机器如果要实现通信,其实就是通过底层的网卡进行数据传输,每个网卡都有一个唯一的MAC地址,网卡又会绑定一个ip地址,只要两台机器的网络可以互通,那么这两台机器就可以进行通信。

image.png

2、Linux中的网卡

  • 查看网卡信息

    • ip a

    • ip link show

[root@10 bin]# ip a
# ...省略其他网卡组件...
# 这是docker安装后用于容器与宿主机通信的网卡
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ea:00:33:b7 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:eaff:fe00:33b7/64 scope link
valid_lft forever preferred_lft forever

# 这是docker在创建容器之后生成的网卡,后面再解释
8: veth3543ea3@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether c2:7a:e1:ac:8b:db brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::c07a:e1ff:feac:8bdb/64 scope link
valid_lft forever preferred_lft forever

想要实现通信,就得有两个同一网段的网卡,两个网卡必须是可以 ping 通的。

Docker在安装成功后,会在宿主机创建一个docker0网卡,这个网卡就是负责容器与宿主机之间通信的桥梁。

通过Docker创建一个容器之后,会在宿主机再创建一个网卡,也就是上面的veth3543ea3@if7,容器内也会创建一个网卡。

[root@10 bin]# docker exec -it 8944 bash
root@8944b2c2cfad:/usr/local/tomcat# ip a

# 容器内部的网卡
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

一般成对的网卡,网卡组件名称后面的数字是连续的,比如宿主机的@if7和容器内的@if8,正是这成对的网卡,才实现了容器与宿主机之间的通信。这其实是利用Linux Kernel的特性NameSpace实现的网卡隔离,不同NameSpace下的网卡是独立的,就像Java程序中的 package 一样。

  • 通信方式

从上面的例子中看到容器与宿主机之间的通信好像并不是通过docker0网卡实现的?

其实这只是单容器的状态,可能看不出docker0的作用。用图来表示一下单容器的网卡通信情况。

image.png

通过docker生成的eth0veth两个网卡实现同一网段内的通信,而这个网卡又是桥接在docker0网卡上的。

再看下有多个容器的情况。

image.png

两个容器之间可以互相通信的原因就是因为docker0的存在,因为它们的网卡都是桥接在docker0上,所以也就有了和另一个容器通信的桥。

我们来验证一下是不是这样!

[root@10 bin]# yum install bridge-utils

[root@10 bin]# brctl show
# docker0中的网卡
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242ea0033b7       no              vethbd21536
                                                        vethf802a96
                                                                                                       
[root@10 bin]# ip a

# docker0 网卡  
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ea:00:33:b7 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:eaff:fe00:33b7/64 scope link
       valid_lft forever preferred_lft forever

# 与容器成对的网卡
10: vethf802a96@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether b6:9d:5d:2d:5a:c9 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::b49d:5dff:fe2d:5ac9/64 scope link
       valid_lft forever preferred_lft forever
       
# 与容器成对的网卡
12: vethbd21536@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 9a:7e:8f:c5:a8:b1 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::987e:8fff:fec5:a8b1/64 scope link
       valid_lft forever preferred_lft forever

通过工具查看,确认了与容器中的网卡对应的宿主机的网卡的确是在 docker0 中的。

2、brige

这种网络连接方法我们称为Bridge,这也是docker中默认的网络模式。可以通过命令查看docker中的网络模式:

  • docker network ls
[root@10 bin]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
3f9cab018571        bridge              bridge              local
d2f72fb35175        host                host                local
a2f09c4b5e89        none                null                local

# 查看brige的详情
[root@10 bin]# docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "3f9cab01857158e89f23b725ecc9d1c7665b49cbc0b7baff41f18673a819736a",
        "Created": "2020-03-12T05:28:35.790892956Z",
        "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": {
            "8f4e1f8fc31f548164700799284c9e77ca3888873f5b1edeb6f7569d8e24e48e": {
                "Name": "tomcat01",
                "EndpointID": "d7821f31180faab94fd7cb9b02d6521e1596d919f357b5ff67326b5b41008bfa",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "bb9ee0e25ddac71de1a3662c9b895d6143cdbe1b234741e0c56d2569f42a0d03": {
                "Name": "tomcat02",
                "EndpointID": "d9bb8e5b817790ada67262206137ad07824cf61f44ed6f48574f6ce14ae7c3f3",
                "MacAddress": "02:42:ac:11:00:04",
                "IPv4Address": "172.17.0.4/16",
                "IPv6Address": ""
            }
        }
        ...省略...
    }
]

可以看到,之前创建的 tomcat01 和 tomcat02 都是用的 brige 模式。

3、Host

通过 docker network ls 命令查看到,docker提供了3种网络模式,brige模式我们已经知道了,那 host 和 none 又是什么意思呢?不妨来验证一下:

# 指定使用 host 网络模式
[root@10 bin]# docker run -it --name host-net-tomcat --network host tomcat bash

root@10:/usr/local/tomcat# 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 52:54:00:8a:fe:e6 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0
       valid_lft 59465sec preferred_lft 59465sec
    inet6 fe80::5054:ff:fe8a:fee6/64 scope link
       valid_lft forever preferred_lft forever

3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:2a:e4:99 brd ff:ff:ff:ff:ff:ff
    inet 192.168.50.224/24 brd 192.168.50.255 scope global dynamic noprefixroute eth1
       valid_lft 59467sec preferred_lft 59467sec
    inet6 fe80::a00:27ff:fe2a:e499/64 scope link
       valid_lft forever preferred_lft forever

4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ea:00:33:b7 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:eaff:fe00:33b7/64 scope link
       valid_lft forever preferred_lft forever

8: veth3543ea3@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether c2:7a:e1:ac:8b:db brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::c07a:e1ff:feac:8bdb/64 scope link
       valid_lft forever preferred_lft forever

10: vethf802a96@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether b6:9d:5d:2d:5a:c9 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::b49d:5dff:fe2d:5ac9/64 scope link
       valid_lft forever preferred_lft forever

12: vethbd21536@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 9a:7e:8f:c5:a8:b1 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::987e:8fff:fec5:a8b1/64 scope link
       valid_lft forever preferred_lft forever

其实 host 模式就是共享宿主机的网卡,不过一般不会使用它,在Docker中最常用的就是 brige 模式。

4、None

这种模式只会创建一个本地的环路网卡,无法与其他容器或宿主机进行通信。

[root@10 bin]# docker run -d --name tomcat-none-net --network none tomcat
aa2a2448c9851f652ede47c453c54c6d1a79408fde755d4965a8d761c7fa3be8
[root@10 bin]# docker exec -it tomcat-none-net bash
root@aa2a2448c985:/usr/local/tomcat# ip a

# 只会有一个local的本地环路网卡
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

5、创建自己的network

在创建自己的network之前先来解释一下为什么要创建新的network。

我们用一个例子来演示一下不同容器之间的通信。

# 创建两个容器
[root@10 bin]# docker run -d --name tomcat01 -p 8081:8080 tomcat
1404349dc899b6a7e6eb3da30bfc2d5ea8a559f6dbd51dd238c039458ed6504f
[root@10 bin]# docker run -d --name tomcat02 -p 8082:8080 tomcat
a3da6eeb60a12e727faa2d65b4ebc9352bea45700b01687c2edda26a3ade9690

[root@10 bin]# docker exec -it tomcat01 bash
root@1404349dc899:/usr/local/tomcat# ip a

# tomcat01的ip是  172.17.0.2
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
root@1404349dc899:/usr/local/tomcat# exit
exit

[root@10 bin]# docker exec -it tomcat02 bash
root@a3da6eeb60a1:/usr/local/tomcat# ip a

# tomcat02的ip是  172.17.0.3
15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

# 在tomcat02 中可以 ping 通 tomcat01
root@a3da6eeb60a1:/usr/local/tomcat# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.058 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.063 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.050 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.055 ms

容器之间通过 ip 是可以正常访问的,但是有没有这种情况:如果一个容器出问题了,我们重启之后它的ip变了,那是不是其他用到这个容器的项目配置是不是都得改。

有没有可能直接通过容器名称来访问呢?来验证一下:

root@a3da6eeb60a1:/usr/local/tomcat# ping tomcat01
ping: tomcat01: Name or service not known

发现并不能 ping 通,但是可以使用别的手段来达到这个目的。

# 删掉容器
[root@10 bin]# docker rm -f tomcat01
tomcat01

# --link tomcat02 表示映射tomcat02的ip
[root@10 bin]# docker run -it --name tomcat01 --link tomcat02 tomcat bash
root@ada7cfa2630a:/usr/local/tomcat# ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.057 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.050 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.049 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=4 ttl=64 time=0.080 ms

通过上面这种方式就可以做到以容器名来 ping 通其他容器,其实它就跟windows系统中在 hosts 文件里加了个映射是一样的。

但是不推荐这么用,因为有更好的办法去实现,那就是创建自定义的 network。
# 创建自定义 network
[root@10 bin]# docker network create tomcat-net
8122e48506f67f003b39e15b9b14a5bfa50de89012381904fb60898774a60a47
[root@10 bin]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
3f9cab018571        bridge              bridge              local
d2f72fb35175        host                host                local
a2f09c4b5e89        none                null                local
8122e48506f6        tomcat-net          bridge              local

# 创建两个容器, 指定使用我们自建的 network
[root@10 bin]# docker run -d --name tomcat01 --network tomcat-net tomcat
c828635fb8899cdfc702ac808c1db770c2d39d636b745dfe512fefe560daec78
[root@10 bin]# docker run -d --name tomcat02 --network tomcat-net tomcat
d18cd55aa3d7cccefb1608644441707cc512c19a31e8e0d659869e9d977d9106

[root@10 bin]# docker exec -it tomcat01 bash

# 发现可以ping通了
root@c828635fb889:/usr/local/tomcat# ping tomcat02
PING tomcat02 (172.18.0.3) 56(84) bytes of data.
64 bytes from tomcat01.tomcat-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.049 ms
64 bytes from tomcat01.tomcat-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.040 ms
64 bytes from tomcat01.tomcat-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.040 ms
64 bytes from tomcat01.tomcat-net (172.18.0.3): icmp_seq=4 ttl=64 time=0.081 ms

可以看到创建自定义的 network 自动帮我们实现了这个功能,而且使用自定义的 network 也方便管理,不同业务类型的容器可以指定不同的 network。

不同的 network ip网段也不一样,这样也可以增加单机中可以创建的容器的数量。

6、端口映射

在创建一个容器的时候,一般都需要讲容器需要暴露的端口映射到物理主机的相同端口或其他端口,因为在外网环境下是不方便直接连接到容器的,所以需要通过映射端口的方式,让外网访问宿主机的映射端口来访问容器。

# 创建一个tomcat容器,名称为port-tomcat
[root@10 bin]# docker exec -it port-tomcat bash
root@b35845089eb3:/usr/local/tomcat# curl localhost:8080
# 得到结果 test port
# 因为这是在容器内部,可以直接访问

# 如果在宿主机
[root@10 bin]# curl 172.17.0.2:8080
# 也能得到结果 test port
# 之所以能够访问成功,是因为centos上的docker0连接了port-tomcat的network namespace

如果想建立多个容器,势必需要端口映射,以满足不同容器使用相同端口的情况。

以上这些内容都是在单容器进行操作,容器之间通信也只是通过 docker0 实现的桥接模式。

如果要实现多机之间的docker通信,其实还是通过网卡,只不过需要其他的技术来实现了。

image.png

本章节就不在演示,到后面的章节再来分析!

相关文章

  • 四、Docker网络揭秘

    Docker 之所以功能这么强大,其实就是充分利用了Linux Kernel的特性:NameSpace、CGrou...

  • Docker的网络模式详解

    1、Docker的四种网络模式 (1)docker四种网络模式如下: Bridge contauner 桥接式网络...

  • docker四种网络模型

    四种网络模式 docker默认是是使用网桥进行交互的. 四种网络模式我们在使用docker run创建Docker...

  • docker的四种网络模式

    Docker有四种网络模式 (1)bridge:这是Docker默认的网络驱动,此模式会为每一个容器分配Netwo...

  • Docker初体验(四)- docker网络

    当使用docker创建服务集群时,容器间的交互都是通过网络请求来实现的。那么docker是怎么管理容器网络来保证容...

  • Docker容器(宿主机应用)相互访问

    Docker容器(宿主机应用)之间的相互访问和使用的网络模式有关。关于Docker的四种网络模式这里不做详细介绍,...

  • Docker网络下-自定义网络实战

    通过前面两篇的学习,我们对docker网络及四大网络类型都了解了。本文,咱们就来学习docker的自定义网络。我们...

  • docker网络

    Docker网络有四个不同的原生网络组成分别是none 网络,host网络、user-defined网络、brid...

  • docker学习(四)docker容器的网络连接

    docker容器的网络基础 查看运行docker守护进程机器上的网络设备 docker0 的网络设备 docker...

  • Docker学习(11) Docker 网络

    Docker学习(11) Docker 网络 Docker网络体系的基本原理:容器网络模型、以及Libnetwor...

网友评论

    本文标题:四、Docker网络揭秘

    本文链接:https://www.haomeiwen.com/subject/jhdgjhtx.html