Docker网络与虚拟机挂起问题

在使用Docker和虚拟机的过程中,我遇到了一个棘手的问题:在恢复挂起的虚拟机后,宿主机原本能够访问的MySQL和Nacos服务突然无法访问了。于是,我开始了一场紧张刺激的排查之旅。

问题排查过程

初步排查

在排查过程中,我首先检查了MySQL和Nacos容器的网络配置。通过以下命令,我发现Nacos容器使用了Docker的默认网络bridge,而MySQL容器则没有:

[root@heima ~]# docker inspect mysql -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
heima-net
[root@heima ~]# docker inspect nacos -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
bridge heima-net

于是,我尝试将MySQL容器也连接到bridge网络,结果发现确实可以访问了:

[root@heima ~]# docker network connect bridge mysql

但它们都连接了heima-net网络,理论上应该可以通信。当时我被一些理论知识误导,没有发现真正的问题所在。于是,我将所有容器都连接到了bridge网络,确实可以通信了。但这样太麻烦了,而且之前它们能够正常工作,说明问题出在其他地方。

进一步排查

经过一系列排查,我发现问题可能出在网络传输上。在添加了以下iptables规则后,问题奇迹般地解决了:

sudo iptables -I DOCKER-USER 1 -d 172.18.0.2 -p tcp --dport 3306 -j ACCEPT

但后来我发现,这并不是网络传输的问题。经过复现问题,我猜测之前乱搞connect连接可能把iptables规则搞乱了。即使断开了连接,端口映射也会有残留的影响。而iptables指令触发了规则重新加载,从而修复了问题。

深入排查

为了找到更根本的解决方法,我分别在虚拟机挂起恢复前后检查了网桥的状态:

  • 挂起恢复后,重启Docker前检查:

    ip link show | grep -E "(docker|br-|veth)"
    
    ip addr show docker0
    ip addr show br-73a00eba170c
    
    iptables -t nat -nL DOCKER | head -10
    

    挂起后的网桥状态如下:

    ip addr show br-73a00eba170c
    617: br-73a00eba170c: ... state UP ...
        link/ether 02:42:10:e3:ad:9b brd ff:ff:ff:ff:ff:ff
        # 缺少 IPv4 地址!只有 MAC 地址
    
  • 重启Docker恢复后:

    ip addr show br-73a00eba170c  
    617: br-73a00eba170c: ... state UP ...
        link/ether 02:42:10:e3:ad:9b brd ff:ff:ff:ff:ff:ff
        inet 172.18.0.1/16 brd 172.18.255.255 scope global br-73a00eba170c  # ← 有这个!
    

通过对比,我发现问题的真正原因:虚拟机挂起时,自定义网络网桥br-73a00eba170c的IP地址配置丢失了!虽然网桥接口本身还在,但失去了172.18.0.1/16这个网关IP,导致宿主机无法路由到172.18.0.0/16这个网段。

解决方法

为了解决这个问题,我通过以下命令重新添加了这个网段:

sudo ip addr add 172.18.0.1/16 dev br-73a00eba170c

为了避免每次手动修复,我创建了一个修复脚本:

if ip addr show br-73a00eba170c | grep -q "172.18.0.1"; then
    echo "Network already fixed"
else
    ip addr add 172.18.0.1/16 dev br-73a00eba170c
    echo "Docker network fixed"
fi

此外,还可以通过编写systemd服务来让脚本自动执行。

后续问题及预防措施

第二天,我的虚拟机又出现了问题。通过journalctl -u docker.service查看日志,发现Docker在启动时发现了一些陈旧的(stale)网络沙盒和端点,并且在清理过程中遇到了冲突。这次问题比较严重,补充网络IP或重启Docker都无法解决问题,我只能重启虚拟机。

这可能是因为虚拟机挂起时Docker的网络清理不完全导致的。为了避免类似问题再次发生,我在挂起虚拟机之前执行了以下命令,将所有Docker容器停掉:

sudo docker stop $(sudo docker ps -q)

总结

通过这次经历,我深刻认识到虚拟机挂起可能会导致Docker网络配置丢失,从而引发一系列问题。虽然目前还不清楚为什么虚拟机挂起会导致网段丢失,但通过创建修复脚本和在挂起前停掉Docker容器,可以有效避免这些问题的发生。

更多推荐