Docker容器非常轻量,系统开销非常少,比VMware或者VirtualBox用起来方便,部署起来也非常容易。官方推荐我们通过端口映射的方式把Docker容器的服务提供给宿主机或者局域网其他容器使用。一般过程是:

1、Docker进程通过监听宿主机的某个端口,将该端口的数据包发送给Docker容器

2、宿主机可以打开防火墙让局域网其他设备通过访问宿主机的端口进而访问docker的端口

这里以CDNS为例,CDNS是一个用于避免DNS污染的程序,通过CDNS可以把你的计算机变成一个抗污染的DNS服务器提供给局域网使用。Docker镜像下载地址:https://hub.docker.com/r/alexzhuo/cdns/

原理是在Docker容器中启动CDNS,监听53端口,Docker容器的IP地址为172.12.0.2,宿主机把5053端口映射到Docker容器上,访问宿主机的127.0.0.1:5053就相当于访问Docker的53端口,所以Docker的启动方法是:

sudo docker run -itd -p 0.0.0.0:5053:53/udp --name=CureDNS alexzhuo/cdns cdns -c /etc/cdns.config.json

这样我们使用dig工具通过5053端口查询DNS就是无污染的DNS了,过程如下:

alex@alex-Lenovo-U310:~$ dig www.facebook.com @127.0.0.1 -p 5053; DiG 9.10.3-P4-Ubuntu <<>> www.facebook.com @127.0.0.1 -p 5053
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9522
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 5;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.facebook.com.        IN    A;; ANSWER SECTION:
www.facebook.com.    1550    IN    CNAME    star-mini.c10r.facebook.com.
star-mini.c10r.facebook.com. 30    IN    A    31.13.95.36;; AUTHORITY SECTION:
c10r.facebook.com.    2010    IN    NS    a.ns.c10r.facebook.com.
c10r.facebook.com.    2010    IN    NS    b.ns.c10r.facebook.com.;; ADDITIONAL SECTION:
a.ns.c10r.facebook.com.    2439    IN    A    69.171.239.11
a.ns.c10r.facebook.com.    2439    IN    AAAA    2a03:2880:fffe:b:face:b00c:0:99
b.ns.c10r.facebook.com.    3351    IN    A    69.171.255.11
b.ns.c10r.facebook.com.    1253    IN    AAAA    2a03:2880:ffff:b:face:b00c:0:99;; Query time: 47 msec
;; SERVER: 127.0.0.1#5053(127.0.0.1)
;; WHEN: Mon Apr 10 16:21:46 CST 2017
;; MSG SIZE  rcvd: 213

这里假设我们的宿主机IP是192.168.12.107
如果现在出现另外一台局域网计算机,IP地址为192.168.12.113,它想把宿主机当成DNS服务器,那么我们就需要在192.168.12.113这台计算机上访问192.168.12.107:5053来查询DNS,dig命令如下

dig www.facebook.com @192.168.12.107 -p 5053

这样做显然是很不方便的,我们现在希望不经过宿主机这一套NAT和代理,想要直接在局域网内的任意一台计算机上直接通过IP访问Docker容器,让Docker容器完整的暴露在局域网里而不是仅单单暴露一个53端口。那么应该如何做呢?

首先通过观察发现,Docker的默认启动方式中,会产生一块虚拟网卡,在这里我们可以理解这块网卡连接着一个虚拟交换机,然后每个Docker容器又会拥有自己单独的网卡和IP,而且所有Docker容器也连接在这个虚拟交换机的下面。我们可以在宿主机上通过ifconfig命令看到这个虚拟网卡。

