docker网络

1. docker网络概述

Docker 网络从覆盖范围可分为 单个host 上的容器网络和 跨多个host 的网络。Docker 安装时会自动在host上创建三个网络,我们可用docker network ls命令查看。

image

1.1 none 网络

顾名思义,none 网络就是什么都没有的网络。挂在这个网络下的容器除了lo,没有其他任何网卡。容器创建时,可以通过--network=none指定使用none网络。

image

这是一个封闭的网络容器,封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。
比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。当然大部分容器是需要网络的。

1.2 host 网络

连接到 host 网络的容器共享Docker host的网络栈,容器的网络配置与host 完全一样。
可以通过 --network-host指定使用 host 网络。

image

在容器中可以看到 host 的所有网卡,并且连hostname也是 host 的。

image

host 网络的使用场景又是什么呢?

  • 直接使用Docker host的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择host网络。
  • 不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。
    Docker host的另一个用途是让容器可以直接配置 host 网路,比如某些跨 host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理iptables。

1.3 bridge网络

Docker 安装时会创建一个命名为 docker0 的Linux bridge。如果不指定--network,创建的容器默认都会挂到 docker0 上。

image

不指定网络,创建一个新的容器,发现一个新的网络接口 veth40ee324被挂载到docker0上了,veth40ee324就是新创建容器的虚拟网卡

image

容器有一个网卡 eth0@if219。为什么不是veth40ee324呢?

image

实际上eth0@if219veth40ee324是一对veth pairveth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if219)在容器中,另一头(veth40ee324)挂在网桥docker0上,其效果就是将eth0@if219也挂在了docker0 上.

image

注意到etho@if219已经配置了IP 172.17.0.6,为什么是这个网段呢?通过docker network inspect bridge 看一下 bridge 网络的配置信息。

image

原来 bridge 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1。这个网关在哪儿呢?大概你已经猜出来了,就是docker0

image

容器创建时,docker会自动从172.17.0.0/16中分配一个P,这里16位的掩码保证有足够多的 可以供容器使用。

2. user-defined 网络

除了 none、host、bridge这三个自动创建的网络,用户也可以根据业务需要创建user-defined 网络。
Docker 提供三种 user-defined 网络驱动:bridgeoverlaymacvlanoverlaymacvlan用于创建跨主机的网络。我们可通过 bridge驱动创建类似前面默认的 bridge 网络。

docker network create --driver bridge my_net

image

查看一下当前host的网络结构变化

image

新增了一个网桥 br-3803a76336c6,这里3803a76336c6正好是新建 bridge 网络 my_net短 id

执行 docker network inspect 查看一下 my net 的配置信息。

image

这里172.19.0.0/16Docker自动分配的IP网段。

我们可以自己指定IP网段,在创建网段时指定--subnet--gateway 参数,

docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2

这里我们创建了新的 bridge 网络 my_net2,网段为 172.22.16.0/24,网关为 172.22.16.1

image

与前面一样,网关在 my_net2对应的网桥 br-024ff4851920 上。

image

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

docker run -it --network=my_net2 busybox

image

容器分配到的IP为172.22.16.2。
容器的IP都是 docker 自动从 subnet 中分配,我们可以指定一个静态IP,通过--ip 指定。

docker run -it --network my_net2 --ip 172.22.16.8 busybox

image

注: 只有使用--subnet 创建的网络才能指定静态。

my_net 创建时没有指定--subnet,如果指定静态IP报错就会报错。

image

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

image

两个 busybox容器都挂在my_net2上,应该能够互通,我们验证一下

172.16.0.2 ping 172.22.16.8

image

172.16.16.8 ping 172.22.0.2

image

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

my_net2与默认的 bridge属于不同的网桥,不能通信

image

如果想要两个不同网桥的容器相互访问怎么办?

加上路由即可,确实,如果host上对每个网络都有一条路由,同时操作系统上打开了ip forwardinghost就成了一个路由器,挂接在不同网桥上的网络就能够相互通信。

image

172.16.0.0/16172.22.16.0/24这两个网络的路由定义好了,查看ip forwarding

image

经观察发现,ip forwarding 也启动了,为什么两个跨网桥的容器不能访问呢?

查看 iptables-save

image

