分布式系统测试那些事儿——信心的毁灭与重建
本話題系列文章整理自 PingCAP NewSQL Meetup 第 26 期劉奇分享的《深度探索分布式系統(tǒng)測(cè)試》議題現(xiàn)場(chǎng)實(shí)錄。文章較長(zhǎng),為方便大家閱讀,會(huì)分為上中下三篇,本文為下篇。
接中篇:
ScyllaDB 有一個(gè)開源的東西,是專門用來給文件系統(tǒng)做 Failure Injection 的, 名字叫做 CharybdeFS。如果你想測(cè)試你的系統(tǒng),就是文件系統(tǒng)在哪不斷出問題,比如說寫磁盤失敗了,驅(qū)動(dòng)程序分配內(nèi)存失敗了,文件已經(jīng)存在等等,它都可以測(cè)模擬出來。
CharybdeFS: A new fault-injecting file system for software testing
Simulate the following errors:
disk IO error (EIO)
driver out of memory error (ENOMEM)
file already exists (EEXIST)
disk quota exceeded (EDQUOT)
再來看看 Cloudera,下圖是整個(gè) Cloudera 的一個(gè) Failure Injection 的結(jié)構(gòu)。
一邊是 Tools,一邊是它的整個(gè)的 Level 劃分。比如說整個(gè) Cluster, Cluster 上面有很多 Host,Host 上面又跑了各種 Service,整個(gè)系統(tǒng)主要用于測(cè)試 HDFS, HDFS 也是很努力的在做有效的測(cè)試。然后每個(gè)機(jī)器上部署一個(gè) AgenTEST,就用來注射那些可能出現(xiàn)的錯(cuò)誤。
看一下它們作用有多強(qiáng)大。
Cloudera: Simulate the following errors:
Packets loss/corrupt/reorder/duplicate/delay
Bandwidth limit: Limit the network bandwidth for the specified address and port.
DNSFail: Apply an injection to let the DNS fail.
FLOOD: Starts a DoS attack on the specified port.
BLOCK: Blocks all the packets directed to 10.0.0.0/8 (used internally by EC2).
SIGSTOP: Pause a given process in its current state.
BurnCPU/BurnIO/FillDISK/RONLY/FIllMEM/CorruptHDFS
HANG: Hang a host running a fork bomb.
PANIC: Force a kernel panic.
Suicide: Shut down the machine.
數(shù)據(jù)包是可以丟的,可以壞的,可以 reorder 的,比如說你發(fā)一個(gè) A,再發(fā)一個(gè) B,它可以給你 reorder,變成先發(fā)了 B 再發(fā)了 A,然后看你應(yīng)用程序有沒有正確的處理這種行為。接著發(fā)完一次后面再給你重發(fā),然后可以延遲,這個(gè)就比較簡(jiǎn)單。目前這個(gè)里面的大部分,TiKV 都有實(shí)現(xiàn),還有帶寬的限制,就比如說把你帶寬壓縮成 1M。以前我們遇到一個(gè)問題很有意思,發(fā)現(xiàn)有人把文件存到 Redis 里面,但 Redis 是帶多個(gè)用戶共享的,一個(gè)用戶就能把整個(gè) Redis 帶寬給打滿了,這樣其他人的帶寬就很卡,那這種很卡的時(shí)候 Redis 可能出現(xiàn)的行為是什么呢?我們并不需要一個(gè)用戶真的去把它打滿,只要用這種工具,瞬間就能出現(xiàn)我把你的帶寬限制到原來的 1%,假設(shè)別人在跟你搶帶寬,你的程序行為是什么?馬上就能出來,也不需要配很復(fù)雜的環(huán)境。這極大的提高了測(cè)試效率,同時(shí)能測(cè)試到很多 corner case。
然后 DNS fail。那 DNS fail 會(huì)有什么樣的結(jié)果?有測(cè)過嗎?可能都沒有想過這個(gè)問題,但是在一個(gè)真正的分布式系統(tǒng)里面,每一點(diǎn)都是有可能出錯(cuò)的。還有 FLOOD,假設(shè)你現(xiàn)在被攻擊了,整個(gè)系統(tǒng)的行為是什么樣的?然后一不小心被這個(gè) IP table 給 block 了,該怎么辦。這種情況我們確實(shí)出現(xiàn)過。我們一上來并發(fā),兩萬個(gè)連接一打出去,然后發(fā)現(xiàn)大部分都連不上,后來一看 IP table 自動(dòng)啟用了一個(gè)機(jī)制,然后把你們都 block。當(dāng)然我們后面查了半個(gè)小時(shí)左右,才把問題查出來。但這種實(shí)際上應(yīng)該是在最開始設(shè)計(jì)的時(shí)候就應(yīng)該考慮的東西。
如果你的進(jìn)程被暫停了,比如說大家在云上跑在 VM 里面,整個(gè) VM 為了升級(jí),先把你整個(gè)暫停了,升級(jí)完之后再把你恢復(fù)的時(shí)候會(huì)怎么樣?那簡(jiǎn)單來講,就是如果假設(shè)你程序是有 GC 的,GC 現(xiàn)在把我們的程序卡了五秒,程序行為是正常的嗎?五十秒呢?這個(gè)很有意思的就是,BurnCPU,就是再寫一個(gè)程序,把 CPU 全占了,然后讓你這個(gè)現(xiàn)在的程序只能使用一小部分的 CPU 的時(shí)候,你程序的行為是不是正常的。正常來講,你可能說我 CPU 不是瓶頸啊,我瓶頸在 IO,當(dāng)別人跟你搶 CPU,把你這個(gè) CPU 壓的很低的時(shí)候,到 CPU 是瓶頸的時(shí)候,正常你的程序的這個(gè)行為是不是正常的?還有 IO,跟你搶讀的資源,跟你搶寫的資源,然后 filedisk 把磁盤寫滿,寫的空間很少。比如說對(duì)數(shù)據(jù)庫而言,你創(chuàng)建你的 redo log 的時(shí)候,都已經(jīng)滿了會(huì)怎么樣?然后我突然把磁盤設(shè)為只讀,就你突然一個(gè)寫入會(huì)出錯(cuò),但是你接下來正常的讀寫行為是不是對(duì)的?很典型的一個(gè)例子,如果一個(gè)數(shù)據(jù)庫你現(xiàn)在寫入,磁盤滿了,那外面讀請(qǐng)求是否就能正常響應(yīng)。 Fill memory,就是瞬間把這個(gè) memory 給壓縮下來,讓你下次 malloc 的時(shí)候可能分布不到內(nèi)存。這個(gè)就和業(yè)務(wù)比較相關(guān)了,就是破壞 HDFS 的文件。其它的就是 Hang、Panic,然后還有自殺,直接關(guān)掉機(jī)器,整個(gè)系統(tǒng)的行為是什么樣的?
現(xiàn)在比較痛苦的一點(diǎn)是大家各自為政,每一家都做一套,但是沒有辦法做成一個(gè)通用的東西給所有的人去用。包括我們自己也做了一套,但是確實(shí)沒有辦法和其他的語言之間去 share,最早提到的那個(gè) libfu 庫實(shí)際上是在 C 語言寫的,那所有 C 相關(guān)的都可以去 call 那個(gè)庫。
Distributed testing
-
Namazu
-
ZooKeeper:
Found ZOOKEEPER-2212, ZOOKEEPER-2080 (race): (blog article)
-
Etcd:
Found etcdctl bug #3517 (timing specification), fixed in #3530. The fix also resulted a hint of #3611, Reproduced flaky tests {#4006, #4039}
YARN: Found YARN-4301 (fault tolerance), Reproduced flaky tests{1978, 4168, 4543, 4548, 4556}
-
然后 Namazu。大家肯定覺得 ZooKeeper 很穩(wěn)定呀, Facebook 在用、阿里在用、京東在用。大家都覺得這個(gè)東西也是很穩(wěn)定的,直到這個(gè)工具出現(xiàn)了,然后輕輕松松就找到 bug 了,所有的大家認(rèn)為的這種特別穩(wěn)定的系統(tǒng),其實(shí) bug 都還挺多的,這是一個(gè)毀三觀的事情,就是你覺得東西都很穩(wěn)定,都很 stable,其實(shí)不是的。從上面,我們能看到 Namazu 找到的 Etcd 的幾個(gè) bug,然后 YARN 的幾個(gè) bug,其實(shí)還有一些別的。
How TiKV use namazu
-
Use nmz container / non-container mode to disturb cluster.
Run container mode in CI for each commit. (1 hour)
Run non-container mode for a stable version. (1 week+)
-
Use extreme policy for process inspector
Pick up some processes and execute them with SCHED_RR scheduler. others are executed with SCHED_BATCH scheduler
Use [0, 30s] delay for filesystem inspector
接下來說一下 TiKV 用 Namazu 的一些經(jīng)驗(yàn)。因?yàn)槲覀冊(cè)?jīng)在系統(tǒng)上、在云上面出現(xiàn)過一次寫入磁盤花了五十幾秒才完成的情況,所以我們需要專門的工具模擬這個(gè)磁盤的抖動(dòng)。有時(shí)候一次寫入可能確實(shí)耗時(shí)比較久,那這種時(shí)候是不是 OK 的。大家如果能把這種東西統(tǒng)統(tǒng)用上,我覺得還能為很多開源系統(tǒng)找出一堆 bug。
稍微介紹一下我們現(xiàn)在運(yùn)行的基本策略,比如說我們會(huì)用 0 到 30 秒的這個(gè) delay (就是每一次你往文件系統(tǒng)的交互,比如說讀或者寫,那么我們會(huì)給你產(chǎn)生隨機(jī)的 0 到 30 秒的 delay ),但我們正常應(yīng)該還是需要去測(cè)三十秒到幾分鐘的延遲的情況,是否會(huì)讓整個(gè)系統(tǒng)崩掉了。
How TiKV simulate network transport
Drop/Delay messages randomly
Isolate Node
Partition [1, 2, 3, 4, 5] -> [1, 2, 3] + [4, 5]
Out of order messages
Filter messages
Duplicate and send redundant messages
怎么模擬網(wǎng)絡(luò)呢?假設(shè)你有網(wǎng)絡(luò),里面有五臺(tái)機(jī)器,那我現(xiàn)在想做一個(gè)腦裂怎么做?不能靠拔網(wǎng)線對(duì)吧?比如在 TiKV 的測(cè)試框架中,我們就可以直接通過 API 把 5 個(gè)節(jié)點(diǎn)腦裂成兩部分,讓 1, 2, 3 號(hào)節(jié)點(diǎn)互相聯(lián)通,4, 5 號(hào)節(jié)點(diǎn)也能聯(lián)通,這兩個(gè)分區(qū)彼此是隔離的,非常的方便。其實(shí)原理很簡(jiǎn)單,這種情況是用程序自己去模擬,假如是你發(fā)的包,自動(dòng)給你丟掉,或者直接告訴你 unreachable,那這個(gè)時(shí)候你就知道這個(gè)網(wǎng)絡(luò)就腦裂了,然后你怎么做?就是只允許特定類型的消息進(jìn)來,把其他的都丟掉,這樣一來你可以保證有些 bug 是必然重現(xiàn)的。這個(gè)框架給了我們極大的信心用來模擬并重現(xiàn)各種 corner case,確保這些 corner case 在單元測(cè)試中每次都能被覆蓋到。
How to test Rocksdb
Treat storage as a black box.
-
Three steps(7*24):
Fill data, Random kill -9
Restart
Consistent check.
-
Results:
Found 2 bugs. Both fixed
然后說說我們?cè)趺礈y(cè) RocksDB。 RocksDB 在大家印象中是很穩(wěn)定的,但我們最近發(fā)現(xiàn)了兩個(gè) bug。測(cè)的方法是這樣的:我們往 RocksDB 里面填數(shù)據(jù),然后隨機(jī)的一段時(shí)間去把它 kill 掉,kill 掉之后我們重啟,重新啟動(dòng)之后去檢測(cè)我們剛才 fail 的 data 是不是一致的,然后我們發(fā)現(xiàn)兩個(gè)可能造成數(shù)據(jù)丟失的 bug,但是官方的響應(yīng)速度非常快,幾天就都 fix 了。可是大家普遍運(yùn)行的是這么 stable 的系統(tǒng),為什么還會(huì)這么容易找到 bug?就說這個(gè)測(cè)試,如果是一直有這個(gè)測(cè)試的 cover,那么這兩個(gè) bug 可能很快就能夠被發(fā)現(xiàn)。
這是我們一個(gè)基本的,也就是當(dāng)成一個(gè)純黑盒的測(cè)。大家在測(cè)數(shù)據(jù)庫的時(shí)候,基本也是當(dāng)黑盒測(cè)。比如說 MySQL 寫入數(shù)據(jù),kill 掉,比如說我 commit 一個(gè)事務(wù),數(shù)據(jù)庫告訴我們 commit 成功,我把數(shù)據(jù)庫 kill 掉,我再去查我剛才提交的數(shù)據(jù)一樣能查到。這是一個(gè)正常的行為,如果查不到,說明整個(gè)系統(tǒng)有問題。
More tools
american fuzzy lop
其實(shí)還有一些更加先進(jìn)的工具,大家平時(shí)覺得特別穩(wěn)定的東西,都被摧殘的不行。Nginx 、NGPD、tcpdump 、LibreOffice ,如果有用 Linux 的同學(xué)可能知道,還有 Flash、sqlite。這個(gè)東西一出來,當(dāng)時(shí)大家很興奮,說怎么一下子找了這么多 bug,為什么以前那么穩(wěn)定的系統(tǒng)這么不堪一擊,會(huì)覺得這個(gè)東西它還挺智能的。就比如說你程序里面有個(gè) if 分支,它是這樣的,假如你程序有一百條指令,它先從前面一直走,走到某條分支指令的時(shí)候,它是一直持續(xù)探索,一個(gè)分支走不下去,它會(huì)一直在這兒持續(xù)探索,再給你隨機(jī)的輸入,直到我探索進(jìn)去了,我記下來了下次我知道我用這個(gè)輸入可以進(jìn)去特定的分支。那我可以再往下走,比如說你 if 分支進(jìn)去之后里面還有 if ,那你傳統(tǒng)手段可能探測(cè)不進(jìn)去了但它可以,它記錄一下,我這個(gè)可以進(jìn)去,然后我重來,反正我繼續(xù)輸入這個(gè),我再往里面走,一旦我探測(cè)到一個(gè)新的分支,我再記住,我再往里面走。所以它一出來的時(shí)候大家都說這個(gè)真厲害,一下發(fā)現(xiàn)這么多 bug。但最激動(dòng)的不是這些人,最激動(dòng)的是黑客,為什么?因?yàn)橥蝗挥泻芏鄺R绯觥⒍岩绯雎┒幢话l(fā)現(xiàn)了,然后就可以寫一堆工具去攻擊線上的這么多系統(tǒng)。所以很多的技術(shù)的推進(jìn)在早期的時(shí)候是黑客做出來,但是他們的目的當(dāng)然不一定是為了測(cè)試 bug,而是為了怎么黑一個(gè)系統(tǒng)進(jìn)去,這是他們當(dāng)時(shí)做的,所以這個(gè)工具也是非常強(qiáng)大、非常有意思的,大家可以拿去研究一下自己的系統(tǒng)。
大家印象里面各種文件系統(tǒng)是很穩(wěn)定的,可是當(dāng)用 American fuzzy lop 來測(cè)試的時(shí)候,被驚呆了。 Btrfs 連 5 秒都沒有堅(jiān)持到就跪了,大家用的最多的 Ext4 是最堅(jiān)挺的,也才抗了兩個(gè)小時(shí)!!!
再來說說 Google,Google 怎么做測(cè)試對(duì)外講的不多,最近 Chrome team 開源了他們的 Fuzz 測(cè)試工具 OSS-Fuzz,這個(gè)工具強(qiáng)大的地方在于自動(dòng)化做的極好:
發(fā)現(xiàn) bug 后自動(dòng)創(chuàng)建 issue
bug 解決后自動(dòng) verify
更驚人的是 OSS-Fuzz 集群一周可以跑 ~4 trillion test cases 更多細(xì)節(jié)大家可以看這篇文章:Announcing OSS-Fuzz: Continuous Fuzzing for Open Source Software
另外有些工具能讓分布式系統(tǒng)開發(fā)人員的生活變得更美好一點(diǎn)。
Tracing tools may help you
Google Dapper
Zipkin
OpenTracing
還有 Tracing,比如說我一個(gè) query 過來,然后經(jīng)過這么多層,經(jīng)過這么多機(jī)器,然后在不同的地方,不同環(huán)節(jié)耗時(shí)多久,實(shí)際上這個(gè)在分布式系統(tǒng)里面,有個(gè)專門的東西做 Tracing ,就是 distribute tracing tools。它可以用一條線來表達(dá)你的請(qǐng)求在各個(gè)階段耗時(shí)多長(zhǎng),如果有幾段,那么分到幾個(gè)機(jī)器,分別并行的時(shí)候好了多長(zhǎng)時(shí)間。大體的結(jié)構(gòu)是這樣的:
這里是一個(gè)具體的例子:
很清晰,一看就知道了,不用去看 log,這事其實(shí)一點(diǎn)也不新鮮,Google 十幾年前就做了一個(gè)分布式追蹤的工具。然后開源社區(qū)要做一個(gè)實(shí)現(xiàn)叫做 Zipkin,好像是 java 還是什么寫的,又出了新的叫 OpenTracing,是 Go 寫的。我們現(xiàn)在正準(zhǔn)備上這個(gè)系統(tǒng),用來追蹤 TiDB 的請(qǐng)求在各個(gè)階段的響應(yīng)時(shí)間。
最后想說一下,大家研究系統(tǒng)發(fā)現(xiàn) bug 多了之后,不要對(duì)系統(tǒng)就喪失了信心,畢竟bug 一直在那里,只是從前沒有發(fā)現(xiàn),現(xiàn)在發(fā)現(xiàn)得多了,總體上新的測(cè)試方法讓系統(tǒng)的質(zhì)量比以前好了很多。好像有點(diǎn)超時(shí)了,先聊到這里吧,還有好多細(xì)節(jié)沒法展開,下次再聊。
(本系列完結(jié))
總結(jié)
以上是生活随笔為你收集整理的分布式系统测试那些事儿——信心的毁灭与重建的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mac sierra 10.12部分注册
- 下一篇: Fiddler快速入门