線上Redis-Docker叢集出現物理機崩潰的一次問題記錄

王亞普發表於2018-07-30

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:磁碟並沒有寫滿,還有很多空間

image

因為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例項基本都無法正常工作,是否跟建立時間有關係?

image

  • 猜想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叢集出現的問題。

image

image

image

結論

出現問題的原因與devicemapper的nodiscard、Redis Aof機制以及磁碟超配相關,但是nodiscard引數絕對不能棄用的,建議的解決方案是:

  • Redis Aof產生的檔案儲存到外掛磁碟。
  • 重新規劃Redis叢集的磁碟使用情況,禁止超配,再做觀察。

轉載請註明出處,歡迎關注我的公眾號:亞普的技術輪子

亞普的技術輪子

相關文章