Docker 网络

docker安装并启动服务后,会在宿主机中添加一个虚拟网卡。
在Docker服务启动前,使用 ifconfigip addr 查看网卡信息:

  • lo:本机回环网络网卡
  • ens33eth0:本机网卡,下面是阿里云内网地址
  • 使用 systemctl start docker启动Docker服务后,会多出一个 docker0 网卡。
  • 可能有virbr0(CentOS安装时如果选择的有相关虚拟化服务,就会多一个以网桥连接的私网地址的virbr0网卡,作用是为连接虚拟网卡提供NAT访问外网的功能。如果要移除该服务,可以使用 yum remove libvirt-libs.x86_64

作用:

  • 容器间的互联和通信以及端口映射
  • 容器IP变动时候可以通过服务名直接网络通信而不受到影响

Docker容器的网络隔离,是通过Linux内核特性 namespacecgroup 实现的。

测试

没有运行任何容器的时候是这个样子的,有loeth0docker0

  1. [root@izj6cev682kqg86i4ogj8rz volumes]# ip addr
  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  4. inet 127.0.0.1/8 scope host lo
  5. valid_lft forever preferred_lft forever
  6. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  7. link/ether 00:16:3e:01:8a:da brd ff:ff:ff:ff:ff:ff
  8. inet 172.17.20.142/18 brd 172.17.63.255 scope global dynamic eth0
  9. valid_lft 299860583sec preferred_lft 299860583sec
  10. 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
  11. link/ether 02:42:f1:29:51:33 brd ff:ff:ff:ff:ff:ff
  12. inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
  13. valid_lft forever preferred_lft forever

当我运行了一个tomcat容器和一个ubuntu容器后,再次ip addr

  1. [root@izj6cev682kqg86i4ogj8rz volumes]# ip addr
  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  4. inet 127.0.0.1/8 scope host lo
  5. valid_lft forever preferred_lft forever
  6. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  7. link/ether 00:16:3e:01:8a:da brd ff:ff:ff:ff:ff:ff
  8. inet 172.17.20.142/18 brd 172.17.63.255 scope global dynamic eth0
  9. valid_lft 299805243sec preferred_lft 299805243sec
  10. 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
  11. link/ether 02:42:f1:29:51:33 brd ff:ff:ff:ff:ff:ff
  12. inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
  13. valid_lft forever preferred_lft forever
  14. 47: veth26b6577@if46: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
  15. link/ether 2e:60:70:a8:2f:91 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  16. 51: veth4638d32@if50: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
  17. link/ether 7a:9f:2f:dc:b0:05 brd ff:ff:ff:ff:ff:ff link-netnsid 1

我进入ubuntu容器的内部,因为是阉割版的,需要执行两条命令安装东西

  • apt-get update
  • apt-get install -y iproute2
  • apt install iputils-ping关于ping命令

安装完了以后执行ip addr

  1. root@90b9a384eb60:/# ip addr
  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  4. inet 127.0.0.1/8 scope host lo
  5. valid_lft forever preferred_lft forever
  6. 50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
  7. link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  8. inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
  9. valid_lft forever preferred_lft forever

发现了第二段代码的51: veth4638d32@if50:和第三段代码的50: eth0@if51:有着映射关系。
而且每次启动容器都是一对一对的数字出现。
我们在linux服务器上也能ping通ubuntu容器内部分配的私有ip。
当然我们在B容器也能通过这个分配的内网ip连通A容器。运行容器的时候没有指定网络连接方式就是默认的桥接方式。并不是AB两个同期直接通信,两个容器之间通过docker0中转通信。

  1. [root@izj6cev682kqg86i4ogj8rz ~]# ping 172.18.0.3
  2. PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
  3. 64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.051 ms
  4. 64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.054 ms
  5. 64 bytes from 172.18.0.3: icmp_seq=3 ttl=64 time=0.051 ms
  6. 64 bytes from 172.18.0.3: icmp_seq=4 ttl=64 time=0.055 ms

Docker 网络模式

Docker 的网络模式:

网络模式 简介 使用方式
bridge 为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,默认为该模式 --network bridge
host 容器将不会虚拟出自己的网卡、配置自己的IP等,而是使用宿主机的IP和端口 --network host
none 容器有独立的 Network namespace,但并没有对齐进行任何网络设置,如分配 veth pari和 网桥连接、IP等 --network none
container 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP、端口范围等 --network container:NAME或者容器ID

查看某个容器的网络模式

  1. # 通过inspect获取容器信息,最后20行即为容器的网络模式信息
  2. docker inspect 容器ID | tail -n 20
  1. [root@izj6cev682kqg86i4ogj8rz ~]# docker inspect ubuntu02 | tail -n 20
  2. "Networks": {
  3. "bridge": {
  4. "IPAMConfig": null,
  5. "Links": null,
  6. "Aliases": null,
  7. "NetworkID": "37b3214ea181ec7d4a0d0eea126df72f72d7ad1e5e1724b4656bf3cb59acec07",
  8. "EndpointID": "4658c0fc37cd88918ce9f92097d23bbf7f8f3cb04256c0af38bee3b236a252ea",
  9. "Gateway": "172.18.0.1",
  10. "IPAddress": "172.18.0.3",
  11. "IPPrefixLen": 16,
  12. "IPv6Gateway": "",
  13. "GlobalIPv6Address": "",
  14. "GlobalIPv6PrefixLen": 0,
  15. "MacAddress": "02:42:ac:12:00:03",
  16. "DriverOpts": null
  17. }
  18. }
  19. }
  20. }
  21. ]

