異なるdocker-composeのMySQLコンテナのデータをRailsに流し込む

レアケースなのであまり参考にならないかもしれませんが、将来の私に何か役に立つことがあるかもしれないので、念の為残しておく備忘録です。

# 背景

関係のない２つのMySQLコンテナが存在している。
片方は非RailsのMySQLデータベース、片方はRailsのマイグレーションで作られたMySQLデータベース。
いずれも別々のdocker-compose環境で動作している。

今回は非RailsのデータをRailsのデータベースに流し込む需要があって、
割とサクッと実現できたので、環境の設定や手法を以下に記します。

# docker-compose.yml

行頭に `+` がついている行が、今回の作業のために追加した設定です。
別々のdocker-compose環境を同一networkで繋ぐために設定を追加します。

## 非Rails環境

```diff:docker-compose.yml
 version: '2'
 services:
   mysql:
     container_name: mysql
     image: mysql:5.7.21
     ports:
       - 3307:3306
     volumes:
       - ./sql:/docker-entrypoint-initdb.d
       - ./mysql:/var/lib/mysql
     environment:
       MYSQL_DATABASE: not_rails
       MYSQL_ROOT_PASSWORD: not_rails
     command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --max_connections=10000
+    networks:
+      - database-link
+networks:
+  database-link:
+    external: true
```

## Rails環境

```diff:docker-compose.yml
 version: '3'
 services:
   server:
     build: ./
     ports:
       - 3000:3000
     depends_on:
       - db
     volumes:
       - ./:/app:cached
     environment:
       MYSQL_DATABASE: hoge_development
       MYSQL_USER: root
       MYSQL_PASSWORD: password
       MYSQL_HOST: db
       MYSQL_PORT: 3306
+    networks:
+      - database-link
   db:
     image: mysql:5.7
     container_name: db
     ports:
       - 3306:3306
     volumes:
       - ./sql:/docker-entrypoint-initdb.d
       - ./tmp/mysql/data:/var/lib/mysql
     environment:
       MYSQL_DATABASE: hoge_development
       MYSQL_USER: user
       MYSQL_PASSWORD: password
       MYSQL_ROOT_PASSWORD: password
     command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max_connections=10000
+    networks:
+      - database-link
+networks:
+  database-link:
+    external: true
```

# dockerのnetworkを作る

networkの設定を追記後、いつもどおりにdocker-composeで起動しようとすると、以下のようなエラーが出ると思います。

```bash
❯ docker-compose up -d mysql
ERROR: Network database-link declared as external, but could not be found. Please create the network manually using `docker network create database-link` and try again.
```

エラー出力にも書いてあるとおり、コマンドを実行します。

```bash
❯ docker network create database-link
029b69a807695fe7d1fa3d0354bbd93a61779659fdb552415728e075e4150b31
```

これでdocker-composeが `database-link` ネットワーク上で起動することができます。
上記は非Rails環境のdocker-compose環境だったので、Rails環境のdocker-composeも起動しておきます。
こちらはすでに `database-link` ネットワークが作成されているので、いつもどおりに起動するはずです。

```bash
❯ docker-compose up -d server
```


# Rubyスクリプト

データの流し込み用のスクリプトファイルをRails側のどこかに置きます。
今回は lib/test.rb に配置しました。

```lib/test.rb
require 'mysql2'

# 非Railsのデータベースに接続する
client = Mysql2::Client.new(
  socket: '/var/lib/mysql/mysql.sock',
  username: 'root',
  password: 'not_rails',
  encoding: 'utf8',
  database: 'not_rails',
  host: 'mysql'
)

# 流し込みたいデータを全部持ってくる
sources = client.query('select * from source')

# 全データをeachでぐるぐる回す
sources.each do |r|
  puts r # {"hoge"=>"aaaaaa", "fuga"=>"bbbbb", "name"=>"test", "value"=>"text"}
  # RailsのModelのcreateメソッドで登録する
  Destination.create(foo: r["hoge"], bar: r["fuga"], name: r["name"], value: r["value"])
end
```

これだけです。
ハマりそうなポイントとしては `host` に設定する値を非Rails環境の docker-compose.yml に記述している `services` の key の `mysql` を指定するあたりでしょうか。

そこらへんをクリアできれば特に難しいことはしていないと思います。

# スクリプトを実行する

以下のようにrubyコマンドで実行すると、エラーになります。

```bash
❯ docker-compose exec server ruby lib/test.rb
{"hoge"=>"aaaaaa", "fuga"=>"bbbbb", "name"=>"test", "value"=>"text"}
Traceback (most recent call last):
        2: from lib/test.rb:20:in `<main>'
        1: from lib/test.rb:20:in `each'
lib/test.rb:22:in `block in <main>': uninitialized constant Destination (NameError)
```

rubyコマンドでスクリプトを実行したときに、Destinationとか言う定数(クラス)なんて知らねぇよというエラーです。そりゃそうだ。でも知っててくれないと困る。

RailsのModelを認識させたい場合は、以下のように `rails runner` (`rails r` でも可) で実行すると幸せになれます。

```bash
❯ docker-compose exec server bundle exec rails runner lib/test.rb
{"hoge"=>"aaaaaa", "fuga"=>"bbbbb", "name"=>"test", "value"=>"text"}
...
```

以上です。
```