沒能躲開的雲服務容器網絡問題
遇到一個詭異的問題,在固定的 VPC 環境裏運行了一年的 ECS 機器,突然連不上 RDS 數據庫,而這個問題在早些時候,也曾在另外一臺機器上出現過。
爲了避免後續在業務日常運行和重大活動過程中出現類似問題,我們和阿里雲進行了反饋,並進行的排查。
寫在前面
由於同一業務分組的幾臺機器中,有兩臺(暫命名爲 host-pre、host-01)都出現了這個問題,所以我們將同一業務分組的幾臺機器同時作爲樣本進行問題排查。
在解決問題過程的時候,我們首先收集了基礎環境的相關線索:
- 機器運行應用均爲無狀態容器,沒有持久化內容存在。
- 被測試的機器均處於相同 VPC 環境內,爲避免容器網絡問題,2019 年初始化 VPC 時使用了比較不容易撞車的 192.168.73.x 網段。。
- 機器、數據庫都沒有關閉 ICMP。
- 機器的安全策略組允許訪問 RDS。
- 機器在數據庫白名單之內。
- 這幾臺機器都是於去年購買,在一個月前執行過系統版本和軟件升級。
- 這幾臺機器在半年前執行過硬件配置升降級。
問題狀況:連不通的數據庫
分別使用服務器對數據庫進行 ping:
ssh host-pre ping rm-intra.mysql.rds.aliyuncs.com
PING rm-intra.mysql.rds.aliyuncs.com (192.168.0.166) 56(84) bytes of data.
From host-pre (192.168.0.1) icmp_seq=1 Destination Host Unreachable
From host-pre (192.168.0.1) icmp_seq=2 Destination Host Unreachable
From host-pre (192.168.0.1) icmp_seq=3 Destination Host Unreachable
ssh host-01 ping rm-intra.mysql.rds.aliyuncs.com
PING rm-intra.mysql.rds.aliyuncs.com (192.168.0.166) 56(84) bytes of data.
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=1 ttl=102 time=1.11 ms
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=2 ttl=102 time=1.09 ms
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=3 ttl=102 time=1.12 ms
ssh host-02 ping rm-intra.mysql.rds.aliyuncs.com
PING rm-intra.mysql.rds.aliyuncs.com (192.168.0.166) 56(84) bytes of data.
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=1 ttl=102 time=0.992 ms
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=2 ttl=102 time=0.994 ms
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=3 ttl=102 time=0.977 ms
ssh host-03 ping rm-intra.mysql.rds.aliyuncs.com
PING rm-intra.mysql.rds.aliyuncs.com (192.168.0.166) 56(84) bytes of data.
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=1 ttl=102 time=1.07 ms
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=2 ttl=102 time=1.07 ms
64 bytes from 192.168.0.166 (192.168.0.166): icmp_seq=3 ttl=102 time=1.04 ms
發現只有第一臺 host-pre 出現了 Destination Host Unreachable,其餘幾臺均正常。host-01 在早些時候也出現過相同的問題。
**登錄服務器:進一步探查問題**
既然 host-pre 出現問題,我們就先來排查下它的容器運行狀況是否出現問題。登錄機器 ,忽略掉最近更新變動的應用,可以看到機器上目前運行最久的應用的啓動時間是七個月前,分別使用 exec ,以及 curl 請求本地服務,都有正常的反饋,所以首先可以排除是容器應用自身的問題。
docker ps -a
...
c092f8c5f41e docker.dev.baai.ac.cn/nesletter-api:0.9.1 "docker-php-entrypoi…" 4 months ago Up 4 months (healthy) 9000/tcp newsletter-api
ed0c7fb1945f docker.dev.baai.ac.cn/hub-node-gate:14 "docker-entrypoint.s…" 4 months ago Up 4 months 3000/tcp xxxxx_1
7aba2cbc2e21 traefik:v2.2.0 "/entrypoint.sh trae…" 5 months ago Up 5 months (healthy) 0.0.0.0:80->80/tcp traefik
1a9e0a150133 xxx:4.7.6-xxx "entrypoint.sh docke…" 7 months ago Up 7 months 8080/tcp xxx_1
接着我們試着在 host-pre 上 ping 其他的服務器,發現也是正常的,所以 VPC 網絡內的連通性也是沒有問題的,那麼問題應該是出現在了 “ECS 或 VPC 到 RDS” 的網絡被“阻塞”了。
**排查路由表:定位問題**
隨後,阿里雲 ECS 的工程師建議我們進行路由表的排查,於是我們分別在幾臺機器上查看了路由規則狀況:
ssh host-pre route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.73.253 0.0.0.0 UG 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-df03b027a5e8
192.168.0.0 0.0.0.0 255.255.240.0 U 0 0 0 br-c3c094fc6759
192.168.73.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.73.253 0.0.0.0 255.255.255.255 UH 100 0 0 eth0
ssh host-01 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.73.253 0.0.0.0 UG 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-0cb13ae8df3c
192.168.32.0 0.0.0.0 255.255.240.0 U 0 0 0 br-bb02c47906ee
192.168.73.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.73.253 0.0.0.0 255.255.255.255 UH 100 0 0 eth0
ssh host-02 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.73.253 0.0.0.0 UG 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-405544233f47
172.28.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-f11216ce202f
192.168.73.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.73.253 0.0.0.0 255.255.255.255 UH 100 0 0 eth0
ssh host-03 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.73.253 0.0.0.0 UG 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-df03b027a5e8
172.19.0.0 0.0.0.0 255.255.255.0 U 0 0 0 br-5089b1504e22
192.168.73.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.73.253 0.0.0.0 255.255.255.255 UH 100 0 0 eth0
可以看到機器基礎 VPC 網絡都在 192.168.73.1/24 段內,唯一有差別的是 docker 創建的橋接網卡。
host-02 和 host-03 的網卡指定網段都在 172.27~30.1.1/8 內,而 host-01 和 host-pre 的網卡則出現了兩張在 192.168.x.x 的網卡:
host-pre
192.168.0.0 0.0.0.0 255.255.240.0 U 0 0 0 br-c3c094fc6759
host-01
192.168.32.0 0.0.0.0 255.255.240.0 U 0 0 0 br-bb02c47906ee
看到路由表之後,阿里雲工程師反饋網絡衝突了,第一反應確實如此,因爲在排查連通性的時候,我們確實看到了當前連接 RDS 的地址是 192.168.0.xxx 的遠程地址。但是隨後我們又想到了一個問題,爲什麼這個問題現在纔出現,或者說,爲什麼之前的業務運行沒有受到影響呢?
此時阿里雲工程師提示我們“RDS 實例IP地址可能發生變化,連接串則始終不變,請使用以上連接串進行實例連接。”
到了這個時候,答案呼之欲出:容器創建應用內部橋接網卡的網段和阿里雲 RDS 網絡撞車了。 雖然概率很小,但是它確實出現了,因爲 CI/CD 過程中容器會隨機創建新的應用內部網卡,趕巧和 RDS 網絡切換後的地址撞在了一起,就會出現這個問題。
和阿里雲工程師溝通確認 “ 192.168.0.0/16 是 RDS 所在 VPC 的網段”後,就可以放心動手解決問題了。
**解決問題:修改容器網卡地址分配規則**
登錄 host-pre ,先再次打印路由表,以及查看容器創建的網卡列表:
host-pre:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.73.253 0.0.0.0 UG 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-df03b027a5e8
192.168.0.0 0.0.0.0 255.255.240.0 U 0 0 0 br-c3c094fc6759
192.168.73.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.73.253 0.0.0.0 255.255.255.255 UH 100 0 0 eth0
host-pre:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
0fba8dbbbff8 bridge bridge local
8b92ba96f640 host host local
c3c094fc6759 project-grpup_project-name bridge local
123b8780367b none null local
df03b027a5e8 traefik bridge local
可以看到 br-c3c094fc6759 這個網卡在 docker 中命名爲 c3c094fc6759,是由 project-grpup_project-name 這個項目創建,使用的網絡段確實是和 RDS 發生了衝突。
通過查看 docker 官方文檔,我們可以找到我們需要的 daemon 配置項 default-address-pools,來主動規避掉和阿里雲 RDS 網絡衝突的問題。
修改 /etc/docker/daemon.json ,聲明和 192.168.0.0/16 不衝突的地址:
{
...
"default-address-pools": [
{
"base": "172.18.0.0/16",
"size": 24
},
{
"base": "172.19.0.0/16",
"size": 24
},
{
"base": "172.20.0.0/16",
"size": 24
},
{
"base": "172.21.0.0/16",
"size": 24
},
{
"base": "172.22.0.0/16",
"size": 24
},
...
]
}
在修改配置後,執行 service docker restart,接着重新啓動應用,觸發重新創建內部網卡邏輯,然後再次查看容器網卡列表:
docker network ls
NETWORK ID NAME DRIVER SCOPE
52a7bbc2b5fe bridge bridge local
8b92ba96f640 host host local
5089b1504e22 project-grpup_project-name bridge local
123b8780367b none null local
df03b027a5e8 traefik bridge local
看到創建的新網卡爲 5089b1504e22,我們使用 inspect 檢查網卡分配網絡:
docker inspect 5089b1504e22 --format='{{json .IPAM.Config}}'
[{"Subnet":"172.19.0.0/24","Gateway":"172.19.0.1"}]
可以看到分配網絡與我們預期一致,規避了 192.168.0.0/16 ,至此問題解決。
**最後**
如同“墨菲定律”所言,凡是可能出錯的事情就一定會出錯。
爲了避免出錯,靠譜的方案是:徹底查出可能出現事故的問題所在和根本原因,對其進行標本兼治的方案,才能防患於未然。
--EOF