Docker网络


docker 网络实现

首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。

Docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势之一是转发效率较高。 Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包被直接复制到接收接口的接收缓存中。对于本地系统和容器内系统看来就像是一个正常的以太网卡,只是它不需要真正同外部网络设备通信,速度要快很多。

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

docker0网络详解

我们只要安装了docker,就会有一个网卡docker0桥接模式,使用的技术是veth-pair技术

image-20221025212733515_20230303153615962136.png

我们每启动一个docker容器,docker就会给docker容器分配一个ip

image-20221025213057103_20230303153631429870.png

再启动一个tomcat,会又多一个网卡

image-20221025213311271_20230303153648454489.png

veth-pair技术虚拟的网卡都是成对出现,一段连着协议,一段彼此相连,在docker内部充当桥梁

宿主机查看到的网卡是9: veth2eb03d3@if8,那么对应的容器内部也会有一个网卡8: eth0@if9

11: veth0eb3855@if10,那么对应的容器内部也会有一个网卡10: eth0@if11

[root@localhost ~]# docker inspect tomcat_01
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "4557187965d8c09296cb8b7f1e9d6270847abed0863a2d29b68db0cc86e6815d",
                    "EndpointID": "ce8a3c0d2675b6bc601ebb945812343f6aa491fce9ce88db4e6f39b955ab9359",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
[root@localhost ~]# docker inspect tomcat_02
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "4557187965d8c09296cb8b7f1e9d6270847abed0863a2d29b68db0cc86e6815d",
                    "EndpointID": "21b743cb639f8820076e25363a3a2e0f3cc62dd163ce5474ee18feb4a919b4d2",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:03",
                    "DriverOpts": null
                }
            }
# 通过docker inspect可以查看到tomcat01的ip为172.17.0.2,tomcat01的ip为172.17.0.3

# 172.17.0.2是可以ping通172.17.0.3的

==结论:容器和容器之间是可以互相ping通的==

image-20221025215456588_20230303153706647330.png

==结论:tomcat01和tomcat02是共用的一个路由器 docker0==

所有的容器不指定网络的情况下,都是由docker0路由的,docker会给我们的容器分配一个默认的可用ip。

只要容器删除,对应网桥一对就没了!

