Flink Checkpoint 問題排查實用指南

在 Flink 中,狀態可靠性保證由 Checkpoint 支持,當作業出現 failover 的情況下,Flink 會從最近成功的 Checkpoint 恢復。在實際情況中,我們可能會遇到 Checkpoint 失敗,或者 Checkpoint 慢的情況,本文會統一聊一聊 Flink 中 Checkpoint 異常的情況(包括失敗和慢),以及可能的原因和排查思路。

1. Checkpoint 流程簡介

首先我們需要了解 Flink 中 Checkpoint 的整個流程是怎樣的,在瞭解整個流程之後,我們才能在出問題的時候,更好的進行定位分析。

從上圖我們可以知道,Flink 的 Checkpoint 包括如下幾個部分:

  • JM trigger checkpoint
  • Source 收到 trigger checkpoint 的 PRC,自己開始做 snapshot,並往下游發送 barrier
  • 下游接收 barrier(需要 barrier 都到齊纔會開始做 checkpoint)
  • Task 開始同步階段 snapshot
  • Task 開始異步階段 snapshot
  • Task snapshot 完成,彙報給 JM

上面的任何一個步驟不成功,整個 checkpoint 都會失敗。

2 Checkpoint 異常情況排查

2.1 Checkpoint 失敗

可以在 Checkpoint 界面看到如下圖所示,下圖中 Checkpoint 10423 失敗了。

點擊 Checkpoint 10423 的詳情,我們可以看到類系下圖所示的表格(下圖中將 operator 名字截取掉了)。

上圖中我們看到三行,表示三個 operator,其中每一列的含義分別如下:

  • 其中 Acknowledged 一列表示有多少個 subtask 對這個 Checkpoint 進行了 ack,從圖中我們可以知道第三個 operator 總共有 5 個 subtask,但是隻有 4 個進行了 ack;
  • 第二列 Latest Acknowledgement 表示該 operator 的所有 subtask 最後 ack 的時間;
  • End to End Duration 表示整個 operator 的所有 subtask 中完成 snapshot 的最長時間;
  • State Size 表示當前 Checkpoint 的 state 大小 -- 主要這裏如果是增量 checkpoint 的話,則表示增量大小;
  • Buffered During Alignment 表示在 barrier 對齊階段積攢了多少數據,如果這個數據過大也間接表示對齊比較慢);

Checkpoint 失敗大致分爲兩種情況:Checkpoint Decline 和 Checkpoint Expire。

2.1.1 Checkpoint Decline

我們能從 jobmanager.log 中看到類似下面的日誌
Decline checkpoint 10423 by task 0b60f08bf8984085b59f8d9bc74ce2e1 of job 85d268e6fbc19411185f7e4868a44178. 其中
10423 是 checkpointID,0b60f08bf8984085b59f8d9bc74ce2e1 是 execution id,85d268e6fbc19411185f7e4868a44178 是 job id,我們可以在 jobmanager.log 中查找 execution id,找到被調度到哪個 taskmanager 上,類似如下所示:

2019-09-02 16:26:20,972 INFO  [jobmanager-future-thread-61] org.apache.flink.runtime.executiongraph.ExecutionGraph        - XXXXXXXXXXX (100/289) (87b751b1fd90e32af55f02bb2f9a9892) switched from SCHEDULED to DEPLOYING.
2019-09-02 16:26:20,972 INFO  [jobmanager-future-thread-61] org.apache.flink.runtime.executiongraph.ExecutionGraph        - Deploying XXXXXXXXXXX (100/289) (attempt #0) to slot container_e24_1566836790522_8088_04_013155_1 on hostnameABCDE

從上面的日誌我們知道該 execution 被調度到 hostnameABCDE 的 container_e24_1566836790522_8088_04_013155_1 slot 上,接下來我們就可以到 container container_e24_1566836790522_8088_04_013155 的 taskmanager.log 中查找 Checkpoint 失敗的具體原因了。

另外對於 Checkpoint Decline 的情況,有一種情況我們在這裏單獨抽取出來進行介紹:Checkpoint Cancel。

當前 Flink 中如果較小的 Checkpoint 還沒有對齊的情況下,收到了更大的 Checkpoint,則會把較小的 Checkpoint 給取消掉。我們可以看到類似下面的日誌:

$taskNameWithSubTaskAndID: Received checkpoint barrier for checkpoint 20 before completing current checkpoint 19\. Skipping current checkpoint.

這個日誌表示,當前 Checkpoint 19 還在對齊階段,我們收到了 Checkpoint 20 的 barrier。然後會逐級通知到下游的 task checkpoint 19 被取消了,同時也會通知 JM 當前 Checkpoint 被 decline 掉了。

在下游 task 收到被 cancelBarrier 的時候,會打印類似如下的日誌:

DEBUG
$taskNameWithSubTaskAndID: Checkpoint 19 canceled, aborting alignment.

或者

DEBUG
$taskNameWithSubTaskAndID: Checkpoint 19 canceled, skipping alignment.

或者

WARN
$taskNameWithSubTaskAndID: Received cancellation barrier for checkpoint 20 before completing current checkpoint 19\. Skipping current checkpoint.

上面三種日誌都表示當前 task 接收到上游發送過來的 barrierCancel 消息,從而取消了對應的 Checkpoint。

2.1.2 Checkpoint Expire

如果 Checkpoint 做的非常慢,超過了 timeout 還沒有完成,則整個 Checkpoint 也會失敗。當一個 Checkpoint 由於超時而失敗是,會在 jobmanager.log 中看到如下的日誌:

Checkpoint 1 of job 85d268e6fbc19411185f7e4868a44178  expired before completing.

表示 Chekpoint 1 由於超時而失敗,這個時候可以可以看這個日誌後面是否有類似下面的日誌:

Received late message for now expired checkpoint attempt 1 from 0b60f08bf8984085b59f8d9bc74ce2e1 of job 85d268e6fbc19411185f7e4868a44178.

可以按照 2.1.1 中的方法找到對應的 taskmanager.log 查看具體信息。

下面的日誌如果是 DEBUG 的話,我們會在開始處標記 DEBUG

我們按照下面的日誌把 TM 端的 snapshot 分爲三個階段,開始做 snapshot 前,同步階段,異步階段:

DEBUG
Starting checkpoint (6751) CHECKPOINT on task taskNameWithSubtasks (4/4)

這個日誌表示 TM 端 barrier 對齊後,準備開始做 Checkpoint。

DEBUG
2019-08-06 13:43:02,613 DEBUG org.apache.flink.runtime.state.AbstractSnapshotStrategy       - DefaultOperatorStateBackend snapshot (FsCheckpointStorageLocation {fileSystem=org.apache.flink.core.fs.SafetyNetWrapperFileSystem@70442baf, checkpointDirectory=xxxxxxxx, sharedStateDirectory=xxxxxxxx, taskOwnedStateDirectory=xxxxxx, metadataFilePath=xxxxxx, reference=(default), fileStateSizeThreshold=1024}, synchronous part) in thread Thread[Async calls on Source: xxxxxx
_source -> Filter (27/70),5,Flink Task Threads] took 0 ms.

上面的日誌表示當前這個 backend 的同步階段完成,共使用了 0 ms。

DEBUG
DefaultOperatorStateBackend snapshot (FsCheckpointStorageLocation {fileSystem=org.apache.flink.core.fs.SafetyNetWrapperFileSystem@7908affe, checkpointDirectory=xxxxxx, sharedStateDirectory=xxxxx, taskOwnedStateDirectory=xxxxx,  metadataFilePath=xxxxxx, reference=(default), fileStateSizeThreshold=1024}, asynchronous part) in thread Thread[pool-48-thread-14,5,Flink Task Threads] took 369 ms

上面的日誌表示異步階段完成,異步階段使用了 369 ms

在現有的日誌情況下,我們通過上面三個日誌,定位 snapshot 是開始晚,同步階段做的慢,還是異步階段做的慢。然後再按照情況繼續進一步排查問題。

2.2 Checkpoint 慢

在 2.1 節中,我們介紹了 Checkpoint 失敗的排查思路,本節會分情況介紹 Checkpoint 慢的情況。

Checkpoint 慢的情況如下:比如 Checkpoint interval 1 分鐘,超時 10 分鐘,Checkpoint 經常需要做 9 分鐘(我們希望 1 分鐘左右就能夠做完),而且我們預期 state size 不是非常大。

對於 Checkpoint 慢的情況,我們可以按照下面的順序逐一檢查。

2.2.0 Source Trigger Checkpoint 慢

這個一般發生較少,但是也有可能,因爲 source 做 snapshot 並往下游發送 barrier 的時候,需要搶鎖(這個現在社區正在進行用 mailBox 的方式替代當前搶鎖的方式,詳情參考[1])。如果一直搶不到鎖的話,則可能導致 Checkpoint 一直得不到機會進行。如果在 Source 所在的 taskmanager.log 中找不到開始做 Checkpoint 的 log,則可以考慮是否屬於這種情況,可以通過 jstack 進行進一步確認鎖的持有情況。

2.2.1 使用增量 Checkpoint

現在 Flink 中 Checkpoint 有兩種模式,全量 Checkpoint 和 增量 Checkpoint,其中全量 Checkpoint 會把當前的 state 全部備份一次到持久化存儲,而增量 Checkpoint,則只備份上一次 Checkpoint 中不存在的 state,因此增量 Checkpoint 每次上傳的內容會相對更好,在速度上會有更大的優勢。

現在 Flink 中僅在 RocksDBStateBackend 中支持增量 Checkpoint,如果你已經使用 RocksDBStateBackend,可以通過開啓增量 Checkpoint 來加速,具體的可以參考 [2]。

2.2.2 作業存在反壓或者數據傾斜

我們知道 task 僅在接受到所有的 barrier 之後纔會進行 snapshot,如果作業存在反壓,或者有數據傾斜,則會導致全部的 channel 或者某些 channel 的 barrier 發送慢,從而整體影響 Checkpoint 的時間,這兩個可以通過如下的頁面進行檢查:

上圖中我們選擇了一個 task,查看所有 subtask 的反壓情況,發現都是 high,表示反壓情況嚴重,這種情況下會導致下游接收 barrier 比較晚。

上圖中我們選擇其中一個 operator,點擊所有的 subtask,然後按照 Records Received/Bytes Received/TPS 從大到小進行排序,能看到前面幾個 subtask 會比其他的 subtask 要處理的數據多。

如果存在反壓或者數據傾斜的情況,我們需要首先解決反壓或者數據傾斜問題之後,再查看 Checkpoint 的時間是否符合預期。

2.2.2 Barrier 對齊慢

從前面我們知道 Checkpoint 在 task 端分爲 barrier 對齊(收齊所有上游發送過來的 barrier),然後開始同步階段,再做異步階段。如果 barrier 一直對不齊的話,就不會開始做 snapshot。

barrier 對齊之後會有如下日誌打印:

DEBUG
Starting checkpoint (6751) CHECKPOINT on task taskNameWithSubtasks (4/4)

如果 taskmanager.log 中沒有這個日誌,則表示 barrier 一直沒有對齊,接下來我們需要了解哪些上游的 barrier 沒有發送下來,如果你使用 At Least Once 的話,可以觀察下面的日誌:

DEBUG
Received barrier for checkpoint 96508 from channel 5

表示該 task 收到了 channel 5 來的 barrier,然後看對應 Checkpoint,再查看還剩哪些上游的 barrier 沒有接受到,對於 ExactlyOnce 暫時沒有類似的日誌,可以考慮自己添加,或者 jmap 查看。

2.2.3 主線程太忙,導致沒機會做 snapshot

在 task 端,所有的處理都是單線程的,數據處理和 barrier 處理都由主線程處理,如果主線程在處理太慢(比如使用 RocksDBBackend,state 操作慢導致整體處理慢),導致 barrier 處理的慢,也會影響整體 Checkpoint 的進度,在這一步我們需要能夠查看某個 PID 對應 hotmethod,這裏推薦兩個方法:

  1. 多次連續 jstack,查看一直處於 RUNNABLE 狀態的線程有哪些;
  2. 使用工具 AsyncProfile dump 一份火焰圖,查看佔用 CPU 最多的棧;

如果有其他更方便的方法當然更好,也歡迎推薦。

2.2.4 同步階段做的慢

同步階段一般不會太慢,但是如果我們通過日誌發現同步階段比較慢的話,對於非 RocksDBBackend 我們可以考慮查看是否開啓了異步 snapshot,如果開啓了異步 snapshot 還是慢,需要看整個 JVM 在幹嘛,也可以使用前一節中的工具。對於 RocksDBBackend 來說,我們可以用 iostate 查看磁盤的壓力如何,另外可以查看 tm 端 RocksDB 的 log 的日誌如何,查看其中 SNAPSHOT 的時間總共開銷多少。

RocksDB 開始 snapshot 的日誌如下:

2019/09/10-14:22:55.734684 7fef66ffd700 [utilities/checkpoint/checkpoint_impl.cc:83] Started the snapshot process -- creating snapshot in directory /tmp/flink-io-87c360ce-0b98-48f4-9629-2cf0528d5d53/XXXXXXXXXXX/chk-92729

snapshot 結束的日誌如下:

2019/09/10-14:22:56.001275 7fef66ffd700 [utilities/checkpoint/checkpoint_impl.cc:145] Snapshot DONE. All is good

2.2.6 異步階段做的慢

對於異步階段來說,tm 端主要將 state 備份到持久化存儲上,對於非 RocksDBBackend 來說,主要瓶頸來自於網絡,這個階段可以考慮觀察網絡的 metric,或者對應機器上能夠觀察到網絡流量的情況(比如 iftop)。

對於 RocksDB 來說,則需要從本地讀取文件,寫入到遠程的持久化存儲上,所以不僅需要考慮網絡的瓶頸,還需要考慮本地磁盤的性能。另外對於 RocksDBBackend 來說,如果覺得網絡流量不是瓶頸,但是上傳比較慢的話,還可以嘗試考慮開啓多線程上傳功能[3]。

3 總結

在第二部分內容中,我們介紹了官方編譯的包的情況下排查一些 Checkpoint 異常情況的主要場景,以及相應的排查方法,如果排查了上面所有的情況,還是沒有發現瓶頸所在,則可以考慮添加更詳細的日誌,逐步將範圍縮小,然後最終定位原因。

上文提到的一些 DEBUG 日誌,如果 flink dist 包是自己編譯的話,則建議將 Checkpoint 整個步驟內的一些 DEBUG 改爲 INFO,能夠通過日誌瞭解整個 Checkpoint 的整體階段,什麼時候完成了什麼階段,也在 Checkpoint 異常的時候,快速知道每個階段都消耗了多少時間。

參考內容

[1] Change threading-model in StreamTask to a mailbox-based approach 
[2] 增量 checkpoint 原理介紹 
[3] RocksDBStateBackend 多線程上傳 State



本文作者:邱從賢(山智)

閱讀原文

本文爲雲棲社區原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章