前言
在容器化時代,使用 Docker 部署像 HBase 這樣復雜的分布式系統也比較方便。社區也提供了許多方便的 HBase Docker 鏡像,沒有找到官方的 apache
的,但有包含許多大數據工具的 harisekhon/hbase
或用于學習目的的 bigdatauniversity/hbase
等,下載量多的一般都比較老了,也可以找最新的。選擇一個合適的鏡像可以快速啟動 HBase 實例。小子是在老鏡像上基礎上創建的。
問題及結論
然而,即使使用了預構建的鏡像,仍然可能遇到挑戰。先總結一下小子在 Docker 環境中使用 HBase 2.5.11 時,遇到的問題及解決方案,后面是詳細介紹:
- HMaster 啟動失敗: 由于本地文件系統缺少
hflush
能力,通過設置hbase.unsafe.stream.capability.enforce=false
繞過強制檢查得以解決。 - Zookeeper 啟動失敗: 因
/tmp
目錄數據不一致,通過清理 Zookeeper 數據目錄并強調持久化存儲的重要性得以解決。 - Snappy 壓縮失敗: 由于默認 Codec 依賴 Native 庫在 Docker 環境中出現問題,通過在
hbase-site.xml
中配置使用純 Java 的 Snappy Codec (hbase.io.compress.snappy.codec
) 解決。
環境
- HBase 版本: 2.5.11
- 部署環境: Docker (未使用 HDFS,
hbase.rootdir
(/hbase-data) 指向本地文件系統路徑) - 1-master+1-regionserver+1-zookeeper
問題一:HMaster 啟動失敗 - “StreamLacksCapabilityException: hflush”
在嘗試啟動 HBase 集群時,遇到的第一個攔路虎是 HMaster 進程無法成功啟動。檢查 Master 日志 (logs/hbase--master-....log
),定位到以下關鍵錯誤信息:
2025-04-26T09:04:32,270 WARN [main] wal.AbstractProtobufLogWriter: Init output failed, path=file:/hbase-data/MasterData/WALs/...
org.apache.hadoop.hbase.util.CommonFSUtils$StreamLacksCapabilityException: hflush# ... (堆棧跟蹤)2025-04-26T09:04:32,277 ERROR [main] wal.AsyncFSWALProvider: The RegionServer async write ahead log provider relies on the ability to call hflush for proper operation... but the current FileSystem does not support doing so. Please check the config value of 'hbase.wal.dir' and ensure it points to a FileSystem mount that has suitable capabilities...2025-04-26T09:04:32,281 ERROR [main] master.HMaster: Failed to become active master
java.io.IOException: cannot get log writer# ... (Caused by: StreamLacksCapabilityException: hflush)2025-04-26T09:04:32,282 ERROR [main] master.HMaster: ***** ABORTING master ...: Unhandled exception. Starting shutdown. *****
原因分析:
日志非常清晰地指出了問題所在:
- HBase 的
hbase.rootdir
被配置為file:/hbase-data
,使用的是本地文件系統。 - HBase 默認(或當前配置)的 Write-Ahead Log (WAL) 提供者是
AsyncFSWALProvider
。 AsyncFSWALProvider
設計上依賴底層文件系統提供hflush
功能,這是 HDFS 文件系統的一個重要能力,用于確保數據被強制刷寫到存儲介質,保證數據持久性。- 然而,標準的本地文件系統 (
file:/
) 實現通常不保證或不報告支持hflush
這個 HDFS 特有的能力。 - 因此,在初始化 WAL 時,由于底層文件系統缺少所需的能力,導致
AsyncFSWALProvider
初始化失敗,進而 HMaster 啟動中止。
解決方案探索與最終方案:
小子查資料(包括從gemini和chatgpt上問)首先嘗試修改 hbase-site.xml
(在$HBASE_HOME/conf下),將 WAL 提供者更改為舊版的、理論上更適合本地文件系統的 filesystem
:
<property><name>hbase.wal.provider</name><value>filesystem</value>
</property>
遺憾的是,僅僅這樣修改并重啟后,問題依舊存在。推測可能是 HBase 或 Hadoop 的底層庫在實際使用 WAL 提供者之前,仍然會進行一個文件系統能力的預檢查,這個檢查仍然失敗了。
最終找到并確認有效的解決方案是在 hbase-site.xml
中添加以下配置,顯式地禁用強制的文件流能力檢查(是聯網的chatgpt給出的答案):
<property><name>hbase.unsafe.stream.capability.enforce</name><value>false</value>
</property>
解釋: 將此參數設置為 false
,相當于告訴 HBase:“我知道我使用的文件系統可能沒有報告支持 hflush
這些高級能力,但請不要因為這個檢查就失敗,繼續嘗試運行。” 這樣就繞過了啟動時的強制檢查。
關于 “unsafe”: 這個名字提醒我們,禁用檢查意味著 HBase 失去了對底層存儲 hflush
能力的強保證。對于本地文件系統,操作系統通常有緩存機制,雖然 filesystem
WAL 提供者會盡力(如使用 fsync
)保證數據落盤,但在極端情況(如 OS 崩潰且 fsync
未完成)下,理論上存在丟失極少量已確認寫入的數據的風險。不過,在 Docker 中使用可靠的本地卷進行開發或測試,這種風險通常是可接受的。生產環境中使用非 HDFS 存儲時,務必謹慎評估此配置的安全性影響。
應用此配置并重啟后,HMaster 終于成功啟動了!
問題二:Zookeeper 啟動失敗 - “No snapshot found, but there are log entries”
在多次重啟(尤其在容器內直接重啟hbase時)的 Zookeeper 進程也可能啟動失敗。查看 Zookeeper 的日志 ($HBASE_HOME/logs/zookeeper.log
),發現了這個錯誤:
2025-04-26T08:46:06,621 INFO [main] persistence.SnapStream: zookeeper.snapshot.compression.method = CHECKED
java.io.IOException: No snapshot found, but there are log entries. Something is broken!at org.apache.zookeeper.server.persistence.FileTxnSnapLog.restore(FileTxnSnapLog.java:290)at org.apache.zookeeper.server.ZKDatabase.loadDataBase(ZKDatabase.java:285)# ... (堆棧跟蹤省略)
日志同時顯示 Zookeeper 的數據和快照目錄位于 /tmp/hbase-root/zookeeper/version-2
。
原因分析:
這個錯誤表明 Zookeeper 的數據目錄處于一個不一致的狀態:存在事務日志文件,卻沒有對應的快照文件作為恢復的起點。在 Docker 環境下,尤其當數據目錄指向 /tmp
時,常見原因有:
/tmp
的易失性: 容器重啟可能導致/tmp
目錄下的內容被部分或全部清除。- 不正常關閉: 上次容器非正常退出,可能導致 Zookeeper 數據文件寫入中斷或損壞。
- 缺少持久化: Zookeeper 的數據目錄沒有配置 Docker Volume 或綁定掛載到宿主機的持久化存儲上。
小子的容器原因,是因為沒有重啟pod,老版本hbase啟用成功過,zk有數據持久化到目錄,升級到高版本不兼容,就啟動不起來了。
解決方案:
最直接有效的辦法是清理掉這個損壞的 Zookeeper 數據目錄。
- 停止 HBase 相關容器。
- 進入 Master 容器(如果 ZK 在此容器內)或操作對應的 Docker Volume。
- 刪除 Zookeeper 數據目錄:
# 示例:在容器內執行 rm -rf /tmp/hbase-root/zookeeper
或直接重新啟動容器。
執行清理操作后,Zookeeper 成功啟動。
配置優化:調整 JVM 堆內存
為了讓 HBase 更穩定、性能更好,可以根據容器或服務器的資源情況,為 HBase 的核心組件(Master, RegionServer, Zookeeper)分配合適的 JVM 堆內存。們通過修改 hbase-env.sh
文件來設置(也可以通過 Docker 環境變量如 HBASE_MASTER_OPTS
等方式傳入)。
在 conf/hbase-env.sh
文件中添加或修改以下行:
# ==============================================================================
# JVM Heap Settings for HBase Components
# ==============================================================================# 設置 HBase Master 最大堆內存為 3GB
export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -Xmx3g"# 設置 HBase RegionServer 最大堆內存為 6GB
export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -Xmx6g"# 設置 HBase 管理的 Zookeeper 最大堆內存為 1GB (僅對內嵌 ZK 有效)
export HBASE_ZOOKEEPER_OPTS="$HBASE_ZOOKEEPER_OPTS -Xmx1g"# ==============================================================================
# 可選: 設置初始堆大小 (-Xms) 與最大堆大小一致以提高性能
# ==============================================================================
# export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -Xms3g"
# export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -Xms6g"
# export HBASE_ZOOKEEPER_OPTS="$HBASE_ZOOKEEPER_OPTS -Xms1g"
應用方式:
- 將修改后的
hbase-env.sh
文件通過 Docker Volume 掛載到容器的/path/to/hbase/conf/hbase-env.sh
位置。 - 或者,基于 HBase 鏡像構建一個新鏡像,在 Dockerfile 中將修改后的文件
COPY
進去。
重要: 修改配置后,需要重啟對應的 HBase 容器才能生效。同時,確保為 Docker 容器本身分配了足夠的內存(例如,使用 docker run --memory
或 docker-compose.yml
中的 deploy.resources.limits.memory
)來容納設置的 JVM 堆以及其他開銷。
問題三:Snappy 壓縮表創建失敗 - “previously failed test”
HBase服務啟動成功后。嘗試創建一個使用 Snappy 壓縮的表:
create 'test_snappy_1', {NAME => 'cf1', COMPRESSION => 'SNAPPY'}
沒想到,RegionServer 日志中又出現了錯誤:
2025-04-26T05:19:20.671847592Z 2025-04-26 05:19:20,490 ERROR [RS_OPEN_REGION-...] handler.OpenRegionHandler: Failed open of region=test_snappy_1...
2025-04-26T05:19:20.671852492Z org.apache.hadoop.hbase.DoNotRetryIOException: Compression algorithm 'snappy' previously failed test.
2025-04-26T05:19:20.671857792Z at org.apache.hadoop.hbase.util.CompressionTest.testCompression(CompressionTest.java:93)# ... (堆棧跟蹤)
錯誤信息 Compression algorithm 'snappy' previously failed test
指示 HBase 在嘗試使用 Snappy 壓縮時,其內部的壓縮算法測試失敗了。
原因分析與純 Java Codec:
根據 HBase 官方文檔,HBase 支持多種壓縮算法,包括 Snappy。默認情況下,SNAPPY
算法通常映射到 org.apache.hadoop.io.compress.SnappyCodec
這個實現。這個實現依賴于 Hadoop Native Library。在 Docker 環境中,如果:
- 基礎鏡像沒有包含 Hadoop Native Library;
- 包含的 Native Library 版本與操作系統或 JVM 不兼容;
- Native Library 因為某些原因加載失敗;
那么,依賴 Native Library 的 Snappy Codec 就會在 HBase 的 CompressionTest
中失敗。
幸運的是,新版的HBase支持純 Java 實現的壓縮 Codec,它們不依賴于操作系統級別的 Native 庫,從而可以避免這類環境兼容性問題:
- 官方介紹的Java 實現
- 2.5.x才支持的jira說明
由于小子使用的是 HBase 2.5.11,這些純 Java 實現都是可用的。不想再引入HDFS,所以使用純 Java 實現是理想的選擇。
解決方案:配置使用 Aircompressor Snappy Codec
在 hbase-site.xml
中添加以下配置,明確指定 SNAPPY
壓縮算法使用 Aircompressor 提供的純Java 實現:
<property><name>hbase.io.compress.snappy.codec</name><value>org.apache.hadoop.hbase.io.compress.aircompressor.SnappyCodec</value>
</property>
然后務必重啟 HBase Master 和所有 RegionServer 進程。
重啟完成后,再次嘗試創建 Snappy 壓縮表:
create 'test_snappy_1', {NAME => 'cf1', COMPRESSION => 'SNAPPY'}
這一次,命令成功執行,表成功創建!
也可以使用以下命令驗證:
$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.util.CompressionTest file:///tmp/test.txt snappy
結語
在 Docker 中部署和運行 HBase 2.5.11 的過程充滿了挑戰,但也收獲頗豐。小子也順手記錄,以饗各位看官,歡迎留言交流!