Apache Iceberg、Hadoop、Hive: データレイク (Lakehouse) を開く -> パート 1
- Claude Paugh
- 7月24日
- 読了時間: 14分
更新日:8月18日
以前の記事で、データレイクとレイクハウスを区別する基準を簡単にまとめました。レイクハウスの主な構成要素はデータの管理と整理であり、データレイクを支持する論拠の少なさ、そしてデータ入力の高速化が主な理由でした。この記事では、データレイクとレイクハウスの両方のモデルをユーザーが利用できるようにする方法について説明しました。このトピックは2つのパートに分かれており、1つはインフラストラクチャに焦点を当て、2つ目はデータコンテンツとアクセスについてです。

これらの違いにもかかわらず、インフラストラクチャを展開する際には、レイクハウスまたはデータレイクアーキテクチャに向けて大きく前進することができます。データ管理業務に固有の追加プロセスはサポートされていませんが、その方向へ進むための準備は整っています。
Apache Icebergに関する以前の投稿では、Icebergのデータ管理機能、特にスキーマ管理について詳しく説明しました。そこで、Icebergを活用してミニモデルアーキテクチャの構築を始めました。
データ処理プラットフォームについては、オープンソースを好んで使用しています。しかし、これより優れた商用製品があるとは思いません。プラットフォームの選択は少し複雑でした。CPU(価格的に非常に評価が高い)とGPU上の並列処理エンジンとして、 DASKとApache Sparkを使用・導入しました。もちろん、どちらかを拡張するにはGPUベンダーのライブラリを使用する必要がありますが、データ処理はGPUの無駄遣いです。GPUはこのタスクには適していません。
Apache Sparkを選んだのは、コミュニティの規模とベンダーサポートが充実しているからです。迅速なスケーリングが可能なクラウドプロバイダーを利用しない限り、ノード数が10%に制限されている場合でも、導入と運用のセットアップは非常に高速です。また、Spark では並列処理を有効にするために Python 関数をコーディングする必要もありません。Iceberg(と Hadoop)を使っていたので、Spark との統合が組み込まれており、統合がより容易になるはずです(と思います)。
最後に、ユーザーがクエリでデータにアクセスできるようにするために、統合性に優れたApache HiveとDrillを選択しました。他にも、具体的なニーズに応じて、より適したオープンソースツールや商用製品が数多く存在します。しかし、このツールならライセンス料なしですぐに使い始めることができます。
以下は、データ フローを表す矢印を使用したこれらの選択肢の表現です (理想的には)。

