hadoop & hive & metastore on mysql & S3AFileSystem をMac上にて動かすまで

# 各種バージョン
この記事の時点でのAmazon EMRは
Hadoop ディストリビューションは、Amazon 2.7.3となっていてhadoop 2.7.3で
アプリケーションは、Hive 2.1.1, Tez 0.8.4　なので極力これに近付ける

* MacOS Sierra 10.12.3
* Java 1.8
* hadoop 2.7.3
* hive　2.1.1
* mysql 5.5.49

EMRのバージョンと関連バージョンの確認はこちら
https://docs.aws.amazon.com/emr/latest/ReleaseGuide/emr-release-components.html

![alt](https://docs.aws.amazon.com/emr/latest/ReleaseGuide/images/emr-5.11.1.png)

---
# インストール作業

## 1.java

```
# javaをインストール
$ brew cask install java

# JAVA_HOMEを設定
$ echo 'export JAVA_HOME=`/usr/libexec/java_home -v 1.8`' >> ~/.bash_profile
$ echo 'export PATH="PATH=$JAVA_HOME/bin:$PATH"' >> ~/.bash_profile

# バージョンを確認
$ java -version
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b16, mixed mode)
```

## 2.mysql

```
# 既に入っていたら消す my.cnfとかも消す方が吉
$ brew uninstall mysql

# インストール
$ brew tap homebrew/versions
$ brew install mysql55

# パスを追加
$ echo 'export PATH="/usr/local/opt/mysql@5.5/bin:$PATH"' >> ~/.bash_profile

# mysql起動
$ brew services start mysql@5.5

# バージョンを確認
$ mysql -u root
Server version: 5.5.49 Homebrew
```

* ハマりポイント

```
# 5.5系でないと、hive 2.1.0のmetastoreの作成が、
# InnoDBでPK・Uniqueカラムのフィールド長の制限に引っ掛かる
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes
```

## 3.hadoop

```
# インストール
$ brew install hadoop

# 環境変数を設定
$ echo 'export HADOOP_HOME="/usr/local/Cellar/hadoop/2.7.3"' >> ~/.bash_profile
$ echo 'export HADOOP_CONF_DIR="$HADOOP_HOME/libexec/etc/hadoop"' >> ~/.bash_profile

# バージョンを確認
$ hadoop version
Hadoop 2.7.3
```

## 4.hive

```
# brewでは、2.1.0までしかないので、本家からダウンロードする
$ curl -O http://ftp.yz.yamagata-u.ac.jp/pub/network/apache/hive/hive-2.1.1/apache-hive-2.1.1-bin.tar.gz

# 解凍
$ tar xvzf apache-hive-2.1.1-bin.tar.gz 

# インストール (今回わかりやすくするため、/usr/local/Cellar/hive/2.1.1へ配置)
$ sudo mv apache-hive-2.1.1-bin /usr/local/Cellar/hive/2.1.1

# 環境変数を設定
$ echo 'export HIVE_HOME="/usr/local/Cellar/hive/2.1.1"' >> ~/.bash_profile
$ echo 'export PATH="PATH=$HIVE_HOME/bin:$PATH"' >> ~/.bash_profile

# バージョンを確認
$ hive -H
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/usr/local/Cellar/hive/2.1.1/lib/log4j-slf4j-impl-2.4.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
```

* homebrewで旧バージョンを使用する
https://qiita.com/cokaholic/items/6db92c23b43d18bdab82

* ハマりポイント

2.1.1でないと、パーティショニング MSCK REPAIR TABLEがエラーになる。

ここまでで、インストールと環境設定が終わり、次は各種設定

---

# 設定作業

## 1.localhostへのssh接続できるようにする設定

Macの場合
システム環境設定 → 共有 から、「リモートログイン」にチェックを入れます。

以下のコマンドでauthorized_keysを追加します。
※まだ公開鍵を作成していない場合はssh-keygenで作成します。

```
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys
```

以下のコマンドでlocalhostにsshで接続できることを確認します。

```
$ ssh localhost
```

## 2.HDFS設定

疑似分散モードでHDFSを構成します。

```$HADOOP_CONF_DIR/core-site.xml
<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:9000</value>
  </property>
</configuration>
```

```$HADOOP_CONF_DIR/hdfs-site.xml
<configuration>
  <property>
    <name>dfs.name.dir</name>
    <value>/var/lib/hdfs/name</value>
  </property>
  <property>
    <name>dfs.data.dir</name>
    <value>/var/lib/hdfs/data</value>
  </property>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>
```

## 3.S3上のファイルへのアクセス設定

hadoopがS3にアクセス出来るようにします。

### 3.1.awsのjarにパスを通す

S3AFileSystemを使えるようにするため、「hadoop-aws-2.7.2.jar」にCLASSPATHを通します。
hadoop-env.shに以下を追記します。

```$HADOOP_CONF_DIR/hadoop-env.sh
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HADOOP_HOME/libexec/share/hadoop/tools/lib/*
```

### 3.2.S3AFileSystemとアクセスキーを設定

```$HADOOP_CONF_DIR/yarn-site.xml
<configuration>
  <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>
```

## 4.mysqlをmetastore用のデータベースとして設定

### 4.1.データベースを作成

```
$ mysql -u root
# databaseのcharactersetはlaten1にしておく必要があります。
# 以下を参照
# http://tagomoris.hatenablog.com/entry/20110310/1299738606
mysql> create database metastore default character set 'latin1';
mysql> use metastore;
mysql> create user 'hive'@'localhost' identified by 'hive';
mysql> grant select, insert, update, delete, alter, create, index, references on metastore.* to 'hive'@'localhost';
```

### 4.2.jdbcドライバをhiveのライブラリへ追加

```
# ダウンロード
$ curl -O https://cdn.mysql.com//Downloads/Connector-J/mysql-connector-java-5.1.40.tar.gz

# 解凍
$ tar xvzf mysql-connector-java-5.1.40.tar.gz

# hiveのライブラリディレクトリに配置
$ mv mysql-connector-java-5.1.40/mysql-connector-java-5.1.40-bin.jar $HIVE_HOME/lib
```

### 4.3.mysqlのmetastoreデータベースをhadoopのメタストアとして使用するための設定

```$HIVE_HOME/conf/hive-site.xml
<configuration>
  <property>
    <name>javax.jdo.option.ConnectionURL</name>
    <value>jdbc:mysql://localhost/metastore?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>hive</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>

<!-- hiveでdynamodbを利用する場合の設定 -->
  <property>
    <name>dynamodb.endpoint</name>
    <value>http://localhost:8000</value>
  </property>
  <property>
    <name>dynamodb.awsAccessKeyId</name>
    <value>hoge</value>
  </property>
  <property>
    <name>dynamodb.awsSecretAccessKey</name>
    <value>hogehoge</value>
  </property>
</configuration>
```

### 4.4.metastoreの初期化

```
# schematoolで初期化を実行します。
$ schematool -dbType mysql -initSchema -verbose
```

## 5.HDFSのフォーマット

```
$ hdfs namenode -format
```

## 6.namenodeの起動とhdfsとS3への接続確認

以下のコマンドでHDFSを起動します。
YARN (Yet-Another-Resource-Negotiator)も起動したいので、　`$HADOOP_HOME/sbin/start-all.sh`を実行する。

※停止する場合は `$HADOOP_HOME/sbin/stop-all.sh`

```
# パスワード聞かれたら、入力する
$ $HADOOP_HOME/sbin/start-all.sh

# hdfsへの接続確認(この時点では、まだHDFS上にファイルが無いので一覧には何も表示されません。)
$ hadoop fs -ls /

# s3のバケットが見れるかも確認
$ hadoop fs -ls s3a://※ここにS3のbucket名※/
```

お疲れ様です。
ここまでで、各種設定が終わりました。
次は、S3上に配置したTSVファイルに対してhiveでクエリを投げてみます。

---

# S3上に配置したTSVファイルに対してhiveでクエリを投げてみます！

## 1.S3バケットの状態
```
# S3バケットには、カレンダーマスターを年でパーティショニングした状態で、TSVファイルを置いてある
$ hadoop fs -ls s3a://hivelocal/input/calendar_master/
Found 2 items
drwxrwxrwx   -          0 1970-01-01 09:00 s3a://hivelocal/input/calendar_master/year=2016
drwxrwxrwx   -          0 1970-01-01 09:00 s3a://hivelocal/input/calendar_master/year=2017

# こんな感じ
$ hadoop fs -ls s3a://hivelocal/input/calendar_master/year=2016/
Found 1 items
-rw-rw-rw-   1       5855 2017-02-19 14:06 s3a://hivelocal/input/calendar_master/year=2016/calendar_master.csv
$ hadoop fs -ls s3a://hivelocal/input/calendar_master/year=2017/
Found 1 items
-rw-rw-rw-   1       5840 2017-02-19 14:06 s3a://hivelocal/input/calendar_master/year=2017/calendar_master.csv
```

## 2.hiveQLを準備

データベース作って、テーブル作って、ロードして、パーティショニングするスクリプト

```/src/hive/create_calendar_master.q
-- データベースを作成
CREATE DATABASE IF NOT EXISTS HIVE_TEST;

USE HIVE_TEST;

-- カレンダーマスタテーブルを削除
DROP TABLE IF EXISTS calendar_master;

-- カレンダーマスタテーブルを作成
CREATE EXTERNAL TABLE IF NOT EXISTS calendar_master (
  dt date,                  -- 日付
  yobi tinyint,             -- 曜日
  shukujitsu_code tinyint   -- 祝日コード
)
PARTITIONED BY (`year` string)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\t'
  LINES TERMINATED BY '\n'
STORED AS TEXTFILE
LOCATION '${INPUT}/calendar_master/';

-- テーブルにすべてのパーティションを挿入
MSCK REPAIR TABLE calendar_master;
```

## 3.テスト用のシェルを用意

```/test.sh
#!/bin/sh

# hiveコマンドのオプション　debug
OPTS=-v
# hiveQLソースがあるフォルダ
SRC=./src/hive
# INPUTデータがある、S3バケット指定
INPUT=s3a://hivelocal/input
# OUTPUTデータの出力先S3バケット指定
OUTPUT=s3a://hivelocal/output

hive $OPTS -f $SRC/create_calendar_master.q -dINPUT=$INPUT || { echo oops!; exit 1; }
```

## 4.いざ実行！

```
$ sh test.sh 
CREATE DATABASE IF NOT EXISTS HIVE_TEST
OK
Time taken: 1.738 seconds


USE HIVE_TEST
OK
Time taken: 0.019 seconds


DROP TABLE IF EXISTS calendar_master
OK
Time taken: 0.127 seconds


CREATE EXTERNAL TABLE IF NOT EXISTS calendar_master (
  dt date,                  -- 日付
  yobi tinyint,             -- 曜日
  shukujitsu_code tinyint   -- 祝日コード
)
PARTITIONED BY (`year` string)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\t'
  LINES TERMINATED BY '\n'
STORED AS TEXTFILE
LOCATION '${INPUT}/calendar_master/'
OK
Time taken: 1.667 seconds


MSCK REPAIR TABLE calendar_master
OK
Partitions not in metastore:	calendar_master:year=2016	calendar_master:year=2017
Repair: Added partition to metastore calendar_master:year=2016
Repair: Added partition to metastore calendar_master:year=2017
Time taken: 3.469 seconds, Fetched: 3 row(s)
```

お！　Partitionも作られてるね〜イイね！

# 5.確認してみる

```
$ hive
# データベースあった！
hive> use HIVE_TEST;
OK
Time taken: 1.254 seconds

# テーブルもあった！
hive> show tables;
OK
calendar_master
Time taken: 0.283 seconds, Fetched: 1 row(s)

# パーティションもあった！
hive> show partitions calendar_master;
OK
year=2016
year=2017
Time taken: 0.266 seconds, Fetched: 2 row(s)

# TSVのデータも取り込まれている！
hive> select * from calendar_master;
OK
2016-01-01	6	1	2016
2016-01-02	7	0	2016
2016-01-03	1	0	2016
2016-01-04	2	0	2016
2016-01-05	3	0	2016
〜省略

2016-12-30	6	0	2016
2016-12-31	7	0	2016
2017-01-01	1	1	2017
2017-01-02	2	9	2017
2017-01-03	3	0	2017
2017-01-04	4	0	2017
2017-01-05	5	0	2017

Time taken: 0.251 seconds, Fetched: 731 row(s)
hive> 
```

やった〜！

お次は、S3上に出力できるか試せたら、追記します。

# 終わりに

hadoop,hiveは、構成がいろいろ出てて、初心者にはかなりハードル高い感じでした。

CDH? EMR なんだかんだ・・・

docker使えば一発じゃんとかあるかもしれませんが

今回は、理解を進める上でも、オールインワンは使わずやってみました。