Apache Hadoop 3 と Apache Hive 3 を Homebrew で macOS にインストールする

## 概要

- macOS 1台に Homebrew で Apache Hadoop と Apache Hive をインストールし、シングルノードで疑似分散モードのクラスターを構築する
- Hadoop の分散ファイルシステム HDFS (Hadoop Distributed File System) を使用する
- Apache Hive から HiveQL でテーブル作成・データ追加・データ参照する
- 今回の環境: macOS Catalina + Java 8 (AdoptOpenJDK 1.8.0_265) + Apache Hadoop 3.3.0 + Apache Hive 3.1.2

## Java 8 のインストール

Java 8 をインストールしていない場合は、Homebrew などでインストールしておき、JAVA_HOME となるべきディレクトリの場所を確認しておく。

```
$ brew tap AdoptOpenJDK/openjdk

$ brew cask install adoptopenjdk8

$ /usr/libexec/java_home -v 1.8
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
```

Apache Hive が Java 9 以降に対応していないため、Java 9 以降を使用すると以下のようなエラーが発生するので注意。

```
Exception in thread "main" java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader (jdk.internal.loader.ClassLoaders$AppClassLoader and java.net.URLClassLoader are in module java.base of loader 'bootstrap')
  at org.apache.hadoop.hive.ql.session.SessionState.<init>(SessionState.java:413)
  at org.apache.hadoop.hive.ql.session.SessionState.<init>(SessionState.java:389)
  at org.apache.hadoop.hive.cli.CliSessionState.<init>(CliSessionState.java:60)
  at org.apache.hadoop.hive.cli.CliDriver.run(CliDriver.java:705)
  at org.apache.hadoop.hive.cli.CliDriver.main(CliDriver.java:683)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.base/java.lang.reflect.Method.invoke(Method.java:564)
  at org.apache.hadoop.util.RunJar.run(RunJar.java:323)
  at org.apache.hadoop.util.RunJar.main(RunJar.java:236)
```

## Apache Hadoop と Apache Hive のインストール

Homebrew で Apache Hive をインストールすると依存関係で Apache Hadoop もインストールされる。

```
$ brew install hive
```

Hadoop のバージョンを確認。

```
$ hadoop version
Hadoop 3.3.0
Source code repository https://gitbox.apache.org/repos/asf/hadoop.git -r aa96f1871bfd858f9bac59cf2a81ec470da649af
Compiled by brahma on 2020-07-06T18:44Z
Compiled with protoc 3.7.1
From source with checksum 5dc29b802d6ccd77b262ef9d04d19c4
This command was run using /usr/local/Cellar/hadoop/3.3.0/libexec/share/hadoop/common/hadoop-common-3.3.0.jar
```

Hive のバージョンを確認。

```
$ hive --version
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/usr/local/Cellar/hive/3.1.2_1/libexec/lib/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/usr/local/Cellar/hadoop/3.3.0/libexec/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
Hive 3.1.2
Git git://HW13934/Users/gates/tmp/hive-branch-3.1/hive -r 8190d2be7b7165effa62bd21b7d60ef81fb0e4af
Compiled by gates on Thu Aug 22 15:01:18 PDT 2019
From source with checksum 0492c08f784b188c349f6afb1d8d9847
```

## 疑似分散モード (pseudo-distributed mode) の設定

設定ファイルは /usr/local/Cellar/hadoop/3.3.0/libexec/etc/hadoop にある。
core-site.xml と hdfs-site.xml を以下のように書き換える。

### core-site.xml

```core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:9000</value>
  </property>
</configuration>
```

### hdfs-site.xml

```hdfs-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>
```

## hadoop.tmp.dir の変更

Hadoop で使用するデータが格納されているディレクトリはデフォルトでは /tmp/hadoop-${user.name} になっているため、macOS の再起動等のタイミングでデータが失われる。

必要に応じて core-site.xml に以下のような hadoop.tmp.dir についての設定を追加することでデータが格納されているディレクトリを指定することができる。

```xml
<property>
  <name>hadoop.tmp.dir</name>
  <value>/Users/foo/hadoop</value>
</property>
```

## JAVA_HOME の設定

