顯示具有 apache hbase 標籤的文章。 顯示所有文章
顯示具有 apache hbase 標籤的文章。 顯示所有文章

2014年3月10日 星期一

台灣 Hadoop 生態系工作坊 2014 Q1 會後心得


參加人數: 約100人以上

心得一: 關貿團隊分享Impala使用經驗
  1. 簡報提到Impala有個metadata模組, 可以讓impalad直接存取node OS的file system block?! 個人是認為應該是Hive metastore的Table/SerDe Location mapping資訊預先暫存在impalad, 省去query execution time訪問Hive MetaStore Server的動作, 存取資料時還是由impalad向NN取得HDFS block location, 再向DN發出short circuit read請求直接讀取OS file內容, 也就是說Impala底層還是需要像HDFS/HBase的分散式儲存服務, 而不是直接存取cluster node的OS file system.
  2. 安全性問題: 與關貿團隊, Jazz, Takeshi討論後覺得Kerberos的導入/建置/維運/整合仍然是管理者的挑戰. 其實在一份由Cloudera 工程師 Patrick Angeles在2014/02發表的簡報就提到這個觀點(Security Configuration is a PITA, Do only what you really need). 目前我是這麼認為的: 如果你是在一個安全的環境(公司內部), 其實有很多手法都可以保證Hadoop叢集的安全; 若是在一個公開的環境(EC2/hicloud), 或許Kerberos就成為一個不得不採取的手法. 但是Kerberos的目的是確保Hadoop叢集內部節點間的安全, 若是要與外部系統整合的話應該還有其他方式, 像Hortonwork就提出Knox專案, 而Cloudera在Hue就區分為用戶身分鑑別(in-DB或LDAP)與叢集節點身分鑑別(Kerberos)兩種模式. 目前看到像是Hive(Using LDAP Authentication with HS2), Impala(LDAP username/password authentication in JDBC/ODBC), 甚至像是Oozie(Enabling Your AltKerberosAuthenticationHandler Subclass on Oozie Web UI)都可能採用LDAP當作前端用戶身分鑑別機制.
  3. Parquet檔案格式: 優點很多, 但是需要付出成本: 轉換原始來源檔案. 第一直覺是採用Impala, 如果轉檔動作是Daily-based的話應該就沒問題, 白日高峰期間Impala提供查詢, 夜晚離峰時間Impala執行轉檔. 但是如果資料匯入頻率是hourly-based呢? 個人認為Impala的Scheduling機制應該沒做到很複雜, 所以Impala在進行轉檔時就有可能影響查詢的SLA. 這個時候Hive就派上用場, 只要作一些設定就可以讓Hive將原始檔案轉成Parquet格式. 這樣只需配置部分的資源給Hive+MR進行ETL轉換, 而Impala仍可支持Interactive query.
  4. 記憶體限制: 到底Impala的記憶體限制是什麼呢? 根據Cloudera簡報說明: Impala does not “spill” to disk -- pipelines are in-memory; Operators’ mem usage need to fit within the memory limit. 所以要取決於每個impalad在執行該operator時所需的記憶體容量, 官方是建議每個node配128GB, 不過這可能不是一般企業能玩得起的, 所以還是回到個人的觀點: 現階段Hive與Impala應該是處於互補態勢. 不過如何讓前端工具能夠無縫整合使用Hive/Impala, 那可能就需要一套DAL Middleware來幫忙了. 淘寶有套TDDL就是提供類似的功能, 而中移動大雲的HugeTable則是這個概念的實作. 
心得二: 亦思科技HareDB: SQL on HBase
  1. 能夠發現台灣公司能夠投注這麼多人力與資源在Hadoop相關專案, 真的是讓人很感動的 事情. 雖然如此個人還是會從商業利益的角度來探討HareDB的產品定位. 就目前看來支援SQL query仍是HareDB的最重要/主要的功能, 而這個功能其實正是目前國際大廠戰火最激烈的區塊. HareDB能夠在眾多解決方案中脫穎而出, 還是要走利基市場, 發展獨特且無法取代功能與產品?
  2. 由於HBase有著single-row transaction的限制, 如果有一套產品能夠支援Transaction block on HBase(cross tables/rows), 而用Impala來支持Real-time query/join/aggregation, 那就真的是太棒了. 不過這真的不是一件容易的事情, so當做作是異想天開吧 !
心得三: 趨勢科技Network Traffic Search using Apache HBase
  1. 使用Flume收集rsyslog的過程中, Evans的作法是先setup一台rsyslog server收集所有log存在本機端磁碟目錄, 然後透過rotate方式將log file移至另一個目錄, 然後經由Spooling Directory Source機制由Flume agent將資料向後傳送.
  2. 傳送過程為確保穩定性, 採用file channel.
  3. 由於raw data最終需存入兩個HBase Table(為優化查詢rowkey值並不同), 因此會有兩個Channel/Sink. 這樣會衍生寫入資料一致性的問題, 未來規劃採用Coprocessor來解決. 個人是想像可能作法是: 寫入Table A時使用prePut將資料寫入Table B, 若B成功則繼續寫入A, 若B失敗則取消寫入A. 
心得四: Tez的前景

  1. 根據Takeshi的說法, Tez相對於Impala擁有以下幾個優勢
    1. Tez是DAG執行框架, 因此相較Impala有更多的應用. 例如Tez可以執行既有的Pig程式
    2. Tez耗用資源比Impala較少(尤其是記憶體), 這對企業而言應該是蠻重要的考量
  2. 個人也是蠻支援上述兩個觀點, 不過覺得還是有幾個需要觀察的地方
    1. 記憶體管理: Impala最讓人頭痛的地方就是不支援spill, 個人覺得Tez應該會支援這個部分, 畢竟MR1已有這個功能, 差別只是在於實際運作的效率. 但是在MR1如果programmer的程式本身沒有寫好仍會拋出OOME! 相較之下我是比較喜歡Spark RDD, 讓框架來進行large dataset的管理與操作.
    2. 最佳化引擎: 如果順利的話Pig/Hive的code應該可以在Tez執行, 不過我覺得Optimization部分應該還是需要改寫. 舉例來說Hive的Shuffle/Broadcast/Short-Merge Bucket Join算法在MR1實作應該是需要進行不少的修改, 才能充分發揮Tez的優點吧!
    3. 穩定性: 畢竟Tez釋出才不到半年, 應該也沒有實際上線系統.
  3. 綜合以上觀點, 個人覺得Tez應該還是列在待觀察名單, 自己應該還是按照既定規劃繼續K "Pragrammin in Scala", 並開始找環境實際操作Spark Example.

