PXF on CDHでHAWQとHDFS/Hiveを連携する

PXF(Pivotal eXtension Framework)とは
-----------------------
[PXF](http://hawq.docs.pivotal.io/docs-hawq/topics/PXFInstallationandAdministration.html)は[HAWQ](http://pivotal.io/big-data/white-paper/a-true-sql-engine-for-hadoop-pivotal-hd-hawq)が管理していないHDFS上のファイルや[Hive](https://hive.apache.org/)のテーブル、[HBase](https://hbase.apache.org/)へのアクセスを提供するフレームワークです。
名前に"フレームワーク"と付くようにJavaのプログラムから使用することもできますが、PXFが提供するサービスを利用してHDFS上のファイルを外部テーブルとして参照することができます。

PXFをインストールする前に
-----------------------
HAWQと同様にPXFは[Ambari](https://ambari.apache.org/)を使えば簡単にインストールできます。
しかし、Ambariは[CDH](https://www.cloudera.com/products/apache-hadoop/key-cdh-components.html)に対応しておらず[^1]、手動でインストールする手順も用意されていません。
でも、HAWQがCDHで動くのですからPXFも動くはずです。
[Pivotal](http://pivotal.io/)が配布している[Pivotal HDB](https://network.pivotal.io/products/pivotal-hdb)に含まれるPXFとAmbari Pluginを確認しながら、インストールを進めましょう。
なお、ダウンロードには[Pivotal Network](https://network.pivotal.io/)への登録が必要です。

[^1]: [[AMBARI-3524] Set up generic puppet module architecture to manage both HDP and CDH (and Apache?) stacks](https://issues.apache.org/jira/browse/AMBARI-3524)

PXFのインストール
-----------------------
CDHのHadoopクラスタにHAWQがインストール済みである前提で話を進めます。[^2]
Ambari経由でPXFをインストールする手順は[Install HAWQ and PXF with Ambari](http://hawq.docs.pivotal.io/docs-hawq/topics/install-ambari.html)にありますが、残念ながら手動でインストールする手順はありません。

ダウンロードしたtarファイルを解凍して、PXFサービスとPXF Pluginをインストールします。
PXFサービスは[vFabric tc Server](https://www.vmware.com/products/vfabric-tcserver/overview)上で動作するWebアプリで、[WebHDFS](https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/WebHDFS.html)のようなREST APIを提供します。
余談ですが、vFabric tc Serverは[Tomcat](http://tomcat.apache.org/)ベースのアプリケーションサーバで、[Spring](https://spring.io/)界隈で使われているものです。
PXF PluginはHDFS/Hive/HBase用が用意されていて、それぞれへのアクセス手段を提供します。
PXF Hive/HBase Pluginは、依存関係でHive/HBaseのインストールが必要です。

ホスト名の一覧を用意して、全ノードにインストールします。

[^2]: [HAWQ を CDH で動かす](http://qiita.com/adachij2002/items/c44e377c3691ffae34bb)

``` console
# mkdir /opt/servers
# vi /opt/servers/all_hosts
hawq-ma01          # マスタ
hawq-sl01          # スレーブ
hawq-sl02          # 〃
:

# tar xvf PADS-1.3.1.1-19631-rhel5_x86_64.tgz
# gpscp -f /opt/servers/all_hosts PADS-1.3.1.1/{pxf*.rpm,vfabric-tc-server-standard-2.9.7-RELEASE.noarch.rpm} =:~/
# gpssh -f /opt/servers/all_hosts -e "rpm -ivh {pxf*.rpm,vfabric-tc-server-standard-2.9.7-RELEASE.noarch.rpm}"
```

インストールができたか確認します。

``` console
# gpssh -f /opt/servers/all_hosts -e "ls -l /usr/lib/gphd"
  publicstage
  pxf -> pxf-2.5.1.1
  pxf-2.5.1.1
  :
```

PXFサービスの初期化
-----------------------
Hadoopのjarファイルを設定ファイルに追加します。
[Pivotal HD](http://pivotalhd.docs.pivotal.io/)のパスが設定されているので、CDHのパスに修正しましょう。
[Avro](https://avro.apache.org/)のjarにバージョン番号が付いていたり、細かな罠があるので注意してください。
hadoop-mapreduce-client-common.jarは[MRVersionのエラー](http://qiita.com/adachij2002/items/fdbcbe0d874ff3707b51#%E8%BF%BD%E8%A8%98)を避けるために必要です。
なお、HiveのMetastoreに[Local Metastore](http://www.cloudera.com/documentation/enterprise/latest/topics/cdh_ig_hive_metastore_configure.html)を使用している場合は、MetastoreのDBのJDBCドライバとdatanucleus-rdbms-*[0-9].jarを追加する必要があります。

``` console
# vi /etc/gphd/pxf/conf/pxf-env.sh
:
export LD_LIBRARY_PATH=/usr/lib/hadoop/lib/native:${LD_LIBRARY_PATH}
```

``` console
# vi /etc/gphd/pxf/conf/pxf-private.classpath
:
/etc/gphd/pxf/conf
/etc/hadoop/conf
/etc/hbase/conf
/etc/hive/conf
/usr/lib/hadoop/client/hadoop-hdfs.jar
/usr/lib/hadoop/client/hadoop-mapreduce-client-core.jar
/usr/lib/hadoop/hadoop-auth.jar
/usr/lib/hadoop/hadoop-common.jar
/usr/lib/hadoop/lib/asm-*[0-9].jar
/usr/lib/hadoop/lib/avro.jar                                      # 注意(バージョン番号なし)
/usr/lib/hadoop/lib/commons-cli-*[0-9].jar
/usr/lib/hadoop/lib/commons-codec-*[0-9].jar
/usr/lib/hadoop/lib/commons-collections-*[0-9].jar
/usr/lib/hadoop/lib/commons-configuration-*[0-9].jar
/usr/lib/hadoop/lib/commons-io-*[0-9].jar
/usr/lib/hadoop/lib/commons-lang-*[0-9].jar
/usr/lib/hadoop/lib/commons-logging-*[0-9].jar
/usr/lib/hadoop/lib/guava-*[0-9].jar
/usr/lib/hadoop/lib/hadoop-lzo-*[0-9].jar
/usr/lib/hadoop/lib/jackson-core-asl-*[0-9].jar
/usr/lib/hadoop/lib/jackson-mapper-asl-*[0-9].jar
/usr/lib/hadoop/lib/jetty-*.jar
/usr/lib/hadoop/lib/jersey-core-*[0-9].jar
/usr/lib/hadoop/lib/jersey-server-*[0-9].jar
/usr/lib/hadoop/lib/log4j-*[0-9].jar
/usr/lib/hadoop/lib/protobuf-java-*[0-9].jar
/usr/lib/hadoop/lib/slf4j-api-*[0-9].jar
/usr/lib/hadoop/lib/htrace-core*[0-9]-incubating.jar              # 注意("htrace-core-*"ではなく"htrace-core*"。"incubating"のサフィックスあり)
/usr/lib/hadoop/lib/snappy-java-*[0-9].jar
/usr/lib/hbase/hbase-client.jar
/usr/lib/hbase/hbase-common.jar
/usr/lib/hbase/hbase-protocol.jar
/usr/lib/hbase/lib/htrace-core-*[0-9]-incubating.jar              # 注意("incubating"のサフィックスあり)
/usr/lib/hbase/lib/netty-*[0-9].Final.jar
/usr/lib/zookeeper/zookeeper.jar
/usr/lib/hive/lib/antlr-runtime-*[0-9].jar
/usr/lib/hive/lib/datanucleus-api-jdo-*[0-9].jar
/usr/lib/hive/lib/datanucleus-core-*[0-9].jar
/usr/lib/hive/lib/hive-exec.jar
/usr/lib/hive/lib/hive-metastore.jar
/usr/lib/hive/lib/jdo-api-*[0-9].jar
/usr/lib/hive/lib/libfb303-*[0-9].jar
/usr/lib/hadoop-mapreduce/hadoop-mapreduce-client-common.jar      # 注意(MRVersionのエラーを回避するため)
/usr/lib/gphd/pxf/pxf-hbase-*[0-9].jar
/usr/lib/gphd/pxf/pxf-hdfs-*[0-9].jar
/usr/lib/gphd/pxf/pxf-hive-*[0-9].jar
# user defined resources:
/usr/lib/gphd/publicstage
```

マスタの設定をスレーブにコピーします。

``` console
# vi /opt/servers/slave_hosts
hawq-sl01          # スレーブ
hawq-sl02          # 〃
:

# gpscp -f /opt/servers/slave_hosts /etc/gphd/pxf/conf/pxf-private.classpath =:/etc/gphd/pxf/conf
```

PXFサービスの初期化
-----------------------
全ノードのPXFサービスを初期化します。
これにより`/var/gphd/pxf`ディレクトリにPXFサービスの環境が作成されます。

``` console
# gpssh -f /opt/servers/all_hosts -e "service pxf-service init"
Creating instance 'pxf-service' ...
:
Instance created
Connector summary
  Port: 51200   Type: Blocking IO   Secure: false

# gpssh -f /opt/servers/all_hosts -e "ls -l /var/gphd/pxf/pxf-service/"
README.txt
bin
conf
lib
logs
temp
webapps
work
```

なお、初期化をやり直す場合は`/var/gphd/pxf`ディレクトリを削除して、`service pxf-service init`を再実行します。

PXFサービスの起動
-----------------------
全ノードでPXFサービスを起動します。

``` console
# gpssh -f /opt/servers/all_hosts -e "service pxf-service start"
:
Checking if PXF webapp is up and running...
PXF webapp is up
```

PXF HDFS Pluginの利用
-----------------------
HAWQで外部テーブルを作成して、HDFS上のファイルを参照します。
詳細は[Accessing HDFS File Data with PXF](http://hawq.docs.pivotal.io/docs-hawq/topics/PXFInstallationandAdministration.html#accessinghdfsfiledatawithpxf)が参考になります。

まずはCSVファイルを用意して、HDFSに配置します。

``` console
# su - gpadmin
$ vi /tmp/ext_t_user.csv
1 ajis_ka
2 adachij2002
3 takeuchikzm

$ hdfs dfs -mkdir /user/gpadmin/ext_t_user
$ hdfs dfs -put /tmp/ext_t_user.csv /user/gpadmin/ext_t_user
```

次にHAWQから外部テーブルとして参照します。
LOCATIONで指定している`Profile=HdfsTextSimple`は、FRAGMENTER/ACCESSOR/RESOLVERを組み合わせたシンタックスシュガーです。[^3]

[^3]: [Built-in Profiles](http://hawq.docs.pivotal.io/docs-hawq/topics/PXFInstallationandAdministration.html#built-inprofiles)

``` console
$ psql hawq_db
hawq_db=#
  CREATE EXTERNAL TABLE ext_t_user
  (
    id INTEGER,
    name TEXT
  )
  LOCATION ('pxf://hawq-ma01:8020/user/gpadmin/ext_t_user?Profile=HdfsTextSimple')
  FORMAT 'TEXT' (DELIMITER = E'\t');
CREATE EXTERNAL TABLE

hawq_db=# SELECT * FROM ext_t_user ORDER BY id;
 id |    name
----+-------------
  1 | ajis_ka
  2 | adachij2002
  3 | takeuchikzm
(3 rows)
```

動きました！

PXF Hive Pluginの利用
-----------------------
続けて、HAWQからHiveのテーブルを参照します。
PXF Hive Pluginを使うためには、全ノードにHiveのインストールとHiveクライアントの設定が必要です。
詳細は[Accessing Hive Data with PXF](http://hawq.docs.pivotal.io/docs-hawq/topics/PXFInstallationandAdministration.html#accessinghivedatawithpxf)が参考になります。

CSVファイルを用意して、Hiveのテーブルにロードします。

``` console
# vi /tmp/t_city.csv
1 tokyo
2 beijing
3 newyork

# sudo -u hive hive
hive> CREATE DATABASE test_db;
hive> USE test_db;
hive>
  CREATE TABLE t_city (
    id INT,
    name STRING)
  ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\t'
  LINES TERMINATED BY '\n';
hive> LOAD DATA LOCAL INPATH '/tmp/t_city.csv' OVERWRITE INTO TABLE t_city;
hive> exit;
```

次にHAWQから外部テーブルとして参照します。
HiveのDBを使用する場合は、LOCATIONに`<DB名>.<テーブル名>`の形式で指定する必要があります。

``` console
# su - gpadmin
$ psql hawq_db
hawq_db=#
  CREATE EXTERNAL TABLE ext_t_city
  (
    id INTEGER,
    name TEXT
  )
  LOCATION ('pxf://hawq-ma01:8020/test_db.t_city?Profile=Hive')
  FORMAT 'CUSTOM' (FORMATTER='pxfwritable_import');
CREATE EXTERNAL TABLE

hawq_db=# SELECT * FROM ext_t_city ORDER BY id;
 id |  name
----+---------
  1 | tokyo
  2 | beijing
  3 | newyork
(3 rows)
```

動きました！

まとめ
-----------------------
HAWQ単体ではHDFSの特定のディレクトリに独自の階層構造でファイルが格納されるため、他のエコシステムとの連携に難がありました。
PXFを組み合わせることでHDFS上のファイルやHiveのテーブルが利用できるようになります。
実はPXFを使用するとパフォーマンスに難があるのですが...それはまた別の機会に。
PostgreSQL互換のSQLで分析を楽しみましょう！