0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS初学者が“実務を意識して”Webアプリ基盤を構築する 【③ Docker × nginx 実践編】

Posted at

本記事は、本連載の 実践パート です。

これまでの記事で、

  • AWS の前提設計
  • コスト感の整理
  • IAM / ネットワーク / EC2 構築
  • Docker を使える実行基盤

が揃いました。

本記事では、それらを土台にして、

  • Docker で nginx コンテナを起動し
  • 設定ファイルを自前で管理し
  • リバースプロキシを設定し
  • 本番を意識した設定分割を行い
  • HTTPS 化まで実装する

という、
「実際にインターネット公開できる状態」
までを一気に仕上げます。

最初は最小構成から始め、
章を追うごとに nginx の役割が
単なる Web サーバ → アプリ全体の入口
へと進化していく流れを体験してください。


第6章:Docker / docker-compose による最小Webコンテナ構築

— ブラウザからアクセスできることを確認する —

1. この章の目的

この章の目的は 1つだけ です。

  • クライアント(ブラウザ)からアクセスし、
    EC2上で動くコンテナがレスポンスを返すことを確認する

ここでは、以下が理解できれば十分です。

  • Docker は「アプリを閉じ込めた実行単位」
  • docker-compose は「コンテナ起動手順の定義」
  • EC2・Docker・nginx がどうつながっているか

👉
複雑なアプリ構成に入る前の“基礎体験”フェーズです。


2. 今回構築する最小構成

構成イメージ

[ Browser ]
     |
   HTTP
     |
[ EC2 ]
     |
[ Docker ]
     |
[ nginx コンテナ ]
     |
[ 静的HTML ]
  • コンテナは 1つだけ
  • nginx の公式イメージを使用
  • 自作の HTML が表示されることを確認する

3. 作業ディレクトリの準備

EC2 に SSH 接続後、以下を実行します。

cd ~/app
mkdir docker-nginx-sample
cd docker-nginx-sample

4. 表示確認用 HTML ファイルを作成

index.html

vi index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Docker Test</title>
</head>
<body>
  <h1>Docker + nginx が動いています</h1>
  <p>EC2上のコンテナから配信されています。</p>
</body>
</html>

👉
内容は重要ではありません
「自分で書いたファイルが表示される」ことが重要です。


5. docker-compose.yml を作成

docker-compose.yml

vi docker-compose.yml
version: "3.9"

services:
  nginx:
    image: nginx:latest
    container_name: nginx-sample
    ports:
      - "80:80"
    volumes:
      - ./index.html:/usr/share/nginx/html/index.html:ro

6. docker-compose.yml の設定を理解する

image

image: nginx:latest
  • nginx の公式イメージ
  • OS や nginx のインストールは不要

ports

ports:
  - "80:80"
  • 左:EC2 側のポート
  • 右:コンテナ内のポート

👉
EC2 の 80 番に来た通信をコンテナへ転送


volumes

volumes:
  - ./index.html:/usr/share/nginx/html/index.html:ro
  • 左:EC2 上のファイル
  • 右:コンテナ内の配置先
  • :ro:読み取り専用

👉
コンテナにファイルを渡している感覚を掴む。


7. コンテナを起動する

docker-compose up -d

成功すると以下のように表示されます。

Creating nginx-sample ... done

8. 起動確認

コンテナ一覧を確認

docker ps
CONTAINER ID   IMAGE          PORTS
xxxxxx         nginx:latest   0.0.0.0:80->80/tcp

9. ブラウザからアクセスする

アクセスURL

http://<Elastic-IP>

表示内容

Docker + nginx が動いています
EC2上のコンテナから配信されています。

👉
この画面が表示されれば 第6章は成功です。


10. 内部で起きていることの整理

  1. ブラウザが EC2 の 80 番へアクセス
  2. EC2 が Docker に通信を渡す
  3. nginx コンテナがリクエストを受信
  4. volume 経由で index.html を返す

👉
EC2 は単なる「箱」、主役は Docker


11. コンテナの停止・再起動

停止

docker-compose down

再起動

docker-compose up -d

12. 初学者がやりがちなNG例

❌ ports を指定し忘れる

  • 外部からアクセスできない
  • コンテナは動いているが見えない