# 查看所有的docker网络
[root@localhost ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
4557187965d8   bridge    bridge    local
42493de06801   host      host      local
16b953b9bcd6   none      null      local
# 4557187965d8这个就是docker0,我们可以通过docker network inspect查看详情

image-20221025224731414_20230303153723633735.png

我们希望可以通过名字来访问容器

# 启动容器的时候加上 --link
[root@localhost ~]# docker run --name tomcat_03 --link tomcat_02 -id -P tomcat
38656c714ff71e3f5d25dd031a7249493419f5d4fdc14a362a5a8f516094056f

此时我们可以在tomcat_03中直接ping tomcat_02,是可以ping通的,但是在tomcat_02中不能直接ping tomcat_03

# 原理 是因为--link在/etc/hosts中增加了tomcat_02的映射,实际访问172.17.0.3
[root@localhost ~]# docker exec -it tomcat_03 cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3  tomcat_02 fa0fc8f313ff
172.17.0.4  38656c714ff7
# 但是tomcat_02中是没有添加tomcat_03的
[root@localhost ~]# docker exec -it tomcat_02 cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3  fa0fc8f313ff

# 使用docker inspect也可以看到Links下有tomcat_02
[root@localhost ~]# docker inspect tomcat_03
        "HostConfig": {
            "Links": [
                "/tomcat_02:/tomcat_03/tomcat_02"
            ]

其实这个tomcat_03就是在本地配置了tomcat_02的配置

自定义网络

查看所有的docker网络

# name为bridge是docker0
[root@localhost ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
046394cab67b   bridge    bridge    local
42493de06801   host      host      local
16b953b9bcd6   none      null      local

网络模式

  • bridge 桥接
  • none 不配置网络
  • host 和主机共享网络
# 我们直接启动的命令 --net bridge,而这个就是我们的docker0

#这两个命令是一样的效果,默认创建docker使用docker0来路由分配网络,而docker0的名字设置的是bridge
docker run -id -P --name tomcat01 tomcat
docker run -id -P --name tomcat01 --net bridge tomcat

#docker0特点:run镜像时默认使用docker0路由,但是不能通过容器名访问,使用--link可以打通连接

image-20221026224036684_20230303153744593569.png

自定义网络

# 创建一个网络
[root@localhost ~]# docker network create --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
fc6b40a6c86123d5d6d7f274ec13fee789746807217fbb8ca2c9ca0271f1ec90
# 可以查看到创建的网络
[root@localhost ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
046394cab67b   bridge    bridge    local
42493de06801   host      host      local
fc6b40a6c861   mynet     bridge    local
16b953b9bcd6   none      null      local
# 通过ip addr也能查看到这个虚拟网卡(这里我只留了docker0和新建的,其他的删除了太多了不好看)
[root@localhost ~]# ip addr
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:63:ae:d2:f0 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
6: br-fc6b40a6c861: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:00:3b:96:5b brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/16 brd 192.168.255.255 scope global br-fc6b40a6c861
       valid_lft forever preferred_lft forever

使用自定义的网络创建一个容器

[root@localhost ~]# docker run --name tomcat_03 -id -P --net mynet tomcat
58554eaff329c6923d01643da856d654dca468b3b6b869b882159016b59d73ea
# 可以查看到Containers下多了一个192.168.0.2网络,这个就是通过mynet分配的网络
[root@localhost ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "fc6b40a6c86123d5d6d7f274ec13fee789746807217fbb8ca2c9ca0271f1ec90",
        "Created": "2022-10-26T07:48:38.722350097-07:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "58554eaff329c6923d01643da856d654dca468b3b6b869b882159016b59d73ea": {
                "Name": "tomcat_03",
                "EndpointID": "f8b72396024a3e23fbb83381732418fd7d677e5de95f085ff284fbee297c8ff0",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
# 同样的通过ip addr查看也多出了一个虚拟网卡,10: vethd850210@if9,在容器内部也有与之对应的一个网卡9: veth2eb03d3@if10(使用的技术是veth-pair技术)
[root@localhost ~]# ip addr
10: vethd850210@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-fc6b40a6c861 state UP group default 
    link/ether e2:0c:94:46:ef:6f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::e00c:94ff:fe46:ef6f/64 scope link 
       valid_lft forever preferred_lft forever

如果我们再通过命令去创建一个tomcat_04docker run --name tomcat_04 -id -P --net mynet tomcat,那么在tomcat_03容器内部可以直接通过ping tomcat_04而不是ping 192.168.0.2

==总结:通过我们自定义的网络,在内部是可以直接通过容器名ping通的==

网络连通

接着上面思考一个问题,docker0的ip地址是172.17.0.1,我们自定义的网络mynet的ip地址是192.168.0.1,这两个是不同网段的,那么在docker0上路由的容器想ping通mynet上路由的容器应该怎么操作??

image-20221027211413178_20230303153808045265.png

# 使用docker network connect将容器tomcat_01连接到网络mynet
[root@localhost ~]# docker network connect mynet tomcat_01
# 使用docker inspect可以查看tomcat_01的详细信息,有两个网卡,一个网卡是来自docker0的172.17.0.2,一个网卡是来自mynet的192.168.0.3
[root@localhost ~]# docker inspect tomcat_01
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "0b138576a54d945a83180ff89fbc1996290aea60df17e11685309c1d4803d55a",
                    "EndpointID": "59c0cd69bb6e9c9111d6cd24d02aa20d8152a778efebf022b9c65fc65ca4f049",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                },
                "mynet": {
                    "IPAMConfig": {},
                    "Links": null,
                    "Aliases": [
                        "00d11d3c4393"
                    ],
                    "NetworkID": "fc6b40a6c86123d5d6d7f274ec13fee789746807217fbb8ca2c9ca0271f1ec90",
                    "EndpointID": "a2888fe7ea8e1029ac9e5a1aa82569556f2e734a3f00aac0c9a73d56c4ff2d10",
                    "Gateway": "192.168.0.1",
                    "IPAddress": "192.168.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:c0:a8:00:03",
                    "DriverOpts": {}
                }
# 再使用docker network inspect查看mynet网络的详细信息,可以看到也有生成的tomcat_01信息
[root@localhost ~]# docker network inspect mynet 
[
    {
        "Name": "mynet",
        "Id": "fc6b40a6c86123d5d6d7f274ec13fee789746807217fbb8ca2c9ca0271f1ec90",
        "Created": "2022-10-26T07:48:38.722350097-07:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "00d11d3c4393d30eb71d34c5648eeae932ec33e8e75e94ddb3578ec85dcad4d4": {
                "Name": "tomcat_01",
                "EndpointID": "a2888fe7ea8e1029ac9e5a1aa82569556f2e734a3f00aac0c9a73d56c4ff2d10",
                "MacAddress": "02:42:c0:a8:00:03",
                "IPv4Address": "192.168.0.3/16",
                "IPv6Address": ""
            },
            "58554eaff329c6923d01643da856d654dca468b3b6b869b882159016b59d73ea": {
                "Name": "tomcat_03",
                "EndpointID": "a83df7bf82a9bf70a3eabd465b29e6bdf42a58c0078d5c8f449ef8c4b51b8d12",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

==总结:说白了就是一个tomcat_01容器有两个网卡,一个网卡是来自docker0的172.17.0.2,一个网卡是来自mynet的192.168.0.3==

==一旦连接,tomcat_01容器就可以与192.168.0.1同一网络中的其他容器通信。==