docker 网路

docker 网路

本文先学习docker提供的几种原生的网络,以及如何创建自定义网络,然后讨论容器间如何通信,以及容器与外界如何交互。
本文先讨论单个host上容器网络通信。如下就是宿主机上的三种网络。

root@ubuntu:~# clear
root@ubuntu:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
5bb52d9dd48f        bridge              bridge              local
43861be79018        host                host                local
f6f4e0306b58        none                null                local
root@ubuntu:~# 

none网络

none网络,就是什么都没有的网络。挂在该网络下的容器除了lo,没有其他网卡。

root@ubuntu:~# docker run -ti --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)

/ # 

封闭网络意味着隔离,一些对安全性要求高,并且不需要联网的应用可以使用none网络。

host 网络

连接到host网络的容器共享docker host的网络栈,容器的网络配置和宿主机完全一样。可以通过--network=host指定使用的host网络

root@ubuntu:~# docker run -ti --network=host busybox
/ # 
/ # ip l
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
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:95:da:0c brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue 
    link/ether 02:42:34:15:ee:d5 brd ff:ff:ff:ff:ff:ff
/ # 
/ # hostname
ubuntu
/ #

直接使用docker host的网络最大的好处就是性能,如果容器对网络传输效率有比较高的要求,则可以选择host网络。不便之处就是要牺牲一些灵活性,比如要考虑端口冲突问题。

bridge网络

docker安装是会创建一个docker0的linux bridge。如果不指定--network, 创建的容器默认都会挂到docker0上。

从上图可以看出来,一开始docker上没有其他网络设备,新建一个容器之后,一个新的网络接口 vetha8bcbf1 被挂到了docker0上,vetha8bcbf1 就是新创建容器的虚拟网卡。

进入容器,执行ip a查看,容器有一个网卡eth0@if11。
实际上eth0@if11 和 vetha8bcbf1 是一对veth pair。veth pair是一种成对出现的特殊网络设备,可以把他们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头是eth0@if11 在容器里,另一头 vetha8bcbf1 挂在网桥docker0上,器效果就是将 eth0@if11 也挂到了网桥docker0上。

通过docker network inspect bridge 查看bridge 网络的配置:

其中网关172.17.0.1,就是docker0。

容器拓扑结构就是:

user-defined 网络

docker提供三中user-define网络驱动:bridge、overlay和macvlan。这里只介绍bridge。

这里通过bridge驱动创建类似前面默认的bridge网络:

查看自定义my_net的配置信息:

自定义网络,可以再穿件网段时指定 --subnet 和 --gateway 参数来实现指定ip 网段。


容器要使用新的网络,需要在启动时通过 --network 指定。

容器的ip都是docker 自动从subnet中分配的,我们可通过 --ip 来指定一个静态ip:

注:只有使用 --subnet 创建的网络才能指定静态IP
my_net 创建的时候没有指定 --subnet,如果指定了静态ip会报错:

现在看看当前 docker host 的网络拓扑结构:

从上图我们可以看到两个busybox容器都挂在my_net2上,应该能够互通,验证一下:

可见同一网络中的容器、网关之间都是可以通信的。

my_net2与默认的docker0 属于不同的网桥,应该是不能通信的:

不同的网络如果加上路由应该就可以通信了,如果主机host上对每个网络都有一个路由,同时操作系统上打开了 ip forwarding,host就蹭了一个路由器,挂在不同网桥上的网络就能够互相通信。来看看docker host是否满足这些条件?

ip r 查看 host 上的路由表, sysctl net.ipv4.ip_forward查看ip转发协议:

条件都满足了,为什么不能通信?
查看下iptables-save:

原因就在这里:iptables DROP掉了网桥 docker0 与 br-f5a07dddf370 之间的双向流量。

那么如何才能让busybox与httpd通信?
答案是:为httpd容器添加一块 net_my2的网卡。 这个可以通过docker network connect 命令实现。

进入httpd容器,执行ipa查看一下网络配置