docker0

Docker 服务默认会创建一个docker0网桥(其上有一个docker0内部接口),该桥接网络的名称为 docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
Docker默认指定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥互相通信。
查看bridge网络的详细信息,并通过grep获取名称:可以看到其名称为docker0

  1. docker network inspect bridge | grep name

网络模式——bridge模式

Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一个宿主机内的容器接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
docker run的时候,没有指定--network的话,默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig就可以看到docker0和自己createnetwork
网桥docker0创建一对对等虚拟设备接口,一个叫veth,另一个叫eth0,成对匹配:
整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫 veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫做 veth pair)。
每个容器实例内部也有一块网卡,容器内的网卡接口叫做eth0
docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
Docker网络 - 图1

例子可以看上面的测试小节

网络模式——host模式

直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行 NAT 转换。
容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network space。
容器将不会虚拟出自己的网卡,而是直接使用宿主机的 IP 和端口。
Docker网络 - 图2
如果在 docker run 命令中同时使用了 --network host-p端口映射,例如:

  1. docker run -p 8082:8080 --network host tomcat

那么会出现一个警告:WARNING: Published ports are discarded when using host network mode
因为此时已经使用了host模式,本身就是直接使用的宿主机的IP和端口,此时的-p端口映射就没有了意义,也不会生效,端口号还是会以主机端口号为主。
正确做法是:不再进行-p端口映射,或者改用bridge模式

网络模式——none模式

禁用网络功能。
none模式下,并不为docker容器进行任何网络配置。进入容器内,使用 ip addr查看网卡信息,只能看到 lo(本地回环网络127.0.0.1网卡)。

网络模式——container模式

新建的容器和已经存在的一个容器共享网络IP配置,而不是和宿主机共享。
新创建的容器不会创建自己的网卡、IP,而是和一个指定的容器共享IP、端口范围。两个容器除了网络共享,其他的如文件系统、进程列表依然是隔离的。
Docker网络 - 图3
示例:

  1. docker run -it --name alpine1 alpine /bin/sh
  2. # 指定和 alpine1 容器共享网络
  3. docker run -it --netrowk container:alpine1 --name alpine2 alpine /bin/sh

此时使用 ip addr查看两台容器的网络,会发现两台容器的eth0网卡内的IP等信息完全相同。
如果关掉了alpine1容器,因为alpine2的网络使用的alpine1共享网络,所以关掉alpin1后,alpine2eth0网卡也随之消失了。

自定义网络

容器间的互联和通信以及端口映射。
容器 IP 变动时候可以通过服务名直接网络通信而不受影响。(类似Eureka,通过服务名直接互相通信,而不是写死IP地址)。 :::danger docker中还有一个 --link 进行容器网络互联,但是已经被标记为过时的,可能会在将来的版本中移除这个功能。推荐使用自定义网络替换link。 :::

  1. 新建自定义网络

