はじめに
Railsで開発したアプリをDockerで動かしたいとき、Dockerとdocker composeを使うことで環境構築をコードとして管理できるようになります。
Docker化の手順を順に追って、つまずいたポイントと合わせてまとめました。
開発環境
- Ruby 3.2.2
- Rails 7.x
- PostgreSQL 12
- Docker / docker compose v2(スペース形式)
1. GitHub上のリポジトリをローカル環境へクローン
今回はDocker化がメインのテーマなので既存のRailsアプリを使用します。
Github上で使いたいプロジェクトのリポジトリページを開き、 Use this template → Create a new repository
を選択し、Repository name*にリポジトリ名を入力してCreate repositoryします。
ターミナルを開いて下記のコマンドを打って、Githubからローカルへプロジェクトを引っ張ってきて、フォルダの中に移動します。
今回はリポジトリ名をrails-docker としています。
git clone https://github.com/YOUR-USERNAME/rails-docker
cd rails-docker
今回はこのクローンしたrails-dockerディレクトリの中で、Docker化の作業を進めていきます。
できればgit checkout -b dockerでdockerブランチに作業ブランチを変更しておきましょう。(※そのまま main で作業しても問題ありません)
2. Dockerfileの作成
Dockerfileとは、Dockerコンテナを作るための設計図です。
プログラムやサーバー環境(RubyやRailsなど)を「この順番で入れて、このコマンドを実行して、、」という手順をテキストで記述します。
ターミナルでひとつひとつコマンドを打つより時間もかからず、再現性が高いです。
ではやっていきます。
ターミナル上でrails-docker内にいることを確認して、code Dockerfile と打ちエディタで Dockerfileを開きます。(VSCode使用の場合。)
エディタで下記のように入力します。
FROM ruby:3.2.2
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
WORKDIR /rails-docker
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
CMD ["bin/rails", "server", "-b", "0.0.0.0"]
-
FROM... Rubyのバージョンを指定します。Gemfile(後述)の中で指定するRailsと互換性のあるバージョンを指定します。 -
RUN...apt-get updateでパッケージリストを更新し、apt-get installでRailsアプリの実行に必要なライブラリをインストールします。-qqは出力されるログを最小限に、-yは全部Yesで進めるオプションです。 -
WORKDIR... コンテナ内の作業ディレクトリ。色々な人がこのDockerファイルを使用して環境構築することが想定されるので、絶対パスで指定します。 -
COPY... 現在のディレクトリ内容をコンテナへコピー。最初にGemfileとGemfile.lockのみコピーすることでキャッシュを活用できるようにします。 -
CMD... コンテナ起動時のデフォルト実行コマンド。後でdocker-compose.ymlで上書きされますが、記述しておいた方がDockerfile単独でも起動できるので安全です。
3. docker-compose.ymlの作成
docker-compose.ymlは、複数のDockerコンテナ(Rails + DBなど)を一括で管理して起動するための設定ファイルです。
たとえば、RailsアプリとPostgreSQLを一緒に動かす場合に、どのイメージを使って、どのポートで公開して、どんな環境変数を渡すか、などを定義できます。
rails-docker直下に docker-compose.ymlファイルを作成し、下記のように記述します。
services:
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/rails-docker
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
tty: true
stdin_open: true
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:12
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=myapp_development
ports:
- "5432:5432"
-
services:... 起動する各コンテナを定義します。ここではweb(Rails)とdb(PostgreSQL)の2つで構成されています。 -
web.build...Dockerfileがあるディレクトリからイメージをビルドします。.は「現在のディレクトリ」を意味します。 -
web.command... コンテナ起動時に実行されるコマンドを指定します。ここでは Rails を3000番ポート(Railsのデフォルトポート)で起動します。 -
web.volumes... ローカルのソースコードをコンテナ内にマウントします。ローカル上に置いてあるコードの開発中の変更をコンテナに即時反映できます。 -
web.environment... 環境変数の設定を行います。Railsのdatabase.ymlなどで読み込まれます。 -
web.tty...rails consoleなどを対話的に使いたいときに必要です。 -
web.stdin_open...標準入力(STDIN)を開いたままにします。ユーザー入力を受け付けることができるようになります。 -
web.ports...ホスト:コンテナの順で指定して、ホストのポートとコンテナのポートをつなげます。この場合`ローカルの http://localhost:3000 にアクセスすると、Railsアプリ(コンテナ内の3000番)できるようになります。 -
web.depends_on...dbコンテナが先に起動されるように順序を制御します。 -
db.image... PostgreSQLの公式イメージを使用します。postgres:12のようにバージョンの指定もできます。 -
db.environment... ユーザー、パスワード、DB名などのデータベースの初期設定ができます。これらはRailsのconfig/database.ymlの記述と揃える必要があります。 -
db.ports... DBに外部からアクセスできるようにするためのポート設定です。
4. database.ymlの作成
database.yml は、Railsアプリとデータベースをつなぐための設定ファイルです。
rails db:create や rails db:migrate はこのファイルの内容に従って処理されます。
rails-docker/config直下にdatabase.yml ファイルを作成し、下記のように記述します。
default: &default
adapter: postgresql
encoding: unicode
host: db
username: postgres
password: postgres
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: myapp_development
test:
<<: *default
database: myapp_test
username: myapp
password: <%= ENV["MYAPP_DATABASE_PASSWORD"] %>
-
adapter:... 使用するデータベースの種類です。 -
encoding:... データベースの文字コードを設定します。通常はunicodeを指定します。 -
host:... データベースサーバーのホスト名です。docker-compose.ymlで定義した DB サービス名(ここではdb)と一致させる必要があります。これが異なると、RailsからDBに接続できずエラーになります。 -
username:... 接続時に使用するユーザー名です。 -
password:... 上記ユーザーに対応するパスワードです。セキュリティを考慮して別途.envファイルを作って管理することもあります。今回は直接記載しています。 -
pool:... データベースへの同時接続数の上限です。スレッド数に応じて調整します。 -
database:... 作成されるデータベース名です。開発環境・テスト環境・本番環境でそれぞれ異なる名前を指定します。
5. Gemfileの作成とGemfile.lock
Gemfileは、Railsアプリで使用するRubyのライブラリ(gem)の依存関係をまとめて管理するためのファイルです。
このファイルを使うことで、アプリケーションに必要な機能をgemという形で追加し、それらをチーム全体で共有できるようになります。
Gemfileを変更するとDockerのビルドキャッシュが無効になるため、docker compose up --buildを実行し直す必要があります。
Gemfile.lock は、Gemfile に書かれた依存関係をもとに 実際にインストールされたgemのバージョンを記録するファイルです。
チーム開発で gem のバージョンを揃えるために重要で、bundle install を実行することで自動生成・更新されます。
rails-docker直下に Gemfileファイルを作成し、下記のように記述します。
source "https://rubygems.org"
ruby "3.2.2"
gem "rails", "~> 7.x"
gem "pg", "~> 1.1"
# 他にも使用したいgemをここに追加していきます
-
source:...使用するGemの取得元(リポジトリ)を指定します。 -
ruby:...使用するRubyのバージョンを指定します。Dockerfileにも同じバージョンを指定しておくと整合性が取れます。 -
gem:... 利用するライブラリ(gem)の名前とバージョンを指定します。~>を使うことで柔軟なバージョン指定ができます。
6. .dockerignoreファイルの作成
.dockerignoreに記載することで、Dockerイメージを作成する際に含めたくないファイルを除外します。現在作成していないファイルやフォルダでも、とりあえず含めておくと後から作った時に安心です。
rails-docker直下に.gitignoreファイルを作成し、下記のように記述します。
log/*
tmp/*
*.log
*.swp
.DS_Store
node_modules
7. コンテナの起動とDB設定
必要なファイルが作成できたのでターミナルに戻り、下記コマンドを打ってコンテナを起動します。
--buildはDockerイメージを再ビルドしてからコンテナを起動するオプションです。DockerfileやGemfile に変更を加えたあとなど、ビルド内容を更新したいときに使用します。
docker compose up --build
その後、下記コマンドでデータベースの作成とマイグレーションをします。
docker compose exec web rails db:create
docker compose exec web rails db:migrate
docker compose exec は、すでに起動しているコンテナの中でコマンドを実行するためのコマンドです。
このとき、どのサービス(コンテナ)に入るかを指定するために、web を付けています。
rails db:createはdatabase.yml に基づいて、指定されたデータベースを作成します。
rails db:migrateはdb/migrate/にあるマイグレーションファイル(設計図)に書かれた内容にしたがって、データベースの構造(テーブルやカラム)を作成・更新します。
「どんなカラムが必要か」「どんな制約があるか」など、アプリで使うデータの形がここで決まります
これでhttp://localhost:3000 にアクセスすると、Railsアプリの起動が確認できるでしょう。
もしhttps://localhost:3000 にリダイレクトされてしまって表示がうまくいかない場合は、ブラウザのキャッシュや履歴を削除してから再読み込みしてみてください。
8. GitHubにpush
http://localhost:3000 に接続してRailsアプリの起動が確認できたら、GitHub上にpushしてローカルとリモートの状態を同期させておきます。
作業ブランチを分けていない人はgit push origin main または git push origin masterでpushします。
git add .
git commit -m "コミットメッセージ"
git push origin docker
また、GitHub ActionsなどのCI環境では docker-compose が使えないので docker compose の方を使います。テストが通らない時は確認してみてください。
ちなみに、大文字と小文字を間違えてDocker-compose.ymlなどとタイポするとローカルでは動くがGiuhub上のテストが通らないなんてことが起こるので注意してください。
ファイル名の変更をしてもローカル上で同じと認識されて普通にはcommitが通らないので少しめんどくさいことになります。
参考にしたページ
公式ドキュメント(日本語訳)を参考にしました。
ぜひハンズオンで学んでみてください。
おわりに
Docker化につまずいた人の参考になれば幸いです!