割と大量のデータを別の場所に転送したい。といった要件があり、諸々模索している最中で、先輩から教えてもらったEmbulkを触ってみたのでまとめました。

Embulkとは

GitHub - embulk/embulk: Embulk: Pluggable Bulk Data Loader.
さまざまなストレージ、データベース、NoSQL、クラウドサービス間のデータ転送を支援する並列バルクデータローダーです。
関数を追加するプラグインをサポートしており、インプット先/アウトプット先に沿ったプラグインを適宜使用する/作成することで、前述の様々なストレージやDBなどでデータ転送を行うことができます。

インストール

Brewでインストール可能です。

$ brew install embulk
# embulk: Java 1.8 is required to install this formula. と言われたらエラー文通り下記でJava入れてもう一回
$ brew cask install homebrew/cask-versions/adoptopenjdk8

必要だなと思ったプラグインをプラグイン一覧から確認してインストール。

# CSVインポート
$ embulk gem install embulk-input-jdbc
# mysqlインポート
$ embulk gem install embulk-input-mysql
# mysqlアウトプット
$ embulk gem install embulk-output-mysql
# などなど

チュートリアル

公式ドキュメントに乗っている、めちゃくちゃ簡単な動作確認チュートリアルです。
CSVを読み込んで画面に表示するだけの簡単なものになっています。

# サンプルCSVファイル、構成ファイルを作成
$ embulk example ./try1
# 推測プラグインを呼び出して適切な構成ファイル(提案)を出力。
$ embulk guess ./try1/seed.yml -o config.yml
# インプット側から引っ張ってこれるデータを確認
$ embulk preview config.yml
# 実行!(csvから読み込んで画面に表示するだけ)
$ embulk run config.yml

Embulkではバルクデータロードを定義するために、yamlファイルを使います。
サンプルはこんな感じ:

in:  # インプットプラグインオプション
  type: file
  path_prefix: ./mydata/csv/
  decoders:
  - {type: gzip}
  parser:
    charset: UTF-8
    newline: CRLF
    type: csv
    delimiter: ','
    quote: '"'
    escape: '"'
    null_string: 'NULL'
    skip_header_lines: 1
    columns:
    - {name: id, type: long}
    - {name: account, type: long}
    - {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S'}
    - {name: purchase, type: timestamp, format: '%Y%m%d'}
    - {name: comment, type: string}
filters:
  - type: speedometer
    speed_limit: 250000
out:  # アウトプットプラグインオプション
  type: stdout

Liquidテンプレートエンジン を使用してこの構成ファイルに環境変数を埋め込むこともできるけど、実験的な機能なので変更される可能性があるとのこと。
また、同じように別の構成ファイルを含めることもできるらしいです。
これらのためにテンプレートエンジンを使用するには、設定ファイルの末尾をyml.liquidにする必要があります。
(今回はこの二つは割愛)

embulk-input-dynamodbをローカルから試してみる

プラグインを作ってくださっている方がいらっしゃったので使わせていただきました!
GitHub - lulichn/embulk-input-dynamodb

構成ファイル

なぜかauth_method : envauth_method : profileは上手く行かなかったので(後述)、ローカルでの動作確認時は直書きのauth_method : basicでやってます。
IAMロールが設定されているものの上で動かすのであれば、credentials情報などは不要らしいです。

in:
  type: dynamodb
  auth_method: basic  # IAMロールが設定されていればこの辺りは不要
  access_key_id: ***  # 同上
  secret_access_key: ***  # 同上
  table: dynamodb-table-name
  region: ap-northeast-1
  scan:
    total_segment: 20  # 要確認
  scan_limit: 10000
  record_limit: 10
  columns:
    - {name: user_id, type: long}
    - {name: name, type: string}
out:
  type: stdout  # 画面に出力する

embulk previewでインプット内容を確認します。
(今回は画面上にアウトプットするだけなので、embulk runでも大丈夫だと思います)

$ embulk preview config.yml
# 上記でplugin is not found 的なエラーが出た時は下記を実行(<>内は適宜書き換えて下さい)
$ embulk preview -I /Users/<user>/.embulk/lib/gems/gems/embulk-input-dynamodb-<varsion>/lib config.yml

無事指定したテーブルの項目が表示されたら成功です!

ハマったところ

実行時にInputPlugin 'dynamodb' is not foundになる

最初は以下の方法でpreviewしようとしていましたが、エラーが出てしまいました。

$embulk preview config.yml

エラー:

Error: InputPlugin 'dynamodb' is not found.
org.jruby.proxy.org.embulk.config.ConfigException$Proxy1: Unknown input plugin 'dynamodb'. embulk/input/dynamodb.rb is not installed. Run 'embulk gem search -rd embulk-input' command to find plugins.

インストールはしているはずだけど、もしかして見にいっている場所が違う?と思ったら割とビンゴでした。
この実行時のログではGem's home and path are set by default: "/var/root/.embulk/lib/gems"と出ていて、プラグインをインストールした先(私の場合は/Users/<user>/.embulk/lib/gems/)とは異なる場所を参照していたため、そんなものはない!と認識されてしまっていたようです。

そのため、最初の一回は使用しているプラグインのパスを明示的に渡してあげる必要があります。

$embulk preview -I /Users/<user>/.embulk/lib/gems/gems/embulk-input-dynamodb-<varsion>/lib config.yml

一度実行が通ってくれた後は、Gem's home and path are set by defaultが上記で設定したパスの方を見にいってくれるようになり、$ embulk preview config.ymlでも実行できるようになりました。

AWS認証情報渡す部分

embulk-input-dynamodbではAWS認証情報を受け取る方法を選ぶことができるのですが、やってみようと思ったenv, profileでエラーが発生し、トラブルシューティングがしきれなかったので一旦上記のbasicで試しました。
(結構初歩的な部分でつまづいている気もするました……)

最初profileでAWS認証情報を取得しようとした構成ファイル:

in:
  type: dynamodb
  auth_method: profile
  profile_file: ~/.aws/credentials
  profile_name: default
  table: dynamodb-table-name
  region: ap-northeast-1
  # 以下同じ

エラー:

Error: java.lang.IllegalArgumentException: ~/.aws/credentials

**追記:**こちらご指摘いただいて解決しました!ありがとうございます!
yamlファイルでは~/.aws/credentials~の部分を解決してくれないので(これはシェル側の機能)、明示的に渡してあげる必要があります。
絶対パスで記述し直すか、環境変数を使用して~の部分を渡してあげる形にすると通るようになります。

envで認証情報を取得しようとした構成ファイル:

in:
  type: dynamodb
  auth_method: env
  table: dynamodb-table-name
  region: ap-northeast-1
  # 以下同じ

エラー:

Error: Unable to load AWS credentials from environment variables (AWS_ACCESS_KEY_ID (or AWS_ACCESS_KEY) and AWS_SECRET_KEY (or AWS_SECRET_ACCESS_KEY))

($envで確かめてみたけどちゃんと環境変数には定義できていた)

まとめ

めちゃくちゃ触りだけをやりましたが、Embulk素敵ですね。
あとはこれをAWS上で定期実行するためにどうするかを調べて行こうかなと思います。