2013年9月30日 星期一

Hadoop in Taiwan 2013 會後心得

時間 2013/9/28 (六) 09:00 ~ 17:00
地點 台北市南港區研究院路二段128號
中央研究院人文社會科學館 4F國際會議廳
研討會網址 http://www.hadoopintaiwan.com/

觀察與發現

  • 本次研討會主題以「即時、安全、易用」為主軸,整體而言涵蓋Hadoop Ecosystem的最新發展現況。
  • 在「即時」的部分,Intel Andrew Purtell提供許多新興技術(Storm、Shark、Impala、Kafka)與Hadoop整合的Architecture Pattern,非常值得對Big Data Platform架構有興趣的同仁學習與驗證。另外有兩場議程討論Cloudera Impala的使用,觀察現階段Real-time Query(RTQ)解決方案應該仍是以Impala為主。不過個人認為Impala仍是Cloudera主導的專案,社群(尤其是Apache)的參與度不高,仍需要觀察後續發展是否能夠維持目前的動能。
  • 在「未來」的部分,Intel Jason Dai則介紹UC Berkeley AMPLab的BDAS架構與組成元件。根據會後與講者交流得知,Intel似乎將BDAS視為下一代的Hadoop,投入80%人力與AMPLab合作研發,並在中國與阿里巴巴進行POC驗證,目前已進入準上線狀態。綜合09/12在Yahoo!TW Tech研討會的資訊,似乎BDAS相關元件已成為Next Gen Big Data Platform的重要核心技術。
  • 在「安全」的部分,趨勢Jason Yueh與騰雲徐啟超兩位講者均提到目前社群已經開始進行Data Encrytion的研究與開發,不過實際仍面臨許多待克服的難題,包含:如何在資料加密與系統效能之間取得平衡、如何提供一致化的資料加密框架讓Hadoop Ecosystem能夠無縫接合。綜合兩位講者的觀察HBase應該是目前最有可能實施Data Encryption的專案。
  • 在「易用」的部分,Revolution Xiaohui Liu介紹ScaleR套件與ClouodR兩項產品。ScaleR能夠讓相同的程式代碼(R language)運作在不同的環境(本機、HPC、Hadoop MR),並提供豐富的函式庫來降低應用開發者的負擔。CloudR則是結合AWS提供on-demand analytics的SaaS環境,使用者只需要幾個簡單的步驟就可以在Web Based R IDE環境RStudio進行開發與驗證。根據講者demo畫面該服務未來支援的Hadoop版本包含IDH、CDH、Hortonwork,並沒有侷限在單一版本或廠商。

講座心得

主題 :Architectural patterns for building real time applications with Apache HBase
講者:Andrew Purtell (Apache HBase計畫提交人at Intel)
摘要:

  • 對於「即時」的要求而言,可以區分為How fast to refresh/process the data與How fast to answer the questions to the user。
  • 目前已有許多Hadoop Integration Pattern分別適用在不同的情境與需求。在這些情境中HBase都能夠扮演Persistence service provider的角色。
  • 在9/18 Apache Fundation已經正式接納Storm為Incubator project。

主題 :Big Data Security
講者:Jason Yueh (Distinguished Technologist at Trend Micro)
摘要: 

  • Big Data Security已從Authentication、Authorization、Gateway發展至Data Encryption。
  • 避免機密資料外洩的最佳作法就是「不要儲存機密資料」。考慮RDBMS已經相對發展成熟,建議將機密資料儲存在RDBMS而Hadoop僅保存非機密資料。實務上Trend Micro的機密資料(客戶個資)約百萬筆,而Log資料為每天幾百GB,因此該作法是可行的。
  • Audit Log對資安而言是非常重要的線索,不過需要搭配良好的工具進行整理與分析,Trend Micro目前是採用Splunk的解決方案。
  • 在Hadoop叢集外加入Gateway限制存取可以提高系統整體的安全性,目前社群也提出數個方案進行這方面的研究與開發。

主題:The Spark Stack: Lightning-Fast Analytics over Hadoop Data
講者:Jason Dai (Engineering Director & Principal Engineer, Intel Software and Services Group)
摘要: 

  • Big Data Analytic Stack是由UC Berkeley AMPLab提出的巨量資料平台架構,範圍涵蓋:資源管理(Mesos)、In-Memory 檔案系統(Tachyon)、In-Memory分散式運算框架(Spark)、RTQ SQL引擎(Shark)、串流處理框架(Spark Streaming)、圖學運算框架(GraphX)、便於開發與使用的Machine Learning框架(MBbase)。
  • 根據講者說明,BDAS能夠協助開發者基於相同的運算框架(Spark)進行資料處理、機器學習、圖學關聯分析、以及即時查詢等應用開發,而無須採用多種不同框架(Hadoop、Storm、Neo4J)與程式語言(Java、Python、Clojure、Scala)
  • Intel與AMPLab進行密切合作,除提升系統的穩定性外並將BDAS推廣至中國的網站用戶(阿里巴巴)進行POC驗證,獲得不錯的成果
備註: 講者同時也參與 Project Panthera: An Analytical SQL Engine for MapReduce on Hive 的開發,詢問結果目前並沒有將Panthera移植到Shark/Spark框架的規劃

主題:基於 R 加Hadoop的高性能預測分析
講者:Liu Xiaohui (Revolution Analytics; Senior Developer)
摘要: 

  • 講者提出一個實際案例,說明用戶首先使用R進行資料建模與評估,然後再將R語言程式改用Hive實作執行在Hadoop叢集,不但增加開發者的負擔,也延長整體專案的時程
  • Revolution提供的ScaleR套件能夠讓用戶在不同的環境(本機、HPC、Hadoop MR)執行相同的程式碼,並提供豐富的函式庫以降低應用開發者的負擔。目前支援的函式庫種類包含:Descriptive Statistics、Statistical Tests、Sampling、Predictive Models、Data Visualization、Simulation、Cluster Analysis、Classification等
  • CloudR則是結合AWS EC2與Hadoop(IDH、Hortonwork、CDH)提供on-demand analytics的SaaS環境,使用者只需要幾個簡單的步驟就可以在Web-based R IDE環境RStudio進行開發與驗證。此外也提供Web-based的SSH與IDH Manager管控畫面