データレイク: Apache Hadoop と Apache Iceberg の構成
Hadoop には JDK v8 以降が必要です。私は、上記のすべての Apache 製品と互換性があるため、openjdk@17 をインストールするために自作ビルドを使用しました。
brew install hadoop
cd "/opt/homebrew/Cellar/hadoop/3.3.6/libexec/etc/hadoop"
vi core-site.xml
Icebergのインストール時に既にこの設定を済ませています(このトピックに関する記事で詳しく説明しています)。core -site.xmlファイルは以下のいずれかの項目のようになっているはずです。注意すべき項目は太字で示しています。また、ローカル環境でのセットアップなので、匿名認証を有効にしています。ほとんどの場合、認証は有効/必須にする必要があります。主な前提条件は、 Hadoopをインストールするための十分な場所とファイルシステムの空き容量です。
コアサイト.xml
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/Volumes/ExtShield/opt/hdfs/tmp</value>
<description>A base for other temporary directories</description>
</property>
<property>
<name>hadoop.http.filter.initializers</name>
<value>org.apache.hadoop.security.AuthenticationFilterInitializer</value>
<description>
Authentication for Hadoop HTTP web-consoles
add to this property the org.apache.hadoop.security.AuthenticationFilterInitializer initializer class.
</description>
</property>
<property>
<name>hadoop.http.authentication.type</name>
<value>simple</value>
<description>
Defines authentication used for the HTTP web-consoles.
The supported values are: simple | kerberos | #AUTHENTICATION_HANDLER_CLASSNAME#.
The dfeault value is simple.
</description>
</property>
<property>
<name>hadoop.http.authentication.token.validity</name>
<value>36000</value>
<description>
Indicates how long (in seconds) an authentication token is valid before it has to be renewed.
The default value is 36000.
</description>
</property>
<property>
<name>hadoop.http.authentication.standard.config.path</name>
<value>/Users/claude_paugh/hadoop/auth.conf</value>
<description>
The signature secret file for signing the authentication tokens.
The same secret should be used for all nodes in the cluster, JobTracker, NameNode, DataNode and TastTracker.
The default value is $user.home/hadoop-http-auth-signature-secret.
IMPORTANT: This file should be readable only by the Unix user running the daemons.
</description>
</property>
<property>
<name>hadoop.http.authentication.simple.anonymous.allowed</name>
<value>true</value>
<description>
Indicates if anonymous requests are allowed when using ‘simple’ authentication.
The default value is true
</description>
</property>
<property>
<name>hadoop.http.authentication.signature.secret.file</name>
<value>/Users/claude_paugh/hadoop/hadoop-http-auth-signature-secret</value>
<description>
The signature secret file for signing the authentication tokens.
The same secret should be used for all nodes in the cluster, JobTracker, NameNode, DataNode and TastTracker.
The default value is $user.home/hadoop-http-auth-signature-secret.
IMPORTANT: This file should be readable only by the Unix user running the daemons.
</description>
</property>
<property>
<name>iceberg.engine.hive.enabled</name>
<value>true</value>
</property>
</configuration>
hdfs-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/Volumes/ExtShield/opt/hdfs/namenode/data</value>
</property>
<property>
<name>dfs.namenode.checkpoint.dir</name>
<value>file:/Volumes/ExtShield/opt/hdfs/namenode/checkpoint</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/Volumes/ExtShield/opt/hdfs/datanode/data</value>
</property>
</configuration>
mapred-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<!-- Put site-specific property overrides in this file. -->
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.application.classpath</name>
<value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
</property>
</configuration>
サイトファイル..xml
<?xml version="1.0"?>
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. See accompanying LICENSE file.
-->
<configuration>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.env-whitelist</name>
<value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_HOME,PATH,LANG,TZ,HADOOP_MAPRED_HOME</value>
</property>
</configuration>
上記の設定ファイルリストは、Apache Icebergで使用している設定も反映しています。その後、ホームディレクトリの.bashrcスクリプトにHadoop変数を追加して設定しました。シェルセッションを開いた際に.bashrcファイルが実行されない場合は、「 source ~/.bashrc 」を使用して手動で実行する必要があります。追加した変数は、Iceberg/Hadoop、Hive、Spark製品の操作(起動/停止、ジョブ)に必要です。
.bashrc -- Hadoop のみ
export PYTHON_BIN="/usr/local/bin"
export JAVA_HOME="/opt/homebrew/opt/openjdk@21"
export PYTHONPATH=$PYTHON_BIN
eval "$(/opt/homebrew/bin/brew shellenv)"
export PYTHONPATH=$PYTHONPATH:/Users/claude_paugh/airflow/arf/site-packages/trading-0.1.0b0.dist-info
export HDFS_NAMENODE_USER="claude_paugh"
export HDFS_DATANODE_USER="claude_paugh"
export HDFS_SECONDARYNAMENODE_USER="claude_paugh"
export HDF5_USE_FILE_LOCKING="FALSE"
export HADOOP_HEAPSIZE=2048
export HADOOP_HOME="/opt/homebrew/Cellar/hadoop/3.4.1/libexec"
export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export HADOOP_CLIENT_OPTS="-Di-Dorg.apache.logging.log4j.simplelog.StatusLogger.level=TRACE"
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib"
export HADOOP_CLASSPATH=${JAVA_HOME}/lib/tools.jar
export PATH=$PATH:$PYTHONPATH:$JAVA_HOME:$HADOOP_HOME:$HADOOP_HOME/bin
Hadoop の起動と停止は、同じ場所にある$HADOOP_HOME/sbin/start-all.shとstop-all.shファイルを使って行うことができます。私のように Python ライブラリを使用している場合は、pip または conda を使って pyiceberg パッケージをインストールする必要があります。「 start-all.sh 」ファイルが正常に動作する場合は、ブラウザで URL http://localhost:9870/にアクセスしてください。次のような画面が表示されるはずです。

