EC2にHadoop/Hive/Presto突っ込んでS3にクエリ投げるまで

Emrでのやり方は需要があったら書くかな。
といっても説明することほとんど無いけどね。細かい設定関係をどこでどうするのかってくらいか。

というわけで、タイトル通りEC2インスタンスにprestoセットアップして、s3に置いたデータファイル(orc)にクエリ投げるまでをざざっと。なお、Hadoopは擬似分散モード、prestoはcoordinator/workerが同じノードです。

## 何故か
作業時、途中までrootでやってたのでそれをここに記します。途中からec2-userでやりました。

```
sudo su -
```

## Hive ローカルメタストアで使用するのでmysqlをいれよう
ぽすぐれでも良いですがmysqlのほうがラクです。
mysqlは大人の事情で5.7系使いたかったのでrpmで

```
yum -y install http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-community-common-5.7.16-1.el6.x86_64.rpm
yum -y install http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-community-libs-5.7.16-1.el6.x86_64.rpm
yum -y install http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-community-libs-compat-5.7.16-1.el6.x86_64.rpm
yum -y install http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-community-client-5.7.16-1.el6.x86_64.rpm
yum -y install http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-community-server-5.7.16-1.el6.x86_64.rpm
yum -y install http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-community-devel-5.7.16-1.el6.x86_64.rpm

service mysqld start
chkconfig mysqld on
```

いらないものも入ってるしなんで1個ずつやってんねんって思いはしますが作業メモにこう書いてあったので･･･
なお、rpmのurlは意外と頻繁に変わる気がするのでバージョン固定の観点からいってもs3かどっかに置いとくほうが良いでしょうね。

## 初期パスワードの変更とpluginの無効化
とりあえず初期パスを獲得します。

```
cat /var/log/mysqld.log | grep "temporary password"
```

↑でroot@localhost: XXXXXXX のXXXXXXXを使ってmysqlへログイン。
開発用の環境だしvalidate_passwordは闇に葬ります。

```
set password for root@localhost=password('passwordPASSWORD@999');
use mysql
uninstall plugin validate_password;
update user set authentication_string=PASSWORD('新しいパスワード') where User='root';
flush privileges;
exit
```

## JDKの入手とインストール
とりあえず1.8系ならそれで良いことにします。

http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

Linux x64のrpmをDL。

```
mv DLしたパス/jdk-8u111-linux-x64.rpm /usr/local/lib
yum -y localinstall /usr/local/lib/jdk-8u111-linux-x64.rpm
java -version
rm -f /usr/local/lib/jdk-8u111-linux-x64.rpm
```

#### JAVA_HOMEの設定
ここからec2-userで作業します。別にrootのままで良い人はそのままで。
user変えたら環境変数JAVA_HOMEを設定します。

```
echo "export JAVA_HOME=/usr/java/jdk1.8.0_111" >> ~/.bashrc
. ~/.bashrc
```

## SSH設定
hadoopはssh使うので、パスワード入力無しでlocalhostにssh接続可能な状態にします。
ssh-keygen時の対話は全部ノールックenterでかまいません。
ついでにフィンガープリントも追加しておきます。

```
ssh-keygen
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
ssh localhost
exit
```

## Hadoopのインストールと設定
#### インストール
http://www.apache.org/dyn/closer.cgi/hadoop/common/
に記載されてる適当なサイトから目的のversionのhadoopのURLを入手します。
自分は2.7.3でやりました。

```
wget http://[[上で入手したURL]]/hadoop-2.7.3.tar.gz
sudo mv DLしたパス/hadoop-2.7.3.tar.gz /usr/local/lib
cd /usr/local/lib
sudo tar zxf hadoop-2.7.3.tar.gz
sudo rm -f hadoop-2.7.3.tar.gz
sudo ln -s hadoop-2.7.3 hadoop

# ownerをec2-userにしておく
sudo su -
cd /usr/local/lib
chown ec2-user hadoop
exit
```

#### 擬似分散モードHDFS設定
なお、複数ノード用意してちゃんと分散しようとするともうちょっと設定が必要なそうです。
今回は擬似分散で。

まず環境変数HAOOP_HOME/HADOOP_CONF_DIRを設定します。

```
echo "export HADOOP_HOME=/usr/local/lib/hadoop" >> ~/.bashrc
echo "export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop" >> ~/.bashrc
. ~/.bashrc
```

HDFS格納先となるディレクトリを生成します。パスはお好みで良いかと。

```
mkdir -p ~/var/hdfs/{name,data}
```

