GP 生產問題:FATAL: DTM Initialization failure during recovery startup, cdbtm.c:1513
問題描述:
XX局點現網 GP 集群 X 月 X 號登錄時報錯,提示如下信息: FATAL: DTM Initialization failure during recovery startup, cdbtm.c:1513 。運維同事提示:重啟 GP 能暫時解決,但是過一段時間還是會出現此問題。進一步咨詢運維同事得知,故障發生的頻率較頻繁,大約1~2周就發生一次。GP 所在機房經常掉電,機器也肯定重啟過。按照規范,機器防火墻都是關閉的。提示運維同事使用 gpstate -e 命令查看發現無 segment 節點故障,使用 gpstate -a 查看也無問題(運維人員應該已經重啟過集群,所以此時查不出集群的故障)。
問題分析:
集群當前狀態應該是正常的,所以只能從日志中獲取一些信息。
首先咨詢運維同事,了解一下 GP 集群的配置:6臺機器均為 256 GB內存,swap 也有將近100 GB。
通過 select * from gp_segment_configuration 查詢得知集群的拓撲結構是: 6 臺機器(node1 ~ node6)均為 segment 節點,其中每臺 segment 節點部署有 8 個 primary segment 實例,且未配置 mirror。此外,master 節點和 node6 上的 segment 實例合設,且未配置 standby master 實例。同時也獲取了各實例的端口號信息。這里可知,由于 無 mirror, 集群中任意 segment 實例出問題,集群都會不可用。由于 node6 是集群內負載相對最重的(部署有一個 master 實例,8 個 segment 實例),因此咨詢運維人員 node6 上有沒有部署其他特別耗內存的應用,得到的回答是,除了 GP 還部署有 zookeeper, kafka, scala, spark, 關聯任務,任務服務等。猜測這臺節點的內存爭用還是比較厲害的。使用 free -g 查詢,發現當前內存還有較多富余, buffer 還有 228 GB,未用到 swap。
通過谷歌和百度,相關報錯信息有可能是機器重啟后防火墻沒有關閉導致的,但是運維人員查詢后確認防火墻全部關閉,排除這種可能。
請運維人員幫忙獲取一些日志:首先是 master 節點 的 pg 日志($GPDATA/master/gpseg-1/pg_log), 搜索定位 cbdtm.c: 1513 相關的錯誤所在的具體日志報錯,獲取了問題大概發生在 7 月9 號凌晨 2:03 , 函數拋出的堆棧信息可以獲得的信息有限,但是 cbdtm.c: 1513 這一條日志報錯信息上下文卻可以獲取到一些重要的其他信息: “LOG:could not connect to server: connection refused (seg1 xx.xx.65.9: 40001 , cbdtm.c: 1491 ”, 且多次出現該信息。可以推測,很有可能是 xx.xx.65.9 上的 seg1 節點 -- 它的端口號是 40001-- 起不來,導致集群整體無法使用.
由于 xx.xx.65.9 這臺機器正好是 node6 , 也就是 master 合設的機器。要求運維人員進一步查詢 node6 的 seg1 的 pg 日志,在生產問題發生時刻點附近沒有查到 PG 的報錯信息,但是有大量的 LOG 級別的 "incomplete startup packet" 信息。
綜合上面的分析,推測 GP 集群的內核參數相關設置需要優化,由于不恰當的內核參數設置導致個別 semgent 節點起不來,使得集群狀態出現異常。進一步查詢內核參數相關信息:
-
首先查詢數據庫內核參數:
通過 gpconfig -s shared_buffers 查詢結果可知,master 和 segment 實例的 shared_buffers 都被設為 20GB,這是一個過大的值,一般來說不需要設置這么大。由于 node6 上實際有 9 個 PG 實例,光是 shared_buffers 就占去了 180GB,相對總 RAM 256 GB,推測這個值有點大了。
通過 gpconfig -s gp_vmem_protect_limit 查詢,master 和 segment 上都設為了 8192, 也就是 8 GB,這個值大小正常,此參數限定單個 segment 實例在所有查詢中能用到的最大總內存。node6 上所有 segment 實例加起來最多到 64 GB,大小差不多。當然這個參數的設置在 GP 官方是有公式計算推薦值的。大約算了一下 ,按照官方的計算方式,結合 node6 上占內存應該不止 GP 一個的實際,GP 占用總量大約 200 GB 的內存,給其他應用預留一部分內存 + swap, gp_vmem_protect_limit 計算下來應該在 10 GB 左右,所以這個值設為 8GB 基本合理。設置過大很有可能導致 OOM。
-
接著查詢 OS 內核參數,GP 的內核參數最重要的有下面幾個,都是配置在 /etc/sysctl.conf 中的:
kernel.shmmax (有推薦計算公式,大約是系統總 RAM 大小的一半,對應于系統總頁數一半的內存大小,單位 Byte)
kernel.shmall (有推薦計算公式, 大約是系統總頁數的一半)
vm.overcommit_memory (默認 95)
vm.overcommit_ratio (官方建議設置為 2 ,防止 OOM)
這四個參數查詢下來發現:
kernel.shmmax 配的是 50000000000
kernel.shmall 配的是 40000000000
后兩者沒有配置。
這里,kernel.shmmax 顯然配置太小,按照官方手冊,kernel.shmmax 不應該低于一臺機器上總 segment 數 * shared_buffers 的配置,按照當前 shared_buffers 是 20GB 的配置,這個值不能低于 160GB,而當前 50 GB都不到。再加上 node6 上還有 master 節點,所以這個值應該比 160 GB 更大,才能保證集群能夠起來,且各節點不會 OOM。
-
-
問題的原因大概定位:
- 內核參數 kernel.shmmax 設置過小,kernel.shmall 設置和 kernel.shmmax 不匹配,shared_buffers 設置過大,導致集群有 OOM 的風險。
原因總結:
內核參數 kernel.shmmax 設置過小,kernel.shmall 設置和 kernel.shmmax 不匹配,shared_buffers 設置過大,導致集群有 OOM 的風險。需要對內核參數的取值進行優化,防止問題的再次產生。
解決對策:
-
重新計算 kernel.shmmax 和 kernel.shmall 的值,配置好 vm.overcommit_memory 和 vm.overcommit_ratio, 避免 OOM 的產生。
按照官方的推薦值,將 kernel.shmmax 設置為 128GB 對應的 Byte 值,同時 kernel.shmall 設置為 128 GB 對應的 OS 頁面數(前者除以 4096)。同時配置好另外兩個參數。所有節點全部修改 sysctl.conf, 并 sysctl -p 生效。
-
調低 shared_buffers
原來的 20 GB 的配置過大,首先使用 gp_config 將其調整到 15 GB,使用 gpstop -r -a 嘗試重啟集群。
發現集群重啟失敗,master 節點無法啟動。故要求將 kernel.shmmax 和 kernel.shmall 的值調整為原來的 2 倍,嘗試重啟集群。此時發現master 節點啟動成功,但是仍有一個 Segment 節點無法起來。
說明 shared_buffers 還是太大,故首先將 master 節點單獨啟動( gpstart -m ), 使用 gpconfig 將 shared_buffers 調整至 8 GB。停止 master 節點( gpstop -M fast)。再次嘗試重啟整個集群(gpstart -a) ,集群重啟成功。
總結
調整了內核參數,將原來過小的 OS 內核參數 kernel.shmmax 和 kernel.shmall 的值調大,并將 GP 的 shared_buffers 調小,使得 GP 啟動時能夠從操作系統分配到足夠多的內存,避免啟動失敗或者即使啟動成功而后續因為內存分配不足導致 OOM 使得實例崩潰。