主題:Savanna - Hadoop跟OpenStack的第一次接觸
講者:macJack
摘要:

  • Savanna是今年4月由Hortonwork、Red Hat、Mirantis提出,希望在OpenStack環境提供Hadoop自動供裝與管理的專案。
  • 主要功能包含
    • designed as an OpenStack component
    • managed through REST API with UI available as part of OpenStack Dashboard
    • support for different Hadoop distributions
  • 系統架構主要包含幾個重要元件,包含:
    • Cluster Configuration Manager
      all the business logic resides here
    • Auth component
      responsible for client authentication & authorization
    • DAL
      Data Access Layer, persists internal models in DB
    • VM Provisioning
      component responsible for communication with Nova and Glance
    • Deployment Engine
      pluggable mechanism responsible for deploying Hadoop on provisioned VMs; existing management solutions like Apache Ambari and Cloudera Management Console could be utilized for that matter
    • REST API
      exposes Savanna functionality via REST
    • Python Savanna Client
      similar to other OpenStack components Savanna has its own python client
    • Savanna pages
      GUI for the Savanna is located on Horizon
  • 在9/25釋出最新版本0.2.2,已完成功能包含
    • Basic cluster provisioning of Apache Hadoop
    • Cluster operation support and integration with tooling, advanced configuration (HDFS, Swift, etc.)
    • 0.2.2可透過Hortonwork提供的plug-in供裝HDP版本

主題:HBase Operation
講者:劉一正
摘要:

  • 講者分享HBase維運常遇見的問題,包含:
  • Region Hotspot
  • Data can’t be inserted
  • Incorrect property setting
  • Backup impacks performance
  • Inconsistent
  • Major compaction
  • -ROOT- .META. corrupt
  • 根據經驗來看,每台Region Server的Region數目是影響效能的最重要因素,Region數目越高將造成主機更忙碌
  • 如果將MapReduce與HBase安裝在相同叢集時,需要特別注意兩者之間是否會產生資源競爭問題
  • 了解整體系統運作架構將有助於釐清問題與提出解決方案 (以下是自己歸納的HBase R/W架構)

主題:Real-time Big Data Analytics Engine using Impala
講者:Jason Shih / Etu
摘要:

  • Impala 1.2新增功能包含
    • cost-based join order optimization
    • support existing Hive UDFs(Java) without recompile
    • add Impala(C++) UDFs and UDAs
    • 支援更多JDBC/ODBC connector,包含:MicroStrategy、Qlikview、Tableau
  • 2.0預計新增功能
    • SQL 2003-compliant analytic window functions (aggregation OVER PARTITION)
    • Additional authentication mechanisms – including the ability to specify username/passwords in addition to the already supported Kerberos authentication
    • Enhanced, production-ready, YARN-integrated resource manager
    • ORDER BY without LIMIT clauses
  • 講者建議不要在Impala使用Insert語法,應該直接將資料存入HDFS/HBase後再從Impala進行查詢

主題:Hadoop Security: Now and future
講者:徐啟超 / TCloud Computing
摘要:


  • Hortonwork的Ambari 1.2.5已經提供Security Wizard,能夠大幅簡化安全相關設定與管理的負擔
  • 目前提升Hadoop安全的主流作法為:叢集內啟動Kerberos、經由Gateway (Apache Knox) 提供對外服務
  • 社群已開始進行資料加密相關patch的研究與開發,包含
    • [HADOOP-9331] Hadoop crypto codec framework and crypto codec implementations
    • [MAPREDUCE-5025] Key Distribution and Management for supporting crypto codec in Map Reduce
    • [HBASE-7544] Transparent table/CF encryption
    • [HIVE-5207] Support data encryption for Hive tables
    • [PIG-3289] Encryption aware load and store functions
  • 由於各專案採取實作的方式有差,因此開發進度上並不一致;HBase-7544直接採用Java內建的安全性框架,而不是基於Hadoop Common專案所提供的安全框架(仍在開發key management、encryption codec management等相關功能),因此將是最有可能提供資料加密功能的Hadoop Ecosystem套件
  • InfoQ的文章 Big Data Security: The Evolution of Hadoop’s Security Model 也有關於這部分的介紹
備註: Intel Rhino Project 應該算是很完整地規劃與實作Hadoop Ecosystem的整體安全性,不過預期應該只有IDH會整合這部分的成果..唉

2013年5月28日 星期二

Setup Stargate for Apache HBase

Reference

HBase Wiki: Stargate

Deployment

start daemon as root (port 51111 by BDCMS)
$ ./bin/hbase-daemon.sh start rest -p  

check process
$ ps -ef|grep org.apache.hadoop.hbase.rest.Main

Query

Query software version

$ curl -s -i http://0a908189.cht.local:51111/version

HTTP/1.1 200 OK
Content-Length: 135
Cache-Control: no-cache
Content-Type: text/plain

rest 0.0.2 [JVM: Sun Microsystems Inc. 1.6.0_33-20.8-b03] [OS: Linux 2.6.32-358.el6.x86_64 amd64] [Server: jetty/6.1.26] [Jersey: 1.8]

$ curl -s -i -H "Accept: text/xml" 0a908189.cht.local:51111/version
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: text/xml
Content-Length: 198

$ curl -s -i -H "Accept: application/json" 0a908189.cht.local:51111/version
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
Transfer-Encoding: chunked

{"JVM":"Sun Microsystems Inc. 1.6.0_33-20.8-b03","Jersey":"1.8","OS":"Linux 2.6.32-358.el6.x86_64 amd64","REST":"0.0.2","Server":"jetty/6.1.26"}

Query Storage Cluster Version

$ curl -s -i -H "Accept: application/json" 0a908189.cht.local:51111/version/cluster
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
Transfer-Encoding: chunked

"0.90.6-cdh3u5"

Query Table List

$ curl -s -i -H "Accept: application/json" 0a908189.cht.local:51111
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
Transfer-Encoding: chunked

{"table":[{"name":"t1"}]}

Query Table Schema

$ curl -s -i -H "Accept: application/json" 0a908189.cht.local:51111/t1/schema
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
Transfer-Encoding: chunked