core-site.xml/hdfs-site.xml/yarn-site.xmlを編集します。

sudo vi /usr/local/lib/hadoop/etc/hadoop/core-site.xml

```xml

<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:9000</value>
  </property>
</configuration>
```

sudo vi /usr/local/lib/hadoop/etc/hadoop/hdfs-site.xml

```xml

<configuration>
  <property>
    <name>dfs.name.dir</name>
    <value>/home/ec2-user/var/hdfs/name</value>
  </property>
  <property>
    <name>dfs.data.dir</name>
    <value>/home/ec2-user/var/hdfs/data</value>
  </property>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>
```


sudo vi /usr/local/lib/hadoop/etc/hadoop/yarn-site.xml
(s3アクセスのため)

```
<configuration>

<!-- Site specific YARN configuration properties -->
  <property>
    <name>fs.s3a.impl</name>
    <value>org.apache.hadoop.fs.s3a.S3AFileSystem</value>
  </property>
  <property>
    <name>fs.s3a.access.key</name>
    <value>AWSアクセスキー</value>
  </property>
  <property>
    <name>fs.s3a.secret.key</name>
    <value>AWSシークレットアクセスキー</value>
  </property>
</configuration>
```

namenodeの初期化をします。
なお、namenodeはdatanodeがどういった情報を記録しているかを管理してるらしいです。

```
/usr/local/lib/hadoop/bin/hadoop namenode -format
```

最後に起動と確認をします。

```
/usr/local/lib/hadoop/sbin/start-dfs.sh
/usr/local/lib/hadoop/bin/hadoop fs -ls /
```

ファイルをまだ生成していないので、とりあえずエラーが出たりしなければ問題なし。

#### S3へ触るために追加設定
org.apache.hadoop.fs.s3a.S3AFileSystemを利用可能にするため、Hadoopにclasspath追加します。
なお、特にどこかから持ってこなくてもデフォルトでjarそのものはshare/hadoop/tools/lib/にもってます。

sudo vi /usr/local/lib/hadoop/etc/hadoop/hadoop-env.sh
ファイル末尾に

```
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HADOOP_HOME/share/hadoop/tools/lib/*
```

アクセスできるか確認します。

```
/usr/local/lib/hadoop/bin/hadoop fs -ls s3a://[[bucket名]]/
```

fs.s3.implも試しましたが、-lsでエラーになってしまい断念。

## Hiveのインストール
http://www.apache.org/dyn/closer.cgi/hive/
hadoopと同じように、上のURLから目的のversionのhiveのURLを入手します。
今回はapache-hive-2.1.1-bin.tar.gz。

```
wget http://[[上で入手したURL]]/apache-hive-2.1.1-bin.tar.gz
sudo mv DLしたパス/apache-hive-2.1.1-bin.tar.gz /usr/local/lib
cd /usr/local/lib
sudo tar zxf apache-hive-2.1.1-bin.tar.gz
sudo rm -f apache-hive-2.1.1-bin.tar.gz
sudo ln -s apache-hive-2.1.1-bin hive

# owner変更
sudo su -
cd /usr/local/lib
chown ec2-user hive
exit
```

環境変数HIVE_HOMEを設定します。

```
echo "export HIVE_HOME=/usr/local/lib/hive" >> ~/.bashrc
. ~/.bashrc

# インストール確認
/usr/local/lib/hive/bin/hive -H
```

#### JDBC
metastoreにmysqlを使用するため、ドライバを設置します。
http://dev.mysql.com/downloads/connector/j/
ここのTAR ArchiveのほうをDL。
今回はmysql-connector-java-5.1.40.tar.gzでした。

これを解凍してhiveで利用できるようにします。

```
tar zxf DLしたパス/mysql-connector-java-5.1.40.tar.gz
sudo mv mysql-connector-java-5.1.40/mysql-connector-java-5.1.40-bin.jar /usr/local/lib/hive/lib
```

#### metastore用DBとuser作成
metastoreのDBとuserを作ります。なお、char setをlatin1にしないとhiveがmetastoreデータ作る時に
ユニークインデックスの合計文字長の制約に引っかかってエラーになるようです。

```
mysql -uroot -p

create database hive_metastore default character set 'latin1';
use hive_metastore;
create user 'hive'@'localhost' identified by 'パスワード';
grant select, insert, update, delete, alter, create, index, references on hive_metastore.* to 'hive'@'localhost';
```

#### metastore設定
hive-site.xmlを作成します。

sudo vi /usr/local/lib/hive/conf/hive-site.xml

