NGINX を使った HTTP Proxy の構築

◆ Live配信スケジュール ◆
サイオステクノロジーでは、Microsoft MVPの武井による「わかりみの深いシリーズ」など、定期的なLive配信を行っています。
⇒ 詳細スケジュールはこちらから
⇒ 見逃してしまった方はYoutubeチャンネルをご覧ください
【5/21開催】Azure OpenAI ServiceによるRAG実装ガイドを公開しました
生成AIを活用したユースケースで最も一番熱いと言われているRAGの実装ガイドを公開しました。そのガイドの紹介をおこなうイベントです!!
https://tech-lab.connpass.com/event/315703/

こんにちは。サイオステクノロジー OSS サポート担当 Y です。

今回は NGINX で Reverse Proxy サーバ (HTTP Proxy) を構築してみました。(※以下の内容は NGINX 1.12.2 にて検証しています。)

■はじめに

さて、今回は NGINX を使った Reverse Proxy サーバのお話です。NGINX では、HTTP Proxy (ngx_https_* モジュール) と TCP/UDP Proxy (ngx_stream_* モジュール) を構築することが可能 (TCP/UDP Proxy はバージョン “1.9.0” 以降) です。今回の検証では以下のモジュールが主に関連します。

(ngx_https_core_module)

(ngx_https_upstream_module)

(ngx_https_proxy_module)

また、今回の検証では利用しませんが、TCP/UDP Proxy の処理に関連するモジュールとして以下の様なものがあります。

(ngx_stream_core_module)

(ngx_stream_upstream_module)

(ngx_stream_proxy_module)

■構成情報

それでは環境を構築してみます。今回は以下の様な環境を構築してみました。

======================================================================
                              |               +-----[web1 (NGINX)]
[client]-------------------[proxy (NGINX)]----|-----[web2 (NGINX)]
                              |               +-----[web3 (NGINX)]
 subnet : 192.168.100.0/24    |    subnet : 192.168.200.0/24
======================================================================

上記の通り、2つのネットワーク (192.168.100.0/24, 192.168.200.0/24) を用意した上で、各ノードに以下の様な IP アドレスを設定し、client から直接 web サーバにはアクセスできないようにしています。(proxy サーバには 2つの NIC を用意し、両方のネットワークに接続できるようにしています。)

==============================
client : 192.168.100.2/24
proxy  : 192.168.100.3/24
         192.168.200.5/24
web1   : 192.168.200.2/24
web2   : 192.168.200.3/24
web3   : 192.168.200.4/24
==============================

上記構成では、client から proxy サーバにリクエストを送信すると、バックエンドの web1 ~ web3 にリクエストが振り分けられ、いずれかの web サーバからレスポンスが返ってくることを想定しています。

それでは、上記構成を実現するための各 proxy, web サーバの設定について見ていきます。(※各ディレクティブの詳細等は割愛していますので、前述したドキュメントも併せてご参照下さい。)

初めに各 proxy, web サーバ共通の設定です。各サーバには以下の様な共通の設定ファイル nginx.conf (/usr/local/nginx/conf/nginx.conf) を配置します。

[nginx.conf (全ノード共通)]
user  nginx;
worker_processes  1;

error_log  logs/error.log  notice;
pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