alex@alex-Lenovo-U310:~$ ifconfig
docker0   Link encap:以太网  硬件地址 02:42:cd:21:5c:81  inet 地址:172.17.0.1  广播:0.0.0.0  掩码:255.255.0.0inet6 地址: fe80::42:cdff:fe21:5c81/64 Scope:LinkUP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1接收数据包:2892 错误:0 丢弃:0 过载:0 帧数:0发送数据包:3517 错误:0 丢弃:0 过载:0 载波:0碰撞:0 发送队列长度:0 接收字节:187022 (187.0 KB)  发送字节:4771886 (4.7 MB)lo        Link encap:本地环回  inet 地址:127.0.0.1  掩码:255.0.0.0inet6 地址: ::1/128 Scope:HostUP LOOPBACK RUNNING  MTU:65536  跃点数:1接收数据包:9993 错误:0 丢弃:0 过载:0 帧数:0发送数据包:9993 错误:0 丢弃:0 过载:0 载波:0碰撞:0 发送队列长度:1 接收字节:934304 (934.3 KB)  发送字节:934304 (934.3 KB)wlp3s0    Link encap:以太网  硬件地址 74:e5:43:b0:dd:b0  inet 地址:192.168.12.107  广播:192.168.12.255  掩码:255.255.255.0inet6 地址: fe80::8adf:28f7:5ec:3a5d/64 Scope:LinkUP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1接收数据包:69760 错误:0 丢弃:0 过载:0 帧数:0发送数据包:64718 错误:0 丢弃:0 过载:0 载波:0碰撞:0 发送队列长度:1000 接收字节:41517057 (41.5 MB)  发送字节:9971762 (9.9 MB)

上面的docker0这块网卡就是虚拟网卡,它的IP地址是172.17.0.1,它和其他的docker容器都连接在一个虚拟交换机上,网段为172.17.0.0/16,下面我们登录到Docker容器里面,查看一下容器的网卡和IP

# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:02  inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0inet6 addr: fe80::42:acff:fe11:2/64 Scope:LinkUP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1RX packets:3449 errors:0 dropped:0 overruns:0 frame:0TX packets:2811 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:0 RX bytes:4763490 (4.7 MB)  TX bytes:219998 (219.9 KB)lo        Link encap:Local Loopback  inet addr:127.0.0.1  Mask:255.0.0.0inet6 addr: ::1/128 Scope:HostUP LOOPBACK RUNNING  MTU:65536  Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1 RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

可以看到这个容器的IP地址为172.17.0.2,现在我们到宿主机里看看ping 172.17.0.2能不能ping通。
答案当然是能ping通,能ping通的原因就是我们的宿主机里知道目标地址为172.17.0.1/16的路由信息,不信我们可以查看一下

alex@alex-Lenovo-U310:~$ ip route
default via 192.168.12.1 dev wlp3s0  proto static  metric 600
169.254.0.0/16 dev docker0  scope link  metric 1000
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1
192.168.12.0/24 dev wlp3s0  proto kernel  scope link  src 192.168.12.107  metric 600

从上面可以看出来,172.17.0.0/16这个网段的数据包可以通过docker0这块网卡发送出去。也就是说,目前宿主机有两个IP,一个是192.168.12.107,用于连接实体的局域网,一个是172.17.0.1,用来和Docker容器通信,从这可以看出宿主机和路由器的作用是一致的。而Docker容器只有一个IP就是172.17.0.2。如果docker容器想要访问外网,那么它就会把数据包发送到网关172.17.0.1,然后由宿主机做NAT发送到192.168.12.1/24这个网段的网关上。

不光宿主机可以ping通容器,而且由于在docker容器中默认路由(网关)是172.17.0.1,所以docker容器不光可以ping主机的172.17.0.1,还能ping通主机的另一个IP:192.168.12.107

此时我们的网络拓扑其实就变成了192.168.12.0/24这个网段里有个宿主机,为了方便理解,我们把这个宿主机看成一个路由器,路由器下面是172.17.0.1/16这个网段。我们把Docker容器看成实实在在的机器设备,连接在宿主机这个路由器的下面。这样Docker的拓扑结构就非常清晰了。我们可以发现这个拓扑结构其实非常的简单。就像家里上网的路由器一样。打个比方:我家里有两个路由器,一个路由器通过PPPOE拨号连接公网,内网地址为192.168.12.1,另一个路由器连接在第一个路由器上面,WAN口IP是192.168.12.107,LAN口地址是172.17.0.1,我们的Docker容器看成一个个的电脑接在第二个路由器LAN上面,所以Docker容器的IP为172.17.0.2。

第二个路由器(宿主机)通过NAT让我们的电脑们(Docker容器)可以访问互联网。电脑们(Docker容器们)可以互相ping通,也能ping通全部两个路由器。第二个路由器可以ping通电脑们,但是第一个路由器ping不同电脑们。如果还是不理解拓扑结构,可以自己在家里买两个路由器一前一后放上试试。

网络拓扑图大致如下:

