🏃‍♂️

ASF Incubatorを卒業したApache Hopで遊んでみる

2022/01/04に公開

はじめに

2022年あけましておめでとうございます。
昨年末 (2021年12月) に ASF Incubator プロジェクトを卒業しトップレベルプロジェクトとなった ETLツール Apache Hop で遊んでみたので記事としてまとめました。

この年末年始に初めて触ったプロダクトなので、内容に誤りがある可能性があります。お気軽にご指摘ください 🙇‍♂️

HOP_logo_RGB-4

Apache Hop とは何か?

ちゃんとした説明は 公式のドキュメント にすべて詳細に書かれています。
ここでは自分の解釈も入れつつ Apache Hop を紹介してみます。

概要と特徴

Apache Hop は ETL (Extract/Transform/Load) の開発ツール・実行エンジンです。データオーケストレーションツールとも呼ばれます。もともとPentahoの一部として開発されていた Kettle というプロジェクトからフォークされたそうです。先月まで (2020年9月から2021年12月まで) はApache Incubatorプロジェクトでした。つい最近 (2021年12月) に ASF Incubatorプロジェクトを卒業しASFのトップレベルプロジェクト になりました。おめでとうございます。

公式ドキュメント の説明を借りてHopの特徴を簡単に説明します。

  • Hopは処理をコードで「どのように」タスクを動かすかではなく、「なにを」したいかに焦点を当てている。それを実現するために「メタデータ」なる概念を定義してデータがどう処理されるかをGuiを通してグラフィカルに設計できる。
  • Hopはどこでも動くようにアーキテクチャが作れられていて、IoTでもビッグデータの分野でも対応できる、オンプレでもクラウドにも対応、実機でも動くしKubernetesでも動く。公式でDockerイメージも提供されている。
  • Hopで作成したワークフロー・パイプラインは、Hopネイティブ実行エンジンやApache Beam APIを経由してApache Spark, Apache Flink, Google Cloud Dataflowといったさまざまな実行エンジンで動かすことができる。

より詳細な情報は、以下のドキュメントもご参照ください。

コンポーネント

Hop Gui

Hop Guiはワークフローとパイプラインを開発するためのグラフィカルユーザインタフェースです。ワークフローやパイプラインの実行やデバッグもGuiから可能です。ローカル環境 (Windows, Mac, Linux) でGuiを動かすこともできるし、サーバで動かしてウェブブラウザ経由でGuiを操作することもできます。すでに公式が提供している Dockerイメージ をそのまま動かすと、ウェブサーバ経由のHop Guiが起動するようになっています。ローカルで自前でも Tomcat (Catalina) を利用することでウェブサーバ経由のHop Guiを構築することができるようですが、手動で構築するのがちょっと大変そうだったので私は今すぐには諦めてローカルで動かすことにして試しました。構築方法は開発者向けドキュメントの Hop Web Development Guide に手順が記載されています。

技術的には、Hop GuiはEclipseが提供している SWT (Standard Widget Toolkit) というGUI用のツールキットを利用して開発されています。ウェブブラウザ経由でのHop Guiでは、RAP (Rich Ajax Platform) が利用されているようです。

Hop Guiでは、ワークフローとパイプラインを Gitでバージョン管理 できることも特徴です。ワークフロー (拡張子 .hwf) やパイプライン (拡張子 .hpl) の実体はxmlファイルです。

多言語にローカライズされていて、日本語の設定にすることもできます。後述しますがまだ部分的に翻訳されている途中の段階のようすです。

ワークフローとパイプライン

Apache Hopにはワークフローとパイプラインという概念があります。

ワークフロー

ワークフロー (Workflow) は「Action」(ノード) と「Hop」(有向エッジ) から構成されるグラフ構造です。Actionは一般に直接データを細かく操作しないようなタスクを表しています。粒度の粗いタスクというイメージです。Actionはデフォルトでは逐次実行されます。ワークフローではジョブの定期実行といったスケジューリングも可能です。

パイプライン

パイプライン (Pipeline) は「Transform」(ノード) と「Hop」(有向エッジ) から構成されるグラフ構造です。Transformとは一般に直接データに触り何らかの操作をするような処理単位です。例えば、DBからデータを取得したり、フィルタリングしたり、テーブルデータをジョインしたり、ファイルに書き込んだりなど、ETLのコアとなるデータ処理です。Transformは並列実行されます。

ワークフローとパイプラインの関係

ワークフローとパイプラインは上下階層の関係ではなく対等な関係のようです。つまり、ワークフローからパイプラインを呼び出して動かすこともできるし、逆も可能です。ただし、より扱う粒度の粗いワークフローから、Actionとしてパイプラインを呼び出して実行するのが一般的な使い方のようです。

ワークフローとパイプラインは分離されているので、ワークフローをRemote Hopのネイティブ実行エンジンで実行して、その中の各パイプラインをそれぞれの実行エンジン (SparkやFlinkなど) で動かすことも可能です。

ActionやTransformはプラグインなので、サードパーティ製のものをインストールしたり、自前で開発したりもできそうです。