/usr/local/Cellar/hadoop/3.3.0/libexec/etc/hadoop にある hadoop-env.sh に JAVA_HOME を記述する。
ここで指定した JAVA_HOME の JVM が Apache Hive からも使われる。

以下は Homebrew でインストールした AdoptOpenJDK 8 の JAVA_HOME を指定する例。

```
export JAVA_HOME="/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home"
```

## リモートログインの許可

macOS の「システム環境設定」→「共有」の「リモートログイン」にチェックを入れてアクセスを許可する必要がある。

リモートログインが許可されていない場合、Hadoop 起動時に以下のようなエラーが発生する。

```
$ /usr/local/Cellar/hadoop/3.3.0/sbin/start-dfs.sh
Starting namenodes on [localhost]
localhost: ssh: connect to host localhost port 22: Connection refused
Starting datanodes
localhost: ssh: connect to host localhost port 22: Connection refused
Starting secondary namenodes [mymacos.local]
mymacos.local: ssh: connect to host mymacos.local port 22: Connection refused
```

## パスフレーズなし ssh の設定

パスフレーズなしの秘密鍵・公開鍵を作成し、ssh 接続を許可するリストに追加する。

```
$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 0600 ~/.ssh/authorized_keys
```

すでに ~/.ssh/id_rsa ファイルが存在しており上書きしたくない場合は、任意のファイル名で作成しても良い。
例えば id_rsa_for_my_hadoop というファイル名を指定する。

```
$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa_for_my_hadoop
$ cat ~/.ssh/id_rsa_for_my_hadoop.pub >> ~/.ssh/authorized_keys
$ chmod 0600 ~/.ssh/authorized_keys
```

任意のファイル名で作成した場合は、Hadoop が ssh を使用する際に指定するオプションを記述できる環境変数 HADOOP_SSH_OPTS にて、秘密鍵のパスを使用するように指定する。
HADOOP_SSH_OPTS は hadoop-env.sh に記述する。

```
$ export HADOOP_SSH_OPTS="-i ~/.ssh/id_rsa_for_my_hadoop"
```

パスフレーズなしでローカルホストに ssh できない場合、Hadoop 起動時に以下のようなエラーが発生する。

```
$ /usr/local/Cellar/hadoop/3.3.0/sbin/start-dfs.sh
Starting namenodes on [localhost]
localhost: Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
localhost: foo@localhost: Permission denied (publickey,password,keyboard-interactive).
Starting datanodes
localhost: foo@localhost: Permission denied (publickey,password,keyboard-interactive).
Starting secondary namenodes [mymacos.local]
mymacos.local: Warning: Permanently added 'mymacos.local' (ECDSA) to the list of known hosts.
mymacos.local: foo@mymacos.local: Permission denied (publickey,password,keyboard-interactive).
```

## ファイルシステムのフォーマット

Hadoop で使用する環境を HDFS (Hadoop Distributed File System) でフォーマットする。

```
$ hdfs namenode -format
```

## Hadoop の起動

HDFS を管理する NameNode (メタ情報) と DataNode (データの実体) を起動する。

```
$ /usr/local/Cellar/hadoop/3.3.0/sbin/start-dfs.sh
```

「WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable」という警告が出るが、このネイティブライブラリは macOS には対応していないので無視する。

