IDCFクラウド DNS冗長化 nginx編

中外の出し分けをするbindをどのように冗長化させるかという話。LVSを使うと接続ソースが全てLVSサーバになってしまうため、振り分けも2系統、bind側のIPアドレスも2系統にしてmatch-destinationsで出し分けるしかなかった。もちろんこれで期待した動作は実現できるんだけど構造が複雑なのが不満。もっとシンプルな構成で実現できないかを模索する中で、改めてnginxによるtcp/udp proxyを検討してみる事にした。

振り分けにLVSを採用した理由をまとめてみると、まずはヘルスチェックをカスタマイズできる事。次にNATで振り分けられるため、ロードバランサーとその先のサーバの共存が可能なこと。つまりLVSサーバ上にDNSサーバをそのまま乗せる事ができる。これはノード数を少しでもケチりたい自分の環境においては非常にありがたかった。また、IDCFクラウド上で利用している仮想ブリッジが不安定なこともあって、幾つかの機能をコンテナではなくサーバ上で共存させたいというモチベーションと、シンプルな運用のために複数IPやポートずらしみたいなこともできる限り避けたい、というこだわりもあった。

しかし、この構造すら複雑に感じていた。そこでnginxの利用を考えたのだが、切り替えてみたい理由は幾つかあった。まずLVSだとL7振り分けが出来ず、それを考慮すると仮想ルータ/LVS > nginx(L7振り分け) > LVS(ラウンドロビン)のような形に。LVSのヘルスチェックを活かそうとすると上記のような多段構成になってしまう。nginxであればL7振り分けからラウンドロビンまで1つでこなしてしまうので構成をシンプルに出来る。また、nginxはリクエスト時に問題があればプールから外して別のノードへ再試行を行う。障害のほぼない自分の環境なら、この方法で充分と思うようになっていた。

そして一番の期待がproxy_protocolの機能。nginxはreverse proxyの際に後段のサーバに元々の接続ソースIPを引き渡す事ができるが、tcp proxyでそれを実現するのがproxy_protocolの機能。これがDNSでも使えれば、match-destinationsによる出し分けから、当初のmatch-clientsによる出し分けに戻せるのではないか、と期待していた。早速、tcp/udp proxyに対応したnginxを用意する。nginxのリポジトリにある最新のnginxは1.10だった。これをインストールしてコンパイルオプションを確認する。

nginx -V
nginx version: nginx/1.10.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_perl_module=dynamic --add-dynamic-module=njs-1c50334fbea6/nginx --with-threads --with-stream --with-stream_ssl_module --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic'

長いので探しづらいが、デフォルトでwith-streamオプションに対応しているので、このままでtcp/udp proxyもいけそう。まずは復習を兼ねてreverse proxyでの振り分けから。この機能は以前からあるものなので問題はないだろうけど。

vi /etc/nginx/conf.d/test.conf
cat /etc/nginx/conf.d/test.conf
upstream test {
    server test1.domain.com;
    server test2.domain.com;
}

server {
    listen 80;
    server_name test.domain.com;
    location / {
        proxy_pass http://test;
    }
}
systemctl restart nginx
curl -H 'Host:test.domain.com' http://localhost/

やっぱり問題なし。続いて、tcp proxyを試してみる。自分の環境だとmysql辺りで試すのがお手頃。httpディレクティブではなく、streamディレクティブに設定を記述する必要があるので、根元のnginx.confから修正する。

vi /etc/nginx/nginx.conf
cat /etc/nginx/nginx.conf
    :
stream {
    include /etc/nginx/stream-conf.d/*.conf;
}
mkdir /etc/nginx/stream-conf.d/
vi /etc/nginx/stream-conf.d/mysql.conf
cat /etc/nginx/stream-conf.d/mysql.conf
upstream mysql {
    server db1.domain.com:3306;
    server db2.domain.com:3306;
}

server {
    listen 3306;
    proxy_pass mysql;
}
systemctl restart nginx
mysql -udbuser -p -hlocalhost

この設定でどちらのサーバのmysqlにも接続する事が出来た。mysql側に接続を許可する設定も入れる必要があるので注意。tcp proxyも問題なかったので、いよいよudp proxyを試してみる。

vi /etc/nginx/stream-conf.d/bind.conf
cat /etc/nginx/stream-conf.d/bind.conf
upstream dns {
    server ns1.domain.com:53;
    server ns2.domain.com:53;
}

server {
    listen 53;
    listen 53 udp;
    proxy_pass dns;
}
systemctl restart nginx
nslookup test.domain.com localhost

これも問題なく動作せる事が出来た。そして期待のproxy_protocolの設定を加える。streamディレクティブ内か、serverディレクティブの中に『proxy_protocol on;』を追記する。どきどきしながら試してみたが、結果は残念ながら失敗。bindへの接続ソースはnginxサーバのままだった。よくよくproxy_protocolの情報を探ってみるとどうもtcpのみの対応っぽい。諦めつつも一応mysqlで動かしてみるが、こちらも動作せず。udpで使えないならどうせ意味はないので追及はせず。

nginxによるtcp proxyは、他にもweightの設定やhealtchcheckのカスタマイズなど、かなり使い勝手がよさそう。単純なラウンドロビンで振り分ければいい部分はnginxを使っていこうという方針に変更。ただし、bindの冗長化に関しては仮に前段をnginxに変更しても構造上のメリットはない。bindの冗長化については、また別の方法を探す事にする。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)