{"name":"t1","IS_META":"false","IS_ROOT":"false","ColumnSchema":[{"name":"fam1","BLOCKSIZE":"65536","BLOOMFILTER":"NONE","BLOCKCACHE":"true","COMPRESSION":"NONE","VERSIONS":"3","REPLICATION_SCOPE":"0","TTL":"2147483647","IN_MEMORY":"false"}]}

Cell Query (Single Value)

$ curl -s -i -H "Accept: application/json" 0a908189.cht.local:51111/t1/rlog-1369657973498-0a908185/fam1:host
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked

{"Row":[{"key":"cmxvZy0xMzY5NjU3OTczNDk4LTBhOTA4MTg1","Cell":[{"column":"ZmFtMTpob3N0","timestamp":1369657977691,"$":"MGE5MDgxODU="}]}]}

$ CN=$(curl -s -H "Accept: application/json" 0a908189.cht.local:51111/t1/rlog-1369657973498-0a908185/fam1:host | grep -Po '"column":.*?[^\\]",' | tr -d '",'|awk -F: '{ print $2 }'|base64 -d);echo $CN
fam1:host

$ TM=$(curl -s -H "Accept: application/json" 0a908189.cht.local:51111/t1/rlog-1369657973498-0a908185/fam1:host | grep -Po '"timestamp":.*?[^\\],' |tr -d '",'|awk -F: '{ print $2 }');echo $TM
1369657977691

$ VL=$(curl -s -H "Accept: application/json" 0a908189.cht.local:51111/t1/rlog-1369657973498-0a908185/fam1:host | grep -Po '"\$":.*?[^\\]"' |tr -d '"'|awk -F: '{ print $2 }'|base64 -d);echo $VL
0a908185

$ VL=$(curl -s -H "Accept: application/json" 0a908189.cht.local:51111/t1/rlog-1369657973498-0a908185/fam1:body | grep -Po '"\$":.*?[^\\]"' |tr -d '"'|awk -F: '{ print $2 }'|base64 -d);echo $VL
sshd[3369]: pam_unix(sshd:session): session closed for user root

$ echo "ZmFtMTpGYWNpbGl0eQ=="|base64 -d
fam1:Facility
$ echo "ZmFtMTpTZXZlcml0eQ=="|base64 -d
fam1:Severity
$ echo "ZmFtMTphZ2VudA=="|base64 -d
fam1:agent
$ echo "ZmFtMTpib2R5"|base64 -d
fam1:body
echo "ZmFtMTpob3N0"|base64 -d
fam1:host
echo "ZmFtMTp0aW1lc3RhbXA="|base64 -d
fam1:timestamp
$ curl -s -H "Accept: application/json" 0a908189.cht.local:51111/t1/rlog-13697257366*
{
    "Row": [
        {
            "key": "cmxvZy0xMzY5NzI1NzM2NjU5LTBhOTA4MThh",
            "Cell": [
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369725741468,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369725741467,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369725741470,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369725741472,
                    "$": "c3NoZFsxMjg2OF06IEFjY2VwdGVkIHB1YmxpY2tleSBmb3Igcm9vdCBmcm9tIDEwLjE0NC4xMjkuMTM3IHBvcnQgNDY1MDcgc3NoMg=="
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741469,
                    "$": "MGE5MDgxOGE="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369725741466,
                    "$": "MTM2OTcyNTczNjY1OQ=="
                }
            ]
        },
        {
            "key": "cmxvZy0xMzY5NzI1NzM2NjYxLTBhOTA4MThh",
            "Cell": [
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369725741474,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369725741473,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369725741475,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369725741476,
                    "$": "c3NoZFsxMjg2OF06IHBhbV91bml4KHNzaGQ6c2Vzc2lvbik6IHNlc3Npb24gb3BlbmVkIGZvciB1c2VyIHJvb3QgYnkgKHVpZD0wKQ=="
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741471,
                    "$": "MGE5MDgxOGE="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369725741470,
                    "$": "MTM2OTcyNTczNjY2MQ=="
                }
            ]
        },
        {
            "key": "cmxvZy0xMzY5NzI1NzM2NjkyLTBhOTA4MThh",
            "Cell": [
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369725741508,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369725741502,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369725741505,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369725741500,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369725741506,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369725741502,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369725741509,
                    "$": "c3NoZFsxMjg2OF06IHBhbV91bml4KHNzaGQ6c2Vzc2lvbik6IHNlc3Npb24gY2xvc2VkIGZvciB1c2VyIHJvb3Q="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369725741503,
                    "$": "c3NoZFsxMjg2OF06IFJlY2VpdmVkIGRpc2Nvbm5lY3QgZnJvbSAxMC4xNDQuMTI5LjEzNzogMTE6IGRpc2Nvbm5lY3RlZCBieSB1c2Vy"
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741506,
                    "$": "MGE5MDgxOGE="
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741501,
                    "$": "MGE5MDgxOGE="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369725741504,
                    "$": "MTM2OTcyNTczNjY5Mg=="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369725741499,
                    "$": "MTM2OTcyNTczNjY5MA=="
                }
            ]
        }
    ]
}

$ curl -s -H "Accept: application/json" 0a908189.cht.local:51111/t1/rlog-13697257366*/fam1:host
{
    "Row": [
        {
            "key": "cmxvZy0xMzY5NzI1NzM2NjU5LTBhOTA4MThh",
            "Cell": [
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741469,
                    "$": "MGE5MDgxOGE="
                }
            ]
        },
        {
            "key": "cmxvZy0xMzY5NzI1NzM2NjYxLTBhOTA4MThh",
            "Cell": [
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741471,
                    "$": "MGE5MDgxOGE="
                }
            ]
        },
        {
            "key": "cmxvZy0xMzY5NzI1NzM2NjkyLTBhOTA4MThh",
            "Cell": [
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741506,
                    "$": "MGE5MDgxOGE="
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369725741501,
                    "$": "MGE5MDgxOGE="
                }
            ]
        }
    ]
}

