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

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.0
   inet6 地址: fe80::42:cdff:fe21:5c81/64 Scope:Link
   UP 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.0
   inet6 地址: ::1/128 Scope:Host
   UP 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.0
   inet6 地址: fe80::8adf:28f7:5ec:3a5d/64 Scope:Link
   UP 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.0
   inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
   UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
   RX packets:3449 errors:0 dropped:0 overruns:0 frame:0
   TX packets:2811 errors:0 dropped:0 overruns:0 carrier:0
   collisions: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.0
   inet6 addr: ::1/128 Scope:Host
   UP LOOPBACK RUNNING MTU:65536 Metric:1
   RX packets:0 errors:0 dropped:0 overruns:0 frame:0
   TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
   collisions: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 LOCAL
Chain 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 LOCAL
Chain 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:53
Chain 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.0.107也就是我们的宿主机,目标地址为172.17.0.0/16的IP包就会发送到宿主机上,而宿主机不同于第一个路由器,它是知道如何路由这些IP包的。于是我们就可以在WinXP上ping通Docker容器了

以上所述是小编给大家介绍的Docker容器通过独立IP暴露给局域网的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 详解Docker 容器互联方法

    Docker容器都是独立的,互相隔离的环境.然而,它们通常只有互相通信时才能发挥作用. 虽然有许多方法可以连接容器们,可是我将并不会试着去将其全部讨论在内.但是在这一系列的方法中,我们将看看那些常用的做法. 虽然看起来是很浅显,但是这对于与Docker成天打交道的朋友来说,理解这些技术及底层的设计理念就显得非常地重要了. 理解这些主题将会: 帮助开发和运维人员探索广泛的容器部署的选择. 让开发和运维人员更自信的着手于微服务microservice架构设计. 让开发和运维人员可以较好的编排更复杂的

  • 使用Grafana 展示Docker容器的监控图表并设置邮件报警规则(图解)

    一.Docker 容器监控报警方式 接着上篇文章的记录,看到grafana的版本已经更新到4.2了,并且在4.0以后的版本中,加入了Alert Notifications 功能,这样在对容器 监控完,可以加入报警规则.根据官网介绍,报警方式也有很多种,常见的Email.Slack即时通讯.webhook等. 本篇记录的是邮件的报警设置.环境和上篇基本一致,都是在Docker 平台测试环境下,另外本篇使用的grafana容器的版本是用的 dockerhub上最新版本,该版本为grafana/gra

  • Docker定制容器镜像的2种方法(推荐)

    一.需求 由于在测试环境中使用了docker官网的centos 镜像,但是该镜像里面默认没有安装ssh服务,在做测试时又需要开启ssh.所以上网也查了查资料.下面详细的纪录下.在centos 容器内安装ssh后,转成新的镜像用于后期测试使用. 二.镜像定制 第一种方式(手动修改容器镜像) 1.先下载centos镜像 [root@docker ~]# docker pull centos 2.启动容器并进行配置 启动容器, [root@docker ~]# docker run -it -d --

  • 详解docker容器硬盘动态扩容

    扩容容器 默认来说,如果你使用 Device Mapper 的存储插件,所有的镜像和容器是从一个初始 10G 的文件系统中创建的.让我们来看看如何从一个更大的文件系统中创建一个容器. 首先,我们用 Ubuntu 的镜像来创建我们的容器.我们不需要在这个容器里运行任何东西,只需要这个文件(或者关联的文件系统)存在.为了演示,我们会在这个容器里运行 df ,来看一下根文件系统的大小. $ docker run -d ubuntu df -h / 4ab0bdde0a0dd663d35993e4010

  • Docker 中的容器完全解析

    Docker 中的容器完全解析 Docker中的容器可以看成是镜像的一个运行环境,它带有额外的可写文件层. 一.创建容器: 1.新建容器: docker create -it --name [CONTAINERNAME] [NAME]:[TAG] 比如: docker create -it --name container ubuntu:add /bin/bash 此为根据镜像的名称创建容器,容器的名称为container 2.查看容器详情列表: docker ps -a 可以查看到容器的ID,

  • Docker 技巧之删除Docker容器和镜像

    公司业务在生产环境100多台服务器上用了docker,已经有大半年了,可是最近发现,每个服务器上的各种镜像好多好乱,就想批量删除镜像,需要的来看一下把. 删除所有未运行 Docker 容器 docker rm $(docker ps -a -q) 删除所有 Docker 镜像 删除所有未打 tag 的镜像 docker rmi $(docker images -q | awk '/^<none>/ { print $3 }') 删除所有镜像 docker rmi $(docker images

  • docker容器跨服务器的迁移的方法

    docker的备份方式有export和save两种. export是当前的状态,针对的是容器,docker save 是针对镜像images. export 找出要备份容器的ID [root@wls12c ~]$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES 037b847bf093 centos "/bin/bash" 3 minutes ago Exited (0) 2 minute naughty_dav

  • 详解docker容器间通信的一种方法

    以我的ghost博客为例进行说明,我在VPS上用docker启动了两个ghost博客,还有一个Nginx做反向代理,将两个域名分别指向两个博客. docker启动命令 ghost: docker run -e NODE_ENV=production --name ghost1 -v /path/to/data/ghost/ghost1/:/var/lib/ghost -d ghost docker run -e NODE_ENV=production --name ghost2 -v /path

  • Docker容器中运行nginx

    nginx简介 Nginx是一款面向性能设计的HTTP服务器,相较于Apache.lighttpd具有占有内存少,稳定性高等优势.与旧版本(<=2.2)的Apache不同,nginx不采用每客户机一线程的设计模型,而是充分使用异步逻辑,削减了上下文调度开销,所以并发服务能力更强.整体采用模块化设计,有丰富的模块库和第三方模块库,配置灵活. 在Linux操作系统下,nginx使用epoll事件模型,得益于此,nginx在Linux操作系统下效率相当高.同时Nginx在OpenBSD或FreeBSD

  • Docker容器通过独立IP暴露给局域网的方法

    Docker容器非常轻量,系统开销非常少,比VMware或者VirtualBox用起来方便,部署起来也非常容易.官方推荐我们通过端口映射的方式把Docker容器的服务提供给宿主机或者局域网其他容器使用.一般过程是: 1.Docker进程通过监听宿主机的某个端口,将该端口的数据包发送给Docker容器 2.宿主机可以打开防火墙让局域网其他设备通过访问宿主机的端口进而访问docker的端口 这里以CDNS为例,CDNS是一个用于避免DNS污染的程序,通过CDNS可以把你的计算机变成一个抗污染的DNS

  • Docker容器开jupyter不能访问到的解决方法

    在本项目中使用了docker容器搭建环境,Dockerfile 如下: FROM tensorflow/tensorflow:1.14.0-gpu-py3 RUN pip install scipy==1.3.3 RUN pip install requests RUN pip install Pillow RUN pip install matplotlib RUN pip install pandas RUN pip install keras==2.2.4 RUN pip install

  • Docker容器迁移到其他服务器的5种方法详解

    迁移在许多情况下都是不可避免的.硬件升级.数据中心变化.过时的操作系统,所有这些都可能成为迁移的触发点. Docker容器迁移通常是迁移任务的一部分.今天我们将看到将Docker容器从现有服务器迁移到另一台服务器的不同方法. 如何将Docker容器迁移到另一台服务器,没有直接将Docker容器从一台服务器迁移到另一台服务器的方法,我们通过使用下面这些方法中的一个或多个来解决Docker容器迁移的问题. 1.导出和导入容器 导出容器意味着从容器的文件系统创建压缩文件,导出的文件保存为"gzip&q

  • Docker容器如何查看ip地址的实现方法

    一直以为Docker是没有IP地址的,其实Docker的网络模板有点类似我们平常使用虚拟机的host-only模式,容器和宿主机组成一个独立的局域网,宿主机的IP为172.17.0.1,对应主机的网络名称为docker0 所以要想看到docker容器的ip地址,只需要安装net-tools就可以了 yum install net-tools -y 之后就可以用ifconfig查看了 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们.

  • 详解docker容器分配静态IP

    最近因为工作要求需要用学习使用docker,最后卡在了网络配置这一块.默认情况下启动容器的时候,docker容器使用的是bridge策略比如: docker run -ti ubuntu:latest /bin/bash 等效于 docker run -ti --net=bridge ubuntu:latest /bin/bash bridge策略下,docker容器自动为我们分配了一个IP地址,并连接到docker0的网桥上.但这里有一个问题,这个IP地址并不是静态分配的,这对我们的对容器的实

  • 详解如何解决docker容器无法通过IP访问宿主机问题

    问题起源 在使用 docker 的过程中我不幸需要在 docker 容器中访问宿主机的 80 端口, 而这个 80 端口是另外一个容器 8080 端口映射出去的. 当我在容器里通过 docker 的网桥 172.17.0.1 访问宿主机时, 居然发现: curl: (7) Failed to connect to 172.17.0.1 port 80: No route to host 查找问题原因 可以确定的是容器与宿主机是有网络连接的, 因为可以在容器内部通过 172.17.0.1 Ping

  • 详解如何获取docker容器(container)的ip地址

    1. 进入容器内部后 cat /etc/hosts 会显示自己以及(– link)软连接的容器IP 2.使用命令 docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container-ID> 或 docker inspect <container id> 或 docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' c

  • Docker容器和本机之间的文件传输方法

    主机和容器之间传输文件的话需要用到容器的ID全称. 获取方法如下: 1.先拿到容器的短ID或者指定的name. 2.然后根据这两项的任意一项拿到ID全称. 有了这个长长的ID的话,本机和容器之间的文件传输就简单了. docker cp 本地文件路径 ID全称:容器路径 进入容器之后就能够看到刚才上传进来的文件了. 如果是容器传输文件到本地的话,反过来就好了: docker cp ID全称:容器文件路径 本地路径 进行挂载的话可以参考这篇:利用Volume在主机和Docker容器文件传输. 以上就

  • Docker容器绑定外部IP和端口的方法

    Docker允许通过外部访问容器或者容器之间互联的方式来提供网络服务. 以下操作通过myfirstapp镜像模拟,如何制作myfirstapp镜像请点击此处. 1.外部访问容器 容器启动之后,容器中可以运行一些网络应用,通过-p或-P参数来指定端口映射. a.用-P(大写)标记时,docker会随机选择一个端口映射到容器内部开放的网络端口上. $ docker run -d -P myfirstapp python app.py $ docker ps CONTAINER ID IMAGE CO

  • 解决Docker容器时区及时间不同步问题的方法

    今天在系统集成测试时由测试人员提交了一个测试bug,原因是提交业务数据时间与实际时间(北京时间)有偏差,导致统计异常.由于我们集成测试是向测试人员直接提供完整的Docker镜像作为测试环境,原因应该是出在容器时间设置上. 拿到交付的docker镜像后,启动后进入容器控制台,使用date命令查看果然时间不正确.再查看宿主机时间是正确,这样肯定是容器启动时未将时区与宿主机保持同步了,由于测试镜像是由dockfile直接构建,因此问题基本了定准在dockerfile文件上了. 打开dockerfile

随机推荐