既存RailsプロジェクトにDocker-composeを導入してみた
こんにちは。
既存の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を作成します。
# 既存のプロジェクトのバージョンに合わせる!
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-recommends
とrm -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.
つまり、サーバを再起動するときに妨げる問題を修正するスクリプトを提供するそうです。(直訳すぎてごめんなさい)こちらに良い記事があるので今すぐみたい方はどうぞ。下記はその記事をそのまま引用しております。
#!/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 の設定に用いられる環境変数のデフォルト値を設定することができます。
それでは以下のコードを記述していきます。
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=root
とMYSQL_PASSWORD=railsdbpassword
は、オプションで新規に作成されるユーザとそのパスワードを設定する際に使用されます。DBに対して、スーパユーザの権限を与えます。 - 参照
docker-compose.ymlの作成
先ほど作成した.env
ファイルを読み込む方法は2つあるそうです。
-
docker-compose.yml
と同じディレクトリに配置して自動的に読み込ませる -
env_file
というオプションをつけて読み込ませる
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につなげます。
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で記載したデータベース名
ここで注意があります。username
とpassword
は先ほど作成した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.env
にMYSQL_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に接続を行ってみましょう。
以下を実行してState
がUp
になっていることを確認。
❯ 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項目の値が間違っているので、正しいパスワードに直す。
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