修改flume agent程式將rowkey格式改為${rowPrefix}${log-src-host}-${suffix}後, 查詢結果
$ curl -s -H "Accept: application/json" http://0a908189.cht.local:51111/t1/rlog-0a90818c*/
{
    "Row": [
        {
            "key": "cmxvZy0wYTkwODE4Yy0xMzY5Nzk5NzgwMjg3",
            "Cell": [
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369799786201,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369799786207,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369799786205,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369799786204,
                    "$": "c3NoZFsyOTc0N106IEFjY2VwdGVkIHB1YmxpY2tleSBmb3Igcm9vdCBmcm9tIDEwLjE0NC4xMjkuMTM3IHBvcnQgNTczNDYgc3NoMg=="
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369799786199,
                    "$": "MGE5MDgxOGM="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369799786196,
                    "$": "MTM2OTc5OTc4MDI4Ng=="
                }
            ]
        },
        {
            "key": "cmxvZy0wYTkwODE4Yy0xMzY5Nzk5NzgwMjkx",
            "Cell": [
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369799786197,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369799786206,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369799786204,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369799786208,
                    "$": "c3NoZFsyOTc0N106IHBhbV91bml4KHNzaGQ6c2Vzc2lvbik6IHNlc3Npb24gb3BlbmVkIGZvciB1c2VyIHJvb3QgYnkgKHVpZD0wKQ=="
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369799786202,
                    "$": "MGE5MDgxOGM="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369799786200,
                    "$": "MTM2OTc5OTc4MDI5MA=="
                }
            ]
        },
        {
            "key": "cmxvZy0wYTkwODE4Yy0xMzY5ODA2OTg4Njc3",
            "Cell": [
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369806994258,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369806994254,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369806994260,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369806994261,
                    "$": "c3NoZFsyOTc0N106IFJlY2VpdmVkIGRpc2Nvbm5lY3QgZnJvbSAxMC4xNDQuMTI5LjEzNzogMTE6IGRpc2Nvbm5lY3RlZCBieSB1c2Vy"
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369806994256,
                    "$": "MGE5MDgxOGM="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369806994237,
                    "$": "MTM2OTgwNjk4Mzc0Mw=="
                }
            ]
        },
        {
            "key": "cmxvZy0wYTkwODE4Yy0xMzY5ODA2OTg4Njc4",
            "Cell": [
                {
                    "column": "ZmFtMTpGYWNpbGl0eQ==",
                    "timestamp": 1369806994261,
                    "$": "ODA="
                },
                {
                    "column": "ZmFtMTpTZXZlcml0eQ==",
                    "timestamp": 1369806994255,
                    "$": "Ng=="
                },
                {
                    "column": "ZmFtMTphZ2VudA==",
                    "timestamp": 1369806994262,
                    "$": "MTAuMTQ0LjEyOS4xMzc="
                },
                {
                    "column": "ZmFtMTpib2R5",
                    "timestamp": 1369806994257,
                    "$": "c3NoZFsyOTc0N106IHBhbV91bml4KHNzaGQ6c2Vzc2lvbik6IHNlc3Npb24gY2xvc2VkIGZvciB1c2VyIHJvb3Q="
                },
                {
                    "column": "ZmFtMTpob3N0",
                    "timestamp": 1369806994259,
                    "$": "MGE5MDgxOGM="
                },
                {
                    "column": "ZmFtMTp0aW1lc3RhbXA=",
                    "timestamp": 1369806994239,
                    "$": "MTM2OTgwNjk4Mzc0Ng=="
                }
            ]
        }
    ]
}

2013年5月21日 星期二

Collecting rsyslog log using Apache Flume and HBase

rsyslog

rsyslog server (0a908186.cht.local)

CentOS 6.4

修改設定檔

$ vim /etc/rsyslog.conf
$ModLoad imuxsock.so
$ModLoad imklog.so
$ModLoad imtcp.so
$InputTCPServerRun 514
$template DailyPerHostLogs,"/var/log/rsyslog/%fromhost%/%$year%%$month%%$day%.log"
*.* -?DailyPerHostLogs;
$ vim /etc/sysconfig/rsyslog
SYSLOGD_OPTIONS="-c 5"

重新啟動

$ service rsyslog restart

當client送出訊息後檢視記錄

$ tail -f /var/log/rsyslog/d23d6ee0.cht.local/20130515.log
…
2013-05-15T23:41:17.461288+08:00 d23d6ee0.cht.local d23d6ee0@Wed May 15 23:41:19 CST 2013
…
2013-05-17T09:06:10+08:00 d23d6ee0 su: pam_unix(su-l:session): session opened for user hdfs by root(uid=0)
2013-05-17T09:06:16+08:00 d23d6ee0 su: pam_unix(su-l:session): session closed for user hdfs

rsyslog client

CentOS 6.4
修改設定檔
$ vim /etc/rsyslog.conf
#$ModLoad imtcp.so
authpriv.*       @@0a908186.cht.local:514
$WorkDirectory /var/lib/rsyslog # where to place spool files
$ActionQueueFileName fwdRule1 # unique name prefix for spool files
$ActionQueueMaxDiskSpace 1g   # 1gb space limit (use as much as possible)
$ActionQueueSaveOnShutdown on # save messages to disk on shutdown
$ActionQueueType LinkedList   # run asynchronously
$ActionResumeRetryCount -1    # infinite retries if host is down
重新啟動
$ service rsyslog restart

送出測試訊息

$ echo "`hostname`@`date`"|nc -t d241288b 514

測試帳號登入登出記錄

$ su - hdfs
$ exit

Flume

CDH3u5, Flume 1.2.0.24

flume installation

$ sudo yum install flume-ng
Name        : flume-ng
Arch        : noarch
Version     : 1.2.0+24.43
Release     : 1
$ sudo yum install flume-ng-agent
Name        : flume-ng-agent
Arch        : noarch
Version     : 1.2.0+24.43
Release     : 1

file location

Resource
Location
Notes
Config Directory
/etc/flume-ng/conf
Config File
/etc/flume-ng/conf/flume.conf
This config will be picked-up by the flume agent startup script.
Template of User Customizable Config File
/etc/flume-ng/conf/flume-conf.properties.template
Contains a sample config. To use this config you should copy this file onto /etc/flume-ng/conf/flume.conf
Template of User Customizable environment file
/etc/flume-ng/conf/flume-env.sh.template
If you modify this file, copy it to /etc/flume-ng/conf/flume-env.sh
Daemon Log Directory
/var/log/flume-ng
Default Flume Home
/usr/lib/flume-ng
Provided by RPMS and DEBS
Flume Agent startup script
/etc/init.d/flume-ng-agent
Provided by RPMS and DEBS
Recommended tar.gz Flume Home
/usr/local/lib/flume-ng
Recommended but installation dependent
Flume Wrapper Script
/usr/bin/flume-ng
Called by the Flume Agent startup script

