安全生產(chǎn)-系統(tǒng)穩(wěn)定性建設(shè)
原創(chuàng)作者 | 彭文清(馬恪)
前言
安全是產(chǎn)品的底座,是體驗的基礎(chǔ),也是企業(yè)的一項核心競爭力。安全生產(chǎn)是一項系統(tǒng)性的工作,同時也是一件比較瑣碎的事,需要做方方面面的考慮盡一切可能保障系統(tǒng)安全穩(wěn)定運行。個人之前一直負責商品的穩(wěn)定性工作,在這方面有比較多的經(jīng)歷和實踐。
記得在18年的時候,我們做商品發(fā)布的組件化改造,當時正好碰上網(wǎng)站剛開始類目調(diào)整,一度連續(xù)3個月每個月都有故障,當時穩(wěn)定性的壓力很大。當然那也是一個契機,商品的穩(wěn)定性建設(shè)也是從那個時候開始起步,然后逐步的完善。
大綱
安全生產(chǎn)建設(shè)大致上可以分為這三個階段:
事前:故障預(yù)防,這里需要考慮的就是怎么通過預(yù)先的設(shè)計,最大限度的保證質(zhì)量,降低風險,提升穩(wěn)定性。
事中:應(yīng)急處置,出了問題以后怎么處理,快恢手段有哪些,流程是什么。
事后:復(fù)盤改進,事后肯定要總結(jié)經(jīng)驗,舉一反三解決同類的問題,不要被同一棵石頭絆倒兩次。
故障預(yù)防
魏文王問扁鵲:你醫(yī)術(shù)為啥這么好?扁鵲說:我醫(yī)術(shù)在我三兄弟里面算差的。最好的是我大哥,還沒有任何癥狀就撲滅了,次好是我二哥,一點點輕微的癥狀就解決了。再次就是我,出了嚴重的問題我才開始醫(yī)治,所以雖然我名滿天下,但是我醫(yī)術(shù)可比我的大哥二哥差得遠啦。
做個類比,故障預(yù)防做的好的就是大哥,應(yīng)急處置做的好的就是二哥,總結(jié)復(fù)盤做的好的就只能是扁鵲了。所以預(yù)防生產(chǎn)事故的發(fā)生是最重要的,也是最基礎(chǔ)的。
架構(gòu)設(shè)計
單個技術(shù)點在絕大部分時候都可以正常工作,但是當規(guī)模和復(fù)雜度達到一定程度的時候,失敗其實無處不在。--《安全生產(chǎn)指南》
面向失敗設(shè)計是應(yīng)對安全生產(chǎn)最重要的方法論和指導(dǎo)方針。只有對各種失敗場景有提前的布防才可以在出問題的時候有效應(yīng)對。那么面向失敗設(shè)計具體有哪些方式方法?
- 冗余設(shè)計避免單點
冗余最早是航空器常用的技術(shù)術(shù)語,飛機設(shè)計最顯著的一個特點就是冗余,比如一般大型客機都會有兩套甚至四套引擎,類似的還有兩套基本同質(zhì)的主副駕駛系統(tǒng)。為什么要這么設(shè)計,還有額外的成本,根本原因還是因為冗余設(shè)計可以在一套出現(xiàn)問題時另一套依然能保障系統(tǒng)正常運行。
在ICBU的場景中中美新容災(zāi)就是我們最主要的冗余設(shè)計,也是穩(wěn)定性保障最核心的手段。商品是ICBU內(nèi)第一個構(gòu)建起中美異地容災(zāi)能力的業(yè)務(wù)場景,也正是因為有了這個能力幫助商品避免了不少的故障發(fā)生。?
- 服務(wù)分治避免雪崩
其實就是做好隔離。中國古代的木船設(shè)計有一個很重要的發(fā)明叫“水密隔艙”,現(xiàn)在很多軍用艦船也有借鑒。典型的像航空母艦據(jù)說有2000多個獨立的密閉隔倉,作用就是當船舶遭遇意外或者破損進水時,可以通過隔離受損艙室減緩下沉。它是一種安全生產(chǎn)的設(shè)計,對我們有借鑒意義,系統(tǒng)要做模塊化的設(shè)計、薄弱點或者風險點可以被單獨隔離和降級,保障整體可用性。我們之前發(fā)生過一次問題,因為ES集群中一臺物理機故障導(dǎo)致上層應(yīng)用的線程池被耗盡從而引起服務(wù)不可用,根本原因就是隔離沒有做好。
?
服務(wù)冪等保證一致服務(wù)或接口應(yīng)該保證冪等,多次調(diào)用和一次調(diào)用結(jié)果應(yīng)該是一致的,避免重復(fù)調(diào)用導(dǎo)致數(shù)據(jù)或邏輯異常。
服務(wù)無狀態(tài)可漂移服務(wù)或接口最好設(shè)計成無狀態(tài)模式,從擴展性的角度來講,無狀態(tài)模式可以很容易的根據(jù)流量情況彈性伸縮。
- 運行狀態(tài)精確監(jiān)控
把我們的應(yīng)用系統(tǒng)比做飛機,我們的系統(tǒng)也需要各種傳感器和儀表盤去實時監(jiān)控系統(tǒng)運行狀態(tài),系統(tǒng)對于運維人員來說應(yīng)該是個白盒而不是黑盒。核心的業(yè)務(wù)或者系統(tǒng)監(jiān)控有助于提前發(fā)現(xiàn)問題,細粒度、多維度的監(jiān)控有助于快速定位問題。
- 自動化運維管控
最好的處理是不需要處理。核心思路是一切操作標準化、流程化、自動化,降低人為干預(yù)、提升操作效率。像商品發(fā)布/商品管理都有一些自動容災(zāi)預(yù)案,可以在出現(xiàn)問題時自動快恢。下面的案例是周末出現(xiàn)的一次風險預(yù)警就是通過自動執(zhí)行快恢避免了線上故障的發(fā)生。
另外今年旺鋪的一次風險預(yù)警,在出現(xiàn)容量性能問題的時候也是通過自動彈性擴容快速解決了問題。
編碼開發(fā)
- 并發(fā)控制
在服務(wù)能力受限的情況下,我們需要主動控制客戶端并發(fā)數(shù)。特別是任務(wù)型場景,通常有靜態(tài)限流和動態(tài)限流兩種選擇。靜態(tài)限流比較簡單但有一個局限就是資源利用率不高。動態(tài)限流的原理是根據(jù)服務(wù)端的響應(yīng)情況動態(tài)調(diào)整客戶端速率,簡單來講就是計算服務(wù)超時/異常的數(shù)量,超時多了請求就放慢一點。
- 數(shù)據(jù)控制
數(shù)據(jù)是信息系統(tǒng)中的血液,通過數(shù)據(jù)的流動才能將一個個業(yè)務(wù)或功能模塊串聯(lián)在一起。一方面是數(shù)據(jù)的生產(chǎn)要嚴格校驗保證數(shù)據(jù)的合法性和準確性。另一方面是數(shù)據(jù)的消費,需要評估單次處理的數(shù)據(jù)量、數(shù)據(jù)處理頻次,不要因為高并發(fā)、海量數(shù)據(jù)導(dǎo)致系統(tǒng)內(nèi)存、負載承壓。商品曾經(jīng)因為這個經(jīng)歷過最慘痛的教訓(xùn):
- 防御編程
批量容錯:比如一個列表肯定不能因為某一行有問題而導(dǎo)致整個列表出不來。同樣的同步任務(wù)也不能因為某一個任務(wù)異常而堵塞整個任務(wù)隊列。所以需要對這些場景做好容錯處理,其實還是在強調(diào)隔離風險。像商品的引擎同步任務(wù)、質(zhì)量分計算任務(wù)都有獨立的在線實時隊列、實時重試隊列、離線隊列,目的就是為了不影響實時增量任務(wù)的正常執(zhí)行。
數(shù)據(jù)兼容:另外有些存量業(yè)務(wù),可能已經(jīng)積累了很多的歷史數(shù)據(jù)。拿商品來舉例,今天我們的認知里moq就是最小起訂量肯定是數(shù)字,包裝重量肯定也是數(shù)字,但其實之前商品的部分屬性是可以自定義的,有些可能是用戶隨便填的,所以在功能升級迭代的過程中需要考慮對歷史數(shù)據(jù)的兼容。
- 簡化代碼
越簡單東西的越可靠,越復(fù)雜的東西容錯性越低。把簡單的事情想復(fù)雜,把復(fù)雜的事情做簡單。少用同步鎖:系統(tǒng)設(shè)計和編碼盡量使用無鎖實現(xiàn),避免引起不必要的死鎖問題。商品同樣因為同步鎖的原因出現(xiàn)過線上問題:
慎用線程池:其實跟同步鎖類似,它是一個比較危險的操作,要做好測試和驗證。
容災(zāi)防護
過載保護過載保護是保障系統(tǒng)整體可用的重要手段之一,設(shè)計過載保護可以有效避免因為流量問題導(dǎo)致的系統(tǒng)不可用。所以我們的應(yīng)用要具備自我防護的能力,web型應(yīng)用接入流量清洗系統(tǒng),服務(wù)型應(yīng)用做好服務(wù)限流。
依賴降級當前大部分的業(yè)務(wù)系統(tǒng)都比較復(fù)雜,特別是在分布式架構(gòu)中,一個請求可能依賴多個系統(tǒng)支撐共同完成。調(diào)用鏈路中的任何一個節(jié)點都可能影響整體穩(wěn)定性。所以一方面我們要保證自身服務(wù)的穩(wěn)定性,另外一方面也要考慮在依賴服務(wù)出現(xiàn)問題后怎么保證主鏈路的可用。而依賴降級的前提是先理清依賴關(guān)系:
1.弱依賴就自動降級比如校驗類的。
2.有一些是強依賴但可以異步處理的就降級后異步重試。
3.有一些是強依賴又不能異步的,就需要產(chǎn)品化了,把降級能力作為產(chǎn)品設(shè)計的一部分。
下圖是商品發(fā)布鏈路的部分容災(zāi)降級點:
這其中超過95%都從來沒有使用過。安全生產(chǎn)的建設(shè)就是這樣,很多穩(wěn)定性保障措施大部分時候都默默無聞,但是真正使用到的時候卻都是關(guān)鍵的時刻。
監(jiān)控預(yù)警
- 監(jiān)控標準化
一堆無意義的"System Error"無濟于事,標準錯誤碼和SPM監(jiān)控是當前比較好的一個實踐。
- 預(yù)警有效性
召回優(yōu)先:預(yù)警有效性其實比較難做,監(jiān)控覆蓋度越廣,預(yù)警機制越靈敏,報警量可能就會越多,誤報也會越多。商品無人值守樣板間在開始時候也是召回優(yōu)先,誤報慢慢收斂,這是一個持續(xù)治理的過程。
報警分層:另外可以根據(jù)監(jiān)控場景對報警分層, 通過報警分層一定程度上可以兼顧召回和免打擾。重要的業(yè)務(wù)系統(tǒng)監(jiān)控推送到核心預(yù)警群,非故障場景或者輔助定位的監(jiān)控報警可以推送到其他普通預(yù)警群。同樣也需要對報警通知方式做分層,比如成本率下跌2%可能是釘釘通知,下跌5%就需要電話通知。
測試回歸
測試回歸是質(zhì)量保障很重要的一個環(huán)節(jié)。
- 單元測試
單測我相信有人會懷疑它的價值,而且在剛開始的時候的確比較耗費時間精力,但是它是一個難而正確的事情,這里分享兩點:
單測是代碼的照妖鏡,我們會發(fā)現(xiàn)通常單測很難跑起來的往往就是代碼設(shè)計不合理,耦合過多,單測可以幫助自己優(yōu)化代碼。
單測是質(zhì)量的保證。前端時間跟我對接的前端同學(xué)突然跟我說,“文清,沒想到這次項目這么順利”,這個項目也是我個人第一次在一個項目里寫完整的單測代碼。從我個人的實踐經(jīng)歷來看,單測特別是對于喜歡重構(gòu)的人來說還是非常有價值的。
- 巡檢/流量回放
我們當前的業(yè)務(wù)大部分都比較復(fù)雜,有非常多的分支流程。舉個例子商品有5000多個葉子類目,每個類目的商品表單是不一樣的,這么多的場景靠人肉是根本覆蓋不了的,只能靠機器和自動化去回歸。前臺頁面場景巡檢是一個比較好的方式,它可以模擬用戶操作。對于后臺服務(wù)流量回放也是一種可行的方案,商品無人值守樣板間也在使用巡檢和流量回放來保障發(fā)布質(zhì)量。
- 壓測
新的服務(wù)上線前需要評估流量和性能情況,根據(jù)預(yù)估對服務(wù)進行壓測以驗證是否能夠滿足需求。同時整個鏈路能夠支撐的QPS上限也需要通過壓測確定,幫助我們合理的評估系統(tǒng)水位。
變更發(fā)布
回過頭去分析會發(fā)現(xiàn)其實我們大部分問題都是變更引入的。商家技術(shù)部三規(guī)九條里有規(guī)定變更發(fā)布三原則 “可灰度、可監(jiān)控、可回滾”。發(fā)布之前要做好發(fā)布計劃和評審,檢查是否滿足這三個要素。
灰度分為功能灰度和發(fā)布灰度,功能灰度可以根據(jù)自己的團隊情況設(shè)計。發(fā)布灰度首先從流程上建議增加小流量灰度環(huán)境,部分流量可以劫持到小流量環(huán)境,一些重大的問題可以在小流量環(huán)境提前發(fā)現(xiàn)。另外有條件的應(yīng)用也建議分單元/分區(qū)域分開部署,短時間內(nèi)模擬藍綠部署,為容災(zāi)切流創(chuàng)造條件。
對于回滾,切記要做回滾驗證,不要一股腦不暫?;貪L完。因為之前碰到過一個案例,越回滾問題越嚴重,原因是當時的問題是遠程緩存異常導(dǎo)致應(yīng)用啟動后出現(xiàn)數(shù)據(jù)異常,回滾重啟后又導(dǎo)致已發(fā)布機器本地緩存失效放大了影響面。
應(yīng)急處置
故障預(yù)防是降低問題發(fā)生的概率,不是消滅問題。根據(jù)墨菲定律,只要存在可能就一定會發(fā)生。所以應(yīng)急處置就是我們的兜底手段,是保證高可用性的重要一環(huán)。
容災(zāi)切流
出現(xiàn)故障我們首先需要做的不是定位原因,而是及時止血和快速恢復(fù),而容災(zāi)切流往往是快恢的首選,時效性最高。
web型應(yīng)用:當前成熟的方案是vipserver容災(zāi)機制。原理也很簡單,多單元的機器都掛載到vipserverkey下,正常情況下因為同機房規(guī)則生效請求都是單元內(nèi)調(diào)用。當需要容災(zāi)時拉黑異常單元下的機器,同機房規(guī)則失效流量就會串流到其他單元從而達到跨單元容災(zāi)的效果。
服務(wù)型應(yīng)用:當前還沒有特別標準的方案,商品的做法是使用代理服務(wù)多地注冊,在容災(zāi)切流時通過調(diào)用不同單元的代理服務(wù)來達到跨單元切流的效果。
變更回滾
出現(xiàn)線上問題的時候,如果無法通過容災(zāi)切流的手段解決就要看一下有沒有變更發(fā)布,判斷如果可能跟變更有關(guān)就先回滾,重要的還是要做好回滾驗證。
擴容重啟
遇到突發(fā)流量、下游系統(tǒng)慢或本身資源容量不足都可能導(dǎo)致應(yīng)用自身負載承壓。在遇到資源瓶頸的時候快速擴容就是首選,這個時候考驗的是系統(tǒng)的彈性能力。當前也有一些可行的方案:
- VPA:基于CPU/內(nèi)存垂直彈性,調(diào)整的是cpu核數(shù)和內(nèi)存大小,不過有上限。
- HPA:基于CPU/內(nèi)存水平彈性,缺點就是擴容速度不夠快,還是要經(jīng)歷應(yīng)用啟動的過程,應(yīng)對突發(fā)的流量波峰可能不太可行。
- KPA:基于流量/響應(yīng)水平彈性,據(jù)稱是CSE的替代者,猜測應(yīng)該可以通過回放內(nèi)存鏡像來實現(xiàn)快速擴容。
當無法擴容的時候,重啟是解決系統(tǒng)性能問題的另一個重要手段。據(jù)不科學(xué)的統(tǒng)計,90%的暫不明原因的性能問題可以通過重啟暫時解決或一定程度上緩解。
限流降級
面對容量問題,除了擴容以外另一個選擇就是限流和降級,可以根據(jù)重要程度優(yōu)先對邊緣應(yīng)用和非核心場景做限制。
- 分層分場景:我們支撐的業(yè)務(wù)肯定是有優(yōu)先級的,有核心的業(yè)務(wù),也有邊緣場景。當需要做限流降級處置的時候不能一刀切,要根據(jù)重要程度先對非重要的應(yīng)用作限制。
- 分用戶粒度:同樣我們服務(wù)的用戶也是有優(yōu)先級的。以商品為例有付費商家和免費商家,那么在整體資源受限的情況下就可以先對免費商家做降級處理。
- 根據(jù)資源占用分層:像商品場景存在品量非常多的商家,這部分用戶的某些操作可能對系統(tǒng)資源占用比較大,比較常見的就是引起數(shù)據(jù)熱點或者數(shù)據(jù)偏移,而這些都是影響性能的因素,所以對資源占用大戶也要有對應(yīng)的處置措施,比如禁讀禁寫。
應(yīng)急公告
如果短時間內(nèi)無法恢復(fù)(參考1-5-10標準),那么就需要掛應(yīng)急公告減少用戶進線,每個業(yè)務(wù)場景的應(yīng)急公告應(yīng)該預(yù)先編制完成,在有需要時一鍵執(zhí)行。
應(yīng)急參考
應(yīng)急處置的方案有很多,但是在處理線上問題時應(yīng)該是有優(yōu)先級的,先做什么其次做什么最后做什么。下圖是整理的一份應(yīng)急處置SOP參考文檔。它不是操作手冊,不能在出問題時再去查閱,而是每個人腦子里應(yīng)該有這樣一張圖,對于線上應(yīng)急處理流程爛熟于胸。
?
故障演練
所有的安全生產(chǎn)的措施都做好了,那么怎么驗證它可以正常工作,故障演練是很好的手段。一方面可以檢驗穩(wěn)定性保障措施的可用性,另外一方面也可以鍛煉技術(shù)人員的應(yīng)急處理能力。我看到過不止一次有人在處理故障時很緊張手都在抖。所以常態(tài)化的故障演練,特別是突襲演練可以讓更多人參與到應(yīng)急處理的過程中去,多實踐才可以在真正發(fā)生問題的時候有條不紊。
復(fù)盤改進
歸納總結(jié)
出問題不可怕,重要的是分析這個過程中存在什么問題,流程機制上有什么漏洞,從而幫助我們做的更好。商品的穩(wěn)定性措施就是在一次次面對各種問題的過程中總結(jié)和實踐的。
經(jīng)驗分享
歸納總結(jié)是利己,避免自己再犯相同的錯誤。經(jīng)驗分享是利他,幫助別人不要掉到同樣的坑里。另外技術(shù)風險防控平臺里的大部分故障都是公開的,可以看看自己的應(yīng)用有沒有同樣的風險。
總結(jié)
風險意識:做好穩(wěn)定性最重要的是要有風險意識,對生產(chǎn)保持敬畏之心。有風險意識才會在產(chǎn)研的全流程中關(guān)注穩(wěn)定性的事情。比如新引入了一個依賴,就會考慮它異常了對于自己有什么影響,有沒有容災(zāi)方案,能不能降級等等。另外有風險意識也會慢慢的形成一些良好的習慣,比如定期Review系統(tǒng)水位,我個人每天早上剛到公司做的第一件事就是看一下監(jiān)控和報警。
減少損失:應(yīng)急處置的第一原則是把損失降到最低,先恢復(fù),再定位。
風險自愈:安全生產(chǎn)建設(shè)最終的目的應(yīng)該是具備自愈能力,在系統(tǒng)出現(xiàn)問題時能夠及時介入并自動恢復(fù)。
向人體學(xué)習,他擁有一套三重的免疫系統(tǒng),這是一個百年的相當穩(wěn)定的系統(tǒng)。-- 魯肅
所以事前要有風險意識,事中要及時止損,事后查漏補缺構(gòu)建風險自愈的能力。更重要的是秉持長期主義的精神,相信最終能夠做好安全生產(chǎn)的事情。