今天跟大家分享的是有關 Cassandra operation 的不幸事件,雖然很慘,好在這些鳥事都是在 2014 年完結了,2015 沒煩惱。
操作員在凌晨四點被叫醒
上個月真是過得挺淒慘的,凌晨四點被 server 警報吵醒,Cassandra server cpu load 滿載,沒有回應。我查了一遍硬體後確認都沒問題,接著看 log 也看不出所以然,只好忍痛將全部 server 重開。最糟的是重開後居然沒有任何改善,server cpu 還是滿載。哇,這時候腦袋已經呈現一片空白,服務已經中斷一個多小時了,時鐘已經指在凌晨 5 點...
修不好而且完全無助,冷汗直直流。怎麼辦 ! 怎麼辦 ! 怎麼辦 !!?
找不到原因,只好開始修改程式,將服務降級,即然是 Cassandra 狂飆壞了,那就把存取 Cassandra 最繁重的功能先給關了。試改了幾種方法,部署到不同主機分別測試。最後找到關閉某個功能後 Cassandra 就開始回穩。呼~ 服務總算是復活了,雖然是血只剩一半,好歹勉強可用。這時時鐘已經指在 6 點 ...
止血成功,爭取到更多的時間追查問題,後來查到是某筆資料太大造成的,刪掉後就正常了。系統總算是血補到滿了,回復所有的功能。
下錯藥
這次當機的 root cause,直到一個月後才被查出來 (不小心發現),因果關係離得非常遠,待會在說。在還沒發現真正的原因前, 某筆資料太大 一直是我們認定的原因,為了避免一而再的發生,只好重新設計 Model,改變資料在 Cassandra 的儲存方式,花了一週重寫,也設立好 migration 的計畫。但很不幸的,因為經驗不足,我們踩到了 Cassandra durable queue anti-pattern 。好了,因為是在正式的環境下踩到這個雷,上面那齣戲 - "操作員在凌晨四點被叫醒" 在上次出包後又重新上映了一次。下圖是當時 CPU 惡化的圖,真是慘到無以復加。
圖說: Cassandra durable queue anti pattern CPU usage
Root Cause
好了,來開始解釋真正的原因。
- 出問題的 Table 是使用 Cassandra 的 Super Column 功能,我們公司很早就開始使用 Cassandra 了,但那時還沒有 CQL, Composite Type 這類的東西,想要儲存進階的資料結構只有 Super Column 可用。Super Column 的缺點在於它在讀取資料時,會整批都讀進記憶體,再回傳篩選的結果給 client。如果資料太大時,Cassandra 的 Heap 不夠用,就會出現 Garbage Collection 風暴,CPU/Load 滿載,無法回應。
- Super Column 現在已經是 Cassandra 的 anti pattern 了。但不管怎樣,我們也用它跑了三年,效率不好是真的,但沒道理三年後才大壞特壞。
- 在這個 use case 中,我們是讀出資料後,就會把它刪掉,在 Cassandra 裡,刪除等同於新增 (它會新增一筆叫 tombstone 的資料,標記這筆已死)。想當然爾,這個 Table 用久了會非常的 髒 ,效率也會低落。
- 所以這三四年來,我們的 Cassandra server ,每週都會用 cron 進行定期的 compaction,(compaction 會去除掉 tombstone 和已經被刪的那一筆),這樣每週的 Table 都是很乾淨的,效率可以維持在一定的水準。
- 9月底時,Linux 界發生了 Shellshock 的大漏洞,而且還連續爆好幾天。那時每天都要看有沒有新的 patch 釋出,有的話就要馬上 patch。
- 我手上有二十幾台 server ,一次 patch 這麼多台很累,所以就寫了 Ansible 的 playbook,讓這件事自動化。Ansible 是透過 ssh 連到遠端的 server 後,以 sudo 的方式執行指令。我們的 server 有 Ubuntu,也有 CentOS。Ubuntu 預設有 sudo,但 CentOS 沒有,所以我替那些 server 設定了 sudo。
- 十月初時,我改變 Cassandra 的配置,嘗試將 server 數量減半,看看可不可以撐住 (就 cost down)。這個計畫試運了一個月,我發現 cpu 使用率每週都在增加,到了十月底,已經撐不住了。最後認定實驗失敗,回復原本的 server 量。
- 十二月時,發生了上面提到的大當機。而十二月中,換上錯的藥,又大當機了一次
- 十二月底時,在測其他功能時,不小心地發現 cron job 已經停了三個月了。
cron job 停了三個月沒跑了! WTF !!
cron job 停了沒跑,三年來首見,這是怎樣?!後來才發現是我在九月底為了 shellshock 事件,改了 sudo 設定後,crond
就出現權限錯誤,它就不跑了。解決方法很簡單,crond
重啟就行了。但我怎麼知道要重啟啊!所以整個事件的因果鏈是這樣的:
- 2014 九月底 Shellshock 事件暴發
- 為了自動化 patch 而改 sudo 設定
- 造成 cron job 停跑
- Cassandra server 沒定期做 compaction,漸漸惡化
- 十月初的 Cassandra cost down,掩蓋了惡化的徵兆,我以為惡化是縮編 server 造成的
- 到了 12 月時,super column table 已經三個月沒清理,搖搖欲墜,後來果然讀到大一點,髒一點的資料就垮了。
Shellshock 真是害人不淺啊 (苦笑
Lessons Learned
Cassandra 的 super column 在過去是我們選擇它的原因之一,它很好用,但也潛伏著巨大的缺點。當然,Cassandra 官方這二年來已經拋棄它了,也提供更好的解決方案。但是這一次我體會到的,不單單是避開 super column,選用更好的解法而已。而是 - 過去曾經的優點,將會變成今日的缺點。我們很早就導入了 Cassandra,作為先期使用者,身上背負的 technical debt 其實是很巨大的。這筆債務可沒那麼好心,還讓你慢慢付利息,它會在出乎意料的時間點上、在你最脆弱的時候狠狠地咬上你一口。
當然這筆債我們當下出錯時就不打算再背了,決定砍掉重練。只是重練時,下錯了藥又走火入魔一次。上面也提了,這次的下錯藥是踩到 Cassandra durable queue anti pattern,雖然是因為經驗不足造成的,但如果有做壓力測試的話,也不會在線上環境裡出包這麼慘。
平常我都是會做壓力測試的,但這次沒有,原因之一是太過自信了:我想說過去的服務,使用 Super column 效率這麼差的設計都跑的動了,換上更新的設計豈不是游刃有餘 ? 應該沒問題才是。另外一個原因是那時還不知道真正的原因是 cron job 壞掉造成的,只想趕鴨子上架,快點修好。這次我重新學了 欲速則不達 這個千年的教誨,用血淋淋的方式。
最後是 cron job 失常造成系統惡化。這表示 cron job 本身其實也是要被稽核的,這個問題實在是無止無盡。你想嘛,原本 cron job 就常常扮演定期稽核別人的功能了,現在連它自己也要被稽核,那那個 meta-稽核者,又是誰來稽核呢 ? 這是個 2015 年新挑戰,待我研究研究。