```
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>javax.jdo.option.ConnectionURL</name>
    <value><![CDATA[jdbc:mysql://localhost/hive_metastore?autoReconnect=true&useSSL=false]]></value>
  </property>
  <property>
    <name>javax.jdo.option.ConnectionDriverName</name>
    <value>com.mysql.jdbc.Driver</value>
  </property>
  <property>
    <name>javax.jdo.option.ConnectionUserName</name>
    <value>hive</value>
  </property>
  <property>
    <name>javax.jdo.option.ConnectionPassword</name>
    <value>パスワード</value>
  </property>
  <property>
    <name>datanucleus.fixedDatastore</name>
    <value>false</value>
  </property>
  <property>
    <name>hive.exec.local.scratchdir</name>
    <value>/tmp/hive</value>
  </property>
  <property>
    <name>hive.downloaded.resources.dir</name>
    <value>/tmp/hive</value>
  </property>
  <property>
    <name>hive.querylog.location</name>
    <value>/tmp/hive</value>
  </property>
  <property>
    <name>hive.server2.enable.doAs</name>
    <value>false</value>
  </property>
</configuration>
```

#### 初期化と接続確認

```
/usr/local/lib/hive/bin/schematool -dbType mysql -initSchema
/usr/local/lib/hive/bin/hive
```

## ORCテーブルデータ作成
#### テキストデータ
接続できたらサンプルデータを作ります。
2017/01/01から03までと見立てて

```
10000,1,AAA
10001,2,BBB
```

```
20000,3,CCC
20001,4,DDD
```

```
30000,5,EEE
30001,6,FFF
```

こんな感じに3ファイル作ってs3へ配置。
その際のロケーションは
[[bucket]]/dir/2017/1/1
[[bucket]]/dir/2017/1/2
[[bucket]]/dir/2017/1/3

こんな感じで。

#### 生データテーブル生成
ファイルを配置したらhiveデータを作ります。

```
/usr/local/lib/hive/bin/hive
```

でhive接続したら

```
CREATE DATABASE test_orc;

use test_orc;

CREATE EXTERNAL TABLE tmp1
(
  time BIGINT,
  id INT,
  name STRING
)
  ROW FORMAT DELIMITED
  FIELDS TERMINATED BY ','
  LINES TERMINATED BY '\n'
  LOCATION 's3a://[[bucket]]/dir/2017/1/1';

CREATE EXTERNAL TABLE tmp2
(
  time BIGINT,
  id INT,
  name STRING
)
  ROW FORMAT DELIMITED
  FIELDS TERMINATED BY ','
  LINES TERMINATED BY '\n'
  LOCATION 's3a://[[bucket]]/dir/2017/1/2';

CREATE EXTERNAL TABLE tmp3
(
  time BIGINT,
  id INT,
  name STRING
)
  ROW FORMAT DELIMITED
  FIELDS TERMINATED BY ','
  LINES TERMINATED BY '\n'
  LOCATION 's3a://[[bucket]]/dir/2017/1/3';
```

色々やり方はありますがとりあえず手っ取り早く3テーブル作ってしまいます。
なお、この時点でクエリ発行すればちゃんとデータ取れます。

#### ORCテーブル生成とinsert
最後にORCデータテーブルを作成し、データを入れます。

```
CREATE EXTERNAL TABLE test
(
  time BIGINT,
  id INT,
  name STRING
)
  PARTITIONED BY (ts BIGINT)
  ROW FORMAT DELIMITED
  FIELDS TERMINATED BY ','
  LINES TERMINATED BY '\n'
  STORED AS ORC
  LOCATION 's3a://[[bucket]]/orc';
  tblproperties("orc.compress"="zlib");
```

```
INSERT INTO TABLE test PARTITION (ts=10000) SELECT time,id,name FROM tmp1 ORDER BY time;
INSERT INTO TABLE test PARTITION (ts=20000) SELECT time,id,name FROM tmp1 ORDER BY time;
INSERT INTO TABLE test PARTITION (ts=30000) SELECT time,id,name FROM tmp1 ORDER BY time;
```

これでORC形式データテーブルが出来ます。
ORDERをつけている理由はsmall files problem対応です。

#### ThriftMetastore
prestoからhive metastoreを触ることになるため、hiveをメタストアモードで起動させる必要があるようです。

```
nohup /usr/local/lib/hive/bin/hive --service metastore &
```

## presto-server
https://prestodb.io/docs/current/installation/deployment.html

