Redis-Docker叢集的一次踩坑記錄
專案背景
線上redis的docker叢集用於生產線上有一段時間,也算是一個全新的嘗試,利用docker的優勢實現高效的redis例項建立和排程。
問題描述
- 最近一段時間,有幾臺出現崩潰問題,機器load不斷升高,有的高達5000多。
- 諸多執行緒處於D狀態,很多請求以及linux命令出現卡死狀態。
現象1:很多執行緒處於D狀態
$ > dmesg
INFO: task jbd2/dm-20-8:198571 blocked for more than 120 seconds.
Not tainted 2.6.32-431.29.2.el6.x86_64 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
jbd2/dm-20-8 D 0000000000000009 0 198571 2 0x00000080
ffff88316ac77b30 0000000000000046 0000000000000000 ffffffffa000169d
ffff88316ac77aa0 ffffffff810149b9 ffff88316ac77ae0 0000000000000286
ffff88316acff058 ffff88316ac77fd8 000000000000fbc8 ffff88316acff058
Call Trace:
[<ffffffffa000169d>] ? __map_bio+0xad/0x140 [dm_mod]
[<ffffffff810149b9>] ? read_tsc+0x9/0x20
[<ffffffff81120d00>] ? sync_page+0x0/0x50
[<ffffffff81529c73>] io_schedule+0x73/0xc0
[<ffffffff81120d3d>] sync_page+0x3d/0x50
[<ffffffff8152a73f>] __wait_on_bit+0x5f/0x90
[<ffffffff81120f73>] wait_on_page_bit+0x73/0x80
[<ffffffff8109c530>] ? wake_bit_function+0x0/0x50
[<ffffffff81136fc5>] ? pagevec_lookup_tag+0x25/0x40
[<ffffffff8112139b>] wait_on_page_writeback_range+0xfb/0x190
[<ffffffff8112145f>] filemap_fdatawait+0x2f/0x40
[<ffffffffa00c5e59>] jbd2_journal_commit_transaction+0x7e9/0x1500 [jbd2]
[<ffffffff810096f0>] ? __switch_to+0xd0/0x320
[<ffffffff81085f3b>] ? try_to_del_timer_sync+0x7b/0xe0
[<ffffffffa00cba48>] kjournald2+0xb8/0x220 [jbd2]
[<ffffffff8109c4b0>] ? autoremove_wake_function+0x0/0x40
[<ffffffffa00cb990>] ? kjournald2+0x0/0x220 [jbd2]
[<ffffffff8109c106>] kthread+0x96/0xa0
[<ffffffff8100c20a>] child_rip+0xa/0x20
[<ffffffff8109c070>] ? kthread+0x0/0xa0
[<ffffffff8100c200>] ? child_rip+0x0/0x20
複製程式碼
現象2:磁碟並沒有寫滿,還有很多空間
因為devicemapper配了nodiscard,docker info看到的data space並不一定是例項真正使用的空間,所以利用指令碼分別統計了每個例項所用的磁碟空間,發現每個例項所使用的磁碟空間大概都不到10g,均是redis aof產出的持久化檔案。
現象3:暫停/刪除例項沒反應
$ > docker stop {containerId}
$ > docker rm -f {containerId}
複製程式碼
現象4:並不是例項太多造成的偶然現象
$ > service docker stop
$ > docker start {containerId}
只啟動一個redis例項,觀察top,load持續上漲,依然出現現象3的問題。
複製程式碼
現象5:devicemapper的使用空間顯示已滿
$ > docker info
Containers: 21
Images: 79
Storage Driver: devicemapper
Pool Name: docker-8:2-4851000-pool
Pool Blocksize: 65.54 kB
Data file: /dev/sda3
Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata
Data Space Used: 1.677 TB
Data Space Total: 1.677 TB
Metadata Space Used: 852.1 MB
Metadata Space Total: 10.74 GB
Library Version: 1.02.82-git (2013-10-04)
Execution Driver: native-0.2
Kernel Version: 2.6.32-431.29.2.el6.x86_64
複製程式碼
問題排查
猜想1:可能跟devicemapper配置了nodiscard有關,有待驗證。
如果不配置nodiscard,使用Docker時核心會隨機crash。具體問題可以參考:蘑菇街的部落格
猜想2:因為devicemapper配置了nodiscard,是不是因為每次Redis產生的aof檔案並沒有被回收掉。
實驗驗證1、2
docker的啟動引數配置成--storage-opt dm.loopdatasize=85G --storage-opt dm.basesize=80G,本地只啟動一個容器,執行一個指令碼,指令碼內容如下:
#!/bin/bash
cd /tmp
while true
do
dd if=/dev/zero of=/tmp/hello.txt bs=1G count=20
echo "creat hello.txt success"
rm -rf hello.txt
echo "rm hello.txt success"
sleep 1
done
複製程式碼
實驗結果
docker info觀察Data Space Used,很快上漲到約80G,但是指令碼依然可以持續執行。說明已經被刪除的檔案佔用的空間可以被重新使用。
-
猜想3:出問題的宿主機都是磁碟超配,是否有關係?
-
猜想4:宿主機上有約20個Redis例項,發現並不是所有例項都掛掉,有一部分還在正常執行,新申請的Redis例項基本都無法正常工作,是否跟建立時間有關係?
猜想5:有可能老的Redis例項已經把磁碟吃滿,新申請的例項無法重複使用已被舊Redis例項所申請過且已經被刪除的檔案所佔用的空間資源。簡單說就是,容器A使用過的空間資源中即使檔案被刪除,容器B也無法重複利用,好像容器A獨享一樣。
實驗驗證3、4、5
docker的啟動引數依然配置成--storage-opt dm.loopdatasize=85G --storage-opt dm.basesize=80G,本地啟動兩個容器A和B,首先在容器A裡執行上述實驗中同樣的指令碼,在Data Space Used停留在80G不再上漲後,進入容器B,執行:
dd if=/dev/zero of=/tmp/hello.txt bs=1G count=20
複製程式碼
實驗結果
在容器B裡執行命令時,當Data Space Used上漲到約85G之後程式卡死,top觀察load持續升高,此時容器docker run一個新的容器也是卡死狀態。基本復現了線上Redis叢集出現的問題。
結論
出現問題的原因與devicemapper的nodiscard、Redis Aof機制以及磁碟超配相關,但是nodiscard引數絕對不能棄用的,建議的解決方案是:
- Redis Aof產生的檔案儲存到外掛磁碟。
- 重新規劃Redis叢集的磁碟使用情況,禁止超配,再做觀察。
轉載請註明出處,歡迎關注我的公眾號:亞普的技術輪子