https {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$https_referer" '
                      '"$https_user_agent" "$https_x_forwarded_for"';

    access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /usr/local/nginx/conf.d/*.conf;
}

この nginx.conf では、エラーログやアクセスログ等の基本的な設定を行なっています。そして、include ディレクティブにて /usr/local/nginx/conf.d/ 配下にある *.conf ファイルを読み込むように設定しています。

次に、各 proxy, web サーバ毎に異なる設定を行います。このサーバ毎に異なる設定を行う設定ファイルは、前述した /usr/local/nginx/conf.d/ 配下の *.conf ファイルとして作成し、共通設定を行なっている nginx.conf から読み込まれるようにします。

まずは proxy サーバの設定です。proxy サーバには以下の様な設定ファイルを配置します。

[proxy.conf (proxy サーバの設定)]
upstream backend {
    server web1.example.com:80;
    server web2.example.com:80;
    server web3.example.com:80;
}

server {
    listen       0.0.0.0:80;
    server_name  localhost;

    location / {
        proxy_pass https://backend;
    }
}

はじめに、upstream ディレクティブにて任意の名前 (backend) を付けたグループを作成しています。グループ内に登録するサーバは upstream コンテキスト内の server ディレクティブで指定します。(※少しややこしいのですが、後述する server ディレクティブとは異なる server ディレクティブです。)

今回はバックエンドのサーバである web1 ~ web3 を upstream コンテキスト内の server ディレクティブに指定しています。(upstream コンテキスト内の server ディレクティブには、FQDN や IP アドレスを指定することが可能です。)

また、server ディレクティブによるバックエンドサーバの指定では、リクエストを転送する先 (web サーバ) の port 番号 “80” を明示的に指定しています。この記載を省略すると、デフォルトで 80 番 port が利用されます。

次に、server ディレクティブ (upstream コンテキスト内の server ディレクティブとは異なる server ディレクティブ) にて proxy サーバがリクエストを受け取るための設定 (listen) 及びバックエンドサーバにリクエスト転送する設定 (proxy_pass) を行います。

listen ディレクティブでは proxy サーバがクライアントからリクエストを受け付ける IP アドレス及び port 番号 (ソケット) を指定します。

proxy_pass ディレクティブではリクエストを転送する設定を行いますが、今回は upstream ディレクティブにて設定したグループ “backend” を転送先に指定するため、https:// の後ろに “backend” を指定しています。

次に、各 web サーバ側の設定です。各 web サーバには以下の様な設定ファイルをそれぞれ配置します。各 web サーバは “/” 宛のリクエストに対して “/usr/local/nginx/html” 配下の index1.html ~ index3.html を返すだけのシンプルな設定にしています。

[upstream1.conf (web1 サーバの設定)]
server {
    listen       0.0.0.0:80;
    server_name  localhost;

    location / {
        root   /usr/local/nginx/html;
        index  index1.html;
    }
}
[upstream2.conf (web2 サーバの設定)]
server {
    listen       0.0.0.0:80;
    server_name  localhost;

    location / {
        root   /usr/local/nginx/html;
        index  index2.html;
    }
}
[upstream3.conf (web3 サーバの設定)]
server {
    listen       0.0.0.0:80;
    server_name  localhost;

    location / {
        root   /usr/local/nginx/html;
        index  index3.html;
    }
}

また、各 index*.html の中身は以下の様になっており、どの web サーバからのレスポンスであるかが分かるようにしてあります。

[index1.html (web1 サーバ用のコンテンツ)]
index of web1
[index2.html (web2 サーバ用のコンテンツ)]
index of web2
[index3.html (web3 サーバ用のコンテンツ)]
index of web3

各サーバの設定については以上です。

■検証

では、前述した設定の環境で実際に動作を確認してみます。

初めに proxy サーバ上から web1 ~ web3 サーバにリクエストを送信してみます。

[root@proxy /]# curl https://web1.example.com/
index of web1
[root@proxy /]# 
[root@proxy /]# curl https://web2.example.com/
index of web2
[root@proxy /]# 
[root@proxy /]# curl https://web3.example.com/
index of web3
[root@proxy /]# 
[root@proxy /]# curl https://192.168.200.2/
index of web1
[root@proxy /]# 
[root@proxy /]# curl https://192.168.200.3/
index of web2
[root@proxy /]# 
[root@proxy /]# curl https://192.168.200.4/
index of web3
[root@proxy /]# 

すると、上記の通り各 web サーバからレスポンスが返ってくることが確認できます。前述した通り、レスポンスの内容から、どの web サーバからのレスポンスであるかが確認できます。

次に、client から各 web サーバに対して直接リクエストを送信してみます。

[root@client /]# curl https://web1.example.com/
curl: (6) Could not resolve host: web1.example.com; Unknown error
[root@client /]# 
[root@client /]# curl https://web2.example.com/
curl: (6) Could not resolve host: web2.example.com; Unknown error
[root@client /]# 
[root@client /]# curl https://web3.example.com/
curl: (6) Could not resolve host: web3.example.com; Unknown error
[root@client /]# 
[root@client /]# curl https://192.168.200.2/
curl: (7) Failed connect to 192.168.200.2:80; Connection timed out
[root@client /]# 
[root@client /]# curl https://192.168.200.3/
curl: (7) Failed connect to 192.168.200.3:80; Connection timed out
[root@client /]# 
[root@client /]# curl https://192.168.200.4/
curl: (7) Failed connect to 192.168.200.4:80; Connection timed out
[root@client /]# 

上記の通り、client と各 web サーバは別のネットワークセグメントに配置されているため、直接リクエストを送信できないことが確認できます。

最後に、client から proxy サーバに対してリクエストを送信してみます。

[root@client /]# curl https://proxy.example.com/
index of web1
[root@client /]# 
[root@client /]# curl https://proxy.example.com/
index of web2
[root@client /]# 
[root@client /]# curl https://proxy.example.com/
index of web3
[root@client /]# 

すると、同一の URL リクエスト (Reverse Proxy として動作しているサーバ “proxy” 宛のリクエスト) に対して、web1, web2, web3 からのレスポンスが返ってきていることが確認できます。

さらに、20回ほど連続でリクエストを送信してみます。

[root@client /]# for i in `seq 1 20` ; do
> curl https://proxy.example.com/
> done
index of web1
index of web2
index of web3
index of web1
index of web2
index of web3
index of web1
index of web2
index of web3
index of web1
index of web2
index of web3
index of web1
index of web2
index of web3
index of web1
index of web2
index of web3
index of web1
index of web2
[root@client /]# 

すると、上記の通り upstream ディレクティブで設定した 3台の web サーバから順にレスポンスが返ってきており、Reverse Proxy として動作してる NGINX が 3台の web サーバにリクエストを振り分けていることが確認できました。

■最後に

今回はシンプルな構成で NGINX を使った HTTP Proxy を構築してみました。HTTP Proxy の場合、NGINX サーバ上でリクエスト/レスポンスのヘッダを追加/修正することも可能ですので、次の機会に検証してみようと思います。また、TCP/UDP Proxy についても別の機会に検証してみたいと思います。

アバター画像
About サイオステクノロジーの中の人 41 Articles
サイオステクノロジーで働く中の人です。
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

3人がこの投稿は役に立ったと言っています。


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


質問はこちら 閉じる