/ # 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
34: eth0@if35: <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.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
38: eth1@if39: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:16:10:04 brd ff:ff:ff:ff:ff:ff
    inet 172.22.16.4/24 brd 172.22.16.255 scope global eth1
       valid_lft forever preferred_lft forever
/ #

容器增加了一个网卡 eth1,分配了my_net2的ip 172.22.16.4。现在busybox应该横沟访问httpd,验证一下:

现在的网络拓扑结构图如下:

容器间通信

容器之间可以通过IP,Docker DNS Server 或 joined 容器三种方式通信。

IP通信

两个容器要能通信,必须要有属于同一个网络的网卡。具体做法是:容器创建出来通过--network 指定响应的网络,或者通过docker network connect将现有容器加入到指定网络。

Docker DNS Server

docker daemon 实现了一个内嵌的DNS server,使容器可以直接通过 “容器名” 通信。方法很简单,只要在启动时用 --name 为容器命名就可以。

docker run -ti --network=my_net2 --name=bbox1 busybox
docker run -ti --network=my_net2 --name=bbox2 busybox

使用docker DNS有个限制:只能在 user-defined 网络中使用。也就是说,默认的bridge网络是无法使用DNS的。

joined 容器

joined 容器非常的特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过127.0.0.1 直接通信。
举例:
先创建一个 httpd 容器,名字是 web1。

docker run -d -ti --name=wei1 httpd

然后创建 busybox 容器并通过 --network=container:web1 指定 joined 容器为 web1

docker run -ti --network=container:web1 busybox

进入容器执行ip a,对比两个容器的网卡,mac地址和ip完全一样,他们共享相同的网络栈。busybox可以通过127.0.0.1 访问web1 的http 服务。

joined 容器适用一下场景:

  • 不同容器中的程序希望通过 loopback 高效快速的通信,比如web server 与 api server。
  • 希望监控其他容器网络流量,比如运行在独立容器中的网络监控程序

将容器与外部世界连接

  • 容器访问外部世界;
  • 外部世界访问容器;

容器访问外部世界

当前环境下,docker host可直接访问外网的,容器也是可以直接访问

原理:
busybox 位于 docker0 这个bridge 网络中(172.17.0.0/16), 当buxybox 从容器ping时, 数据包是怎么到达 baidu.com的呢?
这里的关键是 NAT。我们查看一下 docker host 上的iptables 规则:

在NAT中,有这么一条规则:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
其含义是:如果网桥docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给MASQUERADE 处理。而MASQUERAE的处理方式是将包的原地址替换成host的地址发送出去,即做了一次网络地址转换(NAT)。

下面我们通过tcpdump查看地址是如何转换的。先查看docker host的路由表

默认路由是通过ens33发送出去,所以我们同事监控ens33和docker0上的icmp(ping)数据包。当busybox ping baidu.com时,tcpdump输出如下:

docker0 上收到 busybox 的 ping 包,原地址为容器IP 172.17.0.2, 交给MASQUERADE 处理。这是,在ens33上我们看到了变化,

ping 包源地址编程了 ens33 的IP 192.168.24.136.这就是 iptables NAT 规则的处理结果,从而保证数据包能够到达外网。

容器访问外网流程图:

  • busybox 发送ping 包:172.17.0.2 > www.bing.com
  • doker0 收到包,发现是发送到外网的,交给NAT 处理;
  • NAT 将原地址换成ens33的ip: 192.168.24.136 > www.bing.com
  • PING 包从ens33 发送出去,到达www.bing.com
    通过NAT,docker实现了容器对外的访问。

外部世界访问容器

外网通过端口映射来访问容器。
docker 可将容器对外提供服务的端口映射到host的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口:

容器启动后,通过docker port 查看到host映射的端口。httpd容器的80端口被映射到主机的32773上,这样就可以通过 <host ip>:<32768> 访问容器的web服务了。

除了动态端口映射,也可在-p中指定映射到host某个特定端口,例如可将80端口映射到host的8080端口:

每一个映射的端口,host都会启动一个docker-proxy进程来处理访问容器的流量:


总体流程图:

猜你喜欢

转载自blog.csdn.net/cbmljs/article/details/92805114