记一次docker容器网络问题排查处理过程

  • 内容
  • 评论
  • 相关

缘起

公司php项目都是微服务化架构,并且采用的都是docker容器化部署,但是centos7上的防火墙firewalld一直与docker的默认网络有冲突,docker容器内部网络也稍有点复杂,加上阿里云的安全组使用起来也挺方便的,所以之前一直都是关闭掉firewalld然后使用阿里云的安全组来做网络防护。由于最近公司的服务器越来越多,每次开放服务端口都要登录阿里云去手工操作一番,着实麻烦。这两天只好硬着头皮把firewalld开起来着手解决网络问题。

问题描述

开启firewalld防火墙后,docker容器无法通过域名访问公网外网资源,无法访问局域网内资源,同宿主容器或者同局域网不同宿主容器之间互相访问会提示No route to host。

问题分析与解决过程

网络连接中出现No route to host一般情况下有两种原因:

1、两个网络真的不通。

2、防火墙drop掉了数据包

首先在容器内ping一下baidu.com,依然输出No route to host,再ping一下internal.device.api.com(内网的某个服务的域名),结果也是一样的,然后直接ping百度以及内网服务的对应服务器的ip,结果居然是通的。这表明是dns有问题,进一步测试,进入容器内执行 dig @172.17.0.1 baidu.com 请求超时,退出当前容器检查dns容器的运行状态是否正常,结果是正常的不能再正常了。然后在宿主主机上 dig @127.0.0.1 baidu.com,服务响应正常。从这个测试可以看出是容器之间的数据包全被drop掉了,既然找到问题了那么就很好解决了。直接把firewalld默认zone的对应服务端口打开就行了。直接在docker宿主上执行以下命令:

sudo firewall-cmd --permanent --add-port=53/tcp
sudo firewall-cmd --permanent --add-port=53/udp
sudo firewall-cmd --reload

然后再次进入到容器内部直接ping域名,这次回显的结果是正常响应的(其实之前显示的No route to host是因为内网dns的查询数据包被drop的原因,和服务是没有关系的)。但是这样也有两个比较严重的问题:

1、如果在firewalld默认zone上打开端口那么,每次每个服务开通不同的端口就要重新在firewalld上打开对应端口。

2、默认zone上打开的端口外部网络也是可以直接访问到的,这样一些不想让外网访问的服务资源也会被暴露在外网公网之中,安全隐患非常大。

那么有没有一个一劳永逸而且又两全其美的解决方案呢?答案是肯定的,大家都知道firewalld是根据不同的划分区域(即前面说的zone)来执行不同的数据包数据包筛选规则的(奶嘴之前有篇简单介绍firewalld的文章:传送门)。那么我们可以将docker的全部数据都划分到一个信任的区域中即可。这样就能达到docker容器之间网络无障碍但是外部网络依然遵守默认firewalld规则的效果,具体命令如下:

// 查看docker容器ip段,名称为docker0的网络即是
ip addr
// 将docker容器的ip段添加至trusted信任区
sudo firewall-cmd --permanent --zone=trusted --add-source=172.17.0.1/16
// 重载防火墙规则
sudo firewall-cmd --reload

然后删除掉之前在默认区域开放的端口,再次进入容器测试网络,全部正常响应。

处理完同宿主docker容器的网络互通问题后,部分不同宿主的容器之间相互访问又开始出现异常了,具体表现为ping 内网其他docker宿主VM时网络是通的,直接curl http://ip 也是正常的,但是curl http://ip:[服务端口] 的时候部分服务的端口响应的却是“No route to host” ,这样就很匪夷所思了。

为什么同一个宿主VM为什么有些服务端口是正常的有些服务端口是异常的呢?并且这些服务暴露在公网上又是正常的。

既然不知道问题是怎么产生的那就一点一点排除吧,首先docker inspect查看nginx容器的网络配置,然后再对比另外一个访问出现异常的服务的配置,发现nginx的network模式为host,而出问题的那个容器的network模式为bridge,再多对比几个正常的服务和异常的服务,发现所有正常的服务网络模式都是host,由此可知,docker容器在局域网内通过局域网ip访问其他算宿主VM上的容器,如果容器使用的是bridge模式来暴露的服务则从宿主到docker容器的这一跳网络是不通的。(docker的网络模式可以参考官网的这篇文档:docker network)

找到了问题点那么处理起来也简单了,这一跳的网络不通那么直接在这一段建立一个虚拟网桥就可以了,具体处理过程如下如下:

// 关闭docker引擎
sudo systemctl stop docker
// 关闭掉docker0网卡
ip link set dev docker0 down
// 删除docker0网桥
brctl delbr docker0
// 新增一个bridge0网桥
brctl addbr bridge0
// 设置ip段
ip addr add 192.168.1.1/24 dev bridge0
// 启动bridge0网桥
ip link set dev bridge0 up

然后用vi编辑器修改docker service文件,具体路径(/usr/lib/systemd/system/docker.service),可能因docker的版本不同而路径不同。

在ExecStart的值里面加上一句-b=bridge0,然后执行以下命令:

sudo systemctl daemon-reload
sudo systemctl restart docker

然后再次测试不同VM宿主的docker容器之间的网络访问情况,测试结果表示网络已经完全正常了。

总结

docker bridge0模式下的容器在多宿主以及同宿主容器通信的时候网络问题较多,host模式则问题较少,但是host模型容易造成同宿主的容器出现端口冲突问题。

本文搜索关键字

docker firewalld  docker防火墙 docker centos7防火墙 docker容器网络不通 docker外网不通 docker内网不通 docker公网不通 docker No route to host


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,非商业性质可转载须署名链接,详见本站版权声明。

评论

3条评论
  1. Gravatar 头像

    遗忘大佬 回复

    奶子 牛比

  2. Gravatar 头像

    封尘居 回复

    现在都是这个色儿哈哈,我们.net core微服务化一样样的。也碰到了类似问题,......我们也部署阿里云

  3. Gravatar 头像

    封尘居 回复

    折腾下下次就记住了,其实也不难

发表评论

电子邮件地址不会被公开。 必填项已用*标注