この記事は、アイスタイル アドベントカレンダー2025 1日目の記事です。
はじめに
こんにちは、@cosme の開発をしている kudoma です!
本記事では、nginxの標準モジュールのみでカナリアリリースを実現できるか?というテーマについて、「こうすれば実現できるのでは?」という検討内容をまとめています。
執筆時点(2025/12/1)でnginx1.28未満はセキュリティサポートが終了しています。
可能であれば1.28以上へのアップデートを推奨します。
https://endoflife.date/nginx
また、構成比較のために1.28未満のパターンについても触れています。(※非サポート環境の利用を推奨する意図はありません)
本記事は「こうすれば実現できるのでは?」という検討ベースの内容です。
商用利用する場合は、機能・性能の両面で事前検証をお願いします。
カナリアリリースとは
カナリアリリースとは新バージョンのアプリケーションをリリースする際に、従来バージョンのアプリケーションを並行稼働させ、一部のユーザーだけを新バージョンにアクセスさせる展開(デプロイ)手法のこと。
最初は5%や10%といった少ない割合のリクエストだけを新バージョンのアプリケーションに振り分け、問題が起きないことを確認しながら段階的に割合を増やしていく。
カナリアリリースに必要な要件
nginxでカナリアリリースを行うには、次の2つが必要です。
- weight制御(例: 既存環境95%, 新規環境5%のような割合制御)
- 動的名前解決(例: クラウドLBなど、バックエンドのIP変化に追従するため)
各要件の実現方法を整理します。
各要件の実現方法
1. weight制御
upstreamでserverにweightを設定する。
upstream app {
server backend1.example.com weight=1;
server backend2.example.com weight=1;
}
2. 動的名前解決
nginx1.27.3以上
1.27.3にupstreamでserverにresolveを設定できるようになりました。
Changes with nginx 1.27.3
*) Feature: the "server" directive in the "upstream" block supports the "resolve" parameter.
upstream app {
zone upstream_dynamic 64k;
server backend1.example.com resolve;
server backend2.example.com resolve;
}
1.27.3未満
upstreamでserverにresolveを設定できないので、serverでドメインを変数にセットする。
server {
set $backend "backend1.example.com";
location / {
proxy_pass http://$backend;
}
}
カナリアリリース実現方法
nginx1.27.3以上
upstreamのみでweight制御と動的名前解決を両立できそうです。
http {
server {
location / {
proxy_pass http://app;
}
}
upstream app {
zone upstream_dynamic 64k;
## weight制御 + 動的名前解決
server backend1.example.com resolve weight=1;
server backend2.example.com resolve weight=1;
}
}
1.27.3未満
「upstreamでweight制御」+「serverでドメインを変数にセットして動的名前解決」は、別のコンテキストで動作するため、単純には組み合わせることができません。
そこで、両者を繋ぐための中継として、UNIXドメインソケットを挟むことで、weight制御と動的名前解決を両立できそうです。
UNIXドメインソケットとは、同一マシン内のプロセス間通信を行うための仕組みです。
本構成では、UNIXドメインソケットをnginx自身にリクエストを投げ直すための中継点として利用しています。
これにより、「upstreamでweight制御」と「serverで動的名前解決」という別コンテキストの処理を分離して接続しています。
クライアントからバックエンドへの流れは以下の通りです。
[client]
↓
[nginx (app upstream) ]
↓ weight制御
[nginx (UNIXドメインソケット server) ]
↓ 動的名前解決(ドメインを変数にセットし、proxy_pass)
[backend1, backend2]
実装例は以下の通りです。
http {
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
server {
location / {
proxy_pass http://app;
}
}
server {
listen unix:/var/run/nginx_backend1.sock;
access_log off;
# 動的名前解決
set $backend backend1.example.com;
location / {
proxy_pass http://$backend;
}
}
server {
listen unix:/var/run/nginx_backend2.sock;
access_log off;
# 動的名前解決
set $backend backend2.example.com;
location / {
proxy_pass http://$backend;
}
}
upstream app {
# weight制御
server unix:/var/run/nginx_backend1.sock weight=1;
server unix:/var/run/nginx_backend2.sock weight=1;
}
}
検証結果は以下の通りです。
- weight制御: OK
- 動的名前解決: OK
- アクセスログの二重出力: なし
- サーバー負荷: プロセス間処理の増加でコンテキストスイッチが約2倍、CPU使用率が増加
nginxを複数VMで稼働させている場合
nginxを複数台稼働させている場合、VM台数そのものをweightの代替として扱う方法でも、weight制御と動的名前解決を両立できそうです。
これだとnginxは動的名前解決だけ実現すればいいので、UNIXドメインソケットを中継として噛ませる必要がなくなります。
例: 既存環境75% / 新規環境25%で振り分けたい(既存環境に転送するVM3台 / 新規環境に転送するVM1台)
[client]
│
▼
[ LB ]
│
├───▶ [VM1 nginx] → 既存環境
├───▶ [VM2 nginx] → 既存環境
├───▶ [VM3 nginx] → 既存環境
└───▶ [VM4 nginx] → 新規環境
ただし、以下の理由から用途は限定されそうです。
- カナリアリリース中に他の修正が入らないように調整が必要
- 細かい割合制御には向かない(台数が必要)
まとめ
- nginx1.27.3以上: upstreamの
resolve + weightでシンプルに実現できそう - nginx1.27.3未満: UNIXドメインソケットを噛ませることで実現できそう
- nginxを複数VMで稼働させている場合: VM台数そのものをweightの代替として扱うことで実現できそう
検討ベースの記事ですが、少しでも楽しんでいただけていれば幸いです。
最後まで読んでいただき、ありがとうございました!
引き続き アイスタイル アドベントカレンダー2025 をお楽しみください。