CentOS Stream 9におけるCaddy Web Serverの導入と設定ガイド:SSL/TLSの自動化とパフォーマンス最適化

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

深夜のトラブル:SSL更新の悪夢と手動設定の課題

半年前、弊社の決済システムで苦いトラブルが発生しました。土曜日の深夜23時、APIサブドメインのSSL証明書が突如期限切れになったのです。決済処理は停止し、顧客からのエラー報告が殺到しました。オンコール担当チームが徹夜する羽目になった原因は、cronジョブの権限設定ミスによってcertbot renewコマンドがハングアップしていたことでした。

当時、チームでは約15個のGolangおよびNode.js製マイクロサービスを処理するため、CentOS Stream 9を搭載した新規ノードクラスタを構築したばかりでした。インフラをスケールさせるにつれ、使い慣れていた「Nginx + Certbot」構成の運用上のボトルネックが次々と浮き彫りになってきました。

  • Nginxの設定ファイルが極めて冗長。HTTPからHTTPSへのリダイレクト、証明書・秘密鍵の指定、TLSバージョンの調整だけでも、1ドメインあたり30〜40行を費やしてしまう。
  • Certbotが独立したsystemdタイマーで動作している。証明書が更新されるたびにNginxをリロードする必要があり、リロード処理がスタックするとサイレント障害(silent fail)に陥りやすい。
  • CentOS Stream 9のNginxでHTTP/3(QUIC)を有効化するハードルが非常に高い。標準リポジトリのパッケージでは完全サポートされておらず、ソースコードから手動コンパイルして依存関係を自前で管理する必要があった。

本質を見直す:なぜNginxやApacheは運用コストがかさむのか?

NginxやApache自体が劣っているわけではありません。これらは現在も圧倒的なパフォーマンスを誇る確固たる存在です。しかし、これらのWebサーバーのアーキテクチャは、HTTPSが今のようにデフォルト標準ではなく、単なるオプション機能だった時代に設計されたものです。

従来のモデルには、主に3つの構造的な課題があります。

  1. 証明書管理の分離: Webサーバーはルーティングのみを担い、証明書の発行や更新はサードパーティ製ツール(Certbotやacme.sh)に依存している。両プロセスはフックスクリプトやストレージ上の静的ファイルを介してのみ連携するため、権限設定がわずかに狂っただけでシステム全体が破綻してしまう。
  2. セキュリティ強化の複雑さ: NginxでQualys SSL Labsの「A+」評価を獲得するには、適切な暗号スイート(cipher suites)の選定、HSTSの有効化、OCSP Staplingの設定などをすべて手動で組み込む必要がある。
  3. リバースプロキシ設定の煩雑さ: ポート3000や8080のバックエンドにトラフィックを転送する際、NginxではX-Real-IP、X-Forwarded-For、X-Forwarded-Proto、Upgrade、Connectionといった一連のヘッダーを手動で明示的に記述しなければならない。

Webサーバー自動化における3つのアプローチ

CentOS Stream 9におけるこの課題に対し、私たちは3つの選択肢を検討しました。

  • アプローチ1:Nginx + acme.shを自動化するAnsible Playbookを自作する。 慣れ親しんだNginxを維持できる反面、マイクロサービスの増加に伴って保守スクリプトが肥大化し続けるという懸念がありました。
  • アプローチ2:インフラ全体をCloudflare SSLの背後に配置する。 設定が迅速でサーバー負荷も軽減されます。ただし、CloudflareからオリジンサーバーへのトラフィックはFull (Strict)基準で暗号化する必要があるため、内部サーバー側の証明書管理からは逃れられません。
  • アプローチ3:Caddy Web Server(Caddy 2)へ移行する。 Go言語で開発されたCaddyは、プロセスの中核にACMEエンジンを標準内蔵しています。追加モジュールを設定することなく、デフォルトでHTTPS、HTTP/2、HTTP/3が自動有効化されます。

CentOS Stream 9へのCaddy Web Server導入手順

Caddyを1日あたり約80万リクエストを処理する本番環境に導入して6ヶ月が経過しましたが、システムは極めて安定しており、SSL関連のトラブルは一度も発生していません。以下に、実践に基づく具体的な導入手順を解説します。

ステップ1:COPRリポジトリからCaddyをインストール

CentOS Stream 9向けには、CaddyコミュニティがFedora COPRを通じて公式RPMパッケージをメンテしています。dnfコマンドを用いて迅速にインストールできます。