公式資料。上のほうにserverプログラムがtar.gz形式でリンク貼られているため例によってDLして解凍します。
今回はpresto-server-0.161.tar.gzでした。

```
wget http://[[上で入手したURL]]/presto-server-0.161.tar.gz
sudo mv DLしたパス/presto-server-0.161.tar.gz /usr/local/lib
cd /usr/local/lib
sudo tar zxf presto-server-0.161.tar.gz
sudo rm -f presto-server-0.161.tar.gz
sudo ln -s presto-server-0.161 presto

# owner変更
sudo su -
cd /usr/local/lib
chown ec2-user presto
exit
```

あとは公式に従って設定していきます。

```
mkdir -p ~/var/presto/data
cd /usr/local/lib/presto
mkdir etc
cd etc
```

vi node.properties

```
node.environment=production
node.id=ffffffff-ffff-ffff-ffff-ffffffffffff
node.data-dir=/home/ec2-user/var/presto/data
```

vi jvm.config
Emrを使用するとこの設定は変更できません。
Emrの場合は-Xmxはインスタンスのメモリ量の大体90%が設定されているようです。

```
-server
-Xmx16G
-XX:+UseG1GC
-XX:G1HeapRegionSize=32M
-XX:+UseGCOverheadLimit
-XX:+ExplicitGCInvokesConcurrent
-XX:+HeapDumpOnOutOfMemoryError
-XX:OnOutOfMemoryError=kill -9 %p
```

vi config.properties
パブリックDNSでも動作はするはずです。
query.max-memory-per-nodeはjvmのXmxの40%までしか設定できません。
設定するとサーバ起動時にエラー出ます。

```
coordinator=true
node-scheduler.include-coordinator=true
http-server.http.port=8889
query.max-memory=5GB
query.max-memory-per-node=1GB
discovery-server.enabled=true
discovery.uri=http://[[coordinatorノードのプライベートDNS]]:8889
query.queue-config-file=/home/ec2-user/presto-queues.json
```

vi log.properties

```
com.facebook.presto=INFO
```

#### query.queue-config-file
クエリの最大同時実行数と最大実行待ち数を決める設定値を定義します。
https://prestodb.io/docs/current/admin/queue.html
公式資料。

```
{
  "queues": {
    "user.${USER}": {
      "maxConcurrent": 100,
      "maxQueued": 200
    }
  },
  "rules": [
    {
      "queues": ["user.${USER}"]
    }
  ]
}
```

この例でいくと、実行ユーザが誰であっても最大同時実行数は100で最大実行待ち数は200です。

#### Hiveカタログの生成
今回はHiveデータに触りたいのでHive用のcatalogを作ります。

```
mkdir catalog
cd catalog
```

vi hive.properties
hive.s3.use-instance-credentialsはデフォルトtrue。
IAMを設定していない場合trueだとawsのmetadataが取得できないってことでエラー出ます。
またこちらもパブリックDNSでも動作するはずです。

```
connector.name=hive-cdh5
hive.metastore.uri=thrift://[[hive-metastoreが起動してるサーバ/今回は同じノードなのでcoordinatorノードのプライベートDNS]]:9083
hive.config.resources=/usr/local/lib/hadoop/etc/hadoop/core-site.xml,/usr/local/lib/hadoop/etc/hadoop/hdfs-site.xml
hive.s3.use-instance-credentials=false
hive.s3.aws-access-key=AWSアクセスキー
hive.s3.aws-secret-key=AWSシークレットアクセスキー
hive.s3.endpoint=エンドポイント
```

## presto-client
https://prestodb.io/docs/current/installation/cli.html
例によって公式資料に従ってclientをセットアップします。

```
wget http://[[上で入手したURL]]/presto-cli-0.161-executable.jar
sudo mv DLしたパス/presto-cli-0.161-executable.jar /usr/local/lib/presto/bin/
cd /usr/local/lib/presto/bin/
sudo mv presto-cli-0.161-executable.jar presto
chmod +x presto
```

## 起動とクエリ
あとは起動してクライアントからクエリ投げるだけです。

```
/usr/local/lib/presto/bin/launcher start
/usr/local/lib/presto/bin/presto --server localhost:8889 --catalog hive　--schema test_orc
```

もし起動時にエラーが出る場合は、この設定通りにやっている場合は

```
/home/ec2-user/var/presto/data/var/log/server.log
```

にログが出ていると思います。


めちゃめちゃ長くなりましたが以上でセットアップ完了です。
workerをちゃんと立てたい場合はhadoopの分散設定をする必要がありそうなのでそれはまたいずれということで･･･