🐳

既存RailsプロジェクトにDocker-composeを導入してみた

2022/03/25に公開

こんにちは。

既存のRailsアプリにDocker-composeを導入してみようということで、
今後に役立てるため、記録していきます。

環境

Macbook Air Monterey 12.2.1(M1)
Docker version 20.10.12
docker-compose version 1.29.2


準備

準備として、
既存Appのルートディレクトリに以下のファイルを作成します。

  • Dockerfile
  • docker-compose.yml
  • entrypoint.sh
  • development.env
    development.envは環境変数を設定するために準備をするようです。
    ファイル構成としては以下のようになります。
project
 └── sample_app
      ├── Dockerfile
      ├── docker-compose.yml
      ├── entrypoint.sh
      ├── development.env
      ├── config
      ├── app
      ├── db
      .
      .
      .

Dockerfileの作成

次はDockerfileを作成します。

Dockerfile
# 既存のプロジェクトのバージョンに合わせる!
FROM ruby:3.1.1

# 起動させるためのパッケージを取得
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
    # `build-essential`は開発に必須のビルドツールを提供しているパッケージ
    build-essential \
    mariadb-client \
    nodejs \
    vim \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
# 作業用のディレクトリを作成(存在しない場合は勝手に作成してくれる)
WORKDIR /myproject(アプリのディレクトリ名)

# ホストのGemfile達をコンテナ内にコピー
COPY Gemfile /myproject/Gemfile
COPY Gemfile.lock /myproject/Gemfile.lock

RUN gem install bundler
RUN bundle install
#既存railsプロジェクトをコンテナ内にコピー
COPY . /myproject

# entrypoint.shをコピーし、実行権限を与える
COPY entrypoint.sh /usr/bin/
# chmodコマンドはファイルやディレクトリに権限設定するコマンド。`+`は後に記述した権限を付加する。`x`は実行権限。
# つまり今回は全てのユーザに該当ファイルの実行権限を付与する。
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
# `EXPOSE <ポート>`はコンテナ実行時に<ポート>にリッスンするよう命令するコマンド。
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

