容器作为应用,必然需要与外界通信,包括容器之间和外部网络。
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 就创建了在主机和所有容器之间一个虚拟共享网络。
自定义容器网络
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
Linux路由转发功能
Linux默认一般不开启路由转发 ip forwarding,查看/etc/sysctl.cfg中的
‘1’ 说明启用。
查看防火墙iptables
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 容器非常适合以下场景:
- 不同容器中的程序希望通过 loopback 高效快速地通信,比如 web server 与 app server。
- 希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。
容器访问外部网络
注意:这里外部网络指的是容器网络以外的网络环境,而非特指Internet
具体过程已docker0 为例:
当容器要访问外网时,将数据包封装丢给网关,到达docker0,收到后将其交给MASQUERADE处理(将包源地址替换为主机地址发出去,即做了一次网络地址转换(NAT)),然后就和主机访问外网一样了。
MASQUERADE
在主机通过 iptables -t nat -S
可查看到一条规则
其含义是:如果网桥 docker0
收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。
抓包过程
使用tcpdump
- 查看主机路由表
- 抓取相关网卡上数据包
- 结果分析
ip r
查看默认路由从enp0s3出去,故监控enp0s3和docker0上的icmp数据包。
外部世界访问容器
docker可将容器对外提供服务的端口映射到主机的某个端口,外网通过该端口访问容器。
在容器启动时通过-p参数来映射端口。
默认情况下容器的端口会随机映射到主机的一个端口。
可通过docker ps
和docker port
查看映射的端口情况、
若要指定映射到主机的某个端口,则
意思是将容器的80端口映射到主机的8080端口上(别搞反了)
每一个映射的端口,host 都会启动一个 docker-proxy
进程来处理访问容器的流量。
过程如下:
- docker-proxy 监听 host 的 32773 端口。
- 当 curl 访问 10.0.2.15:32773 时,docker-proxy 转发给容器 172.17.0.2:80。
- httpd 容器响应请求并返回结果。