flume configuration

修改設定檔

$ vim /etc/flume-ng/conf/example.conf
# example.conf: A single-node Flume configuration


# Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1


# Describe/configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = ${hostname}
a1.sources.r1.port = 44444


# Describe the sink
a1.sinks.k1.type = logger


# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100000
a1.channels.c1.transactionCapacity = 10000


# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

重新啟動

$ sudo -u hdfs flume-ng agent --conf-file /etc/flume-ng/conf/example.conf --name a1 -Dflume.root.logger=INFO,console &
...
13/05/16 00:05:32 INFO source.NetcatSource: Created serverSocket:sun.nio.ch.ServerSocketChannelImpl[/X.X.X.X:5140]

送出測試訊息

$ telnet ${hostname} 44444
telnet> Hello World

檢視訊息

13/05/15 23:23:54 INFO sink.LoggerSink: Event: { headers:{} body: 48 65 6C 6C 6F 20 57 6F 72 6C 64 0D             Hello World. }

修改設定檔

# name
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# source
a1.sources.r1.type = org.apache.flume.source.SyslogTcpSource
a1.sources.r1.port = 514
a1.sources.r1.host = 0a908189.cht.local
# interceptor
a1.sources.r1.interceptors = i1 i2
a1.sources.r1.interceptors.i1.type = host
a1.sources.r1.interceptors.i1.hostHeader = hostname
a1.sources.r1.interceptors.i2.type = timestamp
# sink
a1.sinks.k1.type = FILE_ROLL
a1.sinks.k1.sink.directory = /var/log/flume
# channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100000
a1.channels.c1.transactionCapacity = 10000
# bind
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

重新啟動

$ nohup flume-ng agent --conf-file /etc/flume-ng/conf/rsyslog-log.conf --name a1 -Dflume.root.logger=INFO,consoleclient > rsyslog-log.log 2>&1 &

檢視訊息

tail -f /var/log/flume/1369622697744-1
sshd[1742]: Accepted publickey for root from X.X.X.X port 59503 ssh2
sshd[1742]: pam_unix(sshd:session): session opened for user root by (uid=0)
sshd[1742]: Received disconnect from X.X.X.X: 11: disconnected by user
sshd[1742]: pam_unix(sshd:session): session closed for user root
sshd[1751]: Accepted publickey for root from X.X.X.X port 59505 ssh2
sshd[1751]: pam_unix(sshd:session): session opened for user root by (uid=0)
sshd[1751]: Received disconnect from X.X.X.X: 11: disconnected by user
sshd[1751]: pam_unix(sshd:session): session closed for user root

修改設定檔

$ vim /etc/flume-ng/conf/rsyslog-hdfs.conf
# name
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# source
a1.sources.r1.type = org.apache.flume.source.SyslogTcpSource
a1.sources.r1.port = 514
a1.sources.r1.host = ${hostname}
# sink
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /flume/events/%Y%m%d
a1.sinks.k1.hdfs.filePrefix = events-
a1.sinks.k1.hdfs.round = true
a1.sinks.k1.hdfs.roundValue = 10
a1.sinks.k1.hdfs.roundUnit = minute
# channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100000
a1.channels.c1.transactionCapacity = 10000
# bind
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1


# sink
a1.sinks.k1.type = hdfs
# directory path
a1.sinks.k1.hdfs.path = /flume/events/%Y%m%d
# the filename will be {%Prefix}.{%s}
a1.sinks.k1.hdfs.filePrefix = events
a1.sinks.k1.hdfs.fileSuffix = log
# -- Compression codec. one of following : gzip, bzip2, lzo, snappy
# hdfs.codeC = gzip
# fileType: currently SequenceFile, DataStream or CompressedStream
#(1)DataStream will not compress output file and please don't set codeC
#(2)CompressedStream requires set hdfs.codeC with an available codeC
a1.sinks.k1.hdfs.fileType = DataStream
a1.sinks.k1.hdfs.writeFormat = Text
# Number of seconds to wait before rolling current file (0 = never roll based on time interval)
a1.sinks.k1.hdfs.rollInterval = 600
# File size to trigger roll, in bytes (0: never roll based on file size)
a1.sinks.k1.hdfs.rollSize = 0
#Number of events written to file before it rolled (0 = never roll based on number of events)
a1.sinks.k1.hdfs.rollCount = 10000
# number of events written to file before it flushed to HDFS
a1.sinks.k1.hdfs.batchSize = 10000
a1.sinks.k1.hdfs.txnEventMax = 40000
# Number of threads per HDFS sink for HDFS IO ops (open, write, etc.)
a1.sinks.k1.hdfs.threadsPoolSize = 100
# Number of threads per HDFS sink for scheduling timed file rolling
a1.sinks.k1.hdfs.rollTimerPoolSize = 1
a1.sinks.sink1.hdfs.maxOpenFiles=50
a1.sinks.sink1.hdfs.appendTimeout = 10000
a1.sinks.sink1.hdfs.callTimeout = 10000
# hdfs.kerberosPrin--cipal Kerberos user principal for accessing secure HDFS
# hdfs.kerberosKey--tab Kerberos keytab for accessing secure HDFS
# hdfs.round false Should the timestamp be rounded down (if true, affects all time based escape sequences except %t)
# hdfs.roundValue1 Rounded down to the highest multiple of this (in the unit configured using
# hdfs.roundUnit), less than current time.
# hdfs.roundUnit second The unit of the round down value - second, minute or hour.
# serializer TEXT Other possible options include AVRO_EVENT or the fully-qualified class name of an implementation of the EventSerializer.Builder interface.
# serializer.*


# channel
# Use a channel which buffers events to a file
# -- The component type name, needs to be FILE.
agent1.channels.channel1.type = FILE
# checkpointDir ~/.flume/file-channel/checkpoint The directory where checkpoint file will be stored
# dataDirs ~/.flume/file-channel/data The directory where log files will be stored
# The maximum size of transaction supported by the channel
agent1.channels.channel1.transactionCapacity = 1000000
# Amount of time (in millis) between checkpoints
agent1.channels.channel1.checkpointInterval 30000
# Max size (in bytes) of a single log file
agent1.channels.channel1.maxFileSize = 2146435071
# Maximum capacity of the channel
agent1.channels.channel1.capacity 10000000
#keep-alive 3 Amount of time (in sec) to wait for a put operation
#write-timeout 3 Amount of time (in sec) to wait for a write operation
$ sudo -u hdfs hadoop fs -mkdir /flume
$ sudo -u hdfs hadoop fs -chmod 777 /flume

重新啟動

$ nohup flume-ng agent --conf-file /etc/flume-ng/conf/rsyslog-hdfs.conf --name a1 -Dflume.root.logger=INFO,consoleclient > rsyslog-hdfs.log 2>&1 &

修改設定檔

$ vim /etc/flume-ng/conf/rsyslog-hbase.conf
# name
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# source
a1.sources.r1.type = org.apache.flume.source.SyslogTcpSource
a1.sources.r1.port = 514
a1.sources.r1.host = ${hostname}
# interceptor
a1.sources.r1.interceptors = i1 i2
a1.sources.r1.interceptors.i1.type = host
a1.sources.r1.interceptors.i1.hostHeader = agent
a1.sources.r1.interceptors.i2.type = timestamp
# sink
#Use the AsyncHBaseSink
a1.sinks.k1.type =
org.apache.flume.sink.hbase.AsyncHBaseSink
#Use the HBaseSink
#a1.sinks.k1.type = org.apache.flume.sink.hbase.HBaseSink
a1.sinks.k1.table = t1
a1.sinks.k1.columnFamily =
fam1
a1.sinks.k1.batchSize = 5000
#Use the SimpleAsyncHbaseEventSerializer that comes with Flume
a1.sinks.k1.serializer = o
rg.apache.flume.sink.hbase.SimpleAsyncHbaseEventSerializer
#Use the SimpleHbaseEventSerializer that comes with Flume
#a1.sinks.k1.serializer = org.apache.flume.sink.hbase.SimpleHbaseEventSerializer
a1.sinks.k1.serializer.incrementRow =
iRow
a1.sinks.k1.serializer.incrementColumn = iCol
a1.sinks.k1.serializer.rowPrefix = rlog-
a1.sinks.k1.serializer.suffix = timestamp
a1.sinks.k1.serializer.payloadColumn= body
# channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100000
a1.channels.c1.transactionCapacity = 10000
# bind
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

建立Hbase table

$ su - hdfs; hbase shell
hbase> create 't1', {NAME => 'fam1'}
hbase> put ‘t1’,

重新啟動

$ nohup flume-ng agent --conf-file /etc/flume-ng/conf/rsyslog-hbase.conf --name a1 -Dflume.root.logger=INFO,consoleclient >rsyslog-hbase.log 2>&1 &

查詢紀錄

20130520 10:05
hbase> get 't1', 'iRow'
COLUMN                CELL
fam1:iCol            timestamp=1369015910345, value=\x00\x00\x00\x00\x00@\x90\x
                     BB
hbase> count 't1'
Current count: 58000, row: evKey1369012288346
58955 row(s) in 1.9290 seconds
hbase> scan 't1', {STARTROW => 'evKey1369012288346'}
...
May 26 13:26:37 d241288b unix_chkpwd[17982]: password check failed for user (root)
May 26 13:26:37 d241288b sshd[17980]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=61.164.126.60  user=root
May 26 13:26:39 d241288b sshd[17980]: Failed password for root from 61.164.126.60 port 33720 ssh2
...
evKey1369015944037   column=fam1:evtBody, timestamp=1369015955264, value=sshd[2
                     8017]: input_userauth_request: invalid user fluffy
evKey1369015944038   column=fam1:evtBody, timestamp=1369015955337, value=sshd[2
                     8048]: Invalid user library from 200.73.76.20
iRow                 column=fam1:iCol, timestamp=1369015955340, value=\x00\x00\
                     x00\x00\x00@\x94?
983 row(s) in 0.7260 seconds

201210 [Flume-user] syslog source - sinks without datetime/hostname

org.apache.flume.source.SyslogTcpSource
   private SyslogUtils syslogUtils = new SyslogUtils();
   public void messageReceived(ChannelHandlerContext ctx, MessageEvent mEvent) {
     ChannelBuffer buff = (ChannelBuffer) mEvent.getMessage();
     while (buff.readable()) {
       Event e = syslogUtils.extractEvent(buff);
       if (e == null) {
         logger.debug("Parsed partial event, event will be generated when " +
             "rest of the event is received.");
         continue;
       }
       try {
         getChannelProcessor().processEvent(e);
         counterGroup.incrementAndGet("events.success");
       } catch (ChannelException ex) {
         counterGroup.incrementAndGet("events.dropped");
         logger.error("Error writting to channel, event dropped", ex);
       }
     }


   }
 }
org.apache.flume.source.SyslogUtils
 private String timeStamp = null;
 private String hostName = null;
 private String msgBody = null;
 private void formatHeaders() {
    ...
       } else if (grp == SYSLOG_HOSTNAME_POS) {
         hostName = value;
       } else if (grp == SYSLOG_BODY_POS) {
         msgBody = value;
       }
    ...
 }
 Event buildEvent() {
   byte[] body;
   Map headers = new HashMap();
   ...
   if ((timeStamp != null) && timeStamp.length() > 0) {
     headers.put("timestamp", timeStamp);
   }
   if ((hostName != null) && (hostName.length() > 0)) {
     headers.put("host", hostName);
   }
   ...
   return EventBuilder.withBody(body, headers);
 }
 public Event extractEvent(ChannelBuffer in){
    ...
       case DATA:
         // TCP syslog entries are separated by '\n'
         if (b == '\n') {
           e = buildEvent();
           doneReading = true;
         } else {
           baos.write(b);
         }
 }