--no-install-recommendsrm -rf /var/lib/apt/lists/*
無駄なインストールを省き、aptインストール時のキャッシュを削除してくれる。


entrypoint.shの作成

次はentrypoint.shの作成です。このファイルは何のためにあるファイルなのか。
最初は疑問でした。Docker公式によると以下のように記載されています。

Next, provide an entrypoint script to fix a Rails-specific issue that prevents the server from restarting when a certain server.pid file pre-exists. This script will be executed every time the container gets started.

つまり、サーバを再起動するときに妨げる問題を修正するスクリプトを提供するそうです。(直訳すぎてごめんなさい)こちらに良い記事があるので今すぐみたい方はどうぞ。下記はその記事をそのまま引用しております。

entrypoint.sh
#!/bin/bash
set -e

# Rails に対応したファイル server.pid が存在しているかもしれないので削除する。
rm -f /myapp/tmp/pids/server.pid

# コンテナーのプロセスを実行する。(Dockerfile 内の CMD に設定されているもの。)
exec "$@"

development.envの作成

早速envファイルを作成する前に、.envは何かについて少し触れます。
Docker公式によると以下のように記載されています。環境変数というのは、OSが動いている時に使える変数のことです。 - 参照

Compose ファイルが参照する環境変数、あるいは Compose の設定に用いられる環境変数のデフォルト値を設定することができます。

それでは以下のコードを記述していきます。

development.env
MYSQL_DATABASE=アプリ名_development
MYSQL_ROOT_PASSWORD=password
MYSQL_USER=root  # 任意のユーザ名
MYSQL_PASSWORD=railsdbpassword  # 任意のパスワード

2行目のMYSQL_ROOT_PASSWORD=passwordは、MySQLのスーパーユーザであるrootアカウントに設定するためのパスワードです。つまり、先ほどの環境変数というワードと組み合わせると「passwordという値をMYSQL_ROOT_PASSWORDという変数に代入するということになります。3,4行目のMYSQL_USER=rootMYSQL_PASSWORD=railsdbpasswordは、オプションで新規に作成されるユーザとそのパスワードを設定する際に使用されます。DBに対して、スーパユーザの権限を与えます。 - 参照


docker-compose.ymlの作成

先ほど作成した.envファイルを読み込む方法は2つあるそうです。

  1. docker-compose.ymlと同じディレクトリに配置して自動的に読み込ませる
  2. env_fileというオプションをつけて読み込ませる
docker-compose.yml
version: "3.8"
services:
  db:
    image: mariadb:10.6.7
    # データの永続化の為に使用
    volumes:
      - dbvolume:/var/lib/mysql/data
    # Mac(M1)を使用している場合、platformに関するエラーが発生する可能性がある。エラー発生してから追加するのも良し。
    platform: linux/x86_64
    env_file: development.env
  web:
    # 作成済みのDockerfileを元にコンテナを作成
    build:
      # Dockerfileでファイルの読み込み先を指定
      context: .
      dockerfile: Dockerfile
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myproject
      - gem_data:/usr/local/bundle
    ports:
      - "3000:3000"
    # コンテナの立ち上げ順と依存関係を指定。dbコンテナが立ち上がってからwebコンテナが立ち上がるようにする。
    depends_on:
      - db
    env_file: development.env
    # stdin_open, ttyは`pry-byebug`を使用するために記述
    stdin_open: true
    tty: true
    command: bundle exec rails server -b 0.0.0.0
volumes:
  dbvolume:
  gem_data:

バージョンは最新版が推奨されており、Compose ファイルのバージョンとアップグレードで確認ができます。


database.ymlの編集

config/database.ymlのファイルを編集してDockerコンテナをMySQLにつなげます。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root  # development.envで記載したユーザ名
  password: <%= ENV["MYSQL_ROOT_PASSWORD"] %>
  socket: /tmp/mysql.sock
  host: db

development:
  <<: *default
  database: アプリ名_development  # development.envで記載したデータベース名

ここで注意があります。usernamepasswordは先ほど作成したdevelopment.envのものと一致させる必要があります。私はここで何時間か費やしてしまいました。

それではimageを作成していきましょう!

#imageの作成
docker-compose build 

それが終了したらコンテナを立ち上げていきます。

#コンテナの立上げ `-d`はデタッチモードと呼ばれるバックグラウンドでコンテナを実行するオプション
docker-compose up -d

立ち上げられたら以下を実行してlocalhost:3000でページが表示されるか確認してみてください。

#コンテナ内で
docker-compose exec web bundle exec rails db:migrate

表示がされたら完了です!


補足(エラー対処法)

Unknown MySQL server host 'db' (-2)

development.envMYSQL_USER=rootを記述していると、以下のようなエラーが発生する場合があります。

ターミナル
$ docker-compose exec web bundle exec rails db:migrate
Running via Spring preloader in process 50
rake aborted!
ActiveRecord::ConnectionNotEstablished: Unknown MySQL server host 'db' (-2)

# ===== 省略 =====

*DockerDesktopのLOGSを確認するとエラー詳細を確認できます。

MYSQL_USER=rootは新規でユーザを作成するよう命令するものです。
私の場合、DB側で既にrootユーザが作られているため、新しく作れないよ、というメッセージです。
したがってdevelopment.envに記述したMYSQL_USER=rootの行を削除して以下のコマンドを実行し再度コンテナを起動します。

ターミナル
$ docker-compose up -d --build

Access denied for user 'root'@'IPアドレス' (using password: YES)

続いて、こちらのエラー。
多くの場合は、既存のvolumeを参照し変更が適用されていないことから発生するみたいです。
よってvolumeを削除してコンテナを立ち上げ直す必要があります。
注意点として、volumeを削除するとそれまでに作成していたデータは無くなってしまいます。場合によってはバックアップが必要となります。

ターミナル
$ docker-compose down --volumes

再度、立ち上げます。

ターミナル
docker-compose up -d --build

これでもエラーが発生する場合

上記を行っても改善されない場合、そもそものパスワードが間違っておりアクセス拒否されてしまっている場合があります。
よって直接コンテナからMariaDBに接続を行ってみましょう。
以下を実行してStateUpになっていることを確認。

ターミナル
docker-compose ps                                   
        Name                      Command               State           Ports         
--------------------------------------------------------------------------------------
db_1    docker-entrypoint.sh mariadbd    Up      ****              
web_1   entrypoint.sh bundle exec  ...   Up      ****

# ===== 以下のコマンドを実行して、DBコンテナのコンテナIDを確認 =====
# `-a`は`--all`の意味。全てのコンテナを表示するオプション。
$ docker ps -a

# ===== 以下のコマンドを実行して、コンテナに接続 =====
$ docker exec -it <コンテナ名> bin/bash

# ===== 接続できたらMariaDBに接続 =====
/# mysql -u root -p
Enter password:
# パスワードを入力して以下のメッセージが表示されたらパスワードが間違っている
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

恐らくconfig/database.ymlのpassword項目の値が間違っているので、正しいパスワードに直す。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV["MYSQL_ROOT_PASSWORD"] %>  # ===== ここ!! =====
  socket: /tmp/mysql.sock
  host: db

development:
  <<: *default
  database: アプリ名_development  # development.envで記載したデータベース名

参考

Discussion