🚂

複数のRails on Dockerアプリで一つのDBを共有する

2021/12/08に公開

個人開発のRailsアプリが大きくなってきて、APIや管理画面などをプロジェクト単位で割りたいと思い、共通のDB、共通のネットワークに再構成した際の備忘録になります。

今更ながらZennは初投稿、笑

完成例

この記事でのサンプル概要

  • 2つのRails on Docker(app-api, app-admin)のプロジェクトがある
  • app-apiプロジェクトにRailsのapiコンテナ、Postgres(やMySQLなど)のdbコンテナがある。このdbをapp-adminでも使う
  • app-adminプロジェクトはRailsのadminコンテナのみ

この記事でやること

  • 共通ネットワーク(app_common_network)を作成
  • app-api, app-adminの共通ネットワークにする
  • 共通のDB(app-apiのdbコンテナ)を使う

完成図

完成図

この記事での前提(実装前)

各docker-compose.yml

app-api側
app-api/docker-compose.yml
version: '3'
services:
  api:
    build: .
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    environment:
      RAILS_ENV: development
    volumes:
      - bundle_install:/usr/local/bundle
      - .:/app-api
    ports:
      - '3000:3000'
    links:
      - db
  db:
    image: postgres
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: password
    ports:
      - '5432:5432'
    volumes:
      - ./db/postgres:/var/lib/postgresql/data
volumes:
  bundle_install:
    driver: local

app-admin側
app-admin/docker-compose.yml
version: '3'
services:
  admin:
    build: .
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app-admin
    ports:
      - '3000:3000'

現状のコンテナ構成図

デフォルトのコンテナ構成図

1. 共通のネットワークを作成

まずは、共通のネットワーク(app_common_network)を作成します

> docker network create app_common_network

dockerコマンドでネットワークを確認すると、以下のようにapp_common_networkが追加されているのが確認できるかと思います。

> docker network ls
NETWORK ID     NAME                 DRIVER    SCOPE
097xxxxxxxxx   bridge               bridge    local
fa4xxxxxxxxx   app-api_default      bridge    local
69bxxxxxxxxx   app-admin_default    bridge    local
fa0xxxxxxxxx   app_common_network   bridge    local
805xxxxxxxxx   host                 host      local
db6xxxxxxxxx   none                 null      local

2. 共通のネットワークを使う

作成したapp_common_networkを使うように変更します。

最上位のnetworksキーで独自ネットワークを定義します。

networks:
  app_common_network:
    external: true

それぞれのサービスにnetworksキーを使ってapp_common_networkを指定します。

services:
  hoge:
    build: .
  # ...(省略)...
    networks:
      - app_common_network

上記を踏まえ、各docker-compose.ymlを以下のように修正します。

app-api側
app-api/docker-compose.yml
 version: '3'
 services:
   api:
     build: .
     command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
     environment:
       RAILS_ENV: development
     volumes:
       - bundle_install:/usr/local/bundle
       - .:/app-api
     ports:
       - '3000:3000'
     links:
       - db
+    networks:
+      - app_common_network
   db:
     image: postgres
     environment:
       POSTGRES_USER: root
       POSTGRES_PASSWORD: password
     ports:
       - '5432:5432'
     volumes:
       - ./db/postgres:/var/lib/postgresql/data
+    networks:
+      - app_common_network
+networks:
+  app_common_network:
+    external: true
 volumes:
   bundle_install:
     driver: local

app-admin側
-      - '3000:3000'
+      - '23000:3000'

dbを共有する場合、app-api, app-adminの両方を立ち上げる必要があります。現状ポート番号3000を両方使っているため、admin側を23000に変更します(ちなみに13000番はLinden Labで使われている?ので23000番にしてます)。

それを踏まえ、以下のように修正します。

app-admin/docker-compose.yml
 version: '3'
 services:
   admin:
     build: .
     command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
     volumes:
       - .:/app-admin
     ports:
-      - '3000:3000'
+      - '23000:3000'
+    networks:
+      - app_common_network
+networks:
+  app_common_network:
+    external: true

修正したら、app-api, app-adminのコンテナを両方ビルドし直しましょう

> cd app-apiのパス
> docker compose build --no-cache  # 念のためキャッシュなしでビルド
> cd app-adminのパス
> docker compose build --no-cache

3. 共通のDBを使う

dbコンテナがないapp-adminのconfig/database.ymlを追加します。

app-apiと同じDBを使うので、app-api/config/database.ymlの設定がすでにあれば同じ設定で問題ないかと思います。

app-admin/config/database.yml
+default: &default
+  adapter: postgresql
+  encoding: unicode
+  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+  username: root
+  password: password
+  host: db
+
+development:
+  <<: *default
+  database: app_development
+
+test:
+  <<: *default
+  database: app_test
+
+production:
+  <<: *default
+  adapter: postgresql
+  database: <%= ENV['DB_NAME'] %>
+  username: <%= ENV['DB_USERNAME'] %>
+  password: <%= ENV['DB_PASSWORD'] %>
+  host: <%= ENV['DB_HOSTNAME'] %>
+

4. コンテナ起動

これで共通のネットワーク設定は完了しているので、コンテナを起動します。

ネットワーク設定を変更したため、起動中にネットワークエラーが発生する場合があります。その際は--force-recreateを試してみてください(僕も発生したことがあるのですが再現できず、、まだまだDockerやネットワーク周りの知識不足です)。

> cd app-apiのパス
> docker compose up
> docker compose up --force-recreate  # エラーが発生してコンテナ起動できない場合

# app-apiのコンテナは起動したまま別でシェル起動して..
> cd app-adminのパス
> docker compose up

終わり

これでapp-api, app-adminの両方で共通のDBが使えるようになったかと思います!

もし文章中に間違った表現や、再現できないなどがあれば、ご指摘・お知らせいただけると幸いです。

参考

Discussion