# dnf-plugins-coreプラグインのインストール
sudo dnf install -y 'dnf-command(copr)'

# 公式Caddyリポジトリの有効化
sudo dnf copr enable -y @caddy/caddy

# Caddyのインストール
sudo dnf install -y caddy

# インストール後のバージョン確認
caddy version

ステップ2:ファイアウォールのポート開放

Caddyでは、ACMEチャレンジ認証用のポート80(HTTP)と、通常の通信用のポート443(HTTPS)を開放する必要があります。また、CaddyはUDP経由のHTTP/3を標準サポートしているため、ポート443についてはTCPとUDPの両方を開放します。

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=443/udp
sudo firewall-cmd --reload

ステップ3:シンプルなCaddyfileの作成

メイン設定ファイルである/etc/caddy/Caddyfileを開きます。

sudo nano /etc/caddy/Caddyfile

デフォルトの内容を削除し、以下のリバースプロキシ設定に置き換えます。

api.itfromzero.vn {
    # データ圧縮を有効化して転送容量を削減
    encode gzip zstd

    # バックエンドのポート3000へリクエストを転送
    # Caddyが自動的にX-Forwarded-For、X-Forwarded-Proto、Hostヘッダーを付与
    reverse_proxy 127.0.0.1:3000

    # Grafana LokiやELKへ連携しやすいJSON形式でアクセスログを出力
    log {
        output file /var/log/caddy/api_access.log {
            roll_size 50mb
            roll_keep 5
        }
        format json
    }
}

最大のメリットは、ドメインapi.itfromzero.vnのAレコードをサーバーのIPアドレスに向けるだけで済む点です。Caddyが起動すると、HTTP-01またはTLS-ALPN-01チャレンジを介してLet’s EncryptやZeroSSLに自動で証明書発行をリクエストします。証明書は即座にプロビジョニングされ、有効期限が残り30日になると自動更新されます。証明書のパス設定やcronジョブの登録は一切不要です。

ステップ4:リバースプロキシ用SELinuxの設定

CentOS Stream 9では、デフォルトでSELinuxがEnforcingモードで動作しています。適切なパーミッションを付与しない限り、SELinuxはWebサーバーからローカルのアプリケーションポートへの接続をブロックし、502 Bad Gatewayエラーを引き起こします。以下のコマンド1行で解決できます。

# Webサーバープロセスが内部ネットワークおよびバックエンドポートに接続することを許可
sudo setsebool -P httpd_can_network_connect 1

ステップ5:サービスの起動と動作確認

パッケージにはsystemdユニットファイルがあらかじめ含まれています。起動前に、設定ファイルの記述に問題がないか検証します。

# Caddyfileの構文チェック
caddy validate --config /etc/caddy/Caddyfile

# Caddyの起動と自動起動の有効化
sudo systemctl enable --now caddy

# プロセスのステータス確認
sudo systemctl status caddy

ドメインの変更や新規ルートを追加する際は、既存の接続を切断してしまうrestartコマンドは避けてください。Caddyはダウンタイムなしのシームレスなリロードをサポートしています。

sudo systemctl reload caddy

本番運用6ヶ月後の実環境レビュー

リソース消費の面では、CaddyはNginxに比べてわずかにメモリ消費量が多くなります。アイドル状態でのメモリ使用量はNginxが約15MBであるのに対し、Caddyは約32MBでした。ピーク時(2,500リクエスト/秒)の負荷でも、2コア/4GB RAMのVPS環境下で、Caddyの消費は約68MBのメモリとvCPU 1コアの12%程度に収まりました。この差異は本番運用の許容範囲内に十分収まるレベルです。

それ以上に、チームが得られたメリットは絶大でした。

  • SSLトラブルの完全撲滅: 発行と更新がバックグラウンドで自律的に行われるため、権限エラーやcronのスタックによる障害が一切発生しなくなりました。
  • デプロイ時間の劇的短縮: 新しいマイクロサービス向けの設定作業が、従来ディレクティブを1行ずつ調整して20分かかっていたところ、わずか3分未満で完了するようになりました。
  • ユーザー体感レイテンシの改善: HTTP/3が標準で動作するため、電波状況が不安定なモバイルデバイス環境において、ハンドシェイクにかかる時間が120〜180ms短縮されました。

CentOS Stream 9やその他のRHEL系ディストリビューションで新規サーバーを立ち上げる予定があるなら、従来のNginxに代わる有力な選択肢としてCaddyの導入を検討する価値は十分にあります。

Share: