大清早起来一看手机,发现监控推送的告警,网站挂了!淡定煎蛋、冲咖啡,开始排错。
监控显示,发现故障发生在 2024 年 5 月 21 日 22 点 41 分 19 秒。
站点状态:
先说下网站的运行环境:
主机:AlmaLinux
应用中间件均为 Docker 部署:PHP、Redis、OpenResty、RDS-MySQL
版本我都全盘脱出了,希望大佬们不要针对来搞我。
排查思路
- 排查数据库状态是否正常
- 检查PHP状态是否正常
- 检查OpenRsty状态是否正常
- Redis
1、数据库
数据库使用的是阿里云的 RDS-MySQL8;
云管控制台未发现告警,通过站点云主机远程登录数据库正常;
另一站点也是用的此数据库,可正常访问。
故排除数据库故障。
2、PHP
docker ps 查看容器运行状态正常;
查看日志无异常,只是故障时间点请求PHP时超时,无法连接;
估计容器网络有些问题,继续向下排查。
3、OpenResty
检查了OpenResty的容器日志,相关时间节点无报错;
网站WordPress error.log日志无报错;
网站WordPress access.log故障时间节点无法被访问
未发现问题。
4、Redis
Redis 只做缓存,故不考虑。
嗯???
麻了,排查了一遍,均为发现什么问题,那是怎么回事儿呢?
因为网站报错,Error establishing a database connection,此错误是 PHP 无法连接数据库,所以还是把问题定位到 PHP,(是不是PHP有什么bug,导致无法连接数据库了,重启大法?)于是尝试重启PHP容器看是否可以解决。
docker restart php8
居然报错了,无法重新启动,详细报错如下:
服务内部错误:
stderr: Container 1Panel-php8-dC9R Restarting Error response from daemon: Cannot restart container a154463a11dd19ce26527fe69abf78e3c2887463c415e94228f4412c395436c1:
driver failed programming external connectivity on endpoint 1Panel-php8-dC9R
(2a71d44d9496f8a05cfac440bba22a72cc378bca0d75c305927ef852ced8da70):
(COMMAND_FAILED: ‘/usr/sbin/iptables -w10 -t nat -A DOCKER -p tcp -d 127.0.0.1 –dport 9000 -j DNAT –to-destination 172.19.0.2:9000 ! -i br-002aa63fa594’ failed: iptables: No chain/target/match by that name. )
一眼看出,重点在这:
COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -A DOCKER -p tcp -d 127.0.0.1 --dport 9000 -j DNAT --to-destination 172.19.0.2:9000 ! -i br-002aa63fa594' failed: iptables: No chain/target/match by that name.
这什么东西,查不到这个“链”了?怎么会突然消失呢?
于是开始了一番查阅,发现是Firewalld和Docker冲突;
众所周知,docker是默认会自动创建网桥,并使用网桥桥接上网,一般将此网桥自动名为docker0,它会通过iptables来管理它的容器之间的通信和容器与宿主机的通信,又众所周知,Firewalld配置的规则,最终也是使用iptables来管理,并且共同使用 nat
表和 filter
表,所以Firewalld的重载可能会清除覆盖掉docker创建的规则。
查看Firewalld日志:
May 22 09:10:58 wanwan firewalld[184557]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -A DOCKER -p tcp -d 127.0.0.1 --dport 9000 -j DNAT --to-destination 172.19.0.2:9000 ! -i br-002aa63fa594' failed: iptables: No chain/target/match by that name.
确实,添加规则失败了,所以容器无法启动。
尝试把Firewalld关掉,再重启容器,重启成功,网站也可以正常访问了。
ps:但是,确实没人重载docker或者Firewalld配置,至于为啥会突然网络不通了,是不是策略被覆盖、失效,懂的大佬可以评论区指导下。
如何在开启Firewalld的情况下解决此问题呢
其实这个问题在docker的官方文档里也是有记录的:Packet filtering and firewalls | Docker Docs
在Docker 20.10.0后的版本也对此问题进行了解决,但是我的Docker为什么都26.1.1版本了,还是出现了这个问题呢?
可能的原因:(猜测)
- 我是在安装完Docker后才启用的Firewalld,可能在Docker启动过程中,没有检测到Firewalld,所以没进行处理;(猜对了)
- Docker默认的网桥名为docker0,但是我并没有使用它,我使用了自己创建的br-002aa63fa594,并不会把后期自定义的网桥加入进去。
那就要说说Docker是如何在20.10.0后解决的此问题了:
Docker 在启动时会自动检测 firewalld 是否正在运行,如果检测到 firewalld 运行,它会创建一个名为 docker 的区域(zone),并在该区域中应用 Docker 需要的 iptables 规则。
可能在部署完容器后,也没有重启过docker,所以才一直没有将网卡自动加入到docker zone中吧。
可以看出确实有docker zone,但是services中是空的,表明br-002aa63fa594和docker0网桥并没有在其中。
不想直接重启docker服务?那咱就手动添加:
查询了一下现在活动中的zone,只有public,并没有docker
执行:
firewall-cmd --zone=docker --add-interface=br-002aa63fa594 --permanent
firewall-cmd --reload
firewall-cmd --get-active-zones
firewall-cmd --list-all-zones
网桥已经被加入到docker zone中,再起尝试重启php容器;
(重启成功,网站可以正常访问,Firewalld日志无报错。)
问题解决!
猜想验证
重启docker服务,看看docker0网桥会不会自动加入到docker zone中。
停止所有容器、重启docker:
docker stop $(docker ps -q)
systemctl restart docker
确实docker0被加进来了,“可能在部署完容器后,也没有重启过docker,所以才一直没有将网卡自动加入到docker zone中吧。”猜想正确 哈哈哈。
其实
其实,我觉得,如果使用的云主机,没有特殊需求,只控制虚机流量的出入控制,完全不用开启Firewalld,直接disable,毛事儿没有,也不用折腾,云平台的安全组和ACL基本可以满足绝大部分需求了。
评论前必须登录!
立即登录 注册