nginxでセッション維持するロードバランサを構築
はじめに
よくあるWEBサーバ+DBサーバを使用した、とあるサービスを作る事になった
そのサービスをリリース後は結構な利用人数になりそうだった
なので、負荷分散ができる環境に前もって準備をしておこうと思った
ただし管理画面があるので、とあるURL以下はセッション維持したい(Android、iOS等向けのAPIは振り分けて、ブラウザからの管理画面へのアクセスはセッション維持する)
nginxでロードバランサ的なことができるとは知っていたけど、セッション維持がどうやるか・・・
色々調べた結果、セッション維持はnginxにモジュールを追加することで出来そうだと分かった
以下のサイトを参考に環境を構築した
参考サイト
目次
ロードバランサの構成
- CentOS 7
- nginx-1.13.7
- 追加モジュール:nginx-sticky-module-ng-1.2.6
コンパイル環境の準備
更新やら、コンパイルに必要なパッケージ群をインストール
yum update yum install vim yum groupinstall 'Development Tools' yum install epel-release
nginxが必要としているパッケージをインストール
yum install perl perl-devel perl-ExtUtils-Embed libxslt libxslt-devel libxml2 libxml2-devel gd gd-devel GeoIP GeoIP-devel
nginxのインストール
nginxのソースコードをダウンロード・展開する
cd /usr/local/src wget http://nginx.org/download/nginx-1.13.7.tar.gz && tar zxvf nginx-1.13.7.tar.gz
また、nginxのコンパイルに必要な「PCRE」、「zlib」、「OpenSSL」のソースコードをダウンロードする。
cd /usr/local/src wget https://ftp.pcre.org/pub/pcre/pcre-8.41.tar.gz && tar xzvf pcre-8.41.tar.gz wget https://www.zlib.net/zlib-1.2.11.tar.gz && tar xzvf zlib-1.2.11.tar.gz wget https://www.openssl.org/source/openssl-1.1.0g.tar.gz && tar xzvf openssl-1.1.0g.tar.gz
展開し終わったら要らないので削除
rm -rf *.tar.gz
ロードバランサのモジュールのソースコードを以下のサイトのからダウンロードする
wget https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/1.2.6.tar.gz -O nginx-sticky-module-ng-1.2.6.tar.gz && tar xzvf nginx-sticky-module-ng-1.2.6.tar.gz
解凍したら「nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d」というディレクトリになっていた。
あとで見直してもわかるように、ディレクトリ名を変更
mv nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d nginx-sticky-module-ng-1.2.6
manコマンドでnginxのマニュアルが見れるように以下の以下のコマンドを実行
cp /usr/local/src/nginx-1.13.7/man/nginx.8 /usr/share/man/man8 gzip /usr/share/man/man8/nginx.8
以下のコマンドでマニュアルが表示される
man nginx
nginxのディレクトリに移動し「configure」を実行
cd /usr/local/src/nginx-1.13.7 ./configure --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 \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/run/nginx.lock \ --user=nginx \ --group=nginx \ --build=CentOS \ --builddir=nginx-1.13.7 \ --with-select_module \ --with-poll_module \ --with-threads \ --with-file-aio \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_addition_module \ --with-http_xslt_module=dynamic \ --with-http_image_filter_module=dynamic \ --with-http_geoip_module=dynamic \ --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_auth_request_module \ --with-http_random_index_module \ --with-http_secure_link_module \ --with-http_degradation_module \ --with-http_slice_module \ --with-http_stub_status_module \ --http-log-path=/var/log/nginx/access.log \ --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 \ --with-mail=dynamic \ --with-mail_ssl_module \ --with-stream=dynamic \ --with-stream_ssl_module \ --with-stream_realip_module \ --with-stream_geoip_module=dynamic \ --with-stream_ssl_preread_module \ --with-compat \ --with-pcre=/usr/local/src/pcre-8.41 \ --with-pcre-jit \ --with-zlib=/usr/local/src/zlib-1.2.11 \ --with-openssl=/usr/local/src/openssl-1.1.0g \ --with-openssl-opt=no-nextprotoneg \ --with-debug \ --add-module=/usr/local/src/nginx-sticky-module-ng-1.2.6
コンパイルする
make
このときはコンパイルするとエラーが発生した
以下がエラーメッセージ
/usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c: 関数 ‘ngx_http_sticky_misc_md5’ 内: /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c:152:15: エラー: ‘MD5_DIGEST_LENGTH’ が宣言されていません (この関数内での最初の使用) u_char hash[MD5_DIGEST_LENGTH]; ^ /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c:152:15: 備考: 未宣言の識別子は出現した各関数内で一回のみ報告されます /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c:152:10: エラー: 使用されない変数 ‘hash’ です [-Werror=unused-variable] u_char hash[MD5_DIGEST_LENGTH]; ^ /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c: 関数 ‘ngx_http_sticky_misc_hmac_md5’ 内: /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c:189:15: エラー: ‘MD5_DIGEST_LENGTH’ が宣言されていません (この関数内での最初の使用) u_char hash[MD5_DIGEST_LENGTH]; ^ /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c:190:12: エラー: ‘MD5_CBLOCK’ が宣言されていません (この関数内での最初の使用) u_char k[MD5_CBLOCK]; ^ /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c:190:10: エラー: 使用されない変数 ‘k’ です [-Werror=unused-variable] u_char k[MD5_CBLOCK]; ^ /usr/local/src/nginx-sticky-module-ng-1.2.6/ngx_http_sticky_misc.c:189:10: エラー: 使用されない変数 ‘hash’ です [-Werror=unused-variable] u_char hash[MD5_DIGEST_LENGTH]; ^ cc1: all warnings being treated as errors
解決方法は、bitbucketのissuesにあった
(しばらくメンテナンスされていないっぽいけど、今後大丈夫なんだろうか・・・)
ファイル:ngx_http_sticky_misc.cのinclude部分に以下を追記
#ifndef MD5_DIGEST_LENGTH #include <openssl/md5.h> #endif #ifndef SHA_DIGEST_LENGTH #include <openssl/sha.h> #endif
改めて、コンパイルとインストールを実行
make make install
設定でモジュールがロードできるようにシンボリックリンクを作成
ln -s /usr/lib64/nginx/modules /etc/nginx/modules
以下のコマンドが実行できればOK
nginx -V
nginxのユーザを作成する
useradd --system --home /var/cache/nginx --shell /sbin/nologin --comment "nginx user" --user-group nginx
ちなみに、設定ファイルの確認は以下のコマンドで確認できる。
nginx -t
このコマンドを実行すると、「ディレクトリが作れねぇ!」みたいなエラーがでてくる
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (2: No such file or directory)
このときは、「/var/cache/nginx」というディレクトリを作成する。
mkdir -p /var/cache/nginx
nginxの起動と自動起動
nginxを自動起動させるため、「/usr/lib/systemd/system/nginx.service」を作成する
vim /usr/lib/systemd/system/nginx.service
ファイルの中身はこんな感じ(参考サイトのコピペ)
[Unit] Description=nginx - high performance web server Documentation=https://nginx.org/en/docs/ After=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 -c /etc/nginx/nginx.conf ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID [Install] WantedBy=multi-user.target
nginxの起動と自動起動の有効化
systemctl start nginx systemctl enable nginx
nginxの設定
管理画面のみセッションを維持したいので、ロードバランサの方式は、セッションを維持するタイプと維持しないタイプ2種類用意する
- keep_session_backends
- 管理画面用
- default_backends
- その他
upstream keep_session_backends { sticky; server 192.168.33.12:8080; server 192.168.33.13:8080; } upstream default_backends { server 192.168.33.12:8080; server 192.168.33.13:8080; }
「/example」以下は「default_backends」を使用し、 「/example/admin」以下は「keep_session_backends」を使用する設定
location /example { proxy_pass http://default_backends; } location /example/admin { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Port $server_port; proxy_redirect off; proxy_pass http://keep_session_backends; }
「/example/admin」内の「proxy_set_header」は、Webアプリでリダイレクトするときに、ドメイン・ポート番号が変わらないようにしている
参考サイト
全体はこんな感じ(コメントは削除)
worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; upstream keep_session_backends { sticky; server 192.168.33.12:8080; server 192.168.33.13:8080; } upstream default_backends { server 192.168.33.12:8080; server 192.168.33.13:8080; } server { listen 8888; server_name localhost; port_in_redirect on; location /example { proxy_pass http://default_backends; } location /example/admin { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Port $server_port; proxy_redirect off; proxy_pass http://keep_session_backends; } error_page 404 /404.html; location = /404.html { root html; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
※ポート番号は、開発環境のため、8888番を使用
終わりに
「example/admin」以外の「example」以下には、交互にアクセスし、「example/admin」以下には片方のみアクセスするのを確認できた。
ちなみに今回使用した追加モジュールの「nginx-sticky-module-ng」は、2016年08月09日以降コミットがない・・・
nginxのバージョンが上がったときに、ちゃんと動作するのか心配