org.apache.flume.sink.hbase.SimpleAsyncHbaseEventSerializer
 private byte[] payload;
 public List getActions() {
   List actions = new ArrayList();
   if(payloadColumn != null){
     byte[] rowKey;
     try {
       switch (keyType) {
         case TS:
           rowKey = SimpleRowKeyGenerator.getTimestampKey(rowPrefix);
           break;
         case TSNANO:
           rowKey = SimpleRowKeyGenerator.getNanoTimestampKey(rowPrefix);
           break;
         case RANDOM:
           rowKey = SimpleRowKeyGenerator.getRandomKey(rowPrefix);
           break;
         default:
           rowKey = SimpleRowKeyGenerator.getUUIDKey(rowPrefix);
           break;
       }
       PutRequest putRequest =  new PutRequest(table, rowKey, cf,
           payloadColumn, payload);
#PutRequest(byte[] table, byte[] key, byte[] family, byte[][] qualifiers, byte[][] values)
       actions.add(putRequest);
     } catch (Exception e){
       throw new FlumeException("Could not get row key!", e);
     }
   }
   return actions;
 }
 public void setEvent(Event event) {
    #可以將event header + body以JSON形式進行serialization
    String QUOTE = "\"";
    // this.payload = event.getBody();
    // 20130521, leo
    /*
     {"header":[
     {"h1":"v1"},
     {"h2":"v2"}
     ],
     "body":"b1"
     }
     */
    StringBuffer stb = new StringBuffer("{"+QUOTE+"header"+QUOTE+":[");
    
    for (Map.Entry entry : event.getHeaders().entrySet()) {
       stb.append("{"+QUOTE+entry.getKey()+QUOTE+":"+QUOTE+entry.getValue()+QUOTE+"},");
    }
    stb.deleteCharAt(stb.length()-1);
    stb.append("],");
    stb.append(QUOTE+"body"+QUOTE+":"+QUOTE+new String(event.getBody())+QUOTE+"}");
    this.payload = stb.toString().getBytes();
    #或是將event儲存,然後在getActions()建立multiple columns PutRequest
    #PutRequest API
    #http://tsunanet.net/~tsuna/asynchbase/1.2.0/
 }
org.apache.flume.sink.hbase.AsyncHBaseSink
private HBaseClient client;
public Status process() throws EventDeliveryException {
   ...
   try {
     txn = channel.getTransaction();
     txn.begin();
     for (; i < batchSize; i++) {
       Event event = channel.take();
       if (event == null) {
         status = Status.BACKOFF;
         if (i == 0) {
           sinkCounter.incrementBatchEmptyCount();
         } else {
           sinkCounter.incrementBatchUnderflowCount();
         }
         break;
       } else {
         serializer.setEvent(event);
         List actions = serializer.getActions();
         List increments = serializer.getIncrements();
         callbacksExpected.addAndGet(actions.size() + increments.size());


         for (PutRequest action : actions) {
           client.put(action).addCallbacks(putSuccessCallback, putFailureCallback);
         }
         for (AtomicIncrementRequest increment : increments) {
           client.atomicIncrement(increment).addCallbacks(
                   incrementSuccessCallback, incrementFailureCallback);
         }
       }
     }
   } catch (Throwable e) {
     this.handleTransactionFailure(txn);
     this.checkIfChannelExceptionAndThrow(e);
   }
  ...
}

自訂HBase內容 [github]

設計

  • row key 加入 source host, e.g.: ${a1.sinks.k1.serializer.rowPrefix}${a1.sinks.k1.serializer.suffix}${srchost}
  • 將org.apache.flume.Event的Header以multiple column寫入

開發

  • library dependency (flume-ng-1.2.0-cdh3u5/lib/)
    • asynchbase-1.2.0.jar
      • package org.hbase.async
    • hbase-0.90.6-cdh3u5.jar
    • hadoop-core-0.20.2-cdh3u5.jar
  • original source [Cloudera archive]
    • flume-ng-hbase-sink-1.2.0-cdh3u5.jar
  • 0.1.0
    • 將syslog header以JSON方式寫入HBASE
  • 0.2.0
    • 將syslog header以個別qualifier/value寫入HBASE 
    • ROWKEY加入syslog source hostname
      • 範例: rlog-1369638572251-0a90818a

佈署


  • 上傳修改後flume-ng-hbase-sink-0.2.0.jar到/usr/lib/flume-ng/lib, 並將原先檔案改名為flume-ng-hbase-sink-1.2.0-cdh3u5.jar
  • 重新啟動 rsyslog-hbase agent

測試

20130527 16:20
hbase> count ‘t1’
5 row(s) in 0.2770 seconds
hbase> scan ‘t1’
...
rlog-1369642795121-d column=fam1:Facility, timestamp=1369642795401, value=80
23d6ee0
rlog-1369642795121-d column=fam1:Severity, timestamp=1369642795398, value=6
23d6ee0
rlog-1369642795121-d column=fam1:agent, timestamp=1369642795402, value=X.X.X
23d6ee0              .X
rlog-1369642795121-d column=fam1:body, timestamp=1369642795399, value=sshd[1604
23d6ee0              6]: pam_unix(sshd:session): session closed for user root
rlog-1369642795121-d column=fam1:host, timestamp=1369642795400, value=d23d6ee0
23d6ee0
rlog-1369642795121-d column=fam1:timestamp, timestamp=1369642795397, value=1369
23d6ee0              642795121

20130529 14:07

hbase> scan ‘t1’
rlog-0a90818c-136980 column=fam1:Facility, timestamp=1369806994258, value=80
 6988677
 rlog-0a90818c-136980 column=fam1:Severity, timestamp=1369806994254, value=6
 6988677
 rlog-0a90818c-136980 column=fam1:agent, timestamp=1369806994260, value=10.144.1
 6988677              29.137
 rlog-0a90818c-136980 column=fam1:body, timestamp=1369806994261, value=sshd[2974
 6988677              7]: Received disconnect from 10.144.129.137: 11: disconnec
                      ted by user
 rlog-0a90818c-136980 column=fam1:host, timestamp=1369806994256, value=0a90818c
 6988677
 rlog-0a90818c-136980 column=fam1:timestamp, timestamp=1369806994237, value=1369
 6988677              806983743
 rlog-0a90818c-136980 column=fam1:Facility, timestamp=1369806994261, value=80
 6988678
 rlog-0a90818c-136980 column=fam1:Severity, timestamp=1369806994255, value=6
 6988678
 rlog-0a90818c-136980 column=fam1:agent, timestamp=1369806994262, value=10.144.1
 6988678              29.137
 rlog-0a90818c-136980 column=fam1:body, timestamp=1369806994257, value=sshd[2974
 6988678              7]: pam_unix(sshd:session): session closed for user root
 rlog-0a90818c-136980 column=fam1:host, timestamp=1369806994259, value=0a90818c
 6988678
 rlog-0a90818c-136980 column=fam1:timestamp, timestamp=1369806994239, value=1369
 6988678              806983746