はじめに
「Dockerを使ってReact+Railsの環境を作ってみたけど、生成されたファイルが何をしているのか分からない」と言う方に向けに、Dockerの基本概念と、実際にReact+Rails環境を構築したときにできあがるファイル(Dockerfile、compose.yml)の読み方をまとめました。
Dockerとは
一言でいうと、「データやプログラムを隔離できる」仕組みです。
Dockerは、アプリケーションを効率的に開発・配布・実行するためのオープンソースのプラットフォームで、Linux上で動作します。「コンテナ」という単位を使い、アプリケーションの実行環境を素早く構築・運用できるのが特徴です。
なぜ「隔離」したいのか
プログラムは単独で動いているわけではなく、実行環境やライブラリ、他のプログラムに依存しています。そのため、あるプログラムをバージョンアップすると、それに依存している別のプログラムが動かなくなってしまうことがあります。
たとえば、システムAとシステムBが同じライブラリのVer5を使っていたとします。システムAはVer5専用の仕様なのに、そのライブラリをVer6にアップデートしてしまうと、システムAが動かなくなる——という事態が起こり得ます。
こういうとき、システムAとBの実行環境そのものを「コンテナ」として分けてしまえば、互いに影響を与えずに済みます。これがDockerで環境を隔離する狙いです。
コンテナ・イメージ・Dockerfileの関係
この3つの関係は、料理にたとえるとイメージしやすくなります。
- Dockerイメージ=冷凍チャーハン
- コンテナ=皿に盛られた、実際に食べられる状態のチャーハン
- Dockerfile=チャーハンのレシピ(設計図)
もう少し技術的に言うと、次のような関係です。
| 用語 | 役割 |
|---|---|
| Dockerfile | Dockerイメージを作るための手順書(テキストファイル) |
| Dockerイメージ | アプリケーションの実行に必要なファイル一式をまとめたもの(クラスに相当) |
| コンテナ | イメージから実際に起動される、隔離された実行環境(インスタンスに相当) |
Dockerfileはイメージを自分好みにカスタマイズしたいときに書くもので、Docker Hub上の既存イメージだけで十分な場合は不要です。逆に言えば、既存イメージのままでは足りない場合は、Dockerfileを書いてイメージをビルドすることになります。
-
imageだけで足りる例(DBコンテナ):
image: postgres:16のように書くだけでOK。DBサーバーそのものが最初から入ったイメージが配布されているので、追加でインストールするものが何もない。 - Dockerfileが必要な例(Railsコンテナ):Railsの場合は、基本的にDockerfileを書くのが標準だと考えてよいです。
Railsを動かすには、ruby:3.0.5 のような「Rubyの実行環境」だけが入ったイメージに対して、
少なくとも次のような作業を積み重ねる必要があるからです。
- Node.js・Yarnなど、Rails6以降で必要になる周辺ツールを追加インストールする
-
Gemfileをコンテナ内にコピーしてbundle installする(アプリが依存するgemを入れる) - アプリのソースコードをコンテナに持ち込む(volumeでマウントする場合も含む)
- 最後に
rails serverを起動できる状態に仕上げる
これらは「Rubyのバージョンを選ぶ」だけではカバーできない作業なので、Rails用コンテナではDockerfileを書くこと自体がセットになっている、と捉えておくのがおすすめです。DBのように「イメージそのものが完成品」というケースの方が、むしろ例外的だと考えるとイメージしやすいと思います。
Dockerを使うメリット
- 一貫性:開発/テスト/本番のどの環境でも同じように動く。「自分のPCでは動くのに本番では動かない」問題を防げる。
- 移植性:Dockerさえ入っていれば、OSが違うマシンでもコンテナをそのまま動かせる。開発環境と本番環境の差異に悩まされにくくなる。
Dockerの機能を大きく分けると、①イメージを作る(Build)、②イメージを共有する(Ship)、③イメージを動かす(Run)、の3つに集約されます。
Docker Composeとは
実際のWebアプリケーションは、アプリ本体だけでなく、データベースサーバーなど複数のミドルウェアが連携して動いています(例:MySQLやPostgreSQLなどのDBサーバー、Rails用のRackサーバーなど)。
コンテナを1つずつ手作業で起動・削除するのは面倒なので、複数のコンテナ構成をYAMLファイルにまとめて定義し、コマンド1つで一括起動・一括削除できるようにするツールがDocker Composeです。
設定ファイルは compose.yml(または docker-compose.yml)という名前で作成します。
React+Rails環境で出来上がるファイル一覧
DockerでRails(バックエンド)とReact(フロントエンド)の環境を構築すると、大まかに次のようなファイルができあがります。
project/
├── compose.yml (docker-compose.yml) ← 複数コンテナをまとめて定義
├── backend/
│ ├── Dockerfile ← Rails用イメージの設計図
│ ├── Gemfile
│ └── Gemfile.lock
└── frontend/
└── Dockerfile ← React用イメージの設計図
構成の仕方は記事によって多少異なりますが、「Rails側のDockerfile」「React側のDockerfile」「両者をまとめるcompose.yml」の3点セットで環境を定義する、という基本パターンは共通しています。
各ファイルの読み方
1. Rails側のDockerfile
Rails用のイメージは、Rubyの実行環境に加えてNode.jsやYarnなど、Rails6以降で必要になる周辺ツールも一緒にインストールする必要があるため、Dockerfileでのカスタマイズが必須になります。典型的な中身は次のような構成です。
# ベースイメージ(Rubyの実行環境)を取得
FROM ruby:3.0.5
# Node.jsとyarnをインストール
RUN apt-get update -qq
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
&& apt-get install -y nodejs
RUN npm install --global yarn
# コンテナ内の作業ディレクトリを指定
WORKDIR /app
# Gemfileだけ先にコピーしてbundle install
COPY Gemfile Gemfile.lock /app/
RUN bundle install
# コンテナ起動時にRailsサーバーを立ち上げる
CMD ["rails", "server", "-b", "0.0.0.0"]
読み解くポイントは以下の通りです。
-
FROM:土台となるイメージを指定します。ruby:3.0.5を指定することで、Rubyをゼロからインストールする手間を省けます。もしOSのイメージ(例:FROM debian)から始めると、Rubyのインストールから自分で書く必要があります。 -
RUN:イメージをビルドする際に一度だけ実行されるコマンドです。ここでnode.jsやyarnなど、Railsが依存する周辺ツールを入れています。 -
WORKDIR:コンテナ内でコマンドを実行する基準ディレクトリを指定します。以降の相対パスはここが起点になります。 -
COPY:ローカル(ホスト側)のファイルをコンテナ内にコピーします。Gemfileを先にコピーしてからbundle installするのは、アプリのソースコード全体をコピーする前にgemだけ先にインストールし、ビルドキャッシュを効かせるための定石です。 -
CMD:コンテナが起動したときに実行される既定のコマンドです。ここでRailsサーバーを立ち上げているため、Railsの起動に必要なファイルが足りていないとコンテナはすぐ停止してしまいます。
2. React側のDockerfile
React(Node.js)側は、Railsほど複雑な追加インストールが必要ないため、Dockerfileはシンプルになりがちです。
# Node.jsのイメージを指定
FROM node:lts
# 作業ディレクトリを指定
WORKDIR /usr/src/app
create-react-app などのプロジェクト作成コマンドは、Dockerfileの中には書かず、コンテナ起動後に docker compose run などで別途実行するケースが多いです。これは、プロジェクトのひな形自体をホスト側のディレクトリに直接生成させ、ホストとコンテナ間で常に同じファイルを共有(マウント)させたいからです。
3. compose.yml(複数コンテナをまとめる設定ファイル)
Rails+DB、Reactを一括で管理するcompose.ymlは、次のような形になります(DBはPostgreSQLを使う例)。
services:
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: password
volumes:
- db-data:/var/lib/postgresql/data
backend:
build: ./backend
depends_on:
- db
environment:
DATABASE_PASSWORD: password
ports:
- "3000:3000"
volumes:
- ./backend:/app
frontend:
build: ./frontend
ports:
- "3001:3000"
volumes:
- ./frontend:/usr/src/app
stdin_open: true
volumes:
db-data:
読み解くポイントは以下の通りです。
-
services:の下に並ぶdb/backend/frontendが、それぞれ1つのコンテナに対応します。 -
image::Docker Hubから既存のイメージをそのまま使う場合に指定します。DB用のコンテナはカスタマイズが不要なので、専用のDockerfileを作らず、imageだけで済ませています。使うDBの種類によって、渡す環境変数やデータの保存先パスが変わる点には注意が必要です(PostgreSQLならPOSTGRES_USER/POSTGRES_PASSWORDと/var/lib/postgresql/data、MySQL系ならMYSQL_ROOT_PASSWORDなどと/var/lib/mysqlになります)。 -
build::imageの代わりに、指定したディレクトリ内のDockerfileをもとにイメージをビルドします。RailsやReactのように、独自のセットアップが必要なコンテナはこちらを使います。 -
ports::「ホスト側のポート:コンテナ側のポート」の形式で、コンテナの中で動いているアプリにホストからアクセスできるようにします。 -
volumes::ホスト側のディレクトリとコンテナ側のディレクトリを同期(バインドマウント)させます。これにより、ローカルのエディタでコードを編集すると、コンテナ内のファイルにもリアルタイムで反映されます。DB用のdb-dataのように名前付きボリュームを使うと、コンテナを削除してもデータだけは保持できます。 -
depends_on::起動順序の依存関係を指定します。backendはdbに依存しているので、dbが先に立ち上がるよう制御しています(※実際にDBの起動完了を待つわけではない点には注意が必要です)。 -
environment::コンテナ内で使える環境変数を設定します。DBのパスワードなど、database.yml側からENV.fetch(...)で参照する値をここで渡しています。
まとめ
- Docker=アプリの実行環境をコンテナという単位で隔離・再現できる仕組み
- Dockerイメージ=コンテナの元になる「設計図から作られた型」
- Dockerfile=そのイメージを自分好みにカスタマイズするための手順書。DBのように既存イメージがそのまま使える場合は不要だが、Railsのように周辺ツールやgemの追加インストールが必要な場合は、基本的にDockerfileを書くことになる
- Docker Compose(compose.yml) =複数のコンテナ(Rails・React・DBなど)をまとめて定義し、一括で起動・停止するための設定ファイル
React+Rails構成では、「Rails用Dockerfile」「React用Dockerfile」「両者を束ねるcompose.yml」の3点を押さえれば、ファイルが何をしているか読み解けるようになります。
細部のオプションは環境によって変わりますが、「どのイメージを土台にして」「何を追加インストールし」「どのポート・ディレクトリを繋ぐか」 という視点で読むと、初見のDocker構成でも迷いにくくなるはずです。
