UUUM攻殻機動隊(エンジニアブログ)

UUUMのエンジニアによる技術ブログです

embulkに入門してDockerネットワーク内のMySQLデータをコピーしてみました

こんにちは、Webアプリケーションエンジニア(仮称)のナカハシです。

へいしゃでは「アイス補助」というものがあって、8月の間は毎日アイスを食べられます。ガリガリ君やハーゲンダッツの包装を開けるとき、なんでひとはこんなにも笑顔になれんるんでしょうか。

さて今回は、 embulk に入門してMySQLデータの同期にチャレンジしてみたいと思います。ターゲットは、開発環境のDockerネットワークにあるDB2つです。

embulk って?

embulk

embulkは、バッチ処理に特化したデータ転送ツールです。

プラグインによって豊富な種類の入出力ストレージに対応していること、大容量データの転送が可能なことなどが特長です。インストール後、 config.yml に入出力先の情報を記述し、以下のコマンドを叩けばいいだけなので手軽ですね。

$ embulk run config.yml

やりたかったこと

新規にRails製の開発している社内システムに、稼働中のSymfony製の別システムからデータをコピーするという要件が発生しました。

f:id:k_nakahashi:20180809174233p:plain

別システムにデータ公開用の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"

複数のコピータスクがある場合、このようにコマンドをまとめておくと一気に動かせて便利です。

まとめ

へいしゃでは、アイスが好きなエンジニアを募集しています。