Apache Hop と分散処理エンジン

Hopネイティブ実行エンジンはドキュメントを読んだ限りではローカル実行でもリモート実行でも分散処理に現時点では対応していないように見えました。Hop Guiそのものも分散環境で動くような作りにはなっていないようです。

Hopで構築したワークフロー・パイプラインを分散処理で実行させるためには、Apache Beam経由でApach Spark, Apache Flink, Google Cloud Dataflowを実行エンジンとして動かす必要がありそうです。

Apache Hop で遊んでみる

Apache Hopについて説明は以上です。実際に触って遊んでみましょう。

ローカルでビルド

Apache Hop公式の Dockerイメージ が用意されていますが、せっかくなのでローカル環境でソースコードをビルドしてみます。

実行環境

MacBook (Intelプロセッサ) で動かします。

% java -version
openjdk version "1.8.0_312"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM (Temurin)(build 25.312-b07, mixed mode)

% mvn -version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /opt/apache-maven-3.6.3
Java version: 1.8.0_312, vendor: Temurin, runtime: /Library/Java/JavaVirtualMachines/temurin-8.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.16", arch: "x86_64", family: "mac"

ソースコードの取得・ビルド・起動

README の手順に従います。

# 1.0.0 のリリースブランチを指定してクローン
% git clone https://github.com/apache/hop.git -b release1.0.0 hop-1-0-0

# Mavenでビルド
% cd hop-1-0-0
% mvn clean install

# Hop Guiを起動
% cd assemblies/client/target
% unzip hop-client-*.zip
% cd hop
% ./hop-gui.sh

※OSや環境によってはユニットテストが失敗することがあるみたいです[1]。私は Java 8 で試しましたが、Java 11 でも動くみたいです。

パイプライン (ストリーム処理)

Hopの機能をひと通り試す目的で、図1のようなパイプラインを作成してみました。HopとBeamを連携させる方法は Getting started with Apache Beam を参考にしました。

このパイプラインでは、GCP Pub/Sub (メッセージングキュー) からクレジットカード番号をサブスクライブして、番号の有効性確認・ブランド判定・フィルタリングをし、ブランドごとに10分ごとの頻度をカウントしてGCS (オブジェクトストレージ) のバケットに結果を書き込みます。入力データはPub/Subメッセージングキューからのストリームデータですが、出力は時間枠で区切ったデータとしてGCSに書き込みます。パイプライン全体はストリーム処理で動きます。

gui-my-pipeline
図1: とりあえずいろんな機能を動かしてみるためのHopパイプライン。

このパイプラインで利用している各コンポーネント (Transform) は次のとおりです。

Name Tramsform 説明
GCP Pub/Sub : Subscribe Beam GCP Pub/Sub : Subscribe Pub/Subからメッセージを取得する。
Extract number Regex Evaluation 正規表現でメッセージから番号部分を抜き出す。
Credit card validator Credit Card Validator クレジットカード番号の有効性のチェックとブランドを判定する。ピンポイントでこれ専用のコンポーネントがあるのがすごいです笑。
Filter only valid brand Filter Rows 今回必要とするカードブランドだけフィルタリングする。
Replace brand Replace in String いつくかのブランド名を別の名前に置換する。
Beam Fixed Window Beam Window ここまでストリームで処理していたデータを10分ごとの固定時間ウィンドウ (fixed window) へ区切る。
Group by brand and count Group By 区切られたウィンドウ内で、各ブランドの頻度をカウントする。
Beam Output to GCS Beam Output ウィンドウごとのカウント結果をGCSのバケットに書き込む。
Do nothing Dummy 一部Transformのエラー時に分岐する側の枝で処理される(図1では下半分の部分)。実体は何もしないTransformです。

ワークフロー

作成したパイプラインを動かすだけのワークフローを図2のように作成しました。

gui-my-workflow
図2: 図1で作成したパイプラインを動かすだけのHopワークフロー。

Google Cloud Dataflow で分散処理

Hop Guiで作成したワークフロー・パイプラインをGoogle Cloud Dataflowのジョブとして分散処理で実行してみます。

Fat JAR

ジョブの実行に必要な Fat JAR を次のコマンドで作成します。

% cd assemblies/client/target/hop
% ./hop-conf.sh --generate-fat-jar ./config/projects/samples/hop-1.0.0-fat.jar

作成した Fat JAR は Pipeline Run Configuration の設定画面でvariableを用いて Fat jar file location: ${PROJECT_HOME}/hop-1.0.0-fat.jar のように設定すれば問題ないです。

サービスアカウントの秘密鍵

ジョブの実行のために、GCPのサービスアカウントの鍵を登録する必要があります。

次のコマンドで設定するか、環境変数 GOOGLE_APPLICATION_CREDENTIALS に秘密鍵ファイルへのパスを保存すれば大丈夫でした。

% ./hop-conf.sh --google-cloud-service-account-key-file /path/to/gcp-key.json

Hop Guiからワークフローを実行

