企业级微服务实战(六)

Docker 网络剖析与实战

1. Docker 网络驱动模型

在咱们使用的 v18.09 版本中 官方文档,官方文档给出了一下如下五种网络驱动模型,以下:docker

  • bridge:网桥类型网络;其实就是咱们以前介绍五大虚拟化网络模型中的 NAT网络,只不过这里叫作 bridge 网桥类型
  • host:主机类型网络,其原理是容器使用宿主机的网络名称空间,所以会直接看到并使用宿主机的全部网络功能,对应我们以前在虚拟化网络模型中的 “桥接网络”,这里叫作 host 网络驱动
  • none:禁用容器网络,即在容器中不启动网络,仅有 lo 网卡用于本地回环地址通讯;
  • overlay:叠加网络,借助 docker swarm 服务实现的高级叠加网络模型;
  • macvlan:在 macvlan 网络模型下,咱们能够将 MAC 地址分配给某个容器,这样当容器须要经过物理网络传输数据时,能够直接经过 MAC 地址进行路由传输,而再也不依靠 docker host 也就是宿主机的网络栈进行转发,提升报文处理效率;

可是有过 docker 使用经验的朋友,可能都知道还有另一种驱动模型,即 container 容器网络驱动模型,这种模型跟 host 类型有些相似,只不过 host 类型是容器和宿主机共享网络名称空间,而 container 类型是多个容器之间共享网络名称空间。json

注意:这里咱们提到共享网络名称空间(host & container 网络驱动模型),可是实际上除了网络名称空间外,还有 IPC、UTS 两个名称空间被共享,也就是说容器仅 MOUNT、PID、USER 这三个名称空间是本身独有的!

对于 container 网络模型,在生产上主要用于两个容器间的高效通讯,官方文档上虽然已经隐藏了该类型,可是我在测试后发现依然能够正常使用。不过你们要注意,若是没有特别需求,仍是不要使用这种模型了。网络


2. Docker 网络模型架构图

咱们来看下常见的网络驱动模型架构图:
image.png
其中图中 Closed container 对应咱们上面提到的 none 网络模型;Bridged container 对应 bridge 模型;Joined container 对应 container 网络模型;Open container 则对应 host 网络模型。
下面咱们对常见的网络驱动模型进行讲解与演示。架构


3. Docker 网络模型演示

3.1 bridge 网络模型

bridgedocker 上默认的容器网络模型,该模型默认会建立一个 docker0 的网桥,网桥地址为 172.16.0.1,链接到该网桥上的容器会从地址段 172.17.0.0/16 中获取一个随机地址。若是咱们想要修改容器的默认网桥信息,能够经过修改 daemon.json 配置文件,官网中给出了示例,以下:socket

{
  "bip": "192.168.1.5/24",
  "fixed-cidr": "192.168.1.5/25",
  "fixed-cidr-v6": "2001:db8::/64",
  "mtu": 1500,
  "default-gateway": "10.20.1.1",
  "default-gateway-v6": "2001:db8:abcd::89",
  "dns": ["10.20.1.2","10.20.1.3"]
}

你们若是须要修改的,能够参考该配置文件进行修改。
为了更好的演示该网络模型,咱们先中止全部容器。tcp

