こんにちは、Webアプリケーションエンジニア(仮称)のナカハシです。
へいしゃでは「アイス補助」というものがあって、8月の間は毎日アイスを食べられます。ガリガリ君やハーゲンダッツの包装を開けるとき、なんでひとはこんなにも笑顔になれんるんでしょうか。
さて今回は、 embulk に入門してMySQLデータの同期にチャレンジしてみたいと思います。ターゲットは、開発環境のDockerネットワークにあるDB2つです。
embulk って?
embulkは、バッチ処理に特化したデータ転送ツールです。
プラグインによって豊富な種類の入出力ストレージに対応していること、大容量データの転送が可能なことなどが特長です。インストール後、 config.yml
に入出力先の情報を記述し、以下のコマンドを叩けばいいだけなので手軽ですね。
$ embulk run config.yml
やりたかったこと
新規にRails製の開発している社内システムに、稼働中のSymfony製の別システムからデータをコピーするという要件が発生しました。
別システムにデータ公開用のAPIを追加したりしてもよかったのですが、そもそもAPIを定義する工数が惜しかったり、大容量の同期にはAPIは向いていなかったりというところで、embulk を採用することにしました。
入出力先ともに社内システムで、MySQLに直接接続できてスキーマの情報を得られるという点も、embulk採用に向いていました。
インストールはDockerにしてみる
今回は docker-compose.yml で構成している新システム用の開発環境に、 embulk を動かす用のDockerイメージを追加して、DB同期を動かせるようにしてみます。
イメージに関しては公開してらっしゃる方がいたので、とりあえず利用してみます。対応バージョンのjavaやツールが入っているので楽ちんです。
reflet/docker-embulk - Docker Hub
入出力はMySQLなので、上記のイメージを元に対応したプラグインを入れます。
FROM reflet/docker-embulk RUN embulk gem install embulk-input-mysql embulk-output-mysql # コードベースは全部コピーしておく ENV APP_PATH /new-rails-app COPY . $APP_PATH WORKDIR $APP_PATH
設定ファイル書いてみる
コピー元の情報とコピー先の情報を設定ファイルに書いていきます。
embulkの設定記述方法は非常に直感的で、 in:
と out:
にそれぞれ書くだけです。今回はMySQLの接続情報、コピー元テーブルのカラムなどを書きます。
MySQLへの接続情報は環境変数にしておくことで、本番環境でも同じDockerイメージで動かすことを狙っておきます
in: type: mysql user: {{ env.EMBULK_IN_DB_USER }} password: {{ env.EMBULK_IN_DB_PASSWORD }} database: {{ env.EMBULK_IN_DB_DATABASE }} host: {{ env.EMBULK_IN_DB_HOST }} port: {{ env.EMBULK_IN_DB_PORT }} options: {useUnicode: true, characterEncoding: UTF-8, connectionCollation: utf8mb4_general_ci} default_timezone: Asia/Tokyo table: system1_users select: "*" out: type: mysql user: {{ env.EMBULK_OUT_DB_USER }} password: {{ env.EMBULK_OUT_DB_PASSWORD }} database: {{ env.EMBULK_OUT_DB_DATABASE }} host: {{ env.EMBULK_OUT_DB_HOST }} port: {{ env.EMBULK_OUT_DB_PORT }} options: {useUnicode: true, characterEncoding: UTF-8, connectionCollation: utf8mb4_general_ci} default_timezone: Asia/Tokyo mode: merge table: system2_users column_options: created_at: {type: "DATETIME", value_type: string, timestamp_format: "%Y/%m/%d %H:%M:%S"} updated_at: {type: "DATETIME", value_type: string, timestamp_format: "%Y/%m/%d %H:%M:%S"}
他のDockerコンテナとの連携
前述の通り、新しいRailsアプリの開発環境はDockerで構成しており、RailsアプリもMySQLも同じDockerネットワークにいます。なので、その構成用に作成したdocker-compose.ymlにembulkを載せた新しいDockerイメージを追加してみます。
こうすることで動作検証を最短でできますし、環境変数の追加も既存の .env に追加するだけです。
また entrypoint
を上書きしておき、 docker-compose run
で動作を変えられるようにしています。
bulk: build: context: . dockerfile: docker/bulk/Dockerfile env_file: .env volumes: - .:/new-rails-app entrypoint: /bin/bash -c depends_on: - db
これで docker-compose run bulk
でテーブル同期ができるようになりました。
$ docker-compose run --rm bulk "embulk run embulk/users.yml.liquid" Starting rails_app_db_1 ... done 2018-08-09 17:12:44.657 +0900: Embulk v0.9.7 2018-08-09 17:12:45.827 +0900 [WARN] (main): DEPRECATION: JRuby org.jruby.embed.ScriptingContainer is directly injected. 2018-08-09 17:12:49.218 +0900 [INFO] (main): Gem's home and path are set by default: "/root/.embulk/lib/gems" 2018-08-09 17:12:51.638 +0900 [INFO] (main): Started Embulk v0.9.7 2018-08-09 17:12:51.712 +0900 [INFO] (0001:transaction): Loaded plugin embulk-input-mysql (0.9.2) 2018-08-09 17:12:51.764 +0900 [INFO] (0001:transaction): Loaded plugin embulk-output-mysql (0.8.0) ...
複数のテーブルを同期できるように
複数のテーブルをまとめて同期できるよう、Makefileにコマンドを追加してみました。(UUUMでは、タスクランナーとしてわりとmakeを使っています)
.PHONY: debug-users-bulk debug-users-bulk: docker-compose run --rm bulk "embulk run embulk/users1.yml.liquid" docker-compose run --rm bulk "embulk run embulk/users2.yml.liquid"
複数のコピータスクがある場合、このようにコマンドをまとめておくと一気に動かせて便利です。
まとめ
へいしゃでは、アイスが好きなエンジニアを募集しています。