Hop Guiの「▶」ボタンからワークフローの実行を開始します。なお、Hopワークフローは定期実行のような設定も可能です。
また、別途Hopの外から、GCP Pub/Subのトピックに対してテストデータとしてカード番号を適当な間隔でパブリッシュします[2]

Google Cloud Dataflowにデプロイされたジョブの様子を図3に示します。Worker数を2で実行しています。

google-cloud-dataflow-job-graph
図3: 図1で作成したパイプラインがGoogle Cloud Dataflowのジョブとして展開されて実行されている。

このワークフロー(パイプライン)はストリーム処理として動くので、入力のPub/Subからデータを取得し続けて半永続的に動作します。

動作確認のために、GCSに書き込まれた出力結果を覗いてみます。03:40:00 (UTC) から 03:50:00 (UTC) までの時間ウィンドウで処理されたデータの集計結果を確認します。

% gsutil cat 'gs://my-bucket/output/my-pipeline-2022-01-02T03:40:00.000Z-2022-01-02T03:50:00.000Z-*.csv'
Mastercard,1504
Visa,6931
American Express,1435

クレジットカード番号の判定とカウントの集計が実行され、結果がCSVフォーマットでGCSに出力できていることが確認できます。

感想

最後に、遊んでみた感想を雑にメモします。

Good

  • 今回の私のようにHopに触るのが初めてでも、バグが少なく分散処理エンジンで簡単に動くようなパイプラインを短時間で構築できるのはグラフィカルな操作の恩恵を受けている。
  • Kubernetesで動かせるのがモダンなアーキテクチャでよさそう。
  • Gitでバージョン管理できるのがよさそう。
  • Sampleプロジェクト が内蔵されていて、インストール直後(ビルド直後)から多くの具体的な設計方法を参照できるので学習がしやすい。
  • ActionとTransformの各アイコンがひと目見てどのような操作を表しているか直感的でわかりやすい。
  • 他の有名なETLツールと比較してもデータ連携先の対応プラットフォームの種類がかなり揃っている印象。プラグイン可能なので追加で開発もできる。
  • Apache Beamのバックエンドで安定してジョブが実行できていい感じ。
  • 多言語にローカライズされているのがいい感じ。日本語はまだ部分的に翻訳されている段階のようす。(→コントリビュートチャンス)
    • 翻訳のための専用ツール (Translatorツール) が本体とは独立して存在しててすごい。
    • このツールによって開発者でなくとも翻訳に貢献することが可能とのこと。
    • でも私のローカル環境ではこのツール(アプリ)が動かなかった...。
  • 冒頭の紹介で触れなかったが、Hopパイプラインに対するテスト(入出力のゴールデンデータ)も追加できるところが、他のGUIベースのETLツールになかなか無い点なのでよさそう。
  • コミュニティについて

So-so (→コントリビュートチャンス)

  • 慣れるまでGuiのとっつきにくさはやはり感じる。これは私が触ったことがある Apache NiFiTalend Open Studio などの他のETLツールと同じ課題だと思う。
  • ダークモードが見にくい。ライトモードに合わせて文字色が固定色になっているところもあるので、ダークモードで見えないテキストがある。
  • まだ細かなバグが多めな感じ。
    • Guiでいろいろ触っているとNPEで落ちたりする[3]
    • Beam File Definitionの名前にスラッシュ文字を使用するとディレクトリ区切りと判定されてエラーになる。
    • など。うまく整理できたらそのうちJIRAチケット化させていただきます。
  • Hopネイティブ実行エンジンでできることがしょぼく感じる。
    • 基本的に既存の実行エンジンを積極的に利用する感じでいいのかな。
  • 可用性の面が懸念。

可用性についての考察

Hopネイティブ実行エンジンは前述のように分散環境での動作に現時点では対応していないようです。Remote Hop実行エンジンをHA構成にして単一障害点を解消できるかどうかもドキュメント読む限りでは勉強不足でわかりませんでした。これは一切試していないですが、自前でロードバランサを構築してRemote Hop実行エンジンをぶら下げることや、KubernetesでHopエンジンを複数ノードとして構築することでこの点に対応できそうな気もします。一方、Spark, Flink, Google Cloud Dataflowを実行エンジンとして用いればそれらに責任範囲を寄せることができるのでHop側で丁寧に気をかけなくても大丈夫そうです。ただ、基本的に再実行可能(データソースからデータの再取得が可能)なワークフロー・パイプラインを設計するのが無難だと感じました。

Hop Guiも単一サーバで動かすようなコンポーネントですので、可用性を上げるために、Gitでデータ管理しHop Guiを提供するサーバを複数台でHA構成にすることができそうな感じがします(これもあくまでも想像です)。

脚注
  1. 私はUbuntuでは失敗しました。Macでは成功しました。 ↩︎

  2. クレジットカード番号の生成には Faker ライブラリを用いました。便利!パブリッシュに使用したソースコード: https://github.com/kotarot/google-cloud-pubsub-sample ↩︎

  3. 1日触って5回ほど経験しました ↩︎

Discussion