以前に Apache Iceberg のJARをダウンロードしていたので、この時点で Iceberg の jar をクラスパス (または$HADOOP_HOME/lib ) に追加すれば、Iceberg + Hadoop が完全に機能するはずです。
Apache Spark を構成する
次のステップは、Sparkをスタンドアロンモードでセットアップすることでした。バージョン4.0.0を試すことにし、今回もHomebrewを使ってインストールしました。Hadoop/Iceberg環境では問題なく動作しましたが、Apache Hive 4.0.1をインストールした後、カタログ認識に関して断続的な問題が発生し始めました。これは他のユーザーも既に経験している問題です。そこで、今回もHomebrewを使ってSpark 3.5.6を使用することにしました。上記のHadoopインストールと同様に、いくつかの選択肢があります。 スタートオール.sh $SPARK_HOME/sbinディレクトリにstop-all.sh を配置します。起動後、 http://127.0.0.1: 8080/ にアクセスでき、以下の画面が表示されるはずです。

また、Spark の起動時に読み取られる$SPARK_HOME/conf/ spark-defaults.co nfも作成してカスタマイズし、次の内容を含めました。
spark.jars.packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.9.1
spark.sql.extensions org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
spark.sql.hive.metastore.uri thrift://localhost:9083
spark.sql.warehouse.dir hdfs://localhost:9000/user/hive/warehouse
spark.sql.catalogImplementation hive
spark.sql.catalog.spark_catalog org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.spark_catalog.type hive
spark.sql.catalog.spark_catalog.warehouse hdfs://localhost:9000/user/spark/warehouse
これらの値はHiveの使用と統合に重点を置いています。また、 spark-env.shファイル(テンプレートから)を作成し、修正しました。 $SPARK_HOME/confディレクトリのスクリプトに次の内容を含めます。
export SPARK_MASTER_HOST=127.0.0.1
export SPARK_MASTER_PORT=7077
# Options read in any cluster manager using HDFS
export HADOOP_CONF_DIR="$HADOOP_HOME/etc/hadoop"
export HADOOP_HOME=$HADOOP_HOME
# Pyspark
PYSPARK_DRIVER_PYTHON=python
PYSPARK_PYTHON=python
この時点で、Spark 環境が動作可能になり、CLI から Scala シェルと Pyspark シェルを起動できるかどうかをテストしました。起動は成功しました。その後、いくつかのステートメントを実行しました。シェルをテストに使用する予定はなかったため、Pyspark の PIP インストールを実行しました。その後、Spark インストールの残りの部分をテストするために、SparkSession を開き、HDFS から区切りファイルをデータフレームに読み込む簡単なスクリプトを実行しました。このスクリプトには Pyspark がインポートされていました。また、 .bashrcファイルに以下の変数を追加しました。
export SPARK_REMOTE="sc://localhost"
export SPARK_HOME="/Volumes/ExtShield/opt/spark-3.5.6-bin-hadoop3/libexec"
export PYTHONPATH=$HADDOOP_HOME:$SPARK_HOME/jars:$PYTHONP$
export PATH=$PATH:$HADOOP_HOME:$SPARK_HOME:$HADOOP_HDFS_HOME
Apache Hive を構成する
前述の通り、Apache Spark 4.0.0とApache Hive 4.0.1から始めましたが、いくつか問題が発生したため、Sparkをバージョン3.5.6にダウングレードし、Apache Hiveバージョン4.0.0もインストールしました。Hiveの過去のバージョンはこちらでご覧いただけます。apache -hive-4.0.0-bin.tar.gzファイルをダウンロードしてインストール先に移動し、MacBookのシェルで以下を実行しました。
gunzip apache-hive-4.0.0-bin.tar.gz
tar -xvf apache-hive-4.0.0-bin.tar
次に、次の環境変数を.bashrcに追加しました。
export HIVE_OPTS="-hiveconf mapreduce.map.memory.mb=8192 -hiveconf mapreduce.reduce.memory.mb=5120"
export HIVE_HOME="/Volumes/ExtShield/opt/apache-hive-4.0.0-bin"
export HIVE_CONF_DIR=$HIVE_HOME/conf
export HIVE_SERVER2_THRIFT_BIND_HOST="localhost"
export HIVE_SERVER2_THRIFT_PORT="10001"
export PYTHONPATH --> add $HIVE_HOME/lib
export PATH --> add $HIVE_HOME
メモリ制限は「メモリ不足」エラーを防ぐためのものです。Hiveのドキュメントによると、残りは説明不要です。今後のインストールでは、 「HIVE_SERVER2」を参照する変数が必要です。
以前、IcebergカタログにリモートPostgreSQLデータベースを使用していたため、Hiveでも同じ手順に従いました。デプロイメントからPostgreSQLインストールを再利用し、 PostgreSQL JDBCドライバーを$HIVE_HOME/libディレクトリにコピーして、Hiveクラスパスに追加しました。
まず最新のHiveドキュメントと管理マニュアルを読み、次にHiveバージョン4.0.0をダウンロードしてtarballでインストールしました。指示に従い、 .bashrcファイルと設定を指示通りに変更してから、 Hive Metastoreのインストールに進みました。
上で述べたように、PostgreSQL でリモート メタストアを使用する予定だったので、 DataGripを使用して JDBC 接続で Postgres データベースを手動で作成し、 metacatalog という名前を付けました。 次に、提供されたテンプレートファイルからhive-site.xmlファイルを作成し、設定の更新を開始しました。MySQLをPostgresに置き換えました(ウェブサイトのドキュメントを参照)。また、 HiveServer2の設定に使用した設定もいくつか追加しました。詳細は以下をご覧ください。
構成設定 | 設定値 | コメント |
javax.jdo.option.接続URL | jdbc:postgresql://10.0.0.30:5439/metacatalog?ssl=false;create=false | メタデータはPostgreSQLサーバーに保存されます |
javax.jdo.option.接続ドライバ名 | JDBC ドライバークラス | |
javax.jdo.option.接続ユーザー名 | <username> | Postgresに接続するためのユーザー名 |
javax.jdo.option.接続パスワード | <password> | Postgresに接続するためのパスワード |
hive.metastore.warehouse.dir | hdfs:// ローカルホスト:9000/user/hive/warehouse | Hive (Hadoop) テーブルのデフォルトの場所 |
メタストアサービスをバインドするホスト名。空の場合は「localhost」が使用されます。この設定はHive 4.0.0以降で利用可能です。 | ||
hive.metastore.uris | スリフト:// ローカルホスト:9083 | Thriftメタストアサーバーのホストとポート番号。hive.metastore.thrift.bind.host を指定した場合、 ホストはこの設定と一致している必要があります。詳細については、「動的サービス検出の設定」を参照してください。 |
hive-site.xmlファイルにユーザー名とパスワードを平文で保存することは、ほとんどのセキュリティ構成では許容されないことは、皆さんもご存知だと思います。多くの組織ではSAML 2.0とシングルサインオンが使用されると思います。
次に、構成の変更後に、Hive メタストアをバックグラウンド サービスとして起動しました。
$HIVE_HOME/bin/hive --service metastore &
当初、JDBC URLの設定オプション「 create=true 」でメタストアを起動しようとしましたが、うまくいかないことがありました。そこで「 create=false 」に変更し、 schema-toolsを使って手動でデプロイを実行しました。このツールを使うと、サービスを起動してHiveカタログテーブルを正常にデプロイできました。