现在问题来了,如果有一个电脑连接在第一个路由器的下面,和第二个路由器(宿主机)平级,其IP为192.168.12.113,现在它想ping通172.17.0.2这个Docker容器,发现是ping不通的。同样第一台路由器自己也是ping不通Docker容器的

原因很简单,这台新计算机只能ping通同网段的设备,比如路由器2,因为他们同属于192.168.12.1/24这个网段。而172.17.0.2/16这个网段它并不知道怎么路由过去,只能把目标地址为172.17.0.1/16的数据包发给路由器一,可惜就连第一个路由器也不知道怎么个路由法。所以我们就ping不通了。

所以现在问题就很好解决了,我们只需要告诉这台新电脑或者第一个路由器到172.17.0.2/16这个网段的路径就可以了。

于是我们可以在新电脑或者路由器一中这样写

route add -net 172.17.0.1/16 gw 192.168.12.107
或者是
ip route add 172.17.0.1/16 via 192.168.12.107

普通路由器可以像这样设置

现在新电脑访问172.17.0.2的数据包就会先被发送到宿主机(第二个路由器)上,然后宿主机再转发到Docker容器上,我们就把Docker容器暴露到局域网里了。

但此时你会发现你在新计算机上还是ping不通,这是为什么呢。因为路由器二(宿主机)对它的内网机器也就是Docker容器们全部开启了NAT,源IP为172.17.0.2/16的数据包不会出现在宿主机以外的网络中,因为他们被NAT了。这个NAT是Docker进程默认自动帮我们实现的,我们先看一下

alex@alex-Lenovo-U310:~$ sudo iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCALChain INPUT (policy ACCEPT)
target     prot opt source               destination         Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCALChain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:53
MASQUERADE  udp  --  172.17.0.2           172.17.0.2           udp dpt:53Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  0.0.0.0/0            0.0.0.0/0
DNAT       tcp  --  0.0.0.0/0            127.0.0.1            tcp dpt:5053 to:172.17.0.2:53
DNAT       udp  --  0.0.0.0/0            127.0.0.1            udp dpt:5053 to:172.17.0.2:53

注意那句MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0会导致所有172.17.0.0/16的数据包都不能到达docker以外的网络,所以我们要关掉这个NAT,关掉很容易,我们只需删掉这一条iptables规则就可以了。然后源IP为172.17.0.2的数据包就可以出现在192.168.12.1/24的网络中了。
sudo iptables -t nat -D POSTROUTING 3

但是把NAT关掉了以后,虽然内网可以互ping了,但是Docker容器可能上不去网呀。第一个路由器如果自动NAT 了172.17.0.2还好,但要是没有人给Docker容器做NAT,Docker容器就不能上网了,那我们的CDNS也就没法用了。那么如何既保证Docker容器访问外网的数据包被NAT,又保证内网通信不被NAT呢?只要稍微修改一下iptables规则就好了,如下

sudo iptables -t nat -A POSTROUTING -s 172.17.0.2 ! -d 192.168.12.1/24 -j MASQUERADE

上面的iptables规则通过对内外网流量的分离实现区别的NAT对待,就可以既保证Docker容器正常上网,也可以被内网其他主机访问了。
可能会有这么一种情况,上面所说的第一台路由器不是什么智能路由器,或者你没有权限在那个路由器上配置路由条目,让目标为172.17.0.0/16的数据包通过路由器进行路由。同时你的局域网其他电脑是XP系统的,也没法手动配置路由规则,这该怎么办呢?

方法一:

现在以要访问Docker容器的局域网主机为Windows XP系统为例,虽然WinXP不能手动配置路由规则,但是我们可以配置网关,只要我们把网关设置为192.168.12.107也就是我们的宿主机,目标地址为172.17.0.0/16的IP包就会发送到宿主机上,而宿主机不同于第一个路由器,它是知道如何路由这些IP包的。于是我们就可以在WinXP上ping通Docker容器了

方法二:

如果你不想修改windows的网关(怕上不去网)可以为windows系统单独设置172.17.0.1/24的目标地址的数据包路由到192.168.12.107,可以使用下面的命令

route -p add 172.17.0.1 MASK 255.255.255.0  192.168.12.107 METRIC 3

