ステータス同期

ここでは、ステータス同期について紹介します。また具体的な利用サンプルとして Rate Limit、KeyValを紹介します

1. ステータス同期 (Zone Synchronization)

../../_images/nginx-ha-statesync-slide.jpg

ステータス同期の設定を行います

ubuntu01 もしくは ubuntu01-nginx で以下の操作を行ってください

cd ~/
git clone https://github.com/BeF5/f5j-nginx-plus-lab2-conf
sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf-bak
sudo cp ~/f5j-nginx-plus-lab2-conf/lab/zone-sync01-nginx.conf /etc/nginx/nginx.conf
sudo nginx -s reload

ubuntu02 もしくは ubuntu02-nginx で以下の操作を行ってください

cd ~/
git clone https://github.com/BeF5/f5j-nginx-plus-lab2-conf
sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf-bak
sudo cp ~/f5j-nginx-plus-lab2-conf/lab/zone-sync02-nginx.conf /etc/nginx/nginx.conf
sudo nginx -s reload

NGINX Plus APIへ接続しステータス同期(Zone Synchronization)の状態を確認します。

curl -s 127.0.0.1:8888/api/8/stream/zone_sync  | jq .
実行結果サンプル
 1{
 2  "status": {
 3    "nodes_online": 0,
 4    "msgs_in": 0,
 5    "msgs_out": 0,
 6    "bytes_in": 0,
 7    "bytes_out": 0
 8  },
 9  "zones": {}
10}

2. ratelimit

ステータス同期ができる機能の一つである Rate Limit を設定し、動作を確認します。

設定

設定を行います。

ubuntu01 もしくは ubuntu01-nginxubuntu02 もしくは ubuntu02-nginx で以下の操作を行ってください

sudo cp ~/f5j-nginx-plus-lab2-conf/lab/zone-sync-ratelimit.conf /etc/nginx/conf.d/default.conf
sudo nginx -s reload

設定の内容を確認します

cat ~/f5j-nginx-plus-lab2-conf/lab/zone-sync-ratelimit.conf
実行結果サンプル
 1limit_req_zone $remote_addr zone=req:1M rate=1r/m sync;
 2
 3upstream server_group {
 4    zone backend 64k;
 5
 6    server backend1:81;
 7}
 8
 9server {
10   listen 80;
11   #status_zone server;
12   location / {
13       status_zone root;
14       limit_req zone=req;
15       proxy_pass http://server_group;
16   }
17}
  • 1行目、14行目でRate Limitの設定を行っています

  • 1行目の末尾に sync を指定することで、Rate Limitの状態を動悸することが可能となります

NGINX Plus APIへ接続しステータス同期(Zone Synchronization)の状態を確認します。 リクエストリミットを設定することで、カウンターが増加していることが確認できます

curl -s 127.0.0.1:8888/api/8/stream/zone_sync  | jq .
実行結果サンプル
 1{
 2  "status": {
 3    "nodes_online": 1,
 4    "msgs_in": 1,
 5    "msgs_out": 0,
 6    "bytes_in": 29,
 7    "bytes_out": 0
 8  },
 9  "zones": {
10    "req": {
11      "records_total": 0,
12      "records_pending": 0
13    }
14  }
15}

ダッシュボードを開き状態を確認します。 F5ラボ環境を利用の場合、以下のどちらかの手段で接続してください

動作確認

ubuntu01 もしくは ubuntu01-nginx から、 ubuntu01(10.1.1.7) もしくは ubuntu01-nginx(10.1.1.11) / ubuntu02(10.1.1.6) もしくは ubuntu02-nginx(10.1.1.12) 双方に対して接続を行います。 以下の操作を行ってください

ubuntu01/02の場合
echo "== To ubuntu01 =="; for i in {1..2}; do echo "==$i=="; curl -I -s 10.1.1.7; done; sleep 1; echo "== To ubuntu02 =="; for i in {1..2}; do echo "==$i=="; curl -I -s 10.1.1.6; done
ubuntu01/02-nginxの場合
echo "== To ubuntu01 =="; for i in {1..2}; do echo "==$i=="; curl -I -s 10.1.1.11; done; sleep 1; echo "== To ubuntu02 =="; for i in {1..2}; do echo "==$i=="; curl -I -s 10.1.1.12; done

ubuntu01 もしくは ubuntu01-nginx 宛に接続した後、ステータス同期を待つため 1秒停止(sleep 1) した後、 ubuntu02 もしくは ubuntu02-nginx へ接続します

実行結果サンプル
 1$ echo "== To ubuntu01 =="
 2== To ubuntu01 ==
 3$ for i in {1..2}; do echo "==$i==" ; curl -I -s 10.1.1.7 ; done
 4==1==
 5HTTP/1.1 200 OK
 6Server: nginx/1.21.6
 7Date: Thu, 29 Sep 2022 00:15:49 GMT
 8Content-Type: application/octet-stream
 9Content-Length: 65