Hive 4.0.0に同梱されているカタログのバージョンでは、データベースが最初から空であると仮定すると、83個のテーブルが含まれているはずです。スキーマツールを使用した後にパラメータを変更してMetastoreサービスを起動したところ、起動ログにはすべて問題がありませんでした。これでHiveサーバーをデプロイする準備が整ったと思います。
上記の設定のいくつかはすでに HiveServer2ですが、タスクを完了するにはhive-site.xmlファイルにいくつか変更を加える必要がありました。まずバイナリモードではなく「HTTP」モードで実行したかったので、 XMLファイルのhive.server2.transport.mode設定を「 http 」に更新しました。次に、「http」モードで実行するための値(リンク先の表を参照)など、最低限必要な値をhive-site.xmlファイルに追加し、以下の操作も行いました。
SSLを無効にする
http パラメータで使用される JDBC URL: jdbc:hive2:// localhost:10001/default;transportMode=http;httpPath=cliservice
ログの設定
匿名認証
Hiveのスクラッチディレクトリ構成
それから私はサービスを開始しました:
$HIVE_HOME/bin/hive --service hiveserver2
http://localhost:10002にアクセスすると、次の画面が表示されます。

この時点で、少なくとも起動はできたように見えました。そこで、 DBeaverをインストールして起動し、Hiveが動作していることを確認しました。上記のURLを使用して、認証なしでHiveデータソースを追加し、DBeaverでHiveへのJDBC接続を作成しました。