Docker容器通过独立IP暴露给局域网的方法相关推荐

  1. 为docker容器设置独立ip

    docker 1.12使用新版macvlan设置与宿主机同网段ip 由于开发的一些特殊需求,需要将容器部署在与宿主机同一个网段中,部署步骤如下: 步骤一:创建网络 $ docker network c ...

  2. 查看docker容器占用的ip

    转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/81119863 本文出自[我是干勾鱼的博客] 查看某个docker容器占用的ip, ...

  3. 如何手动给Docker容器设置静态IP

    2019独角兽企业重金招聘Python工程师标准>>> 要点: 1.首先需要在宿主机上虚拟出来一个真实可用桥接网卡比如br0 2.docker启动的时候默认使用br0进行桥接网络 3 ...

  4. 为Docker容器设置静态IP

    此文已由作者袁欢授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 创建docker容器 docker run -it --name=yh -h yh --net=none de ...

  5. 如何获取 docker 容器(container)的 ip 地址

    目录 1. 进入容器内部后 2.使用命令 3.可以考虑在 ~/.bashrc 中写一个 bash 函数: 4.要获取所有容器名称及其IP地址只需一个命令. 5.显示所有容器IP地址: 1. 进入容器内 ...

  6. 解决 Nacos 服务注册使用 Docker 容器内网 ip 问题

    一.问题 使用 Docker 集群部署的 jar 包启动时,注册到 nacos 上的 ip 会使用 docker 的内网 ip 作为注册地址,导致多台服务器部署服务时,nacos 监听到的服务永远只有 ...

  7. docker容器时区与宿主机不一致的解决方法

    一般情况下,我们的宿主机服务器的时区都是在东八区,而docker容器里却是在零时区.这样就导致了时间差了8个小时,所以需要把容器的时间也设置成东八区. 在dockerfile中的设置如下: RUN l ...

  8. DOCKER默认虚拟网卡IP地址与局域网冲突解决

    一.背景 docker启动时默认会创建一个docker0网桥,它在内核层连通了其他的物理或虚拟网卡,相当于将所有容器和其主机都放到同一个网络.但是部署在内网中的IP段存在有此网段的IP时,会导致冲突, ...

  9. 获取 docker 容器(container)的 ip 地址

    获取单个IP docker inspect --format '{{ .NetworkSettings.IPAddress }}'  <container-ID> 获取所有容器IP doc ...

最新文章

  1. jQuery中$(function(){})与(function($){})(jQuery)的区别
  2. iOS学习资源(二)
  3. 趋势科技:黑客勒索瞄准智能电视
  4. leetcode算法题--构建乘积数组
  5. SQL中的撤销索引、表以及数据库
  6. 腾讯游戏学院专家分析:Unity在移动设备的GPU内存机制
  7. 企业日志分析 五大问题需重点注意
  8. Session过期,如何跳出iframe框架页的问题
  9. 董明珠自媒体:格力口罩今日开售 上午预约下午抢购
  10. 汇丰银行是哪个国家的
  11. Python中使用tarfile压缩、解压tar归档文件
  12. 机器人把大龙拉出来_LOL-LPL夏季赛:Wei男枪打野未换惩戒 ES让BLG拿下首胜
  13. 详解麦肯锡方法:结构化分析问题的技术
  14. javac编译带有package的java文件
  15. VulnHub - Pluck靶机
  16. 迅睿cms 项目信息自定义字段调用
  17. 联想小新Pad Pro 2021平板(TB-J716F) 解BL锁ROOT过程命令记录
  18. 【IoT开发】D3引擎升级啦!速速来体验机智云新版智能场景推送
  19. matlab surf 坐标设置,matlab中3D曲面函数surf的坐标问题
  20. matlab解对流方程初值问题,对流方程——偏微分方程的数值解法

热门文章

  1. P4166 [SCOI2007]最大土地面积
  2. CCNA学习笔记--静态路由
  3. ASP.NET:为 AJAX 请求添加客户端事件
  4. [Node.js]Restful Api
  5. 以application/json 方式提交 然后用在php中读取原始数据流的方式获取 在json_encode...
  6. 局域网中,ip可以访问其他计算机,“网络”中无法发现共享计算机,也无法通过主机名访问。...
  7. 现代软件工程 团队博客作业 如何评价个人在团队中的绩效
  8. CYQ.Data 轻量数据层之路 使用篇五曲 MProc 存储过程与SQL(十六)
  9. LTE上行加扰的目的是什么?
  10. node.js项目应用