原因: iptables DROP掉了网桥docker0非 docker0 之间双向的流量。

怎样才能让busyboxhttpd通信呢?

答案:为 httpd 容器添加一块my_net2的网卡。这个可以通过 docker network connect命令实现。

docker network connect my_net2 e6c1fdafffee # 给容器添加网卡

image

可以看到,在httpd容器中新增了一个eth1的网卡,IP是 172.22.16.3在http容器中可以ping通busybox容器了

image

可以看到,在busybox容器中直接访问 172.22.16.3是能够正常访问到httpd服务的。

image

当前网络拓扑图

image

3 容器间通信

容器之间可通过 IPDocker DNS Serverjoined 容器三种方式通信。

3.1 IP通信

两个容器要能通信,必须要有属于同一个网络的网卡。满足这个条件后,容器就可以通过IP交互了。具体做法是在容器创建时通过--network 指定相应的网络,或者通过docker network connect将现有容器加入到指定网络。

3.2 Docker DNS Server

通过IP访问容器虽然满足了通信的需求,但还是不够灵活。因为在部署应用之前可能无法确定IP,部署之后再指定要访问IP的会比较麻烦。对于这个问题,可以通过docker自带的 DNS 服务解决。

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

docker run -it --network my_net2 --name bbox1 busybox
docker run -it --network my_net2 --name bbox2 busybox

image

在容器中执行

ping -c 5 bbox1

image

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

docker run -it --name bbox3 busybox
docker run -it --name bbox4 busybox

image

3.3 joined 容器

joined 容器是另一种实现容器间通信的方式。joined 容器非常特别:

  • 它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息

joined 容器之间可以通过 127.0.0.1 直接通信。

创建一个httpd容器,名字为web1、创建另一个容器 连接到web1上

docker run -d -it --name web1 httpd
docker run -it --network container:web1 busybox

busybox容器的网络情况

image

web1容器内的网络情况

image

可以看到busyboxweb1的网卡mac地址与IP完全一样,它们共享了相同的网络栈。busybox可以直接用127.0.0.1访问web1http 服务。

image

joined 容器非常适合以下场景:
(1)不同容器中的程序希望通过loopback高效快速地通信,比如WebServer与AppServer

(2)希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。

4. 将容器与外部世界连接

4.1 容器访问外部世界

image

只要docker host能访问外网,默认容器就能访问外网(容器网络以外的网络环境,不一定是因特网)

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

image

-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

上面那条规整的含义是: 如果网桥docker0收到来自172.17.0.0/6网段的外出包,把它交给MASQUERADE处理。而MASQUERADE的处理方式是将包的源地址替换成host的地址发送出去,即做了一次网络地址转换(NAT)。

image

默认路由通过eno1发出去,所以我们要同时监控eno1docker0上的icmp(ping)数据包。

busybox ping baidu.com时候,tcpdump 输出如下:

image

docker0收到busyboxping包,源地址为容器IP 172.17.0.2,这没问题,交给MASQUERADE处理。这时,在eno1上我们看到了变化,如下所示。

image

ping 包的源地址变成了eno1的IP 172.16.122.177,这就是 iptable NAT 规则处理的结果,从而保证数据包能够到达外网。

image

(1) busybox发送ping包: 172.17.0.2 ---> www.baidu.com
(2) docker0 收到包,发现是发送到外网的,交给 NAT 处理。
(3) NAT将源地址换成eno1的IP:172.16.122.177 ---> www.baidu.com
(4) ping包从eno1发送出去,到达www.baidu.com
通过 NAT,docker实现了容器对外网的访问。

4.2 外部世界访问容器

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

docker run -d -p 80 httpd

image

容器启动后,可通过docker ps或者docker port 查看到 host 映射的端口。httpd 容器的80端口被映射到 host 10000上,这样就可以通过主机IP:端口访问容器的 Web 服务了。

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

docker run -d -p 4555:80 httpd

image

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

image

(1) docker-proxy 监听 host 的 32773 端口。
(2) 当访问172.16.122.177:4555时,docker-proxy 转发给容器 172.17.0.6:80。
(3) httpd 容器响应请求并返回结果。

posted on 2024-03-11 18:52  ccblblog  阅读(5)  评论(0编辑  收藏  举报

导航