Docker-Bridge Network 02 容器与外部通信
本小节介绍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 一张图总结

- bbox1 ping 百度,报文发往docker0;
- docker0发出报文,经过内核的路由转发和SNAT处理,将报文的源IP及MAC换成了enp0s3的IP和MAC,到达enp0s3;
- 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 一张图总结

- 在Host2里面
curl 192.168.0.11:8081,报文到达Host1的enp0s3; - Host1的enp0s3发出报文后,经由内核的转发及DNAT处理,将目的IP替换成nginx01的IP和端口;
- 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 容器与外部通信的更多相关文章
- Docker:macvlan实现容器跨主机通信 [十四]
一.什么是macvlan 1.macvlan 本身是 linux kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址, 2.即多个 interface,每个 interface ...
- Docker容器跨主机通信之:直接路由方式
一.Docker网络基本原理 直观上看,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包:此外,如果不同子网之间要进行通信,需要额外的路由机制. Docker ...
- centos7下安装docker(15.5容器跨主机网络--flanneld)
flannel是由CoreOS研究的一种覆盖网络(overlay network)网络工具,目的是帮助每一个host主机有一个完整的子网: 功能是:让集群中不同节点的主机创建的容器都有一个唯一的虚拟I ...
- AspNetCore容器化(Docker)部署(二) —— 多容器通信
一.前言 着上一篇 AspNetCore容器化(Docker)部署(一) —— 入门,在单个容器helloworld的基础上引入nginx反向代理服务器组成多容器应用. 二.配置反向代理转接 配置转接 ...
- Docker-Bridge Network 01 容器间通信
本小节介绍bridge network模式下,单机上的容器网络拓扑及通信. 1.前言 对于单机上的容器,Docker提供了bridge.host.none三种网络.我们首先介绍经典的bridge模式. ...
- Docker容器跨主机通信--overlay网络
一.Docker主机间容器通信的解决方案 Docker网络驱动 Overlay: 基于VXLAN封装实现Docker原生Overlay网络 Macvlan: Docker主机网卡接口逻辑上分为多个子接 ...
- Docker 网络管理及容器跨主机通信
1.网络模式 docker支持四种网络模式,使用--net选项指定: host,--net=host,如果指定此模式,容器将不会获得一个独立的network namespace,而是和宿主机共用一个. ...
- docker容器之间的通信
容器之间互通 新建两个容器 docker run -d --name box1 busybox /bin/sh -c "while true;do sleep 3600;done" ...
- (转)Docker - 创建 Docker overlay network (containers 通信)
原文链接: http://www.cnblogs.com/AlanWalkOn/p/6101875.html --- 创建基于Key-Value的Docker overlay network. 这样运 ...
随机推荐
- angular自启动过程
angularJS的源代码整体上来说是一个自执行函数,在angularJS加载完成后,就会自动执行了. 即,在window上暴露一个唯一的全局对象angular, 如果window.angular已经 ...
- CMDB资产采集方式
一:Agent方式 原理:在每台服务器装上agent客户端程序,定时向数据库发送指定的资产信息. 优点:速度快. 缺点:服务器上需要多装一个软件 import subprocess import re ...
- wr720n v4 折腾笔记(五):终篇-编译安装openwrt附带njit
前言: 由于没有多余的U盘,所以想直接压缩生成一个带njit-client的openwrt固件.其中按照网上的教程走,遇到了一些问题,这里前面的步骤来源于网络.后面给出处理问题的方法. 一.准备工作 ...
- CF33C Wonderful Randomized Sum 题解
原题链接 简要题意: 你可以无限次的把该数组的一个前缀和后缀 \(\times -1\),问最终的最大序列和. 这题盲目WA了数次才知道本质 这题89个数据吊打std CF真好啊,发现一个错后面就不测 ...
- nginx openssl升级支持http2
阿里云openssl升级,实现nginx主动推送 nginx主动推送能够有效减少不必要的报文传输,减少用户请求次数,以达到更快访问速度 现有版本检查 [root@node3 ~]# openssl v ...
- Docker镜像拉取慢的解决方法
镜像加速器配置: 下文配置引用于阿里云说明文档:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors 1. 安装/升级Docker客户 ...
- 利用 MinIO 轻松搭建静态资源服务
目录 1 引言 2 MinIO 简介 3 MinIO 运行与静态资源使用 3.1 MinIO 获取 3.2 MinIO 启动与运行 3.2.1 前台简单启动 3.2.2 后台指定参数运行 3.2.3 ...
- Building Applications with Force.com and VisualForce(Dev401)(十三):Implementing Business Processes:Automating Business Processes Part II
ev401-014:Implementing Business Processes:Automating Business Processes Part II Module Agenda1.Multi ...
- coding++:java-HashMap的负载因子为什么默认是0.75?
本篇文章基于JDK1.8,特在此说明 1):负载因子的作用 负载因子是和扩容机制有关的,意思是如果当前容器的容量,达到了我们设定的最大值,就要开始执行扩容操作.举个例子来解释,避免小白听不懂: 比如说 ...
- coding++:对List中每个对象元素按时间顺序排序
需求: 需要对List中的每个User按照birthday顺序排序,时间由小到大排列. package com.tree.ztree_demo.orderby; import java.text.Si ...