ngx_dynamic_upstreamでnginxのアップストリームを動的に変更する

  • 148
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

nginxのアップストリームを変更する

通常nginxでアップストリームの変更をするにはnginx.confを変更した後、nginxをreloadないしはrestartしなければなりません。例えば以下のようにアップストリームが定義されている場合、

upstream_backends.conf
upstream backends {
    server 127.0.0.1:6001;
    server 127.0.0.1:6002;
    server 127.0.0.1:6003;
}

このアップストリームから127.0.0.1:6003を外すには以下のように該当行にdownと書いてreloadします。(あるいはコメントアウトする)

upstream_backends.conf
upstream backends {
    server 127.0.0.1:6001;
    server 127.0.0.1:6002;
    server 127.0.0.1:6003 down;
}

あるいはアップストリームの各サーバのパラメータ(weightmax_failsfail_timeout)を変更するのにもreloadが必要です。(下記の各パラメータはデフォルト値)

upstream_backends.conf
upstream backends {
    server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10;
    server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10;
    server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10;
}

もっと言うとアップストリームにサーバを動的に追加することはできないので、サーバを追加するにはやっぱり手でnginx.confを修正した後にreloadが必要です。

upstream_backends.conf
upstream backends {
    server 127.0.0.1:6001;
    server 127.0.0.1:6002;
    server 127.0.0.1:6003;
    server 127.0.0.1:6004; # 追加したサーバ
}

という具合に通常nginxのアップストリームを変更するにはnginxのreloadrestartが必要です。
これで十分な場合も多いですが、一方でサービス要件によってはELBのようにアップストリームを動的に変更したいというニーズもあることでしょう。

nginxのアップストリームを動的に変更する

nginxはマルチプロセスアーキテクチャなのでnginx.confを変更せずにオンザフライでアップストリームを変更するには何らかの方法でアップストリームの情報を各ワーカプロセス間で共有する必要があります。

アップストリームの情報を各ワーカプロセス間で共有する方法には例えば以下のものが挙げられます。

  1. NGINX Plusを購入する
  2. memcached等の外部KVSでアップストリームを共有する
  3. ngx_luaやngx_mrubyで頑張る
  4. アップストリームを共有メモリ化する

NGINX Plusを購入する

有償版のnginxであるNGINX Plusにはngx_http_upstream_conf_moduleというアップストリームを動的に操作するためのHTTPインタフェースモジュールが付属しており、CLIで簡単にアップストリームを操作できます。

NGINX Plusについては2年近く前の記事ですが、@harukasanが軽くまとめていて参考になります。

NGINX Plusでできることまとめ

memcached等の外部KVSでアップストリームを共有する

そもそもnginxのプロセス内にアップストリームのデータを持つのではなくmemcached等の別サーバプロセスでアップストリームの情報を共有するという方法はどうでしょうか。しかし、この方法だとアップストリームの管理を自前で行わなければならないのでnginxが元々サポートしている以下の機能を利用するのが非常に難しくなります。(というか無理?)

  • アップストリーム内サーバへのロードバランスとそのアルゴリズム(e.g. least_conn、consistent-hashing)の利用
  • アップストリーム内サーバの重み付けや不調サーバのアップストリームからの取り外し

あと、そもそも外部KVSサーバといい感じに通信してロードバランスするモジュール的なものを書く必要があってなかなか大変です。

ngx_luaやngx_mrubyで頑張る

ngx_luaやngx_mrubyを利用するとnginx.confに特殊なロジックを埋め込んだり、Cではなくスクリプト形式でnginxのモジュールを書くことができます。外部KVSサーバと通信してアップストリームの情報をやりとりするのも幾分簡単になるでしょう。しかし、やはりアップストリームの管理を自前で行わなければならないので煩雑なことにかわりはありません。

アップストリームを共有メモリ化する

先月リリースされたnginx-1.9.0のCHANGESを見ると以下の一文があります。

 Feature: the "zone" directive inside the "upstream" block.

TCP Load Balancing機能が目立ってたのとあまりにもシレッと書かれていたので1.9.0がリリースされてから1週間ぐらい気付いてなかったのですが、NGINX PlusだけでなくOSS版でもアップストリームを共有メモリ化できるようになりました。(NGINX Plusのソースコードは公開されていないのであくまで推測になります)

こんな感じにupstreamコンテキスト内でzoneディレクティブを定義します。

upstream_backends.conf
upstream backends {
    zone zone_for_backends 128k;
    server 127.0.0.1:6001;
    server 127.0.0.1:6002;
    server 127.0.0.1:6003 down;
}

これでnginxのアップストリームを各ワーカプロセス間で共有できるようになります。

ngx_dynamic_upstreamでnginxのアップストリームを動的に変更する

前置きが長くなりました。さっきも言ったようにnginx-1.9.0からupstreamコンテキストでzoneディレクティブが利用可能になったのでnginxのアップストリームを共有メモリに格納してワーカプロセス間でアップストリームの情報を共有できるようになりました。しかし、OSS版ではそもそもnginxのアップストリームを動的に変更するためのモジュールがありません。

なので、HTTPインタフェースでアップストリームを動的に変更できるモジュールを書いてみました↓

https://github.com/cubicdaiya/ngx_dynamic_upstream

Quick Start

ngx_dynamic_upstreamdynamic_upstreamというただひとつのディレクティブを持ちます。このディレクティブはlocationコンテキストのみで利用可能です。

dynamic_upstream.conf
upstream backends {
    zone zone_for_backends 128k;
    server 127.0.0.1:6001;
    server 127.0.0.1:6002;
    server 127.0.0.1:6003;
}

server {
    listen 6000;

    location /dynamic {
        allow 127.0.0.1;
        deny all;
        dynamic_upstream;
    }

    location / {
        proxy_pass http://backends;
    }
}

ngx_dynamic_upstream自体はアクセス制御機能を持たないのでallowdenyでIPアドレス制限等をするのを忘れずに。

アップストリームの一覧を取得する

upstreamパラメータにzone名を指定するとアップストリームの一覧を返します。

$ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends"
server 127.0.0.1:6001;
server 127.0.0.1:6002;
server 127.0.0.1:6003;
$

アップストリームからのサーバの一時的な取り外し/復帰

downserverパラメータを組み合わせることでアップストリームから特定のサーバを一時的に取り外し可能です。

$ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&down="
server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10;
server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10;
server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10 down;
$

取り外したサーバを復帰させるにはupパラメータを指定します。

$ curl "http://127.0.0.1:6000/dynamic?upstream=zone_for_backends&server=127.0.0.1:6003&up="
server 127.0.0.1:6001 weight=1 max_fails=1 fail_timeout=10;
server 127.0.0.1:6002 weight=1 max_fails=1 fail_timeout=10;
server 127.0.0.1:6003 weight=1 max_fails=1 fail_timeout=10;
$

まとめ

nginxのアップストリームを動的に変更する方法と戦略について解説しました。ngx_dynamic_upstreamはサーバの動的な追加や削除の機能もありますが、updownだけでもかなり便利なのではないかと思います。

なお、GWの暇な時に作ったばかりなので、

This module is significantly experimental and still under early develpment phase.

という感じでまだ絶賛開発中です。追記:2015/06/09に「production ready」になりました。