[Apache Hadoop 3\.3\.0 – Native Libraries Guide](https://hadoop.apache.org/docs/r3.3.0/hadoop-project-dist/hadoop-common/NativeLibraries.html)

> The native hadoop library is supported on *nix platforms only. The library does not to work with Cygwin or the Mac OS X platform.

## Hive で使用するディレクトリの作成

Hive で使用するディレクトリを HDFS 上に作成する。

```
$ hadoop fs -mkdir /tmp
$ hadoop fs -mkdir /user
$ hadoop fs -mkdir /user/hive
$ hadoop fs -mkdir /user/hive/warehouse
$ hadoop fs -chmod g+w /tmp
$ hadoop fs -chmod g+w /user/hive/warehouse
```

## メタストアの作成

Hive に関する情報を管理するメタストアを作成する。

```
$ schematool -initSchema -dbType derby
(中略)
Metastore connection URL:  jdbc:derby:;databaseName=metastore_db;create=true
Metastore Connection Driver :  org.apache.derby.jdbc.EmbeddedDriver
Metastore connection User:   APP
Starting metastore schema initialization to 3.1.0
Initialization script hive-schema-3.1.0.derby.sql
(中略)
Initialization script completed
schemaTool completed
```

## Hive CLI の起動

Hive CLI をインタラクティブモードで起動する。

```
$ hive
```

## データベースの作成

create database でデータベースを作成する。

```
hive> create database foo_database;
OK
Time taken: 0.696 seconds
```

```
hive> show databases;
OK
default
foo_database
Time taken: 0.159 seconds, Fetched: 2 row(s)
```

## テーブルの作成

create table でテーブルを作成する。
読み込むデータのフォーマットとして、カラムをカンマ区切り、レコードを改行コードで区切るように指定する。

```
hive> create table foo_database.foo_table(
    >   id int,
    >   name string,
    >   updated_at timestamp)
    > row format delimited
    > fields terminated by ','
    > lines terminated by '\n';
OK
Time taken: 0.887 seconds
```

```
hive> show tables from foo_database;
OK
foo_table
Time taken: 0.058 seconds, Fetched: 1 row(s)
```

Hive CLI から抜けるときは exit; を入力する。

```
hive> exit;
```

## データのロード

カラムをカンマ区切り、レコードを改行コードで区切るように指定した CSV ファイルを用意する。
今回は /Users/foo/data.csv というパスに以下の内容のファイルを設置した。

```data.csv
100,Alice,2020-02-29 00:00:00.000000001
200,ボブ,1999-12-31 23:59:59
```

load data で CSV ファイルのパスを指定してテーブルにファイルを読み込む。

```
hive> LOAD DATA LOCAL INPATH 'file:/Users/foo/data.csv' OVERWRITE INTO TABLE foo_database.foo_table;
Loading data to table foo_database.foo_table
OK
Time taken: 1.307 seconds
```

## データの参照

クエリ実行時にカラム名を出力するように設定。

```
hive> set hive.cli.print.header=true;
```

select でレコードを取得して表示する。

```
hive> select * from foo_database.foo_table;
OK
foo_table.id    foo_table.name  foo_table.updated_at
100     Alice   2020-02-29 00:00:00.000000001
200     ボブ     1999-12-31 23:59:59
Time taken: 0.131 seconds, Fetched: 2 row(s)
```

## Hadoop の停止

```
$ /usr/local/Cellar/hadoop/3.3.0/sbin/stop-dfs.sh
```

## 参考資料

### Homebrew

- [hadoop — Homebrew Formulae](https://formulae.brew.sh/formula/hadoop)
- [hive — Homebrew Formulae](https://formulae.brew.sh/formula/hive)

### Apache Hadoop

- [Hadoop – Apache Hadoop 3\.3\.0](https://hadoop.apache.org/docs/r3.3.0/)
- [Apache Hadoop 3\.3\.0 – Hadoop: Setting up a Single Node Cluster\.](https://hadoop.apache.org/docs/r3.3.0/hadoop-project-dist/hadoop-common/SingleCluster.html)
- [core\-default\.xml](https://hadoop.apache.org/docs/r3.3.0/hadoop-project-dist/hadoop-common/core-default.xml)
- [hdfs\-default\.xml](https://hadoop.apache.org/docs/r3.3.0/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml)

### Apache Hive

- [Apache Hive TM](https://hive.apache.org/)
- [GettingStarted \- Apache Hive \- Apache Software Foundation](https://cwiki.apache.org/confluence/display/Hive/GettingStarted)
- [Configuration Properties \- Apache Hive \- Apache Software Foundation](https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties)
- [LanguageManual DML \- Apache Hive \- Apache Software Foundation](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DML)
- [LanguageManual Types \- Apache Hive \- Apache Software Foundation](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Types)
- [CSV、TSV、およびカスタム区切りファイルの LazySimpleSerDe \- Amazon Athena](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/lazy-simple-serde.html)
- [Hadoop＋Hive検証環境を構築してみる \(1/3\)：Hive――RDB使いのためのHadoopガイド（前編） \- ＠IT](https://www.atmarkit.co.jp/ait/articles/0902/27/news129.html)