Docker容器端口映射后Hadoop突然无法连接
一、背景
一般需要对外提供服务的Docker容器,我们在启动时后使用-p命令将对外访问端口暴露给外部,故在创建容器是,我们将Hadoop的端口隐射出来供外部访问:
docker run -itd --privileged --name singleNode -h singleNode \
-p 2222:22 \
-p 3306:3306 \
-p 8020:8020 \
-p 9870:9870 \
-p 19888:19888 \
-p 8088:8088 \
-p 9083:9083 \
-p 10000:10000 \
-p 2181:2181 \
-p 9092:9092 \
-p 8091:8091 \
-p 8080:8080 \
-p 16010:16010 \
-p 4000:4000 \
-p 3000:3000 \
centos:7 /usr/sbin/init
# 其中端口号解释
2222:22# SSH
3306:3306 #MySQL
8020:8020 # HDFS RPC
9870:9870 # HDFS web UI
19888:19888 # Yarn job history
8088:8088 # Yarn web UI
9083:9083 # Hive metastore
10000:10000 # HiveServer2
2181:2181 # zk
9092:9092 # kafka
8091:8091 # flink
但最近碰到一个非常奇怪的情况:在一个CentOS 7测试环境里部署有Docker Hadoop,并对外暴露了端口。启动容器后一段时间内都是可以正常工作的,但在不定时间间隔后,Hadoop的web端就访问不了:
原本以为是我们的Hadoop服务没有正常启动,但是Jps查看时,却发现正常:
至于这个问题,只有手动重启出问题的Docker,然后在重启Hadoop服务后,外部才可以重新访问,但只要再过一段时间又会出现这样的问题。
二、问题排查
情况一:开着防火墙但没有开放端口
CentOS 7自带并启用了防火墙FirewallD,我们可以通过下面的命令检查FirewallD的状态:
firewall-cmd --state
如果输出的是“not running”则FirewallD没有在运行,且所有的防护策略都没有启动,那么可以排除防火墙阻断连接的情况了。
如果输出的是“running”,表示当前FirewallD正在运行,需要再输入下面的命令查看现在开放了哪些端口和服务:
firewall-cmd --list-ports
firewall-cmd --list-services
可以看到当前防火墙只开放了ssh服务(22/tcp)和dhcpv6-client服务,并没有打开Docker容器映射的Hadoop端口。
解决方案有两种:
1.关闭FirewallD服务:
如果您不需要防火墙,那直接关掉FirewallD服务就好了
systemctl stop firewalld.service
但是在部署Hadoop服务之前,就已经将firewalld服务禁用了,故此问题的根源不在这
2.添加策略对外打开指定的端口:
比如我们现在要打开对外9870/tcp端口,可以使用下面的命令:
firewall-cmd --add-port=9870/tcp --permanent
firewall-cmd --reload
如果只是临时打开端口,去掉第一行命令中的“–permanent”参数,那么当再次重启FirewallD服务时,本策略将失效。 此命令不建议执行,毕竟Hadoop服务端口那么多…
情况二:没有启用IP_FORWARD
因为一直没法定位出问题的所在,所以不能正常访问HadoopWeb端时,手动登陆宿主机重启Docker,再重启 Hadoop服务。
在有一次登录到宿主服务器上准备重启Docker daemon服务前,我突然想起之前在用Docker的时候还碰到过另一个问题:如果宿主机没有启用IP_FORWARD功能,那Docker容器在启动时会输出一条警告消息:
WARNING: IPv4 forwarding is disabled. Networking will not work.
会不会是因为宿主机的IP_FORWARD功能没有启用所以才引起的这个故障呢?
sysctl net.ipv4.ip_forward
因为,我这里是设置过的,原本的结果为:net.ipv4.ip_forward = 0,表示当前系统的IP_FORWARD功能处于停用状态!
可是问题来了,当时启动容器的时候都是好的啊,什么都没有输出,怎么用着用着IP_FORWARD功能就被禁用了呢?
Docker daemon服务在启动的时候会自动设置iptables设置,难不成它还会检查IP_FORWARD设置,并帮我临时启用吗?
带着这个假设,我手动重启了一下Docker Hadoop服务
[root@singlenode /]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0
[root@singlenode /]# systemctl restart docker
[root@singlenode /]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
果然,Docker 服务在启动过程中会检查系统的IP_FORWARD配置项,如果当前系统的IP_FORWARD功能处于停用状态,会帮我们临时启用IP_FORWARD功能,然而临时启用的IP_FORWARD功能会因为其他各种各样的原因失效…
问题找到,至于修复方案倒非常简单,只要一行命令就可以了:
echo 'net.ipv4.ip_forward = 1' >> /usr/lib/sysctl.d/50-default.conf
执行完成后,重启服务器或使用下面的命令从文件中加载配置:
sysctl -p /usr/lib/sysctl.d/50-default.conf
就OK了。Hadoop正常访问咯!
三、小结
Docker 服务在启动的时候会帮帮我们调整很多的配置项,比如这次出事儿的IP_FORWARD配置。
Docker 启用IP_FORWARD功能是因为Docker容器默认的网络模式(bridge/网桥模式)会给每个容器分配一个私有IP,如果容器需要和外部通信,就需要使用到NAT。NAT需要IP_FORWARD功能支持,否则无法使用。这也解释了为什么会出现在IP_FORWARD功能停用的情况下,使用bridge模式的容器内外均无法访问的情况。
只是在Linux下,出于安全考虑,默认是停用IP_FORWARD功能的,Docker 服务在启动时会检查IP_FORWARD功能是否已经启用,如果没有启用的话,Docker 会悄无声息的临时启用此功能,然而临时启用的IP_FORWARD功能并不能持久化,会因为其他命令的干扰导致失效。
Author:洋群满满