独立站のコンテナ化:Docker設定、イメージ最適化、オーケストレーションとリソース制限
一、独立站はなぜサーバーに直接インストールせずコンテナ化するのか
VPSにPHP、Nginx、MySQLを直接インストールしても動きますが、半年後に3つの問題が噴出します。第一に、新しい担当者が環境構築に2日かかる。第二に、Alibaba CloudからTencent Cloudへ移行する際に依存関係を再インストールする。第三に、セキュリティパッチ適用時に本番バージョンを触れない。Dockerはアプリと依存関係を不変のイメージとしてパッケージ化し、この3つを解決します。
判断基準が一つあります。デプロイ手順書に「まずapt install php8.3-fpm、次にこの12個の拡張をインストール、その後php.iniの8項目を変更」と書いてあれば、コンテナ化が必要です。この12個の拡張と8項目の設定をDockerfileに書けば、以後どのマシンでもdocker runを一度実行するだけで再現できます。CDNと静的リソースの連携はCDNグローバル高速化。
| 実装方法 | 環境再現コスト | サーバー移行 | ロールバック速度 | 適した規模 |
|---|---|---|---|---|
| VPSに直接インストール | 2人日 | 依存関係を再インストール | スナップショットで30分 | プロトタイプ期 |
| Shellスクリプトで環境構築 | 0.5人日 | スクリプト実行、差異は残る | 15分 | 単一サイト運用1年 |
| Dockerイメージ | 5分 | イメージを取得すれば即起動 | イメージタグを切り替え、3分 | 本番サイト、マルチ環境 |
| Docker Composeオーケストレーション | 5分 | yamlをコピーすれば即起動 | ワンクリックロールバック | アプリ+データベース+キュー |
二、Dockerfileをどう書けば肥大化しないか
独立站で最も多いDockerfileの誤りはシングルステージビルドです。FROM ubuntuでPHPをインストール、Composerをインストール、composer install、ソースコードをCOPY、ポートを公開。この方法で作ったイメージは通常1GB以上になります。ビルドツール、npmキャッシュ、.gitディレクトリが含まれるからです。正しい方法はマルチステージビルドです。第一段階でbuilderイメージを使いフロントエンドをビルドしcomposer依存関係をインストール、第二段階でビルド成果物とランタイムだけをalpineイメージにコピーします。
いくつかの鉄則:ベースイメージはubuntuではなくalpineを選ぶ。.dockerignoreではnode_modules、.git、.env、アップロードディレクトリを必ず除外する。npm/composer install時は–no-devと–no-cacheを付ける。実行時は非rootユーザーを使う。イメージサイズの目標:純粋なPHP/Nodeアプリは200MB以内、静的アセットを含む場合は400MB以内。イメージスキャンとセキュリティベースラインは次を参照サイトのバックアップと復元演習。
ある実例:とあるチームがリリースから半年後、イメージが1.4GBになっていることに気づき、docker historyでレイヤーごとに調べたところ、composer installに–no-devを付け忘れてphpunitや開発依存を大量に本番イメージへ含めてしまっていた。さらにCOPY . .で.gitディレクトリまでコピーされていた。マルチステージビルドに変更し、builderステージで依存をインストール、実行ステージではvendorディレクトリとpublicディレクトリだけをコピーするようにしたところ、イメージは180MBまで削減され、デプロイ時間も3分から40秒に短縮された。
| 階層 | よくある誤った書き方 | 最適化の書き方 | サイズ削減 |
|---|---|---|---|
| ベースイメージ | FROM ubuntu:22.04 | FROM php:8.3-fpm-alpine | 約400MB |
| システム依存 | apt-get install build-essential | マルチステージビルド、builderステージでインストール | 約200MB |
| PHP依存 | composer install | composer install –no-dev –no-cache | 約80MB |
| フロントエンド依存 | COPY . . | builderステージでnpm ci && npm run build | 約300MB |
| ソースディレクトリ | COPY . /var/www | COPY –from=builder /app/public ./public | 約100MB |
| 実行ユーザー | rootで直接実行 | RUN adduser -D app | セキュリティ上の利点、サイズ増なし |
三、docker composeで独立サイトをどう構成するか
独立サイトの典型的な構成は4つのコンテナ:Nginxリバースプロキシ、PHP-FPMまたはNodeアプリ、MySQL/PostgreSQL、Redis。1つのdocker-compose.ymlでこの4サービスをつなぎ、データボリュームをマウントしてMySQLデータを永続化し、ポートはNginxの80と443だけを公開、他のサービスは内部ネットワーク経由にする。MySQLのポートをインターネットに公開してはいけない。
実行可能な設定の抜粋:Nginxはphpにdepends_on、phpはdbとredisにdepends_on。ヘルスチェックはコマンドcurl -fsS http://localhost/healthを30秒ごとに実行。再起動ポリシーは一律unless-stopped。バックアップ戦略はMySQLデータボリュームを定期的にホストのバックアップディレクトリへマウントし、rsyncで遠隔地へ同期する。マルチコンテナ構成の監視については次を参照サイト監視とアラート。
シナリオ例:4コア8GのAlibaba Cloud軽量アプリケーションサーバー1台で、WordPressとMySQLとRedisの3コンテナを動かす。MySQLコンテナはnamed volumeをマウントしてデータを保存し、Nginxは80と443をホストにマッピング、WordPressコンテナはポートを公開せず内部ネットワークのみを通す。ホストを再起動してもdocker compose up -dで3サービスが自動的に起動し、その間ログインして手動で何かを起動する必要はない。これがcomposeの最も素朴な価値だ。
| サービス | ベースイメージ | CPU制限 | メモリ制限 | 永続化の有無 |
|---|---|---|---|---|
| nginx | nginx:1.27-alpine | 0.5コア | 128MB | 不要 |
| php-fpm / node | php:8.3-fpm-alpine | 1〜2コア | 512MB至1GB | なし(アップロードディレクトリはボリュームをマウント) |
| mysql | mysql:8.4 | 1〜2コア | 1至2GB | あり(dataディレクトリ) |
| redis | redis:7-alpine | 0.5コア | 256MB | あり(AOF永続化) |
| cron/キュー | アプリと同じイメージ | 0.5コア | 256MB | 不要 |
四、リソース制限:制限のないコンテナはマシン全体を巻き込んで落とす
Dockerを手軽に使いたいあまり、起動時に–cpusや–memoryを書かない人が多い。その結果、PHP-FPMが一度遅いクエリに当たるとメモリが512MBから4GBまで膨らみ、同じマシンのMySQLをOOMに追い込む。すべてのコンテナでリソース上限を明示する。これは必須だ。
独立サイトのリソース割り当ての目安:アプリコンテナはCPU1〜2コア、メモリ512MB〜1GB。MySQLコンテナはデータ量に応じて、10GB以下なら1コア1GBで十分。NginxとRedisはそれぞれ0.5コア、128〜256MB。ホストマシンにはDocker daemonとシステム用に20%を残す。判断基準は一つ:ホストの総メモリの80%をコンテナに、20%をシステムに割り当て、この比率を超えたらマシンを増やす。CPUは平均ではなくピークで割り当てる。独立サイトのトラフィックは平日のピークと夜間の谷があるため、平均で見積もるとピーク時に待ち行列ができる。
| 独立サイトの規模 | 推奨構成 | CPU割り当て | メモリ割り当て | 月間訪問数の目安 |
|---|---|---|---|---|
| 立ち上げ検証期 | 2コア2GB VPS 1台 | アプリ1コア+DB 0.5コア | アプリ512MB+DB 1GB | 5万未満 |
| 安定成長期 | 4コア8GB VPS 1台 | アプリ2コア+DB 1.5コア | アプリ2GB+DB 4GB | 5万〜30万 |
| 多言語のピーク期 | 4コア8GB 2台 | アプリ各2コア | 各2GB、DBは別途4GB | 30万〜100万 |
| 独立記念日のピーク | アプリを3〜4台に横展開 | 各2コア | 各2GB | セール・展示会のトラフィック |
五、いつSwarmやK8sに移行すべきか
直感に反する結論だが、独立サイトの90%の場面でK8sは不要だ。docker composeは1台のVPSで4〜8個のコンテナを動かせ、日次訪問10万以内のB2Bサイトなら十分支えられる。次の3つの兆候のどれかが出たときだけ、Swarmへの移行を検討する:アプリを2台以上のVPSにまたがって動かす必要がある、ブルーグリーン配備で自動的にトラフィックを切り替えたい、ヘルスチェックで落ちたコンテナを自動再起動したい。
判断基準は簡単だ。docker-compose.ymlのサービスが30を超える、あるいは5人以上のチームが同時にこの環境を保守するなら、オーケストレーションを検討する。そうでなければcomposeが最適解であり、K8sの保守に費やす労力はコンテンツとSEOに回したほうがリターンが大きい。
K8sの壁は、etcd、ネットワークプラグイン、Ingressコントローラを専任で保守する人が必要になり、トラブル対応だけで週に1日を奪われることだ。小規模チームなら、その時間をコンテンツとSEOに回したほうがリターンは大きい。将来本当にマルチインスタンス配備が必要になったら、ロードバランシングの設計はこちらを参照。ロードバランシングとマルチインスタンス配備。
| オーケストレーションツール | 学習コスト | 維持コスト | 対象ノード数 | 選ぶ条件 |
|---|---|---|---|---|
| docker compose | 1〜2日 | 極めて低い | 1台 | 独立サイトの90%はこれ |
| docker swarm | 3〜5日 | 低 | 2〜10台 | マシンをまたぐ編成とローリング更新が必要 |
| K8s | 2〜4週間 | 高 | 5台以上 | 複数チーム、複数サービス、自動スケーリング |
| クラウド事業者のマネージドK8s | 1〜2週間 | 中 | 10台以上 | 専任のDevOpsがいる |
FAQ よくある質問
独立サイトでDockerを使うと、サーバーに直接入れるより遅くなるのか?
イメージのサイズはどのくらいが適正ですか?
docker composeで日間10万アクセスに耐えられますか?
コンテナ内のログとアップロードファイルはどう扱いますか?
いつK8sを使い、いつcomposeを使い続けるべきですか?
alpineイメージは依存関係が足りず動かないことはありますか?
最後に
コンテナ化が独立系サイトにもたらす価値は、80%が「環境の一貫性」、20%が「オーケストレーション能力」です。まずDockerfileを正しく書き、イメージを200MBまで抑え、composeファイルで4つのサービスを適切に管理すれば、独立系サイトの技術スタックで最も痛い環境ドリフトの問題は解決します。最初からK8sを追いかけてはいけません。それはまた別のフルタイムの仕事です。
導入ロードマップ:1週目はマルチステージDockerfileを書いて、イメージを1GBから200MBまで削減。2週目はcomposeでNginx+アプリ+MySQL+Redisを構成し、データボリュームをしっかりマウント。3週目はCI/CDパイプラインをイメージレジストリに接続し、コードをプッシュすれば自動でイメージがビルドされる仕組みに。この3ステップで、サーバー移行は2日から2時間に短縮できます。CDNや監視との連携はCDNグローバル高速化をご覧ください。日々の運用では、週に一度docker system dfでダングリングイメージを確認・削除すると、不要なイメージにディスクをじわじわ圧迫されるのを防げます。
覚えておきたい一言:コンテナ化の第一歩はK8s導入ではなく、どんなクリーンなサーバーでもコマンド1つでサイトを起動できるようにすることです。
