本小节介绍bridge network模式下,容器与外部的通信。

1.前言2.容器访问外部2.1 访问外网2.2 原理2.3 一张图总结2.4 抓包3.外部访问容器3.1 创建nginx容器并从外部访问3.2 原理3.3 一张图总结3.4 抓包3.5 docker-proxy4.小结

1.前言

  上一节介绍了容容器之间的通信过程。容器中部署了服务,我们肯定要从外部去访问这个服务,那么,容器如何与外部进行通信呢?

2.容器访问外部

  这一节,我们仍然使用bbox1容器进行实验。首先要确保虚拟机Host1能访问外网。在“准备Docker环境”那一节,介绍了虚拟机如何直接ping通百度。
  要说明一点,外部网络不一定指互联网,外部与内部是相对的概念。只要是容器网络以外的网络,都可以称作外部网络,比如Host1所在的网络。

2.1 访问外网

[root@docker1 ~]# docker exec -it bbox1 sh
/ # ping www.baidu.com
PING www.baidu.com (180.101.49.11): 56 data bytes
64 bytes from 180.101.49.11: seq=0 ttl=51 time=21.308 ms
64 bytes from 180.101.49.11: seq=1 ttl=51 time=24.613 ms

2.2 原理

  回顾一下当前容器的网络连接情况:

  我们试着分析一下,bbox1发出ping包,根据bbox1的路由表,报文被送到了docker0。那docker0和enp0s3是怎么连接的呢?
  Linux本身就有路由转发功能,所以其实linux系统本身可以作为一个路由器。执行命令cat /proc/sys/net/ipv4/ip_forward,能看到ip_forward=1,说明系统开启了路由转发。
  那么docker0发出数据包后,应该是根据Host的路由表进行转发。而Host的默认路由是指向enp0s3的,所以enp0s3收到了数据包。
  我个人理解,此时enp0s3上数据包的源IP是bbox1的,目的IP是baidu,应该是能够到达baidu的;但是在baidu发回应答包的时候,它不知道这个私网IP在哪,通过任何一级路由都找不到,所以enp0s3这里发出报文前就得进行SNAT。
  具体回包过程我不太明白,有明白的同学麻烦具体解释一下,感谢。
  说到SNAT,就必须得提到Linux内核的大杀器——IPtables了。
  在虚拟机中执行iptables -t nat -vnL,查看nat表。

Chain POSTROUTING (policy ACCEPT 21 packets, 1479 bytes)
 pkts bytes target     prot opt in   out      source            destination         
    3   202 MASQUERADE  all  --  *   !docker0  172.17.0.0/16     0.0.0.0/0 

  docker0发出报文后会经过linux内核的路由转发,到达enp0s3。而到达enp0s3之前,会经过iptables的POSTROUTING链进行SNAT,就是上面这条规则。大意是指把源IP为172.17.0.0/16、目的IP任意的报文,进行MASQUERADE(伪装),也就是SNAT,即把源IP和MAC替换,替换成路由选择之后的enp0s3的IP和MAC。

2.3 一张图总结

  1. bbox1 ping 百度,报文发往docker0;
  2. docker0发出报文,经过内核的路由转发和SNAT处理,将报文的源IP及MAC换成了enp0s3的IP和MAC,到达enp0s3;
  3. enp0s3发出报文,访问百度。

2.4 抓包

当前网络设备及地址如下:

设备 IP MAC
容器bbox1 172.17.0.2 02:42:ac:11:00:02
网桥docker0 172.17.0.1 02:42:a8:64:6c:32
虚拟机网卡enp0s3 192.168.0.11 08:00:27:70:b6:ef

在bbox1内部执行ping www.baidu.com

  • 在docker0上抓包:
[root@docker1 ~]# tcpdump -nei docker0 icmp
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:49:11.669684 02:42:ac:11:00:02 > 02:42:a8:64:6c:32, ethertype IPv4 (0x0800), length 98: 172.17.0.2 > 180.101.49.11: ICMP echo request, id 9728, seq 0, length 64
12:49:11.697374 02:42:a8:64:6c:32 > 02:42:ac:11:00:02, ethertype IPv4 (0x0800), length 98: 180.101.49.11 > 172.17.0.2: ICMP echo reply, id 9728, seq 0, length 64
  • 在enp0s3上抓包:
[root@docker1 ~]# tcpdump -nei enp0s3 icmp
listening on enp0s3, link-type EN10MB (Ethernet), capture size 262144 bytes
12:49:11.669700 08:00:27:70:b6:ef > 48:0e:ec:3b:b3:41, ethertype IPv4 (0x0800), length 98: 192.168.0.11 > 180.101.49.11: ICMP echo request, id 9728, seq 0, length 64
12:49:11.697340 48:0e:ec:3b:b3:41 > 08:00:27:70:b6:ef, ethertype IPv4 (0x0800), length 98: 180.101.49.11 > 192.168.0.11: ICMP echo reply, id 9728, seq 0, length 64

  从enp0s3的报文可以看出,源IP和MAC已经是enp0s3的了。

3.外部访问容器

3.1 创建nginx容器并从外部访问

  创建一个nginx容器,执行docker run -it -d --name=nginx01 -p 8081:80 nginx该命令将容器的80端口映射到主机的8081端口。
  从本虚拟机上curl 127.0.0.1:8081,能返回nginx欢迎信息。从另一台虚拟机上curl 192.168.0.11:8081,也能返回nginx欢迎信息。说明外部网络能访问nginx容器。

3.2 原理

  使用了IPtables的DNAT功能。
  执行iptables -t nat -vnL查看IPtables规则,可以发现目的端口8081被替换为172.17.0.4:80。

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source             destination         
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0          0.0.0.0/0           
    0     0 DNAT       tcp  --  !docker0 *      0.0.0.0/0          0.0.0.0/0          tcp dpt:8081 to:172.17.0.4:80

3.3 一张图总结

  1. 在Host2里面curl 192.168.0.11:8081,报文到达Host1的enp0s3;
  2. Host1的enp0s3发出报文后,经由内核的转发及DNAT处理,将目的IP替换成nginx01的IP和端口;
  3. docker0收到报文后,根据目的mac找到对应端口,送出报文到nginx01。

3.4 抓包

  使用tcpdump,在Host1的网卡及docker0上抓包验证。

  • Host1的enp0s3
[root@docker1 ~]# tcpdump -nei enp0s3 tcp port 8081
listening on enp0s3, link-type EN10MB (Ethernet), capture size 262144 bytes
13:25:14.725366 08:00:27:d4:6f:d1 > 08:00:27:70:b6:ef, ethertype IPv4 (0x0800), length 74: 192.168.0.12.48392 > 192.168.0.11.tproxy: Flags [S], seq 1433025043, win 29200, options [mss 1460,sackOK,TS val 4294829465 ecr 0,nop,wscale 7], length 0
13:25:14.725559 08:00:27:70:b6:ef > 08:00:27:d4:6f:d1, ethertype IPv4 (0x0800), length 74: 192.168.0.11.tproxy > 192.168.0.12.48392: Flags [S.], seq 404119170, ack 1433025044, win 28960, options [mss 1460,sackOK,TS val 59419808 ecr 4294829465,nop,wscale 7], length 0
  • Host1的docker0
[root@docker1 ~]# tcpdump -nei docker0 tcp
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:26:07.014766 02:42:a8:64:6c:32 > 02:42:ac:11:00:04, ethertype IPv4 (0x0800), length 74: 192.168.0.12.48396 > 172.17.0.4.http: Flags [S], seq 1839700092, win 29200, options [mss 1460,sackOK,TS val 4294881751 ecr 0,nop,wscale 7], length 0
13:26:07.015357 02:42:ac:11:00:04 > 02:42:a8:64:6c:32, ethertype IPv4 (0x0800), length 74: 172.17.0.4.http > 192.168.0.12.48396: Flags [S.], seq 3312305882, ack 1839700093, win 28960, options [mss 1460,sackOK,TS val 59472098 ecr 4294881751,nop,wscale 7], length 0

  可以看出,docker0上收到的报文,目的IP及MAC已经是nginx01的了。

3.5 docker-proxy

  注意:对于外部访问容器,除了iptables DNAT处理外,还有一种方式docker-proxy。网上很多文章写得并不全面,只说了docker-proxy。其实通过上面的分析,只通过DNAT就可以完成外部访问容器了,那docker-proxy什么时候起作用呢?

[root@docker1 ~]# ps -ef|grep proxy
root      4073   927  0 13:07 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8081 -container-ip 172.17.0.4 -container-port 80
root      4143  1521  0 13:32 pts/0    00:00:00 grep --color=auto proxy

  查看进程可以发现,我们的host上也开启了docker-proxy,将host的0.0.0.0:8081转发到容器172.17.0.4:80。
  关于docker-proxy和DNAT何时起作用,有一篇文章分析的很好,分享给大家《docker-proxy存在合理性分析》。

4.小结

  • 容器访问外部,由iptables SNAT实现
  • 外部访问容器,由iptables DNAT实现,另外在一些场景下,通过docker-proxy进行转发

下一节,我们介绍bridge network的自定义网络。 点击此处回到docker系列文章目录

原创文章,转载请声明出处!

-----------------------------------------------------------------------------------------------