自定义桥接网络(自定义网络默认使用的是桥接网络 bridge

  1. docker network create new_network
  2. #指定bridge方式
  3. docker network create -d bridge new_bridge
  4. #详细
  5. docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
  1. 查看网络列表

    1. docker network ls
  2. 创建容器时,指定加入我们自定义的网络中 ```shell docker run -it —name u1 —network mynet newubuntu:1.0 /bin/bash

docker run -it —name u2 —network mynet newubuntu:1.0 /bin/bash

  1. 4. 这时候随便进去某个容器,都能通过名字ping对方
  2. ```shell
  3. root@89c03e48cf85:/# ping u1
  4. PING u1 (172.19.0.2) 56(84) bytes of data.
  5. 64 bytes from u1.new_network (172.19.0.2): icmp_seq=1 ttl=64 time=0.084 ms
  6. 64 bytes from u1.new_network (172.19.0.2): icmp_seq=2 ttl=64 time=0.069 ms
  7. 64 bytes from u1.new_network (172.19.0.2): icmp_seq=3 ttl=64 time=0.058 ms

网络连通

如果处于两个子网网段,比如docker0的172.18.x.x和自己创建的网络192.168.x.x是不能进行通信的。
下面我们启动三个容器,u1和u2是通过上面创建的mynet启动,d1默认启动,用的网络用的docker0。
在d1里面ping u1,连不通。

  1. [root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u1 --network mynet newubuntu:1.0 /bin/bash
  2. root@65e4415ddb7e:/# [root@izj6cev682kqg86i4ogj8rz ~]#
  3. [root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u2 --network mynet newubuntu:1.0 /bin/bash
  4. root@866b78daecc8:/# [root@izj6cev682kqg86i4ogj8rz ~]#
  5. [root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name d1 newubuntu:1.0 /bin/bash
  6. root@a0941b086d6d:/# ping u1
  7. ping: u1: Name or service not known

我们通过通过docker network connect命令让mynet网络连通a0941b086d6d容器。然后就可以在d1里面ping u1了

  1. [root@izj6cev682kqg86i4ogj8rz ~]# docker network connect mynet a0941b086d6d
  2. [root@izj6cev682kqg86i4ogj8rz ~]# docker exec -it a0941b086d6d /bin/bash
  3. root@a0941b086d6d:/# ping u2
  4. PING u2 (192.168.0.3) 56(84) bytes of data.
  5. 64 bytes from u2.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.059 ms
  6. 64 bytes from u2.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.057 ms
  7. 64 bytes from u2.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.060 ms

我们查看网络详情docker network inspect mynet,会发现直接把d1加入到了mynet网络下,并给它分配了一个ip,也就是一个容器两个ip
image.png

—link

是不是感觉容器之间通过分配的ip来通信,不太方便,要是这个ip换了不就通信错了吗。我们可以通过名字来和对方通信

  1. #启动一个容器,--name去一个名字叫u1
  2. [root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u1 newubuntu:1.0 /bin/bash
  3. #启动另一个容器,u1后台运行没关,这个容器用--link u1,可以通过u1这个名字直接通信u1容器
  4. [root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u2 --link u1 newubuntu:1.0 /bin/bash
  5. root@3d69b873181a:/# ping u1
  6. PING u1 (172.18.0.2) 56(84) bytes of data.
  7. 64 bytes from u1 (172.18.0.2): icmp_seq=1 ttl=64 time=0.082 ms
  8. 64 bytes from u1 (172.18.0.2): icmp_seq=2 ttl=64 time=0.080 ms
  9. #再启动一个容器,用了两个--link,u1:a,冒号后面是别名,可以通过别名访问
  10. [root@izj6cev682kqg86i4ogj8rz ~]# docker run -it --name u3 --link u1:a --link u2:b newubuntu:1.0 /bin/bash
  11. root@a1c729316a7e:/# ping a
  12. PING a (172.18.0.2) 56(84) bytes of data.
  13. 64 bytes from a (172.18.0.2): icmp_seq=1 ttl=64 time=0.073 ms
  14. 64 bytes from a (172.18.0.2): icmp_seq=2 ttl=64 time=0.061 ms
  15. 64 bytes from a (172.18.0.2): icmp_seq=3 ttl=64 time=0.077 ms

:::danger 注意:
—link是单项的连接,u2连接u1,u1并不能连接u2。而且每次启动容器都要—link的配置,已经不推荐使用—link了,推荐自定义网络 :::