ログインすると次の画面が表示されました。

HiveServer2 の URL を開きます:

Hiveデータベースへの接続を検証した後、Hive + Iceberg + Hadoopの統合を検証するためのテーブルを作成したいと考えました。Apache Icebergの記事で使用されている「create table」ステートメントに、 Hive/Icebergのserde行形式を含めるように簡単な変更を加えました。
次のスクリプトを実行しました:


Hive UI 出力: ドリルダウン リンクをクリックすると、ステートメント実行の実行プランと実行ログが表示されます。

Apache Iceberg のメタデータ フォルダーを含むHadoopフォルダー:


Pycharm IDEにHiveとHadoopへの接続も追加しました。これにより、Pysparkスクリプトを作成する際に拡張テーブル属性を参照・表示できるようになります。古いバージョンのIDEをお使いの場合はSparkプラグインを追加する必要がありますが、「Pro」ライセンスをお使いの場合は、新しいバージョンに既にSparkプラグインが含まれています。
「Big Data Tools」を開いて接続を作成します。

「 Hive Metastore 」アイコン(左)をクリックすると、テーブル仕様の詳細が表示されます。ボタンをクリックすると、画面右側にテーブルの属性情報を表示するパネル(ページ1と2)が表示されます。



次に、 Iceberg記事シリーズで使用したデータファイルをSparkで読み込み、「test_company_stocks」テーブルにレコードを作成しました。Sparkの使用法についてはこの記事の後半で説明しますが、読み込み後、データが永続化されていることに気づきました。そこで、別のステージングテーブルにファイルを読み込み、IDEでSQL操作「 INSERT INTO.....SELECT * FROM staging_table」を実行して結果を確認しました。
DBeaverで「 INSERT 」コマンドを実行したら、すぐにHiveServer2コンソールに移動し、クエリをクリックしてから「操作ログ」(以前の設定どおりにした場合)をクリックします。次のような結果が表示されるはずです。

選択したURLをブラウザのタブにコピー/貼り付けすると、「Hadoopアプリケーション」ウィンドウが表示されます。ここで、Hadoop内のテーブル間でデータを変換する「MapReduce」ジョブを監視できます。

左側のメニューで「ツール > ログ」をクリックすると、ログファイルでジョブの進行状況を確認できます。ご興味があれば、Hive の「tmp」または「scratchdir」に特定の場所を設定すると、オペレーティングシステムのファイルシステム内、またはスクラッチファイルで Hadoop ファイルシステム内の一時オブジェクトを確認できます(Hadoop ファイルシステムのオプションを選択した場合)。
完了したら、DBeaver に戻って「select * from test_company_stock」を実行すると、次のようになりました。

Hadoopの「test_company_stocks」フォルダには、「data」というサブフォルダが作成され、パーティションとParquetファイルが格納されているはずです。これはIcebergを使用しているためです。私のケースでは、以下のように作成されました。

