2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Nginx を TCP ロードバランサーとして稼働させてカナリアリリースを実現する

Last updated at Posted at 2021-07-29

はじめに

サービス利用者への影響を最小限にすることを目的に、新しいバージョンを段階的にサービスインさせるカナリアリリース(カナリアデプロイメントと呼ばれることもある)というデプロイ戦略をご存知の方は多いかと思います。サービス開発において、アプリケーションの冗長化や負荷分散のために上位にロードバランサーを配置することは一般的なので、ロードバランサーで重み付けによるトラフィック振り分けが可能であれば、それだけでカナリアリリースを実現できます。しかし、ロードバランサーの機能不足や、ロードバランサー運用者とサービス開発者が異なることによる連携負荷など、様々な事情によりそれが難しいケースも存在します。

このような背景を受けて、この記事ではロードバランサーとアプリケーションの間に TCP ロードバランサーとして機能する Nginx を配置してカナリアリリースを実現する方法を紹介します。

TCP/UDP Proxy Module

Nginx を TCP ロードバランサーとして稼働させるためには、TCP/UDP Proxy Module を使用する必要があります。しかし、公式から提供されている Nginx バイナリには TCP/UDP Proxy Module が組み込まれていないため、Building NGINX From Source を参考にソースコードから自前でビルドする必要があります。

ビルド手順

公式サイトからソースコードをダウンロードします。

cd /usr/local/src
sudo wget https://nginx.org/download/nginx-1.21.0.tar.gz
sudo tar -xf nginx-1.21.0.tar.gz
cd nginx-1.21.0

ビルドに必要なライブラリをインストールします。

sudo yum install gcc
sudo yum install pcre-devel
sudo yum install openssl-devel

Nginx 実行用のシステムグループとシステムユーザーを作成します。

sudo groupadd --system nginx
sudo useradd --system --shell /usr/sbin/nologin --gid nginx nginx

必要なオプションを付与して Nginx バイナリをビルドします。

sudo ./configure \
    --sbin-path=/usr/sbin/nginx \
    --conf-path=/etc/nginx/nginx.conf \
    --pid-path=/var/run/nginx.pid \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --user=nginx \
    --group=nginx \
    --with-stream

sudo make
sudo make install

Nginx を TCP ロードバランサーとして使用する際に明示的に組み込む必要がある Module は以下のようになっていますが、今回は必要最低限として TCP/UDP Proxy Module を組み込むオプションのみを指定しています。

$ ./configure --help | grep with-stream
  --with-stream                      enable TCP/UDP proxy module
  --with-stream=dynamic              enable dynamic TCP/UDP proxy module
  --with-stream_ssl_module           enable ngx_stream_ssl_module
  --with-stream_realip_module        enable ngx_stream_realip_module
  --with-stream_geoip_module         enable ngx_stream_geoip_module
  --with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module
  --with-stream_ssl_preread_module   enable ngx_stream_ssl_preread_module

NGINX systemd service file を参考に systemd の Unit ファイルも自前で用意します。

cat << EOF | sudo tee /lib/systemd/system/nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT \$MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

Split Clients Module

Nginx でカナリアリリースをするためには Split Clients Module を使用する必要があります。こちらは TCP/UDP Proxy Module とは異なりデフォルトで Nginx バイナリに組み込まれているため、自前でのビルドは不要となります。

Split Clients Module は、MurmurHash2 を使用して第一引数で指定された変数(Core Module でサポートされている変数は こちら)のハッシュ値を求めて、その値から割合に応じた値を返却する仕様のため、クライアントの IP アドレスが格納される $remote_addr を指定するのが良いかと思います。

split_clients "${remote_addr}" $variant {
    10%    one;
    20%    two;
    *      other;
}

蛇足ですが、以下の記事で Split Clients Module は map ディレクティブの応用であることが知れて勉強になりました。

設定ファイル例

以下は、version1 グループ(192.168.100.10, 192.168.100.11, 192.168.100.12)にトラフィックの30%を、version2 グループ(192.168.100.20, 192.168.100.21, 192.168.100.22)にトラフィックの70%を振り分ける設定となっています。

/etc/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;
}

stream {
    log_format basic '[$time_local] $remote_addr:$remote_port -> $upstream_addr '
                 '$protocol $status $bytes_sent $bytes_received '
                 '$session_time';

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

    split_clients $remote_addr:$remote_port $upstream {
        30% version1;
        *  version2;
    }

    upstream version1 {
        server 192.168.100.10:8080;
        server 192.168.100.11:8080;
        server 192.168.100.12:8080;
    }
    upstream version2 {
        server 192.168.100.20:8080;
        server 192.168.100.21:8080;
        server 192.168.100.22:8080;
    }
    server {
        listen 8080;
        proxy_pass $upstream;
    }
}

なお、ログの設定は ngx_stream_log_module Module の例を参考にしています。ログフォーマットとして使用できる変数は以下で定義されているので、要件によって設計するのが良いかと思います。

設定ファイルが用意できたら、以下のコマンドで Nginx を起動します。

sudo systemctl daemon-reload
sudo systemctl enable nginx
sudo systemctl start nginx

あとは、上位のロードバランサーから Nginx にトラフィックを流せば完成です。カナリアリリースを開始した後は、それぞれのバージョンのアプリケーションメトリクスや、サービス利用者への影響を確認しながら、割合を変更していくのが良いかと思います。

さいごに

今回は Nginx を TCP ロードバランサーとして稼働させてカナリアリリースを実現する方法を紹介しました。ニッチなナレッジかもしれませんが、どなたかの参考になれば幸いです。

参考資料

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?