# 中止并删除全部容器
[root@docker-server ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

查看当前宿主机网络信息微服务

[root@docker-server ~]# ip addr show
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
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:88:01:70 brd ff:ff:ff:ff:ff:ff
    inet 172.24.236.96/22 brd 172.24.239.255 scope global dynamic eth0
       valid_lft 74261sec preferred_lft 74261sec
    inet6 fe80::a00:27ff:fe88:170/64 scope link
       valid_lft forever preferred_lft forever
5: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:83:32:b3 brd ff:ff:ff:ff:ff:ff
    inet 10.0.3.15/24 brd 10.0.3.255 scope global dynamic eth1
       valid_lft 74263sec preferred_lft 74263sec
    inet6 fe80::a00:27ff:fe83:32b3/64 scope link
       valid_lft forever preferred_lft forever
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:39:1f:ab:bd 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:39ff:fe1f:abbd/64 scope link
       valid_lft forever preferred_lft forever
由于我实验环境比较特殊,因此启动了两个网卡 eth0、eth1
咱们能够看到除了在 docker daemon 启动后建立的 docker0 网卡外,没有其余虚拟网卡,等咱们建立完容器以后,再来对比看下宿主机上的网卡信息。

查看此时的网桥信息工具

注: brctl 工具默认没有,须要咱们手动安装 bridge-utils 安装包。
[root@docker-server ~]# brctl show
bridge name    bridge id        STP enabled    interfaces
docker0        8000.0242391fabbd    no

【建立两个容器】oop

[root@docker-server ~]# docker run --name demo1 --rm -it busybox:latest
[root@docker-server ~]# docker run --name demo2 --rm -it busybox:latest
[root@docker-server ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
880ca55196da        busybox:latest      "sh"                26 seconds ago      Up 11 seconds                           demo1
f46be5a89f6d        busybox:latest      "sh"                18 seconds ago      Up 17 seconds                           demo2

【再查看网卡和网桥信息】学习

[root@docker-server ~]# ip link show
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
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:88:01:70 brd ff:ff:ff:ff:ff:ff
5: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:83:32:b3 brd ff:ff:ff:ff:ff:ff
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:39:1f:ab:bd brd ff:ff:ff:ff:ff:ff
106: veth0813d85@if105: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether ca:5e:7e:19:06:dd brd ff:ff:ff:ff:ff:ff link-netnsid 0
108: veth789d62b@if107: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 3e:6b:52:7d:27:a3 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@docker-server ~]# brctl show
bridge name    bridge id        STP enabled    interfaces
docker0        8000.0242391fabbd    no        veth0813d85
                                  veth789d62b

能够看到此时多了两个虚拟网卡,并且该网卡是成对出现的,一半链接在宿主机上的 docker 网桥上,一半在容器中。
那么咱们怎么去判断网卡的另外一半是在哪一个容器中,请仔细看视频教程。
那此时咱们如何看容器的 IP 地址,固然能够经过登陆到容器以后再查看,可是比较麻烦,使用 docker [container] inspect 就能够查看,以下:

[root@docker-server ~]# docker inspect demo1 -f {{.NetworkSettings.Networks.bridge.IPAddress}}
172.17.0.2
[root@docker-server ~]# docker inspect demo2 -f {{.NetworkSettings.Networks.bridge.IPAddress}}
172.17.0.3

【容器网络测试】

[root@docker-server ~]# docker exec -it demo1  ping -c 2 baidu.com
PING baidu.com (39.156.69.79): 56 data bytes
64 bytes from 39.156.69.79: seq=0 ttl=61 time=6.608 ms
64 bytes from 39.156.69.79: seq=1 ttl=61 time=39.276 ms

--- baidu.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 6.608/22.942/39.276 ms

为何网络能通呢

[root@docker-server ~]# iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0

Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  0.0.0.0/0            0.0.0.0/0

其中

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0

该规则的意义是,让全部从 172.17.0.0/16 网段的源地址,发往 !docker0 网卡的数据(即 非本地容器地址)所有作源地址转换,也就是我们以前虚拟化网络模型中的 NAT 模型。如今来看下下面这幅 bridge 模型图,应该能清晰的描绘出数据流了吧。
image.png

3.2 host 网络模型

咱们演示一下该网络模型的建立,以及该网络模型下的网卡特性。
【建立 host 网络模型容器】

[root@docker-server ~]# docker run --name demo3 -it --rm --network host busybox:latest

查看网络信息

/ # ip addr show
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
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 08:00:27:88:01:70 brd ff:ff:ff:ff:ff:ff
    inet 172.24.236.96/22 brd 172.24.239.255 scope global dynamic eth0
       valid_lft 70857sec preferred_lft 70857sec
    inet6 fe80::a00:27ff:fe88:170/64 scope link
       valid_lft forever preferred_lft forever
5: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 08:00:27:83:32:b3 brd ff:ff:ff:ff:ff:ff
    inet 10.0.3.15/24 brd 10.0.3.255 scope global dynamic eth1
       valid_lft 70859sec preferred_lft 70859sec
    inet6 fe80::a00:27ff:fe83:32b3/64 scope link
       valid_lft forever preferred_lft forever
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
    link/ether 02:42:39:1f:ab:bd 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:39ff:fe1f:abbd/64 scope link
       valid_lft forever preferred_lft forever
106: veth0813d85@if105: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
    link/ether ca:5e:7e:19:06:dd brd ff:ff:ff:ff:ff:ff
    inet6 fe80::c85e:7eff:fe19:6dd/64 scope link
       valid_lft forever preferred_lft forever
108: veth789d62b@if107: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
    link/ether 3e:6b:52:7d:27:a3 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::3c6b:52ff:fe7d:27a3/64 scope link
       valid_lft forever preferred_lft forever
能够看到在 host 网络模型下, docker container 能看到的网络信息和宿主机是同样的,也就是与宿主机共享NET网络名称空间与 IPC 名称空间,那么咱们继续看下其余的名称空间 PID、MOUNT、USER、UTS

名称空间的共享与独享

# PID 名称空间独享
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 sh
   16 root      0:00 ps
   
# MOUNT 名称空间独享
/ # ls /root/
/ #
[root@docker-server ~]# ls /root/
a  anaconda-ks.cfg

# USER 名称空间独享
/ # cat /etc/passwd
root:x:0:0:root:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/false
bin:x:2:2:bin:/bin:/bin/false
sys:x:3:3:sys:/dev:/bin/false
sync:x:4:100:sync:/bin:/bin/sync
mail:x:8:8:mail:/var/spool/mail:/bin/false
www-data:x:33:33:www-data:/var/www:/bin/false
operator:x:37:37:Operator:/var:/bin/false
nobody:x:65534:65534:nobody:/home:/bin/false
/ #

# UTS 名称空间独享
/ # hostname
docker-server
通常容器只具备宿主机网络的使用权限,而不具备修改权限。
这种网络模型,容器能够直接使用宿主机网络,由于不须要额外的地址转换,因此效率很高。可是容器与宿主机使用相同网络,就致使了网络资源的占用,好比容器占用了 tcp 80 端口,那么宿主机就不能在使用这个端口。
3.3 none 网络模型

一块儿来看下这种网络模型怎么建立:

[root@docker-server ~]# docker run --name demo4 -it --rm --network none busybox
/ # ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ #

为何存在这种模型呢?由于企业中有时候还真须要这种容器,好比咱们以前作数据清洗的一个程序,它其实不须要网络,只须要把数据处理完保存到本地便可(固然这借助到容器的“卷存储”知识,咱们后面再说)。

3.4 container 网络模型

若是同一个宿主机上的不一样容器想要通讯,最容易想到的方法是什么?两个容器都使用 bridge 模型,即至关于容器链接到同一个虚拟网桥上,只要配置好 IP 地址就能够直接基于 tcp/ip 通讯,这种方式固然能够,只不过还有另一种方式,即让两个容器使用相同的网络名称空间,他们之间基于 IPC 通讯,效率更好。下面咱们跟你们演示一下如何建立这种模型:
建立一个新的容器

[root@docker-server ~]# docker run --name demo5 --rm -it busybox:latest
/ #

建立 container 网络类型容器

[root@docker-server ~]# docker run --name demo6 --rm -it --network container:demo5 busybox:latest
/ #
注意:须要指定使用哪一个容器的网络。

名称空间的共享与独享

# IPC 共享,两个容器能够直接经过 Unix socket 通讯
# UTS 共享(两个容器中主机名相同)
/ # hostname
7104b99d8de4

# NET 共享(两个容器中网络信息相同)
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:04
          inet addr:172.17.0.4  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:108 errors:0 dropped:0 overruns:0 frame:0
          TX packets:104 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:9969 (9.7 KiB)  TX bytes:9597 (9.3 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

# PID 独享
(demo5)/ # ping -c 100 baidu.com &
/ # ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 sh
   15 root      0:00 ping -c 100 baidu.com
   16 root      0:00 ps -ef
(demo6)/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 sh
   13 root      0:00 ps
   
# MOUNT 独享
(demo6)/ # touch test
/ # ls
bin   dev   etc   home  proc  root  sys   test  tmp   usr   var
(demo5)/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var

# USER 独享(busybox 没法测试,你们可使用其余镜像进行测试)
注意:这种网络模型通常用于两个容器之间网络依赖性特别强,相比较 bridge 模型能够提升网络效率,可是一样带来了容器之间的服务依赖。以我们实例为例,demo6 依赖与 demo5 的网络,一旦 demo5 容器终止,则 demo6 也没法使用网络。微服务初衷是想让服务之间解耦,可是这种网络模型反而使二者的依赖程度增高,因此这也多是 docker 官方文档中再也不体现这种网络模型的缘由。

注:更多技术博客请关注 字节教育官网,若是在学习或者本身实验的过程当中遇到问题,及时与做者沟通。【QQ群 374106486

相关文章
相关标签/搜索