❌ セキュリティグループで 80 番を開けていない

  • Docker 側が正しくても通信不可
  • EC2 側の設定ミス

❌ localhost にアクセスしてしまう

  • 自分の PC を見ている
  • EC2 ではない

13. この章のまとめ

  • docker-compose は起動定義ファイル
  • コンテナは EC2 上で動く
  • volume でファイルを渡せる
  • nginx は最小構成の入口
  • ブラウザで表示できれば成功

第7章:nginx 設定ファイルを自前で管理する

— 「動いた」から「制御できる」nginx へ —

1. この章の目的

この章では、以下をゴールとします。

  • nginx が どの設定ファイルを、どの順番で読み込むか を理解する
  • デフォルト設定に頼らず、自分で書いた設定ファイルを使う
  • 将来の拡張(API / HTTPS / 複数サービス)に耐えられる形にする

👉
この章が、学習用途と実務用途の分かれ目です。


2. nginx 公式イメージの基本構造

まず、nginx コンテナ内部の構造を整理します。

/etc/nginx/
├─ nginx.conf
├─ conf.d/
│   └─ default.conf
/usr/share/nginx/html/
└─ index.html

重要ポイント

  • nginx.conf
    → nginx 全体の共通設定
  • conf.d/*.conf
    → 実際の Web / API の設定を書く場所
  • /usr/share/nginx/html
    → 静的ファイルの配置先

👉
実務では default.conf をそのまま使わないのが普通です。


3. なぜ設定ファイルを自前管理するのか

デフォルト設定の問題点

  • 中身が見えない
  • 変更履歴が残らない
  • 複数人で触れない
  • 構成が複雑になると破綻する

👉
「設定もコード」= Git 管理前提


4. ディレクトリ構成の整理

第6章の構成を以下のように拡張します。

docker-nginx-sample/
├─ docker-compose.yml
├─ index.html
└─ nginx/
   └─ default.conf

5. nginx 設定ファイルを作成する

nginx/default.conf

mkdir nginx
vi nginx/default.conf
server {
    listen 80;
    server_name _;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

6. 設定内容の解説

server ブロック

server {
    listen 80;
    server_name _;
  • listen 80
    → 80番ポートで待ち受ける
  • server_name _;
    → すべてのホスト名を受ける(最小構成)

root / index

root /usr/share/nginx/html;
index index.html;
  • 静的ファイルの配置先
  • / にアクセスした際に index.html を返す

location /

location / {
    try_files $uri $uri/ =404;
}
  • ファイルが存在すれば返す
  • 無ければ 404

👉
静的サイト配信の基本形


7. docker-compose.yml を修正する

修正後の docker-compose.yml

version: "3.9"

services:
  nginx:
    image: nginx:latest
    container_name: nginx-sample
    ports:
      - "80:80"
    volumes:
      - ./index.html:/usr/share/nginx/html/index.html:ro
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro

ポイント

  • default.conf上書きしている
  • nginx は起動時にこの設定を読む

8. 設定を反映する

コンテナ再起動

docker-compose down
docker-compose up -d

ログ確認(任意)

docker logs nginx-sample

9. ブラウザで動作確認

http://<Elastic-IP>

第6章と同じ画面が表示されれば成功です。

👉
見た目は同じでも、中身は完全に別物


10. よくあるミスと対処

❌ 設定ファイルの配置先を間違える

  • 正解:/etc/nginx/conf.d/default.conf
  • nginx.conf を直接触らない

❌ 設定を書き換えたのに反映されない

原因:

  • volume マウントしていない
  • コンテナを再起動していない

対処:

docker-compose down
docker-compose up -d

❌ nginx が起動しない

確認:

docker logs nginx-sample

→ 構文エラーが原因のことがほとんど。


11. この章のまとめ

  • nginx は設定ファイルで動く
  • 実務では設定を自前管理する
  • conf.d 配下で Web 設定を書く
  • docker-compose で設定を注入する
  • 将来の拡張に耐えられる構成になる

第8章:nginx でリバースプロキシを設定する

— 「入口」と「内部サービス」を分離する —

1. この章の目的

この章では、以下をゴールとします。

  • リバースプロキシとは何かを構造で理解する
  • nginx を「静的配信サーバ」から
    **アプリ全体の入口(ゲートウェイ)**として使えるようにする
  • 将来の API(Node / Python 等)を
    安全に内部接続する前提構成を作る

👉
この章を理解すると、
**「なぜ nginx が最前段に置かれるのか」**が腹落ちします。


2. リバースプロキシとは何か

一言で言うと

クライアントからのリクエストを nginx が受け取り、
内部の別サービスへ代理で転送する仕組み


構成イメージ

[ Browser ]
     |
   HTTP
     |
[ nginx ]
     |
   /api/
     |
[ backend コンテナ ]
  • ブラウザは nginx しか知らない
  • backend は外部公開しない
  • nginx が通信の振り分け役になる

3. なぜ API を直接公開しないのか

理由① ポート管理が破綻する

  • APIごとに 3000 / 8000 / 8080 …
  • セキュリティグループが複雑化

理由② URL設計が汚くなる

http://<IP>:3000/api

nginx 経由なら:

http://<IP>/api

理由③ HTTPS 化が困難になる

  • APIごとに証明書?
  • 非現実的

👉
外部公開は nginx だけが正解


4. 今回の構成方針(APIはダミー)

この章では以下を方針とします。

  • 本物の Node / Python は使わない
  • **「APIっぽく振る舞うだけの軽量コンテナ」**を使用
  • 目的は「流れの理解」

5. 作業ディレクトリの準備

cd ~/app
mkdir docker-nginx-proxy
cd docker-nginx-proxy
mkdir nginx

6. docker-compose.yml を作成する

vi docker-compose.yml
version: "3.9"

services:
  nginx:
    image: nginx:latest
    container_name: nginx-proxy
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - backend

  backend:
    image: hashicorp/http-echo
    container_name: backend-api
    command: ["-text=Hello from backend API"]

7. ダミーAPIコンテナの説明

hashicorp/http-echo とは

  • HTTP リクエストを受けると
  • 指定した文字列を返すだけのコンテナ
command: ["-text=Hello from backend API"]

👉
APIの中身は重要ではない
重要なのは「nginx が中継している」こと。


8. nginx のリバースプロキシ設定

nginx/default.conf

vi nginx/default.conf
server {
    listen 80;
    server_name _;

    location / {
        return 200 "nginx is running\n";
    }

    location /api/ {
        proxy_pass http://backend:5678/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

9. 設定の重要ポイント

proxy_pass の指定

proxy_pass http://backend:5678/;
  • backend は docker-compose の service 名
  • Docker 内部 DNS により自動解決される
  • IP アドレス指定は不要・非推奨

ヘッダ転送

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
  • 元のホスト名
  • クライアントIP
    を backend 側で参照できるようにする

👉
実務では必須に近い設定


10. コンテナを起動する

docker-compose up -d

確認:

docker ps

11. 動作確認

nginx 単体

http://<Elastic-IP>/

表示:

nginx is running

API 経由

http://<Elastic-IP>/api/

表示:

Hello from backend API

👉
この表示が出れば リバースプロキシ成功


12. 内部で起きていること

  1. ブラウザ → nginx(80番)
  2. nginx が /api/ を検知
  3. backend コンテナへ転送
  4. レスポンスを受け取り返却

👉
外部からは backend が見えない


13. 初学者がやりがちなNG例

❌ proxy_pass に localhost を書く

proxy_pass http://localhost:5678;
  • コンテナ内の localhost は自分自身
  • 通信できない

❌ backend の ports を公開する

ports:
  - "5678:5678"
  • 外部公開不要
  • セキュリティリスク

❌ IP アドレスで backend を指定する

  • 再起動で変わる
  • Docker DNS を使うべき

14. この章のまとめ

  • nginx は「入口」として使う
  • API は外部公開しない
  • リバースプロキシで内部転送する
  • service 名で通信する
  • 本番構成への重要な一歩

第9章:nginx 設定の分割と本番向け構成

— 保守・拡張に耐える nginx 設計 —

1. この章の目的

この章では、以下をゴールとします。

  • nginx 設定を 役割ごとに分割する理由を理解する
  • 小規模でも 本番を意識した設定構成を作る
  • 将来の HTTPS 化・複数サービス化に備える

👉
「設定が1ファイルに全部書いてある」状態は、実務では早晩破綻します。


2. なぜ nginx 設定を分割するのか

1ファイル運用の問題点

  • どこに何が書いてあるか分からない
  • 差分レビューが困難
  • HTTPS・API追加時に事故りやすい
  • 複数人で触れない

👉
設定もアプリと同じで「構造化」が必要


3. nginx の設定読み込み順序

nginx は以下の順で設定を読み込みます。

  1. /etc/nginx/nginx.conf
  2. nginx.conf 内の include 指定
  3. /etc/nginx/conf.d/*.conf

👉
Web / API 設定は conf.d 配下に置くのが定石


4. 推奨ディレクトリ構成(実務寄り)

第8章の構成を以下に整理します。

docker-nginx-proxy/
├─ docker-compose.yml
└─ nginx/
   ├─ nginx.conf
   └─ conf.d/
      ├─ web.conf
      └─ api.conf

役割分担

ファイル 役割
nginx.conf 全体共通設定
web.conf Web(静的)
api.conf API(proxy)

5. nginx.conf(全体共通設定)

nginx/nginx.conf

user  nginx;
worker_processes auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main
      '$remote_addr - $remote_user [$time_local] "$request" '
      '$status $body_bytes_sent "$http_referer" '
      '"$http_user_agent"';

    access_log  /var/log/nginx/access.log main;

    sendfile        on;
    keepalive_timeout 65;

    include /etc/nginx/conf.d/*.conf;
}

ポイント

  • server は書かない
  • 全体挙動・ログ・パフォーマンスのみ定義
  • アプリ固有設定は持たせない

6. Web 用設定(web.conf)

nginx/conf.d/web.conf

server {
    listen 80;
    server_name _;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}
  • 静的配信専用
  • API と責務を分離

7. API 用設定(api.conf)

nginx/conf.d/api.conf

server {
    listen 80;
    server_name _;

    location /api/ {
        proxy_pass http://backend:5678/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

👉
Web と API を別ファイルにすることで、変更影響を局所化


8. docker-compose.yml の修正

修正後の docker-compose.yml

version: "3.9"

services:
  nginx:
    image: nginx:latest
    container_name: nginx-proxy
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
    depends_on:
      - backend

  backend:
    image: hashicorp/http-echo
    container_name: backend-api
    command: ["-text=Hello from backend API"]

9. 設定反映と確認

docker-compose down
docker-compose up -d

確認ポイント

  • / → Web 表示
  • /api/ → API 応答

10. 実務でこの構成が評価される理由

  • 設定差分が追いやすい
  • 機能追加時に影響範囲が明確
  • HTTPS 化時に server ブロック追加だけで済む
  • 設定レビューがしやすい

👉
「この構成にしている理由」を説明できる


11. 初学者がやりがちなNG例

❌ nginx.conf に全部書く

  • 責務が混ざる
  • 読めない・直せない

❌ conf.d を使わない

  • nginx の思想に反する
  • 本番構成から乖離する

❌ Web / API を同一 server に詰め込む

  • 見通しが悪い
  • HTTPS 移行時に地獄

12. この章のまとめ

  • nginx 設定は分割が前提
  • nginx.conf は共通設定のみ
  • server 定義は conf.d 配下
  • Web / API を責務で分ける
  • 小規模でも本番設計を意識する

第10章:HTTPS 化(Let’s Encrypt + nginx)

— 実務で必須の通信暗号化を自動更新まで含めて実装する —

1. この章の目的

この章では、以下をゴールとします。

  • HTTP 通信を HTTPS(TLS) に切り替える
  • Let’s Encrypt を用いて 無料で証明書を取得する
  • 証明書の 自動更新 を設定する
  • 実務で通用する HTTPS 構成を理解する

👉
インターネット公開サービスで HTTPS なしは失格です。


2. なぜ HTTPS が必須なのか

理由① 通信内容の盗聴防止

  • ID / パスワード
  • セッション情報
  • API トークン

👉
HTTP は 平文。盗聴可能。


理由② なりすまし防止

  • DNS / Wi-Fi 偽装
  • 中間者攻撃(MITM)

👉
HTTPS は サーバの正当性を証明する。


理由③ ブラウザ・API の制約

  • Chrome:HTTP は「安全でない」表示
  • Cookie の Secure 属性
  • 一部 API(Service Worker 等)は HTTPS 必須

3. Let’s Encrypt とは何か

Let’s Encrypt の特徴

  • 無料
  • 世界的に利用されている認証局
  • 証明書の有効期限は 90日
  • 自動更新前提

👉
手動更新は運用事故の元


4. 今回の HTTPS 化方針

  • ドメインを使用(IP直指定は不可)
  • Certbot を使用
  • nginx と連携
  • EC2 上で証明書を管理
  • Docker nginx から証明書を参照

5. 事前準備(必須)

5.1 ドメインの準備

  • Route53 以外でも可
  • A レコードを Elastic IP に向ける

例:

example.com → 18.xxx.xxx.xxx

👉
DNS 反映完了後でないと証明書取得不可。


5.2 セキュリティグループ設定

ポート 用途
80 認証用
443 HTTPS

6. Certbot のインストール(EC2)

sudo dnf install certbot python3-certbot-nginx -y

確認:

certbot --version

7. nginx を一時的に停止

Certbot が 80 番を使用するため、
Docker nginx を一度停止します。

cd ~/app/docker-nginx-proxy
docker-compose down

8. 証明書の取得

sudo certbot certonly --standalone -d example.com

実行中に聞かれること

  • メールアドレス:通知用(必須)
  • 利用規約:同意
  • メール配信:任意

成功すると以下に生成されます。

/etc/letsencrypt/live/example.com/
├─ fullchain.pem
└─ privkey.pem

9. nginx に HTTPS 設定を追加する

nginx/conf.d/ssl.conf を作成

vi nginx/conf.d/ssl.conf
server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }

    location /api/ {
        proxy_pass http://backend:5678/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

10. HTTP → HTTPS リダイレクト

nginx/conf.d/http.conf

server {
    listen 80;
    server_name example.com;

    return 301 https://$host$request_uri;
}

👉
HTTP を残したままにしない


11. docker-compose.yml の修正

証明書を nginx コンテナに渡す

volumes:
  - /etc/letsencrypt:/etc/letsencrypt:ro

※ nginx サービスに追加


12. nginx を起動する

docker-compose up -d

13. 動作確認

HTTPS アクセス

https://example.com
  • 鍵マークが表示される
  • 警告が出なければ成功

API 経由

https://example.com/api/

14. 証明書の自動更新設定

dry-run で確認

sudo certbot renew --dry-run

自動更新(systemd timer)

Amazon Linux では 自動で設定済み

確認:

systemctl list-timers | grep certbot

👉
cron を自分で書く必要はない


15. 実務でよくある落とし穴

❌ IPアドレスで HTTPS 化しようとする

  • 証明書はドメイン必須
  • IP直指定は不可

❌ 証明書をコンテナ内で管理する

  • 再起動で消える
  • 更新が壊れる

👉
証明書はホスト側で管理


❌ 自動更新を確認しない

  • 90日後に突然死
  • 実務事故あるある

16. この章のまとめ

  • HTTPS は必須
  • Let’s Encrypt は実務標準
  • Certbot で自動更新前提
  • nginx は HTTPS 終端として使う
  • 証明書はホスト管理が基本

おわりに

本連載では、AWS 初学者が

  • 理由を説明できる構成で
  • 小さく始めて
  • 実務につながる形で

Web アプリ実行環境を構築する流れを解説しました。

今回扱った構成は、
あくまで 学習・有志開発向けの最小構成 です。

実務ではここからさらに、

  • RDS による DB 分離
  • ALB による負荷分散
  • ECS / Fargate への移行
  • CI/CD の自動化
  • 監視・ログ基盤の整備

といったステップが続きます。

ただし、
今回の内容を理解していれば、
それらは「新しい知識」ではなく「拡張」として扱える
はずです。

まずはこの構成を
「自分で説明できる」「自分で再現できる」
状態になることを目標にしてみてください。

ここまで読んでいただき、ありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?