もなかアイスの試食品

「とりあえずやってみたい」そんな気持ちが先走りすぎて挫折が多い私のメモ書きみたいなものです.

nginxでセッション維持するロードバランサを構築

はじめに

よくあるWEBサーバ+DBサーバを使用した、とあるサービスを作る事になった

そのサービスをリリース後は結構な利用人数になりそうだった

なので、負荷分散ができる環境に前もって準備をしておこうと思った

ただし管理画面があるので、とあるURL以下はセッション維持したい(AndroidiOS等向けのAPIは振り分けて、ブラウザからの管理画面へのアクセスはセッション維持する)

nginxでロードバランサ的なことができるとは知っていたけど、セッション維持がどうやるか・・・

色々調べた結果、セッション維持はnginxにモジュールを追加することで出来そうだと分かった

以下のサイトを参考に環境を構築した

参考サイト

www.vultr.com

www.kumoyanet.com


目次

ロードバランサの構成

  • 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のソースコードをダウンロード・展開する

nginx: download

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

ロードバランサのモジュールのソースコードを以下のサイトのからダウンロードする

bitbucket.org

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にあった

(しばらくメンテナンスされていないっぽいけど、今後大丈夫なんだろうか・・・)

nginx-goodies / nginx-sticky-module-ng / issues / #33 - Cannot compile with nginx 1.13.4 and module version 1.2.6 - error: ‘MD5_DIGEST_LENGTH’ undeclared — Bitbucket

ファイル: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アプリでリダイレクトするときに、ドメイン・ポート番号が変わらないようにしている

参考サイト

qiita.com

qiita.com

全体はこんな感じ(コメントは削除)

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のバージョンが上がったときに、ちゃんと動作するのか心配