docker单主机网络管理实践

前言

容器作为应用,必然需要与外界通信,包括容器之间和外部网络。

Docker网络从覆盖范围分

  • 单台主机上的容器网络★
  • 跨多台主机的容器网络

容器间通信

容器间通信的三种方式:

  • IP
  • Docker DNS Server
  • joined

IP通信

Docker安装时,默认会在主机上创建三个网络,bridge、host、none。可用docker network ls查看

none

就是只有loopback的网络,无其他任何网卡。容器创建时可以用 --network=none来指定容器使用none网络

适应场景:一些安全性要求较高且不需联网的应用,如生成随机密码的容器,放在none里防止窃取。

host

类似于虚拟机中共享主机网络的选项,即使容器共享其所在主机的网络栈,该容器的网络配置和主机完全一样。

适应场景:如容器对网络传输效率较高要求,则可使用host网络。注意考虑端口冲突。

bridge

类似于虚拟机中的桥接模式,可理解为一个软件交换机。默认情况下,不指定--network参数,创建的容器都会挂到一个叫docker0的Linux bridge上。可以使用命令brctl show查看。

注:首次使用brctl命令,可能会提示安装。brctl 命令在 Debian、Ubuntu 中可以使用 sudo apt-get install bridge-utils 来安装

当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即 eth0;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头(例如 vethAQI2QT)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker 就创建了在主机和所有容器之间一个虚拟共享网络。

network

自定义容器网络

user-defined网络

Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan。bridge与前面类似,overlay 和 macvlan 用于创建跨主机的网络。

docker network create --driver bridge my_net

还可指定网段和网关,增加参数--subnet--gateway

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

要使用相关网络驱动即在创建docker容器时,指定--network==xxx其中xxx为驱动名称,当指定为有--subnet参数的驱动时,该容器可以通ip参数指定静态IP地址

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

容器间连通

增加网卡使用docker network connect实现。

例如:为 CONTAINER ID为2a256932添加my_net2

1
docker network connect my_net2 2a256932

Linux路由转发功能

Linux默认一般不开启路由转发 ip forwarding,查看/etc/sysctl.cfg中的

1
2
#sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

‘1’ 说明启用。

查看防火墙iptables

1
2
3
4
iptables-save

-A DOCKER-ISOLATION -i br-5d863e9f78b6 -o docker0 -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-5d863e9f78b6 -j DROP

iptables DROP 掉了网桥 docker0 与 br-5d863e9f78b6 之间双向的流量

从规则的命名 DOCKER-ISOLATION 可知 docker 在设计上就是要隔离不同的 netwrok。

Docker DNS Server

当部署应用之前可能无法确定IP,部署之后再指定要访问的IP会比较麻烦。可通过docker自带的DNS解决问题。

在docker daemon中实现了一个内嵌的DNS Server ,使容器可以直接通过“容器名”通信。容器名为创建运行容器时指定--name参数确定。

注:docker dns 只能在user-defined网络中使用。

joined容器

joined指多个容器共享网络栈,使容器之间可以通过127.0.0.1直接通信。

当一个容器需要joined另一个容器,则在创建运行使指定--network=container:容器name即可。

joined 容器非常适合以下场景:

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

容器访问外部网络

注意:这里外部网络指的是容器网络以外的网络环境,而非特指Internet

具体过程已docker0 为例:

当容器要访问外网时,将数据包封装丢给网关,到达docker0,收到后将其交给MASQUERADE处理(将包源地址替换为主机地址发出去,即做了一次网络地址转换(NAT)),然后就和主机访问外网一样了。

MASQUERADE

在主机通过 iptables -t nat -S可查看到一条规则

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

其含义是:如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。

抓包过程

使用tcpdump

  1. 查看主机路由表
  2. 抓取相关网卡上数据包
  3. 结果分析

ip r查看默认路由从enp0s3出去,故监控enp0s3和docker0上的icmp数据包。

外部世界访问容器

docker可将容器对外提供服务的端口映射到主机的某个端口,外网通过该端口访问容器。

1
<host ip>:<映射的端口>

在容器启动时通过-p参数来映射端口。

1
docker run -d -p 80 httpd

默认情况下容器的端口会随机映射到主机的一个端口。

可通过docker psdocker port查看映射的端口情况、

若要指定映射到主机的某个端口,则

1
docker run -d -p 8080:80 httpd

意思是将容器的80端口映射到主机的8080端口上(别搞反了)

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

过程如下:

  1. docker-proxy 监听 host 的 32773 端口。
  2. 当 curl 访问 10.0.2.15:32773 时,docker-proxy 转发给容器 172.17.0.2:80。
  3. httpd 容器响应请求并返回结果。
© 2019 lvbin's Blog All Rights Reserved.
Theme by hiero