本人微信公众号同步更新云计算、容器、网络、编程等文章,欢迎参观!

Docker-Bridge Network 02 容器与外部通信的更多相关文章

  1. Docker:macvlan实现容器跨主机通信 [十四]

    一.什么是macvlan 1.macvlan 本身是 linux kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址, 2.即多个 interface,每个 interface ...

  2. Docker容器跨主机通信之:直接路由方式

    一.Docker网络基本原理 直观上看,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包:此外,如果不同子网之间要进行通信,需要额外的路由机制. Docker ...

  3. centos7下安装docker(15.5容器跨主机网络--flanneld)

    flannel是由CoreOS研究的一种覆盖网络(overlay network)网络工具,目的是帮助每一个host主机有一个完整的子网: 功能是:让集群中不同节点的主机创建的容器都有一个唯一的虚拟I ...

  4. AspNetCore容器化(Docker)部署(二) —— 多容器通信

    一.前言 着上一篇 AspNetCore容器化(Docker)部署(一) —— 入门,在单个容器helloworld的基础上引入nginx反向代理服务器组成多容器应用. 二.配置反向代理转接 配置转接 ...

  5. Docker-Bridge Network 01 容器间通信

    本小节介绍bridge network模式下,单机上的容器网络拓扑及通信. 1.前言 对于单机上的容器,Docker提供了bridge.host.none三种网络.我们首先介绍经典的bridge模式. ...

  6. Docker容器跨主机通信--overlay网络

    一.Docker主机间容器通信的解决方案 Docker网络驱动 Overlay: 基于VXLAN封装实现Docker原生Overlay网络 Macvlan: Docker主机网卡接口逻辑上分为多个子接 ...

  7. Docker 网络管理及容器跨主机通信

    1.网络模式 docker支持四种网络模式,使用--net选项指定: host,--net=host,如果指定此模式,容器将不会获得一个独立的network namespace,而是和宿主机共用一个. ...

  8. docker容器之间的通信

    容器之间互通 新建两个容器 docker run -d --name box1 busybox /bin/sh -c "while true;do sleep 3600;done" ...

  9. (转)Docker - 创建 Docker overlay network (containers 通信)

    原文链接: http://www.cnblogs.com/AlanWalkOn/p/6101875.html --- 创建基于Key-Value的Docker overlay network. 这样运 ...

随机推荐

  1. Andorid 添加MenuPopup

  2. Scapy编写UDP扫描脚本

    脚本内容如下: from scapy.all import * import optparse import threading def scan(target,port): pkt=IP(dst=t ...

  3. Cisco 综合配置(二)

    要求: 1. PC1 属于VLAN10,PC2属于VLAN20,网关:Master Router2. VLAN10.20 的网段为:192.168.10.0/24 . 192.168.20.0/24 ...

  4. zabbix笔记_008 zabbix监控交换机路由器

    zabbix监控交换机路由器 要监控路由器交换机,需要使用到SNMP协议 SNMP是一个简单网络管理协议,他基于C/S模型实现的监控和管理. 服务器安装SNMP: yum -y install net ...

  5. POJ 1182食物链(分集合以及加权两种解法) 种类并查集的经典

    题目链接:http://icpc.njust.edu.cn/Problem/Pku/1182/ 题意:给出动物之间的关系,有几种询问方式,问是真话还是假话. 定义三种偏移关系: x->y 偏移量 ...

  6. 一起学习vue源码 - Vue2.x的生命周期(初始化阶段)

    作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/58c61b4361ff4b005d9e8 ...

  7. OpenCV-Python 相机校准 | 四十九

    目标 在本节中,我们将学习 由相机引起的失真类型, 如何找到相机的固有和非固有特性 如何根据这些特性使图像不失真 基础 一些针孔相机会给图像带来明显的失真.两种主要的变形是径向变形和切向变形. 径向变 ...

  8. GANs和低效映射

    生成对抗网络(GANs)被誉为生成艺术领域的下一纪元,这是有充分理由的.新技术一直是艺术的驱动因素,从颜料的发明到照相机再到Photoshop-GAN是自然而然的.例如,考虑下面的图片,由埃尔加马勒发 ...

  9. 使用Keras进行深度学习:(五)RNN和双向RNN讲解及实践

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! 笔者:Ray 介绍 通过对前面文章的学习,对深度神经网络(DNN) ...

  10. 【Pytest03】全网最全最新的Pytest框架fixture应用篇(1)

    fixtrue修饰器标记的方法通常用于在其他函数.模块.类或者整个工程调用时会优先执行,通常会被用于完成预置处理和重复操作.例如:登录,执行SQL等操作. 完整方法如下:fixture(scope=' ...