10Connection: keep-alive
11
12==2==
13HTTP/1.1 503 Service Temporarily Unavailable
14Server: nginx/1.21.6
15Date: Thu, 29 Sep 2022 00:15:49 GMT
16Content-Type: text/html
17Content-Length: 197
18Connection: keep-alive
19
20$ sleep 1;
21$ echo "== To ubuntu02 =="
22== To ubuntu02 ==
23$ for i in {1..2}; do echo "==$i==" ; curl -I -s 10.1.1.6  ; done
24==1==
25HTTP/1.1 503 Service Temporarily Unavailable
26Server: nginx/1.21.6
27Date: Thu, 29 Sep 2022 00:15:50 GMT
28Content-Type: text/html
29Content-Length: 197
30Connection: keep-alive
31
32==2==
33HTTP/1.1 503 Service Temporarily Unavailable
34Server: nginx/1.21.6
35Date: Thu, 29 Sep 2022 00:15:50 GMT
36Content-Type: text/html
37Content-Length: 197
38Connection: keep-alive
  • ubuntu01 もしくは ubuntu01-nginx に対して接続した結果を確認すると、5行目の1回目が 200 、13行目の2回目が 503 となり RateLimitにより通信が拒否されています

  • ubuntu02 もしくは ubuntu02-nginx に対して接続した結果を確認すると、25行目の1回目、33行目が2回目の双方が 503 となり RateLimitにより通信が拒否されています。これはステータスが同期されたためこのような動作となります

ダッシュボードの状態を確認します

../../_images/nginx-ha-statesync-ratelimit1.jpg
  • HTTP Zones のタブの内容を開きます。左が ubuntu01 もしくは ubuntu01-nginx 、右が ubuntu02 もしくは ubuntu02-nginx となります。

  • 通信の結果、 5xx の結果を確認すると ubuntu01 もしくは ubuntu01-nginx1ubuntu02 もしくは ubuntu02-nginx2 となっていることがわかります

  • Limit Req のグラフを見ると、 ubuntu02 は通信の許可がなく Rejected されていることがわかります

3. Key Value Store

KeyValue Storeのステータス動機を確認します

設定

設定を行います。

ubuntu01 もしくは ubuntu01-nginxubuntu02 もしくは ubuntu02-nginx で以下の操作を行ってください

sudo cp ~/f5j-nginx-plus-lab2-conf/lab/zone-sync-keyval.conf /etc/nginx/conf.d/default.conf
sudo nginx -s reload

設定の内容を確認します

cat ~/f5j-nginx-plus-lab2-conf/lab/zone-sync-keyval.conf
実行結果サンプル
 1keyval_zone zone=iplist:32k state=/etc/nginx/conf.d/iplists.keyval timeout=20s sync;
 2keyval $arg_user $permit_ip zone=iplist;
 3
 4upstream server_group {
 5    zone backend 64k;
 6
 7    server backend1:81;
 8}
 9
10server {
11   listen 80;
12   location / {
13
14       # check variable is blank
15       if ( $arg_user = "" ) {
16         return 403 "No Username";
17       }
18
19       if ( $permit_ip = "" ) {
20         set $permit_ip $remote_addr;
21       }
22
23       if ( $remote_addr != $permit_ip ) {
24         return 403 "Mismatch client IP address";
25       }
26
27       # Here, we have arg_user & remote_addr = permit_ip
28       proxy_pass http://server_group;
29   }
30}
  • 1-2行目でKeyValを指定しています。1行目の末尾に sync を指定することで、複数のNGINXでステータスが同期されます

  • また sync を指定する場合は、 timeout の指定が必要となります。 timeout は各エントリの残存期間を示し、指定の時間を経過するとエントリが削除されます

  • 2行目で KeyValの内容を指定します。 $arg_user が Key となり、 $permit_ip が Valとなります。ステータス同期がなされたNGINXは、 Key を指定することで、KeyVal内の Val を参照し、指定の変数名でアクセスすることが可能となります。 Val の値は参照されるまでに適切に変数として格納する必要がありますので注意ください

  • その他の if などは以下の挙動を実装するため指定しています

このサンプル設定では以下のような挙動となります

  • URLパラメータとして user という名称のパラメータが指定されていない通信を拒否します

  • user のURLパラメータの値を ユーザ とします。パラメータが指定されている場合、 接続元のIPアドレスユーザ に紐づけてKeyValueに登録します

  • ユーザ からのアクセスに置いて、異なる 接続元のIPアドレス である場合、通信を拒否します

動作確認

ubuntu01 もしくは ubuntu01-nginxiplist という名称のKeyValが生成されていることが確認できます

curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1{
2  "iplist": {}
3}

ubuntu01 もしくは ubuntu01-nginx でサンプルのリクエストを送信し、KeyVal の結果を確認します

curl 10.1.1.7/; echo"" ; curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
もしくは
curl 10.1.1.11/; echo"" ; curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1No Username
2{
3  "iplist": {}
4}
  • リクエストで user を指定しないため、応答が No Username となります。

  • iplist にエントリは追加されていません

ubuntu01 もしくは ubuntu01-nginx でURLパラメータに user を含むサンプルのリクエストを送信し、KeyVal の結果を確認します

curl 10.1.1.7/?user=user1 ; echo"" ; curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
もしくは
curl 10.1.1.11/?user=user1 ; echo"" ; curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1{ "request_uri": "/?user=user1","server_addr":"10.1.1.8","server_port":"81"}
2{
3  "iplist": {
4    "user1": "10.1.1.7"
5}
  • 正しくURLパラメータの useruser1 が指定されているため、正しい応答が確認できます

  • iplistubuntu01 もしくは ubuntu01-nginx のIPアドレス 10.1.1.7/0.1.1.11 が追加されていることが確認できます

ubuntu02 もしくは ubuntu02-nginx でURLパラメータに user を含むサンプルのリクエストを送信し、KeyVal の結果を確認します

curl 10.1.1.7/?user=user1 ; echo"" ; curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
もしくは
curl 10.1.1.11/?user=user1 ; echo"" ; curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1Mismatch client IP address
2{
3  "iplist": {
4    "user1": "10.1.1.7"
5  }
6}
  • URLパラメータの useruser1 が指定されていますが、 Mismatch client IP address が応答されています

  • iplist をみると ubuntu01 もしくは ubuntu01-nginx のIPアドレス 10.1.1.7/10.1.1.11 が追加されており、 ubuntu02 もしくは ubuntu02-nginx のアドレスと一致しないためエラーとなったことがわかります

このように、KeyVal を利用することで、複雑な処理が可能となります。 またステータス同期により複数のホストで状態を同期し、協調した処理を行うことが可能となります

../../_images/nginx-ha-statesync-keyval1.jpg

画像を確認いただくとカウンターの値が表示さています。 この数についての詳細ですが、この数はKeyvalのエントリが追加されたタイミングでのKeyの数を指しています。 一定時間経過後(今回の設定サンプルでは20秒)で各エントリが消えますが、そのタイミングで値は変化しません。 その後、新たにエントリの追加(または削除)を行った際に、全体の数の値が変更となります

4. APIを使ったKeyValの操作

KeyValの機能は、NGINX Plus APIを使って外部から操作することが可能です。

詳細は以下のページを参照してください

以下のコマンドを実行し動作を確認してください

追加

POST Method を利用し、エントリを 追加 します

curl -s 127.0.0.1:8888/api/8/http/keyvals/iplist -X POST -d '{
   "user1":"10.0.0.1" ,
   "user2":"10.0.0.2" ,
   "user3":"10.0.0.3" ,
   "user4":"10.0.0.4"
}'

KeyValの状態を確認します

curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1{
2  "iplist": {
3    "user2": "10.0.0.2",
4    "user3": "10.0.0.3",
5    "user4": "10.0.0.4"
6  }
7}

変更

PATCH Method を利用し、エントリを 変更 します

curl -s 127.0.0.1:8888/api/8/http/keyvals/iplist -X PATCH -d '{ "user1":"192.168.0.1" }'

KeyValの状態を確認します

curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1{
2  "iplist": {
3    "user2": "10.0.0.2",
4    "user3": "10.0.0.3",
5    "user1": "192.168.0.1",
6    "user4": "10.0.0.4"
7  }
8}

エントリの削除

PATCH Method で、特定エントリの値として null を指定することで、対象のエントリを削除することができます

curl -s 127.0.0.1:8888/api/8/http/keyvals/iplist -X PATCH -d '{ "user1":null }'

KeyValの状態を確認します

curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1{
2  "iplist": {
3    "user2": "10.0.0.2",
4    "user3": "10.0.0.3",
5    "user4": "10.0.0.4"
6  }
7}

すべてのエントリの削除

DELETED Method を利用し、すべてのエントリを 削除 します

curl -s 127.0.0.1:8888/api/8/http/keyvals/iplist -X DELETE

KeyValの状態を確認します

curl -s 127.0.0.1:8888/api/8/http/keyvals | jq .
実行結果サンプル
1{
2  "iplist": {}
3}

5. その他ステータス同期に関する設定

本番環境では、対向のホストをFQDNで指定し、SSL/TLSの利用、mTLS・証明書認証などを実装することが考えられます。 これらの設定について以下のページを参照してください