1台のサーバーで Dev / Staging を同時運用する方法
Docker + Nginx Reverse Proxy + 自動SSL(実践構成)
はじめに
スタートアップや小規模開発チームでは、次のような課題に直面することがよくあります。
- Development 環境と Staging 環境を同時に動かしたい
- ただし、サーバーコストはできるだけ抑えたい
- 1台の VPS / EC2 / 物理サーバーで運用したい
- 両方とも Docker ベースで動いている
- HTTPS もきちんと対応したい
ここで必ず起きる問題があります。
Dev も Staging も
80/443ポートを使いたい
当然ながら、後から起動したコンテナはポート競合で立ち上がりません。
そこで今回は、実務でも使いやすい構成として、
- Nginx Reverse Proxy
- ドメインごとの振り分け
- Let's Encrypt + Certbot によるSSL自動更新
- Dev / Staging の独立デプロイ
を実現する方法を、わかりやすく整理して紹介します。
全体構成
Internet
│
│ 80 / 443
▼
┌─────────────────────────────┐
│ nginx_proxy │
│ Reverse Proxy + SSL Gateway │
└──────────┬──────────────────┘
│
┌─────┴─────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ web_dev │ │ web_stg │
└──────────┘ └──────────┘
ポイント
外部公開されるのは nginx_proxy のみです。
80:80443:443
各アプリケーションコンテナは内部ネットワーク上で通信し、公開ポートは不要です。
Nginx が Host ヘッダーを見て振り分けます。
-
dev.example.com→ Development -
stg.example.com→ Staging
事前準備
以下を用意しておきます。
- ドメイン(またはサブドメイン)
- DNS A レコード
- 80 / 443 ポート開放
例:
dev.example.com -> 1.2.3.4
stg.example.com -> 1.2.3.4
Step 1: 共通 Docker Network を作成
Proxy と各アプリが別々の compose 管理でも通信できるように、共通ネットワークを作成します。
docker network create proxy-network
各アプリ側の docker-compose.yml
services:
web_dev:
networks:
- proxy-network
networks:
proxy-network:
external: true
Staging 側も同様です。
services:
web_stg:
networks:
- proxy-network
補足
web_dev や web_stg という service 名が、そのまま Nginx から参照されるホスト名になります。
Step 2: Reverse Proxy 用 Stack を作成
ディレクトリ構成:
nginx-proxy/
├── docker-compose.yml
├── nginx.conf
├── certs/
└── certbot_webroot/
docker-compose.yml
version: "3.8"
services:
nginx_proxy:
image: nginx:alpine
container_name: nginx_proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/letsencrypt
- ./certbot_webroot:/var/www/certbot
networks:
- proxy-network
certbot:
image: certbot/certbot:latest
container_name: certbot_proxy
restart: unless-stopped
volumes:
- ./certs:/etc/letsencrypt
- ./certbot_webroot:/var/www/certbot
networks:
proxy-network:
external: true
なぜ Proxy を分離するのか
1. デプロイを独立できる
アプリ更新時に Proxy を触る必要がありません。
2. 将来的な拡張がしやすい
- QA
- Preview
- Demo 環境
などを追加しやすくなります。
3. SSL 管理を集約できる
証明書管理が1箇所で済みます。
Step 3: Nginx 設定
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
real_ip_header X-Forwarded-For;
set_real_ip_from 172.16.0.0/12;
real_ip_recursive on;
server {
listen 80;
server_name dev.example.com stg.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
server_name dev.example.com;
ssl_certificate /etc/letsencrypt/live/dev.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dev.example.com/privkey.pem;
location / {
proxy_pass http://web_dev:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 443 ssl;
http2 on;
server_name stg.example.com;
ssl_certificate /etc/letsencrypt/live/stg.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stg.example.com/privkey.pem;
location / {
proxy_pass http://web_stg:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
実務で重要なポイント
クライアントIPを正しく取得する
Reverse Proxy 配下では、設定しないとバックエンド側で本来のアクセス元IPが取得できません。
そのため以下は非常に重要です。
real_ip_header X-Forwarded-For;
Step 4: 初回SSL発行
初回は HTTP Challenge を利用して証明書を取得します。
Dev 用
docker run --rm \
-v ./certs:/etc/letsencrypt \
-v ./certbot_webroot:/var/www/certbot \
certbot/certbot certonly \
--webroot -w /var/www/certbot \
-d dev.example.com
Staging 用
docker run --rm \
-v ./certs:/etc/letsencrypt \
-v ./certbot_webroot:/var/www/certbot \
certbot/certbot certonly \
--webroot -w /var/www/certbot \
-d stg.example.com
取得後に起動します。
docker compose up -d
よくあるトラブル
502 Bad Gateway
- コンテナ未起動
- service 名の誤り
- network 未接続
SSL 認証失敗
- DNS未反映
- Port 80 閉塞
- challenge path ミス
ポート競合
既にホスト側で Nginx / Apache が起動しているケースがあります。
この構成が向いているケース
- 小規模チーム
- MVP 開発
- 社内システム
- Staging / QA 環境
- コスト最適化したい案件
まとめ
1台のサーバーでも、設計次第で
- Development
- Staging
を安全かつスマートに共存させることができます。
さらに、
- HTTPS対応
- SSL自動更新
- 独立デプロイ
- ポート競合回避
まで実現できます。
小さく始めて、必要になれば後からスケールする。
その第一歩として、とても扱いやすい構成です。