Categories
程式開發

從二十個嚴重的配置故障中我們能學到什麼?


配置故障是運維人員在工作中經常會遇到的問題,如何才能避免配置故障的發生呢?本文作者列出了自己職業生涯遇到的20個不同類型的配置故障,並分析了故障發生的原因,提出了相關的解決方法。

配置變更的痛點

時效性,很多配置變更的場景,其生效時間是有明確要求的,尤其是止損相關的操作。如流量調度的配置生效時間不超過5min,這是由SLA所決定的。因為5min的生效要求,所以這類配置變更基本上不會進行灰度,直接就全量生效了。

動態化,在上下游關聯關係的場景中,服務的IP地址列表和數量均會發生變化的,站在整個系統角度去看,可能每時每刻都會有服務因擴容、縮容、故障轉移而產生IP列表的變化,加之這類配置是最終一致性的特性,因此,就很難對語義內容的正確性做及時準確的判斷。

差異化,同一服務的不同實例或者不同集群間,配置會有一定的區別。以監控系統為例,因為每台機器上部署的服務組合不相同,那麼每台機器上需要採集的內容就不相同,那相應的監控配置也會不同。許多的機器生效的配置不同,版本不同,那麼要想檢查哪些機器未生效最新配置,未完全生效配置,就有困難了。

碎片化,每個服務都需要配置管理,但現實卻是大多數服務都是自己寫的這套邏輯,且實現非常之簡單。如果現實是建立在大多數服務自己實現配置管理的基礎上,那麼想要做到相對統一的標準規範,你就需要做各種對接和適配,開發各種外掛,持續處在一個較低的層次上。配置中心成熟的開源解決方案有很多,因此內部開發人員也少有動力基於現狀寫一套適配的方案,大家都會說推動改造是最好的,那改造之前呢怎麼辦呢

二十個嚴重的配置故障

筆者曾長期負責配置服務的運維工作,也經常參加公司的故障复盤會,感慨於配置故障影響的嚴重程度,因此將過去多年和配置相關的各種服務故障脫敏後進行羅列,希望對大家有所幫助。限於篇幅的關係,無法對每個case進行準確詳盡描述,僅描述故障原因部分,因此閱讀下面的case,需要讀者俱備一定的相關背景才能更好的理解(本文中羅列的case較多,有內容不合適的地方,可直接聯繫筆者處理)

case1:在WAF中部署了配置錯誤的規則,其中一個規則包含有一個正則表達式,導致節點100%CPU佔用。和CloudFlare WAF的故障是較為相似的

case2:某服務訪問數據庫錯誤,獲取合法用戶列表的結果為空值,該服務認為沒有合法用戶,將所有的用戶請求全部封禁,導致系統完全不可用。和阿里雲20180627的故障較為相似的

case3:每台物理機上均有一個單機部署器,單機部署器定期從服務端同步配置,用以在單機上部署需要的客戶端列表。因為網絡的問題,配置被截斷,部署器只獲取了上半部分配置,部署器基於此進行更新,所有未在配置中的服務被全部卸載,影響極為嚴重

case4:計算模塊對加載的處理規則數量進行了限制,單個租戶最大可處理1000條規則,經過幾個月的使用後,某個租戶的規則數超過1000條,計算模塊全部coredump

case5:服務啟動前需要從遠程加載相關配置,某次變更添加配置條目較多,服務請求配置超時後,不斷重新啟動,直至將配置中心打垮,進而影響多個業務,導致系統徹底崩潰

case6:Zookeeper的initlimit設置為5(意思是實例啟動後可以有10s的時間來同步數據),該數值對於大部分集群來說,都沒有問題,但是如果ZK集群的快照體積超過1GB,那麼節點重啟後就可能無法在10s內同步完數據進而導致節點異常

case7:Agent上傳給服務端的數據包出現了異常,在頭部信息中,100KB的體積變成了無限大,然後後端模塊就按照該值進行內存分配,因為超過Ulimit的限制,所以coredump了,一部分服務器異常後,Agent繼續將請求發送給剩餘的服務器,直到集群全部被打死為止

case8:通過配置服務獲取下游服務列表,運維人員誤將服務下所有實例從配置中刪除,而該服務也未使用系統提供的熔斷閾值,導致從配置服務中獲取該服務列表為空,其他依賴服務加載了該配置後,導致服務整體故障

case9:新上架的交換機未添加完整的ACL規則,導致線下服務請求到線上集群中

case10:用戶提交了語法結構不正確的配置,然後該配置下發到了後端,所有加載該配置的後端模塊全部coredump了,後端模塊使用了通用的語法校驗庫,但和前端使用的校驗庫不同

case11:後端服務異常後,觸發了自動降級(通過修改Nginx的配置文件來實現),修改配置文件出現了邏輯錯誤,該配置通過了語法校驗並立即進行了全量自動更新,導致集群整體崩潰

case12:將某台服務器上的stats.conf文件同步到全部nginx集群上,忽略了allow字段的ip地址不同,致使lvs認為監聽的後端nginx全部故障,系統直接出默認頁面了

case13:批量刪除線上服務器的日誌,因路徑中包含特殊字符,運維繫統未能很好的處理該字符,只保留了特殊字符前的路徑,導致線上服務器根目錄被清空

case14:配置項結尾多了特殊字符,導致策略匹配全部失敗。原因是windows下的文本文件換行符是rn,linux下的換行符是n,在linux下vim打開windows的文本文件,在行尾會顯示^M字符

case15:通過配置服務獲取下游服務列表,因下游服務列表包含了四萬多個IP地址,導致獲取超時,服務繼續使用過期的下游列表,而該過期的下游列表中無效實例逐漸增加,最後導致服務連續重試到無效實例,流量被丟棄

