Apache HAWQ を Hadoop パッケージ ( HDP 2.5.3 ) で使ってみた(後編)
こんにちは。次世代システム研究室のデータベース・NoSQL・Hadoop関連を担当しているM.K.です。
今回は Apache HAWQ を試した前編の続きの後編です。
後編では、HDFS や Hive と HAWQ をデータ連携させるための PXF のビルド&インストール、そして実際の検証を行ってみました。
HAWQ クラスターに PXF を構築
早速、PXF のビルド&インストールを行います。前編で構築した HAWQ クラスター環境に PXF を追加で構築しています。公式サイトの手順に従って構築しました。PXFの仕組みは、PXF のスライド資料(英語) によくまとまっていますのであわせて参考にしてみてください。
PXF のビルド&インストール
HAWQ クラスターサーバー全て に root ユーザーでログイン、手順通りに以下を実施。PXF も HAWQ を管理するために作った gpadmin ユーザーで起動するので、gpadmin ユーザーのパーミッションにしておきます。
git clone https://git-wip-us.apache.org/repos/asf/incubator-hawq.git cd incubator-hawq/pxf make make unittest export GPHOME=/usr/local/hawq export PXF_HOME=$GPHOME/pxf make install mkdir /var/log/pxf mkdir /var/run/pxf chown -R gpadmin:hadoop $PXF_HOME /var/log/pxf /var/run/pxf
メモリーエラー
なかなか一回で構築できないことが世の常で、サーバーの一つのメモリが厳しくて make が通らなかったり、HAWQ クラスターが起動できずハマりました。HDP の Hadoop クラスター (Hadoop 関連プロセス多数) と HAWQ クラスター (PosgreSQL プロセス多数) があって、更に PXF も Web サーバー起動するのでメモリを思った以上に消費します。
今回のクラスター環境では、データ処理しないマスター系のサーバーはメモリ 16G × 3台で構築していましたがメモリが少なかったです。
HAWQ クラスター環境を検討する際は、マスター系サーバーのメモリを 24G 以上か、4台以上準備することをオススメします。
今回行った対応は次の2つです。
- PostgreSQL の shared_buffers を抑える変更
- マスター系サーバーを1台増やして HDP のサービスを分散
PostgreSQL の shared_buffers を抑える変更
まずOSパラメータの変更をしました。/etc/sysctl.d/hawq_os_setting.conf に以下を変更し、sysctl –system を実施。
kernel.shmmax = 5368709120 kernel.shmall = 335544320続いて、
/usr/local/hawq/etc/hawq-site.xml の shared_buffers の設定値を変更して、HAWQ クラスター (PostgreSQL) を再起動します。
マスターサーバーは 512MB、セグメントサーバーは 2GB に減らしました。
HAWQ クラスターが起動可能な状態であれば、gpadmin ユーザーで以下の hawq config コマンドを利用すると便利です。
今回はメモリが厳しく HAWQ クラスターが起動できなかったので、それぞれのサーバーにログインして手作業で修正しました。
hawq config コマンドの例
hawq config -c shared_buffers -v "2GB"
マスター系サーバーを1台増やして HDP のサービスを分散
shared_buffers を減らしただけではまだメモリが厳しくてエラーた出たため、次にクラスターのマスター系サーバーを1台増やして HDP のサービスを分散させることを行いました。HDP 2.5.3 (Ambari 2.4.1) ではほとんど Ambari を使って画面上でできました。
Hosts > Summary > 各コンポーネントのプルダウンで “move” が選択できるものは Ambari でホストを移すことができます。
まず、今回利用している GMO アプリクラウドのコンパネから新しいサーバーを起動、HAWQ クラスター用に色々とセットアップした後、Ambari でホスト追加を行います。
Hosts > Actions ボタンで “+ Add New Hosts” でセットアップした新しいサーバーを追加した後、今回はメモリが厳しかったサーバーの Hive Metastore と HiveServer2 をそれぞれ別のところ新たに追加したサーバーに移しました。
なお HAWQ クラスターがちゃんと起動できているかどうかは、hawq state コマンドで簡単に見ることができます。
hawq state 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- HAWQ instance status summary 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:------------------------------------------------------ 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Master instance = Active 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Master standby = tst-hawq-m2.tst.hawqtest.com 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Standby master state = Standby host passive 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Total segment instance count from config file = 5 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Current HAWQ acl type = standalone 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:------------------------------------------------------ 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Segment Status 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:------------------------------------------------------ 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Total segments count from catalog = 5 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Total segment valid (at master) = 5 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Total segment failures (at master) = 0 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Total number of postmaster.pid files missing = 0 20170618:15:10:33:751721 hawq_state:tst-hawq-m1:gpadmin-[INFO]:-- Total number of postmaster.pid files found = 5Total segment valid と Total number of postmaster.pid files found が全セグメントサーバーの台数を一致していればOKです。
PXF の設定
PXF のビルド&インストールが終わったら、幾つかの設定ファイルを編集します。tst-hawq-m1 サーバーで gpadmin ユーザーで作業します。
pxf-env.sh を編集
/usr/local/hawq/pxf/conf/pxf-env.sh の以下の環境変数を変更します。HDP 2.5.3 をインストールしたときのパスに合わせます。
# Path to HDFS native libraries export LD_LIBRARY_PATH=/usr/hdp/2.5.3.0-37/hadoop/lib/native:${LD_LIBRARY_PATH} # Path to JAVA export JAVA_HOME=${JAVA_HOME:=/usr/jdk64/jdk1.8.0_77} # Configured user export PXF_USER=gpadminPXF_LOGDIR と PXF_RUNDIR は今回は変えずにそのパスへディレクトリを作成し、gpadmin のパーミッションにしています。
PXF_PORT も特に変える必要がないため、51200 のままにしました。
pxf-log4j.properties を編集
log4j のログ出力先を変える場合は、log4j.appender.ROLLINGFILE.File のパスを変更します。今回は pxf-env.sh の PXF_LOGDIR をそのまま利用するのもあり、初期設定通りで変更はしていません。
pxf-private.classpath を編集
一番やっかいなのがこの設定で、非常にたくさん記述しないといけません。HDP が利用する jar ライブラリを一通り指定するものです。この設定にあたっては「PXF on CDHでHAWQとHDFS/Hiveを連携する」のサイトも参考にさせて頂きました。
pxf-public.classpath もありますが、編集するのは pxf-private.classpath です。
HDP は /usr/hdp/current の下にシンボリックリンクを張って jar ライブラリを管理してますのでそれに合わせました。
HDP のバージョンアップ等でバージョンが変わってもシンボリックリンクが切り替わるだけでパスは変わらないので、PXF 管理もよりやさしくなります。
それから今回は HBase は利用していませんので、HBase 関連はすべてコメントアウトしました。
HDP クラスターをベースにした HAWQ クラスターで PXF を利用する場合は以下の設定が大体そのまま使えると思います。
# PXF Configuration /usr/local/hawq/pxf/conf # Hadoop Configuration /etc/hadoop/conf # Hive Configuration /etc/hive/conf # Hbase Configuration ##/etc/hbase/conf # PXF Libraries ##/usr/local/hawq/pxf/lib/pxf-hbase-*[0-9].jar /usr/local/hawq/pxf/lib/pxf-hdfs-*[0-9].jar /usr/local/hawq/pxf/lib/pxf-hive-*[0-9].jar /usr/local/hawq/pxf/lib/pxf-json-*[0-9].jar # Hadoop Libraries /usr/hdp/current/hadoop-hdfs-client/hadoop-hdfs.jar /usr/hdp/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core.jar /usr/hdp/current/hadoop-mapreduce-client/hadoop-mapreduce-client-common.jar /usr/hdp/current/hadoop-client/hadoop-auth.jar /usr/hdp/current/hadoop-client/hadoop-common.jar /usr/hdp/current/hadoop-client/lib/asm-*[0-9].jar /usr/hdp/current/hadoop-client/lib/avro-*[0-9].jar /usr/hdp/current/hadoop-client/lib/commons-cli-*[0-9].jar /usr/hdp/current/hadoop-client/lib/commons-codec-*[0-9].jar /usr/hdp/current/hadoop-client/lib/commons-collections-*[0-9].jar /usr/hdp/current/hadoop-client/lib/commons-configuration-*[0-9].jar /usr/hdp/current/hadoop-client/lib/commons-io-*[0-9].jar /usr/hdp/current/hadoop-client/lib/commons-lang-*[0-9].jar /usr/hdp/current/hadoop-client/lib/commons-logging-*[0-9].jar /usr/hdp/current/hadoop-client/lib/guava-*[0-9].jar /usr/hdp/current/hadoop-client/lib/htrace-core-*[0-9]*.jar /usr/hdp/current/hadoop-client/lib/jetty-*.jar /usr/hdp/current/hadoop-client/lib/jackson-core-asl-*[0-9].jar /usr/hdp/current/hadoop-client/lib/jackson-mapper-asl-*[0-9].jar /usr/hdp/current/hadoop-client/lib/jersey-core-*[0-9].jar /usr/hdp/current/hadoop-client/lib/jersey-server-*[0-9].jar /usr/hdp/current/hadoop-client/lib/log4j-*[0-9].jar /usr/hdp/current/hadoop-client/lib/protobuf-java-*[0-9].jar /usr/hdp/current/hadoop-client/lib/slf4j-api-*[0-9].jar # Hive Libraries /usr/hdp/current/hive-client/lib/antlr-runtime-*[0-9].jar /usr/hdp/current/hive-client/lib/datanucleus-api-jdo-*[0-9].jar /usr/hdp/current/hive-client/lib/datanucleus-core-*[0-9].jar /usr/hdp/current/hive-client/lib/hive-exec.jar /usr/hdp/current/hive-client/lib/hive-metastore.jar /usr/hdp/current/hive-client/lib/jdo-api-*[0-9].jar /usr/hdp/current/hive-client/lib/libfb303-*[0-9].jar # when running on OSx, 1.0.5 or higher version is required ##hive/lib/snappy-java-*[0-9].jar # HBase Libraries ##/usr/hdp/current/hbase-client/lib/hbase-client-*[0-9].jar ##/usr/hdp/current/hbase-client/lib/hbase-common-*[0-9].jar ##/usr/hdp/current/hbase-client/lib/hbase-protocol-*[0-9].jar ##/usr/hdp/current/hbase-client/lib/htrace-core-*[0-9]*.jar ##/usr/hdp/current/hbase-client/lib/netty-*[0-9].Final.jar ##/usr/hdp/current/hbase-client/lib/zookeeper-*[0-9].jartst-hawq-m1 で編集した設定ファイルを hawq scp コマンドを利用してその他の HAWQ クラスターサーバーに配布します。
hawq scp -h tst-hawq-m2 pxf-env.sh pxf-private.classpath =:/usr/local/hawq/pxf/conf hawq scp -f /usr/local/hawq/etc/slaves pxf-env.sh pxf-private.classpath =:/usr/local/hawq/pxf/conf
.bash_profile に PXF_HOME を追加
PXF を起動するため、gpadmin ユーザーの .bash_profile に PXF_HOME を追加します。前編で、
. /usr/local/hawq/greenplum_path.shを追加済みですので、今回それより後の行に
PXF_HOME=/usr/local/hawq/pxf export PXF_HOMEを追加します。tst-hawq-m1 サーバーで編集したら、hawq scp コマンドで配布します。
hawq scp -h kat-hawq-m2 .bash_profile =:/home/gpadmin hawq scp -f /usr/local/hawq/etc/slaves .bash_profile =:/home/gpadmin
PXF の初期化と起動
設定ファイルの編集の次は、PXF の初期化と起動です。初期化は設定ファイルの編集とは違って、pxf init コマンドを叩くのみです。
初期化すると、PXF_HOME のディレクトリに、pxf-service という tomcat 用のディレクトリが新たに作成されます。
/usr/local/hawq/pxf/bin/pxf init hawq ssh -h kat-hawq-m2 -e "/usr/local/hawq/pxf/bin/pxf init" hawq ssh -f /usr/local/hawq/etc/slaves -e "/usr/local/hawq/pxf/bin/pxf init"初期化が終わったら、PXF を起動します。
gpadmin ユーザーで実施しようとすると、pxf start コマンドで su していることもあり、gpadmin ユーザーが 自分自身にパスワードなしで su できるようになっていないとコケます。
今回は root ユーザーで起動するようにしました。root ユーザーで pxf start を実施する際にも PXF_HOME の環境変数を通すようにします。
export PXF_HOME=/usr/local/hawq/pxf /usr/local/hawq/pxf/bin/pxf start上記を PXF をインストールした全サーバーで行って、PXF を起動します。
PXF をテスト
gpadmin ユーザーで、公式サイトの手順にあるようにテストを実施して、select 文の結果が見られたら、PXF のテスト成功です。PXF の利用は、HAWQ の外部テーブルを作る際に PXF 連携のための LOCATION 句と FORMAT 句を指定するだけで OK です。
PXF は全セグメント/スレーブサーバーで 51200 ポートで起動しているので、
LOCATION 句には、”pxf://localhost:51200/{HDFS上のフルパス}” + “?PROFILE={プロファイル名}” の形で指定します。
echo 'Prague,Jan,101,4875.33 Rome,Mar,87,1557.39 Bangalore,May,317,8936.99 Beijing,Jul,411,11600.67' > pxf_hdfs_simple.txt hdfs dfs -mkdir /tmp/pxf_examples hdfs dfs -copyFromLocal pxf_hdfs_simple.txt /tmp/pxf_examples/ psql -d hawqdb hawqdb=# CREATE EXTERNAL TABLE pxf_hdfs_textsimple( location text, month text, num_orders int, total_sales float8 ) LOCATION ('pxf://localhost:51200/tmp/pxf_examples/pxf_hdfs_simple.txt?PROFILE=HdfsTextSimple') FORMAT 'TEXT' (delimiter=E',') ; hawqdb=# select * from pxf_hdfs_textsimple; location | month | num_orders | total_sales -----------+-------+------------+------------- Prague | Jan | 101 | 4875.33 Rome | Mar | 87 | 1557.39 Bangalore | May | 317 | 8936.99 Beijing | Jul | 411 | 11600.67 (4 rows)うまく見ることができました。
LOCATION 句と FORMAT 句 の詳細な設定は、Pivotal の公式ドキュメント「Accessing HDFS File Data」を参照してください。
PXF を利用した Hive と HAWQ の連携
いよいよここからが本題です。PXF を実践する場合、想定するのはやっぱり Hive と HAWQ の連携だと思います。そこで、Hive と HAWQ (PXF) 間で何ができるかと、処理性能を簡単に測ってみました。
Hive と HAWQ (PXF) 間でできること
PXF はあくまで HAWQ 側のプラグインですので、HDFS や Hive など HDP 側から HAWQ のデータを見たり書き込んだりすることはできません。HAWQ 側から PXF 経由では、HDFS や Hive または HBase のデータを見ることはできますが、書き込みについては HDFS のみ可能で Hive には直接書き込めません。
ORC や PARQUET フォーマットの Hive テーブルに書き込めないのでそこは不便ですが、HDFS に書き込んだ後にそのパスを指定したテキストフォーマットの Hive テーブルを作ってあげれば解決できます。
PXF から何が見たり書き込んだりできるかは、Pivotal の公式ドキュメント「Using Profiles to Read and Write Data」に書かれています。
HAWQ へのデータロード
HAWQ へのデータロードは GreenplumDB のときと同じです。「GreenplumDB と Hive on tez でデータのロードとクエリを試してみました」でやったように yml ファイルを使ってロードします。
今回もこのときに使った page_views.csv サンプルデータを使いました。zip 圧縮で 30GB ある大きなデータです。
HAWQ テーブル作成
HAWQ では、フォーマットに PARQUET が選べるようになりました。ロード先のテーブルは PARQUET + SNAPPY 圧縮の組み合わせにします。hawqdb という名前のデータベースを作ってそこにテーブルを作りました。gpadmin ユーザーで psql に接続して作業します。
psql -d postgres postgres=# create database hawqdb; postgres=# \c hawqdb; hawqdb=# CREATE TABLE page_views ( uuid varchar(20), document_id int, pv_timestamp bigint, platform smallint, geo_location varchar(20), traffic_source smallint ) WITH ( APPENDONLY=TRUE, ORIENTATION=PARQUET, COMPRESSTYPE=SNAPPY ) DISTRIBUTED BY (uuid) ;
データをロード
ロード用の page_views.yml ファイルを準備します。VERSION: 1.0.0.1 DATABASE: hawqdb USER: gpadmin HOST: tst-hawq-m1.tst.hawqtest.com PORT: 5432 GPLOAD: INPUT: - SOURCE: LOCAL_HOSTNAME: - tst-hawq-m1.tst.hawqtest.com PORT: 8081 FILE: - /var/tmp/hawq_load/page_views.csv.gz - COLUMNS: - uuid: varchar(20) - document_id: int - pv_timestamp: bigint - platform: smallint - geo_location: varchar(20) - traffic_source: smallint - FORMAT: text - DELIMITER: ',' - ESCAPE: 'off' - NULL_AS: '' - HEADER: true OUTPUT: - TABLE: public.page_views - MODE: insert PRELOAD: - TRUNCATE: trueGreenplumDB の gpload コマンドの代わりに HAWQ では hawq load コマンドを利用します。
yml ファイルの FILE: で指定したところにロードするデータを配置し、hawq load コマンドを実施します。
hawq load -f page_views.yml
HAWQ から HDFS への書き出し
HAWQ から HDFS に書き込むには、専用の WRITABLE EXTERNAL テーブルを作成します。WRITABLE EXTERNAL テーブルを CSV フォーマットの gzip 圧縮で作成し insert 文で書き込みました。
hawqdb=# CREATE WRITABLE EXTERNAL TABLE page_views_hdfs_wex ( uuid varchar(20), document_id int, pv_timestamp bigint, platform smallint, geo_location varchar(20), traffic_source smallint ) LOCATION ('pxf://localhost:51200/user/gpadmin/hawqdb/page_views_hdfs?PROFILE=HdfsTextSimple&COMPRESSION_CODEC=org.apache.hadoop.io.compress.GzipCodec') FORMAT 'TEXT' (delimiter=E',') ; hawqdb=# insert into page_views_hdfs_wex select * from page_views;zip 圧縮で 30GB 規模のデータを insert するのにかかった時間は30分ほどでした。
INSERT 0 2034275448 Time: 1821696.784 ms
Hive で ORC テーブルに変換
HAWQ から PXF を利用した外部テーブルで書き込んでから、この後のクエリテストのために ORC フォーマットの Hive テーブルを作り直しました。hive> CREATE EXTERNAL TABLE page_views_hdfs ( uuid string, document_id int, pv_timestamp bigint, platform tinyint, geo_location string, traffic_source tinyint ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS textfile LOCATION '/user/gpadmin/hawqdb/page_views_hdfs' ; hive> CREATE TABLE page_views_orc ( uuid string, document_id int, pv_timestamp bigint, platform tinyint, geo_location string, traffic_source tinyint ) STORED AS ORC ; hive> insert overwrite table page_views_orc select * from page_views_hdfs;19分ほどかかりました。
Time taken: 1118.613 secondsHive 側で作成した ORC テーブルを、HAWQ から PXF を利用した外部テーブルで読み込めるようにします。
hawqdb=# CREATE EXTERNAL TABLE page_views_hiveorc_ex ( uuid text, document_id int, pv_timestamp bigint, platform smallint, geo_location text, traffic_source smallint ) LOCATION ('pxf://localhost:51200/hawqdb.page_views_orc?PROFILE=hiveorc') FORMAT 'custom' (formatter='pxfwritable_import') ;細かい注意点として、Hive側で文字列カラムを string 型で定義すると、外部テーブルで元の varchar(20) の定義のままでは作成時にエラーが出ます。Hive の string 型は HAWQ では text 型にマッピングします。
Hive テーブルの HAWQ (PXF) から参照するクエリの処理時間
HAWQ のテーブルを直接参照した場合と、HAWQ から Hive の ORC テーブルを PXF 経由で参照する2種類のやり方で比較しました。参照クエリは性能を見るため like による絞り込みと、group by および having 句を使った select 文にしています。
- page_views : HAWQ のテーブル (PARQUET フォーマット)
- page_views_hiveorc_ex : ORC 参照に特化した HAWQ 外部テーブル
- hcatalog.hawqdb.page_views_orc : 外部テーブルを作らず hcatalog を利用
-- クエリ 1 select document_id, platform, count(*) from page_views where geo_location like 'US%' group by document_id, platform having count(*) > 200; -- クエリ 2 select document_id, platform, count(*) from page_views_hiveorc_ex where geo_location like 'US%' group by document_id, platform having count(*) > 200; -- クエリ 3 select document_id, platform, count(*) from hcatalog.hawqdb.page_views_orc where geo_location like 'US%' group by document_id, platform having count(*) > 200 ;それぞれ2回ずつ実施した処理結果は以下です。
/* 1回目 */ Time: 151195.705 ms -- クエリ 1 Time: 624600.887 ms -- クエリ 2 Time: 7617435.742 ms -- クエリ 3 /* 2回目 */ Time: 129710.046 ms -- クエリ 1 Time: 643936.812 ms -- クエリ 2 Time: 7725716.146 ms -- クエリ 3hcatalog は外部テーブル不要で便利なものの大きなサイズでは性能が良くないのと、ORC 参照用の外部テーブルは直接 HAWQ テーブルを見るより4~5倍くらい遅いという結果になりました。
ORC 参照用の外部テーブルを毎回見るというよりは、必要なものだけ HAWQ の PARQUET テーブルに取り込んで、HAWQ 側で作業するというやり方もありそうです。
そう思って試しに ORC 参照用の外部テーブルから、HAWQ のテーブルに insert してみたところ、思ったより時間が伸びて約40分かかりました。
/* 1回目 */ Time: 2186627.429 ms /* 2回目 */ Time: 2498086.305 ms
後編まとめ
ブログで GreenplumDB と Hive (on Tez) との比較や、HDP + Apache HAWQ の検証を前後編に渡ってやってきましたが、いろいろ試してわかったことは、- HAWQ の構築・設定がやっぱり大変。Hadoop と PostgreSQL 両方の知識がある程度あって HAWQ のことをよく知る必要がある
- まだまだ日本で HAWQ が広く普及しているわけでなく、運用やメンテナンスの知見を溜めていくのは大変
- アドホックな SQL 分析をたくさんしたい場合は、現状の Hive (on Tez) をフルチューンして使うより、少ないメモリでも安定した速さでクエリが返る GreenplumDB / HAWQ の方がオススメ
- PXF 連携はちょっと微妙。HDFS 書き出し用、もしくは ORC 参照用の外部テーブルはある程度使えそう
HAWQ のユースケースとしては、Kafka や Spark などでバッチ処理した Hadoop 側のデータを、PXF 経由 で HAWQ が参照もしくは取り込んで RDB レベルの SQL でアドホックに分析する、などが想定されます。
でも Hive の LLAP が進化して安定版になったら、アドホックな分析がしやすくなるので、HAWQ の進化と競ってほしいですね。
最後に
次世代システム研究室では、データ分析エンジニアやアーキテクトを募集しています。ビッグデータの解析業務など次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。皆さんのご応募をお待ちしています。