概要
Webアプリケーション開発において、チーム間での環境差異やホスト環境の汚染を防ぐため、DevcontainerとDocker Composeを組み合わせた開発環境を構築します。
今回は、Rails 7.2 API-only + PostgreSQL構成で、開発環境と本番環境の両方をDockerで管理するテンプレートを紹介します。
なぜ Devcontainer + Docker Compose を使うのか?
VSCodeのRemote Containers(devcontainer)機能を使えば、以下のようなメリットがあります。
- 開発環境の再現性が高い - チームメンバー全員が同じ環境で開発できる
- ホスト環境を汚さない - Ruby、Node.js、PostgreSQLなどをホストにインストール不要
- 複数のプロジェクト間で環境を分離できる - バージョン違いのRailsプロジェクトも共存可能
- 開発環境と本番環境の構成を統一できる - Dockerfileを共通化し、差分のみ管理
ディレクトリ構成例
個人開発ではモノレポを活用しているため、.devcontainerディレクトリ下にbackendディレクトリを作成し、そこにDockerfile等を置いています。
project-root/
├── .devcontainer/
│ └── backend/ # 開発環境用設定
│ ├── devcontainer.json # VSCode設定
│ ├── docker-compose.yml # 開発用compose
│ ├── Dockerfile # 開発用Dockerfile
│ └── .env # 環境変数(Git管理外)
│
└── backend/
├── docker/ # 本番環境用設定
│ ├── docker-compose.yml # 本番用compose
│ ├── Dockerfile # 本番用Dockerfile
│ └── .env # 本番環境変数(Git管理外)
├── app/
├── config/
├── Gemfile
└── Gemfile.lock
開発環境の構築例
Dockerfile(開発用)
.devcontainer/backend/Dockerfile:
FROM ruby:3.4.7-bookworm
ENV DEBIAN_FRONTEND=noninteractive \
RAILS_ENV=development \
TZ=Asia/Tokyo \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
# Railsで必要な基本ツール+pg用ヘッダ
RUN apt-get update && apt-get install -y --no-install-recommends \
bash \
git \
curl \
tzdata \
ca-certificates \
tree \
build-essential \
pkg-config \
libpq-dev \
nodejs \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
RUN useradd -m -s /bin/bash rails && mkdir -p /workspace && chown -R rails:rails /workspace
WORKDIR /workspace
USER rails
EXPOSE 3000
Dockerfile 解説(開発環境)
ベースイメージの指定
FROM ruby:3.4.7-bookworm
公式のRubyイメージを使用。bookwormはDebian 12のコードネーム。
環境変数の設定
ENV DEBIAN_FRONTEND=noninteractive \
RAILS_ENV=development \
TZ=Asia/Tokyo \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
-
RAILS_ENV=development: 開発モードで起動 -
TZ=Asia/Tokyo: タイムゾーンを日本時間に設定 -
LANG=C.UTF-8: 文字化け防止
必要なパッケージのインストール
RUN apt-get update && apt-get install -y --no-install-recommends \
bash git curl tzdata ca-certificates tree \
build-essential pkg-config libpq-dev \
nodejs postgresql-client \
&& rm -rf /var/lib/apt/lists/*
-
libpq-dev: PostgreSQL接続用ヘッダファイル(pggemのビルドに必要) -
postgresql-client:psqlコマンド用 -
build-essential: ネイティブ拡張のgemをビルドするため -
rm -rf /var/lib/apt/lists/*: イメージサイズ削減
非rootユーザーの作成
RUN useradd -m -s /bin/bash rails && mkdir -p /workspace && chown -R rails:rails /workspace
WORKDIR /workspace
USER rails
セキュリティのため、rootではなくrailsユーザーで実行。
docker-compose.yml(開発用)
.devcontainer/backend/docker-compose.yml:
version: "3.9"
services:
app:
build:
context: ../.. # プロジェクトルートをビルドコンテキストに指定
dockerfile: .devcontainer/backend/Dockerfile
volumes:
- ../..:/workspace # ローカルのプロジェクト全体をマウント
working_dir: /workspace/backend
ports:
- "3000:3000"
environment:
RAILS_ENV: development
POSTGRES_HOST: db
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
depends_on:
- db
command: sleep infinity
tty: true
db:
image: postgres:16
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "5432:5432"
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
docker-compose.yml 解説(開発環境)
appサービス
-
build.context: プロジェクトルート(
../..)をビルドコンテキストに - volumes: ホストのコードをコンテナにマウント(リアルタイム反映)
- command: sleep infinity: コンテナを起動したまま維持
- depends_on: PostgreSQLコンテナが先に起動
dbサービス
- image: postgres:16: 公式PostgreSQL 16を使用
- volumes: db-data: データ永続化(コンテナ削除後もデータ保持)
devcontainer.json
.devcontainer/backend/devcontainer.json:
{
"name": "Backend Dev Container",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"remoteUser": "rails",
"updateRemoteUserUID": true,
"overrideCommand": false,
"postCreateCommand": "bash -lc 'gem install bundler -N && gem install rails -N -v \"~> 7.2\" && bundle install'"
}
devcontainer.json 解説
| 項目 | 説明 |
|---|---|
dockerComposeFile |
使用するdocker-compose.ymlを指定 |
service |
VSCodeが接続するサービス名(app) |
workspaceFolder |
コンテナ内の作業ディレクトリ |
remoteUser |
コンテナ内でのユーザー(rails) |
postCreateCommand |
コンテナ作成後に自動実行(bundler、Railsのインストール) |
環境変数ファイル
.devcontainer/backend/.env:
POSTGRES_USER=app
POSTGRES_PASSWORD=password
POSTGRES_DB=backend_development
注意: .envファイルは.gitignoreに追加し、Git管理しないこと。
本番環境の構築
Dockerfile(本番用)
backend/docker/Dockerfile:
FROM ruby:3.4.7-bookworm
ENV DEBIAN_FRONTEND=noninteractive \
RAILS_ENV=production \
TZ=Asia/Tokyo \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y --no-install-recommends \
bash git curl tzdata ca-certificates \
build-essential pkg-config libpq-dev \
nodejs postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# アプリケーションディレクトリ
WORKDIR /app
# Gemfileをコピーして依存関係をインストール
COPY Gemfile Gemfile.lock ./
RUN bundle config set --local deployment 'true' && \
bundle config set --local without 'development test' && \
bundle install
# アプリケーションコードをコピー
COPY . .
# アセットのプリコンパイル(エラー回避用)
RUN bundle exec rails assets:precompile || true
EXPOSE 3000
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
Dockerfile 解説(本番環境)
開発環境との違い
- RAILS_ENV=production: 本番モード
- bundle config set --local deployment 'true': Gemfile.lockを厳密に遵守
- bundle config set --local without 'development test': 開発・テスト用gemを除外
- COPY . .: コードをコンテナ内にコピー(ボリュームマウントしない)
- CMD: Railsサーバーを自動起動
docker-compose.yml(本番用)
AWSのS3からデータをインポートしたりしていたので、その分の設定が追加されています
backend/docker/docker-compose.yml:
version: "3.9"
services:
app:
build:
context: ..
dockerfile: docker/Dockerfile
env_file:
- .env
ports:
- "3000:3000"
environment:
RAILS_ENV: production
POSTGRES_HOST: db
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
AWS_REGION: ${AWS_REGION}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
S3_BUCKET_NAME: ${S3_BUCKET_NAME}
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
depends_on:
- db
command: bundle exec rails server -b 0.0.0.0
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- db-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
db-data:
docker-compose.yml 解説(本番環境)
開発環境との違い
- env_file: - .env: 環境変数ファイルを読み込み
- AWS関連の環境変数: S3連携のため
- SECRET_KEY_BASE: Rails本番環境で必須
- restart: unless-stopped: コンテナが落ちたら自動再起動
- ボリュームマウントなし: コードはコンテナ内に固定
開発環境と本番環境の違い
| 項目 | 開発環境 | 本番環境 |
|---|---|---|
| ベースイメージ | ruby:3.4.7-bookworm |
ruby:3.4.7-bookworm |
| RAILS_ENV | development | production |
| 依存関係 | development/test含む | productionのみ |
| コード配置 | ホストをボリュームマウント | COPYでコンテナ内に固定 |
| bundle deployment | false | true(Gemfile.lock厳守) |
| 環境変数 | .devcontainer/backend/.env |
backend/docker/.env |
| 自動起動 | なし(sleep infinity) | Railsサーバー自動起動 |
| 再起動ポリシー | なし | unless-stopped |
| ユーザー | rails(非root) | root |
使用方法
開発環境の起動
- VSCodeでプロジェクトルートを開く
-
Cmd/Ctrl + Shift + P→Dev Containers: Reopen in Container -
Backend Dev Containerを選択 - 初回はビルドに数分かかる
- コンテナ内のターミナルが開く
データベース準備
# マイグレーション実行
rails db:create
rails db:migrate
# 開発サーバー起動
rails server
ブラウザで http://localhost:3000 にアクセス
本番環境の起動
cd backend/docker
# .envファイルを作成(.env.exampleを参考に)
cp .env.example .env
vim .env
# コンテナビルド・起動
docker-compose up -d --build
# データベース作成
docker-compose exec app bundle exec rails db:create
docker-compose exec app bundle exec rails db:migrate
おわりに
Devcontainer + Docker Composeを使うことで、環境構築の手間を大幅に削減し、チーム開発をスムーズに進められます。
開発環境と本番環境の設定を分離しつつ、Dockerfileを共通化することで、「ローカルでは動くのに本番で動かない」という問題を防げます。