case16:配置中心對外提供配置下載服務,其LB的負載均衡策略使用了隨機策略,因下載集群規模超過百台機器,配置更新需要一定時間,在此期間,請求配置中心獲取的配置會不停的在新舊版本之間變化,導致相關的服務被不停的卸載,升級,無法提供服務

case17:配置中心的負載均衡策略使用了Ip_hash策略,部分下載集群數據未更新,導致被分配到這些集群的服務,無法獲取最新配置,且多次重試依然無效,導致流量統計始終缺少20%的流量,對收入造成嚴重的損失

case18:高防回源攜帶的頭部字段中新增的內容與源站iis配置不兼容,導致回源請求被拒絕

case19:named服務在重載配置時,產生了新的進程但老進程沒有正常退出,服務請求數據時,將請求打到了老named的進程,該進程的配置沒有更新,導致用戶拿到的結果不符合預期。類似的問題,nginx也有,在很多開源軟件上也都有存在

case20:自研nginx的擴展來實現upstream的定位,hash桶的大小設置為1024,因ip重複個數較多,超過了hash桶的大小,導致hash分桶策略不斷的進行rehash任務,請求全部被卡死

故障改進

對於上述的故障,我們從加強測試,加強數據校驗,集群隔離,灰度發布,緩存和熔斷六個維度來看各個case的適用性,結果如下圖所示。

從二十個嚴重的配置故障中我們能學到什麼? 1

假設筆者的給出的改進方案是正確的,對統計結果做如下分析:

  • 加強測試,從有效性來說,該方案可以搞定85%的故障,如邊界值的問題,異常輸入的問題,特殊字符串的問題等。但是對於部分場景,加強測試可以搞定,但成本非常高,例如WAF的規則問題,不同的用戶,配置的規則不同,如果要在測試階段解決,那就需要對所有用戶的所有規則進行測試,且要有足夠豐富的請求組合,這時候,可行但成本很高。還有,加強測試雖然可以解決很多問題,但沒有從本質上徹底消除問題,且依賴於測試團隊的經驗和能力,結果並不可控。

  • 加強校驗,從有效性來說,該方案可以搞定65%的故障,以case2獲取空值的故障來講,對請求結果進行校驗,完全可以避免該問題,且這是程序機制天然具備的,並不依賴於人的經驗和能力,因此效果更好。另外,加強校驗,並非局限於對空值的校驗,還包括配置文件完整性的校驗,數據包合法性的校驗,配置文件的版本、時間戳、更新時間的校驗,對特殊字符串容錯的能力,還提供一些故障場景下的兜底訪問策略,同時還可以將配置變更的信息以http接口進行暴露,便於監控。美中不足之處在於,如果對每個模塊分別實現上述的功能,那成本太高了。

  • 集群隔離,筆者之前最為推薦的能力建設內容之一,本次居然只能解決35%的故障,究其原因,主要在於很多業務雖然隔離部署,但依賴了相同的配置文件,且大部分是高頻熱加載的,因此集群隔離在此處,略顯蒼白。

  • 灰度發布,這也是筆者最為推薦的能力建設內容之一,本次也只能解決35%的故障,主要是因為大部分的配置變更是沒有灰度發布的,直接靠程序的定時輪詢熱加載,因此灰度在此,也略顯蒼白。

綜合來看,三個建議:

  • 將校驗,緩存,熔斷等相關的能力合併在一起,在通用的配置服務中實現,會是比較好的方案,退而求其次,如果沒有通用的配置服務,也應該具備這些能力。

  • 將配置變更盡可能的視為一種上線,涵蓋從版本提交,代碼檢查,測試,灰度發布,效果監控等各個環節,也能夠更好的保障穩定性,畢竟很多配置是最終一致性,而非強一致性的,因此,用時間來換取穩定性,也是值得的

  • 將強一致性轉為最終一致性,問題就不會那麼棘手了。以賬號密碼變更為例來說明,賬號密碼變更是強一致性要求的,但是如果通過新增一組賬號密碼逐步進行替換,全部替換完畢後刪除舊的賬號密碼,則就將強一致性轉為最終一致性了。

還有兜底建議:

  • 通過資源限制+SSH+Puppet多種方式的互備,確保任何場景下,不論是sshd異常,還是cpu打滿,都能夠通過批量操作服務器快速止損,決不要出現無法登錄服務器的慘劇

場景化建議

熱加載配置的場景,盡量使用配置中心這種解決方案,類似於上文的加強校驗,緩存,熔斷等等,都是標配能力了,而且一旦發現了潛在的隱患,通過升級lib庫,大家都能避免發生同類問題,效果極好。當然,你得用好才行,胡亂使用zookeeper,最後導致各種故障,然後怪罪於zookeeper有問題,人家很冤枉啊。

需要通過重載配置文件才能生效的場景,則盡量使用集群隔離+灰度發布+加強校驗的解決方案,以DNS為例說明,不同的zone由不同的集群來處理請求,每類zone再按需實現同城多活/異地多活,並新增小流量集群,盡量杜絕跨集群的變更操作,對每次變更都要進行嚴格的自動化校驗,並分批次灰度生效,通過提昇實時監控能力來降低批次間的時間間隔,進而減少故障的發生。

在整理上面資料的同時,配置故障讓我聯想到和無性繁殖有些類似,文末的參考資料中附帶了香蕉和咖啡樹的故事,有興趣的讀者可以看看。

擴展閱讀:

香蕉可能要滅絕?還不是第一次?香蕉:救救孩子吧…

為什麼英國人對茶情有獨鍾而不是喜歡咖啡?背後原因你一定想不到