搞容器混搭搞出了線(xiàn)上 Redis 事故什么的
事情是這樣的, 現(xiàn)在有一個(gè) redis 3.0 集群節(jié)點(diǎn)都是裸 redis 或 host 網(wǎng)絡(luò)模式部署的容器 redis (基本上跟裸 redis 差不多), 需要把它們替換成 macvlan 網(wǎng)絡(luò)模式的 redis 容器, 以顯得我們的 dockerized redis cluster 很上檔次.
這事情幾個(gè)月前也搞過(guò)一次毫無(wú)壓力.
然而這一次又搞, 就出岔子了. (這劇本不對(duì)啊摔)
于是開(kāi)始加了兩個(gè) macvlan 的容器到上述 redis 集群作為從節(jié)點(diǎn), 打算稍候 failover 替換掉主, 過(guò)了十分鐘左右群里炸毛, 說(shuō)數(shù)據(jù)都取不到, 或者格式不對(duì)...
上線(xiàn)一查, 發(fā)現(xiàn)正在加從節(jié)點(diǎn)的這個(gè)集群跟另一個(gè)集群的節(jié)點(diǎn)混到一起去了.
這里吐槽一下 redis 集群的協(xié)議, 兩個(gè)正常服務(wù)的集群可以直接通過(guò)一個(gè) cluster meet 合并成一個(gè)集群, 然后槽位分布亂了...
首先當(dāng)然是緊急恢復(fù)線(xiàn)上業(yè)務(wù), 先拉一個(gè)新集群出來(lái)再說(shuō) (所幸這個(gè)集群的數(shù)據(jù)不需要持久化).
結(jié)果, 新集群剛弄出來(lái), 又被合進(jìn)了上面那個(gè)集群. (這時(shí)我滿(mǎn)腦子都是某個(gè)科教片里兩個(gè)星系合并的一段視頻, 滿(mǎn)天都在炸! (論腦洞
然后 cluster nodes 看了一下, 發(fā)現(xiàn)集群里有幾個(gè)節(jié)點(diǎn)地址變成了 172.17.x.x, 這應(yīng)該是 docker 的內(nèi)部網(wǎng)段地址, 所以反應(yīng)過(guò)來(lái), 可能是 docker 網(wǎng)絡(luò)配置問(wèn)題, 將握手流量發(fā)給了錯(cuò)誤的節(jié)點(diǎn), 然后那些節(jié)點(diǎn)被并了進(jìn)來(lái).
這時(shí)候創(chuàng)建一個(gè)新網(wǎng)段有點(diǎn)來(lái)不及了 (還打了個(gè)電話(huà)給已經(jīng)請(qǐng)假回家的 @小六哇啦啦 老師...) 換了個(gè)思路, 把新 redis 換個(gè)端口部署, 再組個(gè)集群, 觀(guān)察了一會(huì)兒, 這方法起作用了 -.-!!
恢復(fù)了被炸得雞飛狗跳的線(xiàn)上業(yè)務(wù)之后, 就開(kāi)始排查問(wèn)題了.
線(xiàn)索還是之前 cluster nodes 看到的那個(gè) 172.17.x.x 網(wǎng)段, 測(cè)試確認(rèn)了一下, 從 docker 容器內(nèi)連宿主機(jī), 宿主機(jī) accept 得到的會(huì)是 172.17.x.x 這個(gè)地址. 而容器內(nèi)路由表是這樣的

確實(shí)如果宿主機(jī)的 IP 是 10.100.1.100 那么流量走的是 eth0 也就是 172.17.x.x 網(wǎng)卡. (10.222.0.0/16 是容器 macvlan 地址)
之后就明白了, 172.17.x.x 這樣的網(wǎng)卡地址在不同物理機(jī)上是可能相同的. 也就是說(shuō), 遭遇的問(wèn)題可能是如下過(guò)程所致

- 四個(gè) redis #a #b #c #d
- #a #b 是兩個(gè) host 網(wǎng)絡(luò)的 redis, 在同一個(gè)集群中, #d 是 macvlan 部署的 redis, 在另一個(gè)集群中
- #c 是一個(gè)空閑的 redis, 它與 #d 恰好有相同的 eth0 地址
- #c 通過(guò) eth0 向 #a 發(fā)送了一個(gè) handshake
- #a 確認(rèn), 這時(shí), 它認(rèn)為 #c 的地址是 172.17.0.55
- #a 將新節(jié)點(diǎn)地址廣播給 #b
- #b 向 172.17.0.55 發(fā)送一個(gè)握手請(qǐng)求, 然而, 此地址在它所在機(jī)器上對(duì)應(yīng)的是 #d, 之后兩個(gè)集群就混一起去了
這也解釋了為啥幾個(gè)月之前這么搞的時(shí)候沒(méi)出問(wèn)題, 應(yīng)該是那時(shí)候運(yùn)氣好沒(méi)有相同地址的容器; 同時(shí)也解釋了為啥不是每個(gè)純 macvlan 模式的 redis 集群都中槍.
后來(lái)在測(cè)試機(jī)房找了兩個(gè)恰好相同網(wǎng)卡的容器, 按上述思路搭了集群試了試, 果然重現(xiàn)了.
解決方案
- 因噎廢食 : 以后別這么混搭玩了
- 繞過(guò) : 端口號(hào)不一樣法
- 改默認(rèn)路由 : 默認(rèn)就走 vlan 網(wǎng)卡, 不過(guò)這樣的話(huà)不能訪(fǎng)問(wèn)外網(wǎng), 對(duì) redis 而言沒(méi)問(wèn)題, 但其他業(yè)務(wù)可能就不行了
- 加路由 : 其實(shí)可以通過(guò)在容器內(nèi)加一條路由 10.100.0.0/16 走 vlan 這樣宿主機(jī) accept 到的地址就會(huì)是機(jī)房***的 vlan 網(wǎng)卡地址了, 這個(gè)方案 @CMGS 正在評(píng)估中
EOF