APIGW 通信制御の実施

1. NICのトラフィック分割(Canary Release)

以下の構成で動作を確認します

../../_images/nic-traffic-split.png

NICの背後にあるアプリケーションのトラフィック分割が求められる状況を想定した構成となります。

1. 設定のデプロイ (割合9:1)

設定の内容を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat split-nic-vs/nic-vs-split1.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: nic
 5spec:
 6  host: nic.example.com
 7  upstreams:
 8  - name: target-v1
 9    service: target-v1-0
10    port: 80
11  - name: target-v2
12    service: target-v2-0
13    port: 80
14  routes:
15  - path: /
16    splits:
17    - weight: 90
18      action:
19        pass: target-v1
20    - weight: 10
21      action:
22        pass: target-v2
  • Upstreamとして 8-9行目 target-v1-0 を、 11-12行目 target-v2-0 を転送先サービスとして指定しています

  • 17-19行目で、 target-v1-0 に対し wegiht 90(90%) 、20-22行目で、 target-v2-0 に対し wegiht 10(10%) の割合を指定して転送します

設定をデプロイします

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f split-nic-vs/nic-vs-split1.yaml -n staging

正しく反映されたことを確認します

kubectl get vs nic -n staging
実行結果サンプル
1NAME   STATE   HOST              IP    PORTS   AGE
2nic    Valid   nic.example.com                 49s

STATEが Valid であることを確認します

2. 動作確認 (割合9:1)

正しく疎通があることを確認します

curl -s -H "Host: nic.example.com" http://localhost/
実行結果サンプル
1target v1.0

複数回実行いただくと target v1.0 または target v2.0 が応答され、2種類のVersionのアプリケーションから応答されている状況が確認いただけます。

以下のコマンドで 20回 リクエストを送付します。結果を確認します

for i in {1..20}; do curl -s -H "Host: nic.example.com" http://localhost/  ; done ;
実行結果サンプル
 1target v1.0
 2target v1.0
 3target v1.0
 4target v1.0
 5target v2.0
 6target v1.0
 7target v1.0
 8target v1.0
 9target v2.0
10target v1.0
11target v1.0
12target v1.0
13target v1.0
14target v1.0
15target v1.0
16target v1.0
17target v1.0
18target v1.0
19target v1.0
20target v1.0

v1v2 が指定の値に近い割合で応答が返答されていることが確認できます。

3. 設定のデプロイ (割合2:8)

設定の内容を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat split-nic-vs/nic-vs-split2.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: nic
 5spec:
 6  host: nic.example.com
 7  upstreams:
 8  - name: target-v1
 9    service: target-v1-0
10    port: 80
11  - name: target-v2
12    service: target-v2-0
13    port: 80
14  routes:
15  - path: /
16    splits:
17    - weight: 20
18      action:
19        pass: target-v1
20    - weight: 80
21      action:
22        pass: target-v2
  • Upstreamは変更ありません

  • 17-19行目で、 target-v1-0 に対し wegiht 20(20%) 、20-22行目で、 target-v2-0 に対し wegiht 80(80%) の割合を指定して転送します。V2の安定した動作が確認できたため割合を増加する想定のシナリオとなります

設定をデプロイします

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f split-nic-vs/nic-vs-split2.yaml -n staging

正しく反映されたことを確認します

kubectl get vs nic -n staging
実行結果サンプル
1NAME   STATE   HOST              IP    PORTS   AGE
2nic    Valid   nic.example.com                 49s

STATEが Valid であることを確認します

4. 動作確認 (割合2:8)

以下のコマンドで 20回 リクエストを送付します。結果を確認します

for i in {1..20}; do curl -s -H "Host: nic.example.com" http://localhost/  ; done ;
実行結果サンプル
 1target v2.0
 2target v2.0
 3target v1.0
 4target v2.0
 5target v2.0
 6target v2.0
 7target v2.0
 8target v2.0
 9target v2.0
10target v2.0
11target v2.0
12target v1.0
13target v2.0
14target v2.0
15target v2.0
16target v2.0
17target v2.0
18target v1.0
19target v1.0
20target v2.0

先程の内容から割合を変更したため、 v2 が多くなっています。 v1v2 が指定の値に近い割合で応答が返答されていることが確認できます。

5. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f split-nic-vs/nic-vs-split2.yaml -n staging

2. NSMのトラフィック分割(Canary Release)

以下の構成で動作を確認します

../../_images/nsm-traffic-split.png

NICでの制御と異なり、NICの背後のアプリケーションは単一です。 そのアプリケーションの背後にあるアプリケーションのトラフィック分割が求められる状況を想定した構成となります。

1. NIC設定のデプロイ

設定の内容を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat split-nsm-smi/nic-vs-nsmsplit.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: webapp
 5spec:
 6  host: webapp.example.com
 7  upstreams:
 8  - name: webapp-svc
 9    service: webapp-svc
10    port: 80
11  routes:
12  - path: /
13    action:

NICの設定内容は大変シンプルで、後段の webapp-svc へ転送する構成となります

設定をデプロイします

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f split-nsm-smi/nic-vs-nsmsplit.yaml -n staging

正しく反映されたことを確認します

kubectl get vs webapp -n staging
実行結果サンプル
1NAME     STATE   HOST                 IP    PORTS   AGE
2webapp   Valid   webapp.example.com                 25s

STATEが Valid であることを確認します

2. 動作確認

正しく疎通があることを確認します

curl -s -H "Host: webapp.example.com" http://localhost/
実行結果サンプル
1target v1.0

複数回実行すると target v1.0target v2.0 が交互に応答されることが確認できます

以下のコマンドで 6回 リクエストを送付します。結果を確認します

for i in {1..6}; do curl -s -H "Host: webapp.example.com" http://localhost/  ; done ;
実行結果サンプル
1target v1.0
2target v2.0
3target v1.0
4target v2.0
5target v1.0
6target v2.0

v1v2 が交互に応答されていることがわかります。これは webapp-svc が、 target-svc に通信を転送した結果となります。

3. NSM設定のデプロイ (割合9:1)

NSMを使い target-svc から、 target-v1-0 `` ``target-v2-0 に対する通信を対象に割合の指定を行います

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat split-nsm-smi/nsm-split1.yaml
実行結果サンプル
 1apiVersion: split.smi-spec.io/v1alpha3
 2kind: TrafficSplit
 3metadata:
 4  name: target-ts
 5spec:
 6  service: target-svc
 7  backends:
 8  - service: target-v1-0
 9    weight: 90
10  - service: target-v2-0
11    weight: 10
  • 8-9行目で、 target-v1-0 に対し wegiht 90(90%) 、10-11行目で、 target-v2-0 に対し wegiht 10(10%) の割合を指定して転送します。

設定をデプロイします

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f split-nsm-smi/nsm-split1.yaml -n staging

正しく反映されたことを確認します

kubectl get trafficsplit -n staging
実行結果サンプル
1NAME        AGE
2target-ts   19s

4. NSM設定の動作確認 (割合9:1)

以下のコマンドで 20回 リクエストを送付します。結果を確認します

for i in {1..20}; do curl -s -H "Host: webapp.example.com" http://localhost/  ; done ;
実行結果サンプル
 1target v1.0
 2target v1.0
 3target v1.0
 4target v1.0
 5target v1.0
 6target v2.0
 7target v1.0
 8target v1.0
 9target v1.0
10target v1.0
11target v1.0
12target v1.0
13target v1.0
14target v1.0
15target v1.0
16target v1.0
17target v1.0
18target v1.0
19target v1.0
20target v2.0

先程の設定では、均等(5:5)に分散されていた通信ですが、 v1v2 が指定の値に近い割合で応答が返答されていることが確認できます。

5. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f split-nsm-smi/nic-vs-nsmsplit.yaml -n staging
kubectl delete -f split-nsm-smi/nsm-split1.yaml -n staging

3. NIC/NSMのJWT制御

以下の構成で動作を確認します

../../_images/nic-nsm-jwt.png
  • NICでクライアントのJWTの制御を行います。

  • 適切なJWTである場合、JWTの情報をHTTPヘッダーに情報を付与します

  • 付与されたHTTPヘッダーの情報を元にNSMで通信の制御を行います。この例では割合を指定し v2 に通信を転送します

1. 設定のデプロイ

利用するJWT Policyは Ingress Controller で JWT Validation のデプロイ を利用しています。 jwk-secret.yamljwt.yaml の解説はこちらを参照ください

その他の設定の内容を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat jwt-nic-nsm/nic-vs-jwt-addheader.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: webapp
 5spec:
 6  host: webapp.example.com
 7  policies:
 8  - name: jwt-policy
 9  upstreams:
10  - name: webapp-svc
11    service: webapp-svc
12    port: 80
13  routes:
14  - path: /
15    action:
16      proxy:
17        upstream: webapp-svc
18        requestHeaders:
19          pass: true
20          set:
21          - name: jwtscope
22            value: ${jwt_claim_scope}
  • 7-8行目で、 webapp.example.com 宛の通信に対してJWT Validationを設定しています

  • 16-22行目で、有効なJWTに指定された Scope の情報を、HTTPリクエストの jwtscope というHTTPヘッダーに付与する設定をします

次にNSMの設定を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat jwt-nic-nsm/nsm-split-jwt.yaml

基本的な設定は TrafficSplit です。

実行結果サンプル
 1apiVersion: split.smi-spec.io/v1alpha3
 2kind: TrafficSplit
 3metadata:
 4  name: target-ts
 5spec:
 6  service: target-svc
 7  backends:
 8  - service: target-v1-0
 9    weight: 0
10  - service: target-v2-0
11    weight: 100
12  matches:
13  - kind: HTTPRouteGroup
14    name: target-scope
15---
16apiVersion: specs.smi-spec.io/v1alpha3
17kind: HTTPRouteGroup
18metadata:
19  name: target-scope
20spec:
21  matches:
22  - name: jwt-group2-users
23    headers:
24      jwtscope: ".*group2.*"
  • TrafficSplitは、 target-v2-0 にすべての通信を転送する内容となります。ただし、 12-14行目に指定の通り条件を付与しています

  • 条件が16行目から記述されており、19行目の target-scope が 14行目に指定されていることがわかります

  • 条件の内容は21-24行目となり、 jwtscope というHTTPヘッダーに group2 という文字列が含まれている場合該当する、という内容を指定しています。

設定を反映します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f jwt-nic-nsm/jwk-secret.yaml -n staging
kubectl apply -f jwt-nic-nsm/jwt.yaml -n staging
kubectl apply -f jwt-nic-nsm/nic-vs-jwt-addheader.yaml -n staging
kubectl apply -f jwt-nic-nsm/nsm-split-jwt.yaml -n staging

2. 動作確認

JWT Validationの動作を確認します

curl -s -H "Host: webapp.example.com" http://localhost/
実行結果サンプル
1<html>
2<head><title>401 Authorization Required</title></head>
3<body>
4<center><h1>401 Authorization Required</h1></center>
5<hr><center>nginx/1.21.6</center>
6</body>
7</html>

401 Authorization Required が応答されていることが確認できます

次に適切なJWTをJWT Policyで指定した通り CookieToken に指定して通信を行います

## cd ~/f5j-nginx-k8s-apigw-lab/example
curl -s -H "Host: webapp.example.com" http://localhost/ -H "Token: `cat jwt-nic-nsm/nginx1.jwt`"
実行結果サンプル
1target v1.0

エラーなく応答が確認できました

複数回のリクエストを実行します。予め用意したJWTの nginx1.jwtnginx3.jwt の動作の違いを確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
for i in {1..4}; do curl -s -H "Host: webapp.example.com" http://localhost/ -H "Token: `cat jwt-nic-nsm/nginx1.jwt`" ;done;
実行結果サンプル
1target v1.0
2target v2.0
3target v1.0
4target v2.0
## cd ~/f5j-nginx-k8s-apigw-lab/example
for i in {1..4}; do curl -s -H "Host: webapp.example.com" http://localhost/ -H "Token: `cat jwt-nic-nsm/nginx3.jwt`" ;done;
実行結果サンプル
1target v2.0
2target v2.0
3target v2.0
4target v2.0

nginx1.jwt を指定した場合には、通信が均等に分散されていることが確認できます。 nginx3.jwtv2.0 のみに通信が転送されていることが確認できます。これは、NIC / NSMで指定したポリシーが正しく動作していることを示します

この条件の設定を組み合わせることで、詳細な条件をKubernetes内部の通信に適用することが可能となります。

3. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f jwt-nic-nsm/jwk-secret.yaml -n staging
kubectl delete -f jwt-nic-nsm/jwt.yaml -n staging
kubectl delete -f jwt-nic-nsm/nic-vs-jwt-addheader.yaml -n staging
kubectl delete -f jwt-nic-nsm/nsm-split-jwt.yaml -n staging

4. JWT制御とWAFによる防御

以下の構成で動作を確認します

../../_images/nic-jwt-waf.png

JWTによる通信制御はAPIを保護する有効な手段ですが、正しい認証情報を持ったクライアントが悪意あるプログラムなどにより想定外の動作を行うなどの場合が考えられます。 このような状況を想定して悪意ある通信を防御する方法を確認します。

1. 設定のデプロイ

設定の内容を確認します。 JWTに関する設定は 3. NIC/NSMのJWT制御 と同様に Ingress Controller で JWT Validation のデプロイ の内容を利用しています。

WAFの設定は最低限の設定を行い、外部からの攻撃をブロックできる設定としています。

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat waf-nic-vs/simple-ap.yaml
実行結果サンプル
 1apiVersion: appprotect.f5.com/v1beta1
 2kind: APPolicy
 3metadata:
 4  name: simple-ap
 5spec:
 6  policy:
 7    name: simple-ap
 8    applicationLanguage: utf-8
 9    enforcementMode: blocking
10    template:
11      name: POLICY_TEMPLATE_NGINX_BASE

enforcementModeblocking と指定することでWAFの通信を防御します WAFは数多くの設定により悪意ある通信をブロックすることが可能です。詳細を確認する場合、以下のページを参照してください。

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat waf-nic-vs/nic-vs-waf-jwt.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: nic
 5spec:
 6  host: nic.example.com
 7  policies:
 8  - name: waf-policy
 9  - name: jwt-policy
10  upstreams:
11  - name: target-v1
12    service: target-v1-0
13    port: 80
14  - name: target-v2
15    service: target-v2-0
16    port: 80
17  routes:
18  - path: /
19    splits:
20    - weight: 50
21      action:
22        pass: target-v1
23    - weight: 50
24      action:
25        pass: target-v2
  • 8行目に WAF 、 9行目に JWT のポリシーを割り当てています。この様に設定することで nic.example.com に対する通信に対しJWT Validation及びWAFの防御が可能になります

  • Policyは HostRoute など柔軟に指定することが可能です。詳細は NIC: VS/VSRpolicies Fieldを参照ください

設定を反映します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f jwt-nic-nsm/jwk-secret.yaml -n staging
kubectl apply -f jwt-nic-nsm/jwt.yaml -n staging
kubectl apply -f waf-nic-vs/ap-logconf.yaml -n staging
kubectl apply -f waf-nic-vs/simple-ap.yaml -n staging
kubectl apply -f waf-nic-vs/waf.yaml -n staging
kubectl apply -f waf-nic-vs/nic-vs-waf-jwt.yaml -n staging

反映の結果を確認します

kubectl get aplogconf,appolicy,policy -n staging
実行結果サンプル
1NAME                                  AGE
2aplogconf.appprotect.f5.com/logconf   21s
3
4NAME                                   AGE
5appolicy.appprotect.f5.com/simple-ap   21s
6
7NAME                              STATE   AGE
8policy.k8s.nginx.org/jwt-policy   Valid   22s
9policy.k8s.nginx.org/waf-policy   Valid   20s

2. 動作確認

対象のFQDNにJWTを指定せず、動作確認します

curl -s -H "Host: nic.example.com" http://localhost/
実行結果サンプル
1<html>
2<head><title>401 Authorization Required</title></head>
3<body>
4<center><h1>401 Authorization Required</h1></center>
5<hr><center>nginx/1.21.6</center>
6</body>
7</html>

401 Authorization Required が応答されることがわかります。適切に JWT Validation が動作しています

適切なJWTを指定し、動作確認します

curl -s -H "Host: nic.example.com" http://localhost/ -H "Token: `cat jwt-nic-nsm/nginx1.jwt`"
実行結果サンプル
1target v1.0

正しい応答が確認できます

この正しいJWTを提示している通信で攻撃トラフィックを送信します

curl -s -H "Host: nic.example.com" "http://localhost//?<script>" -H "Token: `cat jwt-nic-nsm/nginx1.jwt`"
実行結果サンプル
1<html><head><title>Request Rejected</title></head><body>The requested URL was rejected.Please consult with your administrator.<br><br>
2Your support ID is:   16465265100495552517<br><br><a href='javascript:history.back();'>[Go Back]</a>
3</body></html>

Request Rejected と表示されエラーが応答されました

この様にVSで複数のポリシーを指定することにより、正しいJWTを持つクライアントが悪意ある通信を行った際にも防御することができることが確認できました

3. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f jwt-nic-nsm/jwk-secret.yaml -n staging
kubectl delete -f jwt-nic-nsm/jwt.yaml -n staging
kubectl delete -f waf-nic-vs/ap-logconf.yaml -n staging
kubectl delete -f waf-nic-vs/simple-ap.yaml -n staging
kubectl delete -f waf-nic-vs/waf.yaml -n staging
kubectl delete -f waf-nic-vs/nic-vs-waf-jwt.yaml -n staging

5. NICによる条件に応じた制御

以下の構成で動作を確認します

../../_images/nic-vs-acl.png

request_path , methods , headers による通信制御を確認します

1. 設定のデプロイ

設定の内容を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat << EOF > ./jwt-nic-nsm/nic-vs-acl.yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: nic
spec:
  host: nic.example.com
  policies:
  upstreams:
  - name: target-svc
    service: target-svc
    port: 80
  routes:
  - path: ~ /.*valid.*
    action:
      pass: target-svc
  - path: /
    matches:
    - conditions:
      - header: X-Type
        value: valid
      action:
        pass: target-svc
    - conditions:
      - variable: $request_method
        value: POST
      action:
        pass: target-svc
    action:
      return:
        code: 403
        type: text/plain
        body: "Error\n"
EOF
cat jwt-nic-nsm/nic-vs-acl.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: nic
 5spec:
 6  host: nic.example.com
 7  policies:
 8  upstreams:
 9  - name: target-svc
10    service: target-svc
11    port: 80
12  routes:
13  - path: ~ /.*valid.*
14    action:
15      pass: target-svc
16  - path: /
17    matches:
18    - conditions:
19      - header: X-Type
20        value: valid
21      action:
22        pass: target-svc
23    - conditions:
24      - variable: $request_method
25        value: POST
26      action:
27        pass: target-svc
28    action:
29      return:
30        code: 403
31        type: text/plain
32        body: "Error\n"
  • 13-15行目で、 PATHの条件を正規表現で指定し、 valid の文字列が含まれる場合、 target-svc に転送します

  • 19-22行目で、 X-Type というHTTP Headerの値をチェックし valid である場合、 target-svc に転送します

  • 24-27行目で、 $request_method という変数を指定しHTTP Methodが POST である場合、 target-svc に転送します

  • その他の通信は、 path: / の 28-32行目の条件に該当します

設定を反映します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f acl-nic-vs/nic-vs-acl.yaml -n staging

2. 動作確認

動作を確認します

まずシンプルなリクエストを送付し、結果を確認します

curl -v -H "Host: nic.example.com" http://localhost/
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host: nic.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 403 Forbidden
11< Server: nginx/1.21.6
12< Date: Wed, 30 Nov 2022 12:32:55 GMT
13< Content-Type: text/plain
14< Content-Length: 6
15< Connection: keep-alive
16<
17Error

通信を送付したところ 403 が応答されていることがわかります。curlコマンドではデフォルトのMethodがGETであり、指定したポリシーの条件に該当しないためエラーとなっています

ポリシーに記述したMethodでリクエストを送付します。

curl -v -H "Host: nic.example.com" http://localhost/ -X POST
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> POST / HTTP/1.1
 5> Host: nic.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Server: nginx/1.21.6
12< Date: Wed, 30 Nov 2022 12:37:24 GMT
13< Content-Type: text/plain
14< Content-Length: 12
15< Connection: keep-alive
16< X-Mesh-Request-ID: 3d4569c9fb09e210121aa3efca06ca85
17<
18target v1.0

4行目で POST で通信が送付され、 10行目で 200 OK 18行目で正しく応答が返されていることが確認できます

制御対象のURL ポリシーに記述したPathの条件を満たすリクエストを送付します。

curl -v -H "Host: nic.example.com" http://localhost/dummy/this-is-valid-path/a.jpg
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET /dummy/this-is-valid-path/a.jpg HTTP/1.1
 5> Host: nic.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Server: nginx/1.21.6
12< Date: Wed, 30 Nov 2022 12:38:02 GMT
13< Content-Type: image/jpeg
14< Content-Length: 12
15< Connection: keep-alive
16< X-Mesh-Request-ID: ef2205a2b89c70b653c642df14dc2f4d
17<
18target v2.0
19* Connection #0 to host localhost left intact

4行目で 指定のPATHに通信が送付され、 10行目で 200 OK 18行目で正しく応答が返されていることが確認できます

ポリシーに記述したCookieの条件を満たすリクエストを送付します。

curl -v -H "Host: nic.example.com" http://localhost/ -H "X-Type: valid"
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host: nic.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8> X-Type: valid
 9>
10* Mark bundle as not supporting multiuse
11< HTTP/1.1 200 OK
12< Server: nginx/1.21.6
13< Date: Wed, 30 Nov 2022 12:38:41 GMT
14< Content-Type: text/plain
15< Content-Length: 12
16< Connection: keep-alive
17< X-Mesh-Request-ID: cc3e1e8df58f0a1200456d76a551f6c7
18<
19target v1.0

8行目で指定したHTTP Headerが付与された通信が送付され、 11行目で 200 OK 19行目で正しく応答が返されていることが確認できます

このサンプルでは、条件に該当する場合サービスに転送し、それ以外をエラーとする設定です。 condition は様々な条件を記述することが可能です。該当する処理をエラーだけでなくリダイレクト、その他通信と違うServiceに転送するなどが可能となります

3. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f acl-nic-vs/nic-vs-acl.yaml -n staging

6. NSMによる条件に応じた制御

以下の構成で動作を確認します

../../_images/nsm-smi-acl.png

request_path , methods , headers による通信制御を確認します。 詳細は以降の設定で確認しますが、SMIの記述では 許可する条件 を指定することが可能となります。 5. NICによる条件に応じた制御 では条件に対して自由なActionを指定できましたが、その点が異なることを注意ください

1. 設定のデプロイ

ここで実施するNSMのSMIによる通信制御では、Deploymentに指定されたServiceAccountを確認し、その送信元・送信先ServiceAccountを指定し通信を制御します。

サービスアカウントを確認します

kubectl get sa -n staging
実行結果サンプル
1NAME          SECRETS   AGE
2default       1         6d9h
3target-v1-0   1         11m
4target-v2-0   1         14s
5webapp        1         25s

DeploymentのPod Templateで指定されている Service Account を確認します

kubectl describe deployment -n staging | egrep 'Pod Template:|Service Account:|^Name:'
実行結果サンプル
1Name:                   target-v1-0
2Pod Template:
3  Service Account:  target-v1-0
4Name:                   target-v2-0
5Pod Template:
6  Service Account:  target-v2-0
7Name:                   webapp
8Pod Template:
9  Service Account:  webapp

各Deploymentに対し、それぞれService Accountが指定されていることが確認できます。

設定の内容を確認します。VSの内容は 2. NSMのトラフィック分割(Canary Release) のNIC設定 と同じであり大変シンプルな内容のため割愛します。

NSMに指定するポリシーの内容を確認します。

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat acl-nsm-smi/nsm-acl.yaml
実行結果サンプル
 1apiVersion: specs.smi-spec.io/v1alpha3
 2kind: HTTPRouteGroup
 3metadata:
 4  name: route-group
 5spec:
 6  matches:
 7  - name: method
 8    methods:
 9    - POST
10  - name: path
11    pathRegex: "/.*valid.*"
12  - name: header
13    headers:
14      X-Type: "valid"
15
16---
17apiVersion: access.smi-spec.io/v1alpha2
18kind: TrafficTarget
19metadata:
20  name: traffic-target-v1
21spec:
22  destination:
23    kind: ServiceAccount
24    name: target-v1-0
25  rules:
26  - kind: HTTPRouteGroup
27    name: route-group
28    matches:
29    - method
30    - path
31    - header
32  sources:
33  - kind: ServiceAccount
34    name: webapp
35
36---
37apiVersion: access.smi-spec.io/v1alpha2
38kind: TrafficTarget
39metadata:
40  name: traffic-target-v2
41spec:
42  destination:
43    kind: ServiceAccount
44    name: target-v2-0
45  rules:
46  - kind: HTTPRouteGroup
47    name: route-group
48    matches:
49    - method
50    - path
51    - header
52  sources:
53  - kind: ServiceAccount
54    name: webapp
  • 1-14行目で、対象とする条件を指定します。kind は HTTPRouteGroup となり、オブジェクト名は route-group です

  • 条件は以下の三種類となります

    • 7-9行目: HTTP Method で POST を指定

    • 10-11行目: path で "/.*valid.*" を指定し、 valid が含まれる pathを対象とする

    • 12-14行目: HTTP Header を対象とし、 X-Type の値が valid となっているものを対象とする

  • 17-34行目が、 webapp から target-v1-0 に対する設定、27-54行目が、 webapp から target-v2-0 に対する設定となります

  • これらの違いは destination のみで、24行目で target-v1-0 、 44行目で target-v2-0 を指定しています。その他の内容は同様です

設定を反映します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl apply -f acl-nsm-smi/nic-vs-acl.yaml -n staging
kubectl apply -f acl-nsm-smi/nsm-acl.yaml -n staging

2. 動作確認

動作を確認します

まずシンプルなリクエストを送付し、結果を確認します

curl -s -H "Host: webapp.example.com" http://localhost/
実行結果サンプル
1<html>
2<head><title>403 Forbidden</title></head>
3<body>
4<center><h1>403 Forbidden</h1></center>
5<hr><center>nginx/1.21.6</center>
6</body>
7</html>

通信を送付したところ 403 が応答されていることがわかります。curlコマンドではデフォルトのMethodがGETであり、指定したポリシーの条件に該当しないためエラーとなっています

ポリシーに記述したMethodでリクエストを送付します。

curl -v -H "Host: webapp.example.com" http://localhost/ -X POST
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> POST / HTTP/1.1
 5> Host: webapp.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Server: nginx/1.21.6
12< Date: Wed, 30 Nov 2022 11:54:06 GMT
13< Content-Type: text/plain
14< Content-Length: 12
15< Connection: keep-alive
16< X-Mesh-Request-ID: 4a64156f62a3a613671af6e6650b9ac5
17< X-Mesh-Request-ID: 1df02eadd498d94aaaa0db3d76b901a3
18<
19target v1.0
20* Connection #0 to host localhost left intact

4行目で POST で通信が送付され、 10行目で 200 OK 19行目で正しく応答が返されていることが確認できます

ポリシーに記述したPathの条件を満たすリクエストを送付します。

curl -v -H "Host: webapp.example.com" http://localhost/dummy/this-is-valid-path/a.jpg
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET /dummy/this-is-valid-path/a.jpg HTTP/1.1
 5> Host: webapp.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Server: nginx/1.21.6
12< Date: Wed, 30 Nov 2022 11:58:30 GMT
13< Content-Type: image/jpeg
14< Content-Length: 12
15< Connection: keep-alive
16< X-Mesh-Request-ID: 0fa1636fee4c962e79fc7091bdb47e01
17< X-Mesh-Request-ID: 53d69013d169237c948b2a1ca0962428
18<
19target v2.0

4行目で 指定のPATHに通信が送付され、 10行目で 200 OK 19行目で正しく応答が返されていることが確認できます

ポリシーに記述したCookieの条件を満たすリクエストを送付します。

curl -v -H "Host: webapp.example.com" http://localhost/ -H "X-Type: valid"
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host: webapp.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8> X-Type: valid
 9>
10* Mark bundle as not supporting multiuse
11< HTTP/1.1 200 OK
12< Server: nginx/1.21.6
13< Date: Wed, 30 Nov 2022 11:59:23 GMT
14< Content-Type: text/plain
15< Content-Length: 12
16< Connection: keep-alive
17< X-Mesh-Request-ID: aa232231e3b082bd8487a907ec3d8e32
18< X-Mesh-Request-ID: 103858cec23cdc936331e4009aa20759
19<
20target v1.0
21* Connection #0 to host localhost left intact

8行目で指定したHTTP Headerが付与された通信が送付され、 11行目で 200 OK 20行目で正しく応答が返されていることが確認できます

この様にコンテナ内部の通信に対して、Deploymentに指定したService Accountを使って通信の制御を行うことが可能です

3. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f acl-nsm-smi/nic-vs-acl.yaml -n staging
kubectl delete -f acl-nsm-smi/nsm-acl.yaml -n staging

7. NICのCircuit Breaker (Passive Health Check)

まずCircuit Breakerの機能について説明します。Kubernetes環境等でデプロイされる昨今のWebサービスは、機能ごとにサービスが分割され、APIで接続している場合があります。

../../_images/circuit-breaker1.png

サービス内で障害がした際に、その障害が連続的に伝播し、サービス全体に影響が及ぶ状態となる可能性があります。

../../_images/circuit-breaker2.png

Circuit Breakerは、この障害の初期の時点で適切なフォールバックなどを実施することにより、サービス全体の停止や障害規模の拡大を防ぐことを目的とした機能です。

../../_images/circuit-breaker3.png

Circuit Breakerの動作を確認します。以下の構成で動作を確認します

../../_images/nic-vs-cb.png
  • NICで転送先サービスの状態を監視します

  • エラーと判定された場合に、別のサービスへリダイレクトします

  • target-v2-0-timeouttarget-v2-0-rst を用意しそれぞれの動作を確認します

1. サンプルアプリケーションのデプロイ

エラーを発生させるため、サンプルアプリケーションをデプロイします。

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl replace --force -f sample-app/target-v2.0-fail.yaml -n staging

デプロイの結果を確認します

kubectl get svc,pod -n staging
実行結果サンプル
 1NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
 2service/target-svc            ClusterIP   10.106.239.184   <none>        80/TCP    9m56s
 3service/target-v1-0           ClusterIP   10.108.92.55     <none>        80/TCP    132m
 4service/target-v2-0           ClusterIP   10.97.187.172    <none>        80/TCP    90m
 5service/target-v2-0-rst       ClusterIP   10.111.46.119    <none>        81/TCP    3s
 6service/target-v2-0-timeout   ClusterIP   10.108.182.110   <none>        80/TCP    4s
 7service/webapp-svc            ClusterIP   10.106.222.191   <none>        80/TCP    52s
 8
 9NAME                               READY   STATUS            RESTARTS   AGE
10pod/target-v1-0-6bc4b7d57f-klwk6   2/2     Running           0          132m
11pod/target-v2-0-847595548b-jbn5b   2/2     Running           0          4s
12pod/webapp-578d87f489-fjhvn        2/2     Running           0          52s

2. NIC設定のデプロイ・動作確認

まずは、通信を転送しエラーとなることを確認します。

NICに適用する設定の内容を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat cb-passive-nic-vs/nic-vs-cb1.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: nic
 5spec:
 6  host: nic.example.com
 7  upstreams:
 8  - name: target-v1
 9    service: target-v1-0
10    port: 80
11  - name: target-v2-timeout
12    service: target-v2-0-timeout
13    port: 80
14    fail-timeout: 20s
15    max-fails: 1
16    connect-timeout: 2s
17    send-timeout: 2s
18    read-timeout: 2s
19  - name: target-v2-rst
20    service: target-v2-0-rst
21    port: 81
22  routes:
23  - path: /v1
24    action:
25      pass: target-v1
26  - path: /v2-timeout
27    action:
28      pass: target-v2-timeout
29  - path: /v2-rst
30    action:
31      pass: target-v2-rst
  • 14-18行目に、upstream target-v2-timeout に対する、 Passive HealthCheck を設定しています

  • 16-18行目に、各種タイムアウト値を指定しています

  • パラメータの詳細は VS/VSR Upstream を参照してください

  • 23行目で、 /v1 の場合 target-v1 への転送、 26行目で、 /v2-timeout の場合 target-v2-timeout への転送、29行目で、 /v2-rst の場合 target-v2-rst への転送を設定しています

設定を反映します

kubectl apply -f cb-passive-nic-vs/nic-vs-cb1.yaml -n staging

それぞれの動作を確認します

アプリケーションがエラーとならない /v1 宛の通信を確認します

curl -v -H "Host: nic.example.com" http://localhost/v1
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET /v1 HTTP/1.1
 5> Host: nic.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Server: nginx/1.21.6
12< Date: Wed, 28 Dec 2022 04:06:28 GMT
13< Content-Type: text/plain
14< Content-Length: 12
15< Connection: keep-alive
16< X-Mesh-Request-ID: 5ee860e2d2437c81304a4503bfa0db35
17<
18target v1.0
19* Connection #0 to host localhost left intact

期待の通り 200 OK が応答され、正しくレスポンスの文字列 target v1.0 が表示されています

アプリケーションがタイムアウトする /v2-timeout 宛の通信を確認します

date; curl -v -H "Host: nic.example.com" http://localhost/v2-timeout; date
実行結果サンプル
 1Wed Dec 28 04:06:34 UTC 2022
 2*   Trying 127.0.0.1:80...
 3* TCP_NODELAY set
 4* Connected to localhost (127.0.0.1) port 80 (#0)
 5> GET /v2-timeout HTTP/1.1
 6> Host: nic.example.com
 7> User-Agent: curl/7.68.0
 8> Accept: */*
 9>
10* Mark bundle as not supporting multiuse
11< HTTP/1.1 504 Gateway Time-out
12< Server: nginx/1.21.6
13< Date: Wed, 28 Dec 2022 04:06:36 GMT
14< Content-Type: text/html
15< Content-Length: 167
16< Connection: keep-alive
17<
18<html>
19<head><title>504 Gateway Time-out</title></head>
20<body>
21<center><h1>504 Gateway Time-out</h1></center>
22<hr><center>nginx/1.21.6</center>
23</body>
24</html>
25* Connection #0 to host localhost left intact
26Wed Dec 28 04:06:36 UTC 2022
  • 19行目に、 504 Gateway Time-out と応答されることが確認できます

  • 1行目と26行目の時間を比較し、curlコマンド実行前後の時間を確認すると約2秒であることが確認できます。これは先程確認したNICの設定のタイムアウト値と同様となっています

アプリケーションがRSTを返す /v2-rst 宛の通信を確認します

curl -v -H "Host: nic.example.com" http://localhost/v2-rst
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET /v2-rst HTTP/1.1
 5> Host: nic.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 502 Bad Gateway
11< Server: nginx/1.21.6
12< Date: Wed, 28 Dec 2022 04:07:13 GMT
13< Content-Type: text/html
14< Content-Length: 157
15< Connection: keep-alive
16<
17<html>
18<head><title>502 Bad Gateway</title></head>
19<body>
20<center><h1>502 Bad Gateway</h1></center>
21<hr><center>nginx/1.21.6</center>
22</body>
23</html>
24* Connection #0 to host localhost left intact
  • 18行目に、 502 Bad Gateway と応答されることが確認できます

3. CircuitBreakerのデプロイ・動作確認

エラーに対し、フォールバックを行うためフォールバックの設定を行います。

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat cb-passive-nic-vs/nic-vs-cb2.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: nic
 5spec:
 6  host: nic.example.com
 7  upstreams:
 8  - name: target-v1
 9    service: target-v1-0
10    port: 80
11  - name: target-v2-timeout
12    service: target-v2-0-timeout
13    port: 80
14    fail-timeout: 20s
15    max-fails: 1
16    connect-timeout: 2s
17    send-timeout: 2s
18    read-timeout: 2s
19  - name: target-v2-rst
20    service: target-v2-0-rst
21    port: 81
22  routes:
23  - path: /v1
24    action:
25      pass: target-v1
26  - path: /v2-timeout
27    action:
28      pass: target-v2-timeout
29    errorPages:
30    - codes: [504]
31      redirect:
32        code: 301
33        url: ${scheme}://nic.example.com/v1
34  - path: /v2-rst
35    location-snippets: |
36      error_page 502 =302 "${scheme}://${host}/v1?${args}";
37      proxy_intercept_errors on;
38    action:
39      pass: target-v2-rst
  • 29-33行目に、 errorPage を指定します。このパラメータにより、対象のエラーコード(504)の場合の挙動を指定することができます。ここでは指定のパラメータでリダイレクトするよう指定しています

  • 35-37行目は、 location-snippets でNGINXの設定を指定します。29-33行目の記述と比較し、より詳細なパラメータの指定が必要となる場合にはこのようにコンフィグを記述することが可能です

  • errorPage のパラメータの詳細は VS/VSR ErrorPage を参照してください

  • location-snipetsで記述した各Directiveの詳細は error_pageproxy_intercept_errors を参照してください

設定をデプロイします

kubectl apply -f cb-passive-nic-vs/nic-vs-cb2.yaml -n staging

動作を確認します

アプリケーションがタイムアウトする /v2-timeout 宛の通信を確認します

date; curl -v -H "Host: nic.example.com" http://localhost/v2-timeout; date
実行結果サンプル
 1Wed Dec 28 04:17:25 UTC 2022
 2*   Trying 127.0.0.1:80...
 3* TCP_NODELAY set
 4* Connected to localhost (127.0.0.1) port 80 (#0)
 5> GET /v2-timeout HTTP/1.1
 6> Host: nic.example.com
 7> User-Agent: curl/7.68.0
 8> Accept: */*
 9>
10* Mark bundle as not supporting multiuse
11< HTTP/1.1 301 Moved Permanently
12< Server: nginx/1.21.6
13< Date: Wed, 28 Dec 2022 04:17:27 GMT
14< Content-Type: text/html
15< Content-Length: 169
16< Connection: keep-alive
17< Location: http://nic.example.com/v1
18<
19<html>
20<head><title>301 Moved Permanently</title></head>
21<body>
22<center><h1>301 Moved Permanently</h1></center>
23<hr><center>nginx/1.21.6</center>
24</body>
25</html>
26* Connection #0 to host localhost left intact
27Wed Dec 28 04:17:27 UTC 2022
  • 504 Gateway Time-out に変わり、 301 Moved Permanently が応答されることが確認できます。

  • レスポンスヘッダーの Location の値を確認すると、 http://nic.example.com/v1 となっていることが確認できます

アプリケーションがRSTを返す /v2-rst 宛の通信を確認します

curl -v -H "Host: nic.example.com" "http://localhost/v2-rst?a=1&b=2"
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET /v2-rst?a=1&b=2 HTTP/1.1
 5> Host: nic.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 302 Moved Temporarily
11< Server: nginx/1.21.6
12< Date: Wed, 28 Dec 2022 04:20:16 GMT
13< Content-Type: text/html
14< Content-Length: 145
15< Connection: keep-alive
16< Location: http://nic.example.com/v1?a=1&b=2
17<
18<html>
19<head><title>302 Found</title></head>
20<body>
21<center><h1>302 Found</h1></center>
22<hr><center>nginx/1.21.6</center>
23</body>
24</html>
25* Connection #0 to host localhost left intact
  • 502 Bad Gateway に変わり、 302 Found が応答されることが確認できます。

  • レスポンスヘッダーの Location の値を確認すると、 http://nic.example.com/v1?a=1&b=2 となり、設定の通りArgsの値を取得しリダイレクトしていることが確認できます

4. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f cb-passive-nic-vs/nic-vs-cb2.yaml -n staging
kubectl delete --force -f sample-app/target-v2.0-fail.yaml -n staging
kubectl apply -f sample-app/target-v2.0-successful.yaml -n staging

8. NICのCircuit Breaker (Active Health Check)

RSTの場合には即座にエラーコードに合わせた処理を実施していますが、 タイムアウトが発生した場合には、設定値に応じた待ち時間が発生します。

クライアントの通信が発生する前に、NICがアプリケーションの状態を能動的に確認する機能がアクティブヘルスチェックとなります

1. サンプルアプリケーションのデプロイ

7. NICのCircuit Breaker(Passive) で利用したサンプルアプリケーションをデプロイします

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl replace --force -f sample-app/target-v2.0-fail.yaml -n staging

デプロイの結果を確認します

kubectl get svc,pod -n staging
実行結果サンプル
 1NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
 2service/target-svc            ClusterIP   10.106.239.184   <none>        80/TCP    9m56s
 3service/target-v1-0           ClusterIP   10.108.92.55     <none>        80/TCP    132m
 4service/target-v2-0           ClusterIP   10.97.187.172    <none>        80/TCP    90m
 5service/target-v2-0-rst       ClusterIP   10.111.46.119    <none>        81/TCP    3s
 6service/target-v2-0-timeout   ClusterIP   10.108.182.110   <none>        80/TCP    4s
 7service/webapp-svc            ClusterIP   10.106.222.191   <none>        80/TCP    52s
 8
 9NAME                               READY   STATUS            RESTARTS   AGE
10pod/target-v1-0-6bc4b7d57f-klwk6   2/2     Running           0          132m
11pod/target-v2-0-847595548b-jbn5b   2/2     Running           0          4s
12pod/webapp-578d87f489-fjhvn        2/2     Running           0          52s

2. NICの設定をデプロイ

設定の内容を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat cb-active-nic-vs/nic-vs-act-cb.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: nic
 5spec:
 6  host: nic.example.com
 7  upstreams:
 8  - name: target-v1
 9    service: target-v1-0
10    port: 80
11  - name: target-v2-timeout
12    service: target-v2-0-timeout
13    port: 80
14    healthCheck:
15      enable: true
16      interval: 5s
17      fails: 3
18      passes: 3
19      port: 80
20      statusMatch: "! 400-599"
21  - name: target-v2-rst
22    service: target-v2-0-rst
23    port: 81
24  routes:
25  - path: /v1
26    action:
27      pass: target-v1
28  - path: /v2-timeout
29    action:
30      pass: target-v2-timeout
31    errorPages:
32    - codes: [502]
33      redirect:
34        code: 301
35        url: ${scheme}://nic.example.com/v1
  • 14-20行目で、Active Health Checkの設定をしています。宛先サービスのinterval、port番号、応答

  • 31-35行目で、エラーが発生した際のリダイレクトを設定します

設定をデプロイします

kubectl apply -f cb-active-nic-vs/nic-vs-act-cb.yaml -n staging

設定のデプロイ後、10秒ほど経過した後、NICのログを確認します

kubectl logs nic1-nginx-ingress-6764d84695-89ldh -n nginx-ingress --tail=10 | grep -a3 unhealthy
実行結果サンプル
12022/12/28 04:28:55 [notice] 21#21: signal 17 (SIGCHLD) received from 620
22022/12/28 04:28:55 [notice] 21#21: worker process 620 exited with code 0
32022/12/28 04:28:55 [notice] 21#21: signal 29 (SIGIO) received
42022/12/28 04:29:04 [warn] 625#625: peer is unhealthy while checking status code, health check "vs_staging_nic_target-v2-timeout_match" of peer 192.168.127.29:80 in upstream "vs_staging_nic_target-v2-timeout", port 80

3行目が設定の反映に関するログとなります。その約10秒後に、healthcheckで unhealthy となったことが記録されています

3. 動作確認

動作を確認します

date; curl -v -H "Host: nic.example.com" http://localhost/v2-timeout; date
実行結果サンプル
 1Wed Dec 28 04:31:33 UTC 2022
 2*   Trying 127.0.0.1:80...
 3* TCP_NODELAY set
 4* Connected to localhost (127.0.0.1) port 80 (#0)
 5> GET /v2-timeout HTTP/1.1
 6> Host: nic.example.com
 7> User-Agent: curl/7.68.0
 8> Accept: */*
 9>
10* Mark bundle as not supporting multiuse
11< HTTP/1.1 301 Moved Permanently
12< Server: nginx/1.21.6
13< Date: Wed, 28 Dec 2022 04:31:33 GMT
14< Content-Type: text/html
15< Content-Length: 169
16< Connection: keep-alive
17< Location: http://nic.example.com/v1
18<
19<html>
20<head><title>301 Moved Permanently</title></head>
21<body>
22<center><h1>301 Moved Permanently</h1></center>
23<hr><center>nginx/1.21.6</center>
24</body>
25</html>
26* Connection #0 to host localhost left intact
27Wed Dec 28 04:31:33 UTC 2022

リダイレクトの内容は 7. NICのCircuit Breaker(Passive) の手順と同様です。先程と異なりタイムアウトなく即座にリダイレクトしていることが確認できます

4. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f cb-passive-nic-vs/nic-vs-cb2.yaml -n staging
kubectl delete --force -f sample-app/target-v2.0-fail.yaml -n staging
kubectl apply -f sample-app/target-v2.0-successful.yaml -n staging

9. NSMのCircuit Breaker

Circuit Breakerの機能については 7. NICのCircuit Breaker(Passive) の内容を確認してください

NSMのCircuit Breakerの動作を確認します。以下の構成で動作を確認します

../../_images/nsm-cb.png
  • 通信の中継を行う webapp の転送先を target-v2-0-rst のみに変更します

  • target-v2-0-rst7. NICのCircuit Breaker(Passive) の通りRSTを返すアプリケーションが動作します

  • NSMは通信のエラーを判定した場合に、別のサービスへフォールバックします

1. サンプルアプリケーションをデプロイ

サンプルアプリケーションをデプロイします。

kubectl replace --force -f sample-app/target-v2.0-fail.yaml -n staging
kubectl replace --force -f sample-app/webapp-gw-targetv2.yaml -n staging

デプロイの結果を確認します

kubectl get svc,pod -n staging
実行結果サンプル
 1NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
 2service/target-svc            ClusterIP   10.106.239.184   <none>        80/TCP    62m
 3service/target-v1-0           ClusterIP   10.108.92.55     <none>        80/TCP    3h5m
 4service/target-v2-0           ClusterIP   10.97.187.172    <none>        80/TCP    143m
 5service/target-v2-0-rst       ClusterIP   10.105.133.134   <none>        81/TCP    10s
 6service/target-v2-0-timeout   ClusterIP   10.99.212.248    <none>        80/TCP    10s
 7service/webapp-svc            ClusterIP   10.111.198.141   <none>        80/TCP    7s
 8
 9NAME                               READY   STATUS    RESTARTS   AGE
10pod/target-v1-0-6bc4b7d57f-klwk6   2/2     Running   0          3h5m
11pod/target-v2-0-847595548b-85z7r   2/2     Running   0          10s
12pod/webapp-578d87f489-89hwz        2/2     Running   0          7s

2. NIC設定のデプロイ・動作確認

設定の内容を確認します

NICは特殊な設定は行わず、シンプルな通信制御の内容となります

cat cb-nsm-smi/nic-vs-cb.yaml
実行結果サンプル
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: webapp
 5spec:
 6  host: webapp.example.com
 7  upstreams:
 8  - name: webapp-svc
 9    service: webapp-svc
10    port: 80
11  routes:
12  - path: /
13    action:
14      pass: webapp-svc

設定をデプロイします

kubectl apply -f cb-nsm-smi/nic-vs-cb.yaml -n staging

デプロイの結果を確認します

kubectl get vs webapp -n staging
実行結果サンプル
1NAME     STATE   HOST                 IP    PORTS   AGE
2webapp   Valid   webapp.example.com                 40s

動作を確認します

curl -v -H "Host: webapp.example.com" http://localhost/
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host: webapp.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 502 Bad Gateway
11< Server: nginx/1.21.6
12< Date: Wed, 28 Dec 2022 04:39:41 GMT
13< Content-Type: text/html
14< Content-Length: 157
15< Connection: keep-alive
16<
17<html>
18<head><title>502 Bad Gateway</title></head>
19<body>
20<center><h1>502 Bad Gateway</h1></center>
21<hr><center>nginx/1.21.6</center>
22</body>
23</html>
24* Connection #0 to host localhost left intact

エラーが表示されることが確認できます

2. CircuitBreaker(NSM)のデプロイ・動作確認

NSMに適用するCircuit Breakerの設定を確認します

## cd ~/f5j-nginx-k8s-apigw-lab/example
cat cb-nsm-smi/nsm-smi-cb.yaml
実行結果サンプル
 1apiVersion: specs.smi.nginx.com/v1alpha1
 2kind: CircuitBreaker
 3metadata:
 4  name: circuit-breaker
 5spec:
 6  destination:
 7    kind: Service
 8    name: target-v2-0-rst
 9    namespace: staging
10  errors: 1
11  timeoutSeconds: 5
12  fallback:
13    service: staging/target-v1-0
14    port: 80
  • 2行目の通り、 kind: CircuitBreaker を設定しています

  • 6-9行目で、CircuitBreakerの対象の宛先を指定します

  • 10-11行目で、CircuitBreaker発生の条件を指定します

  • 12-14行目で、フォールバックする先のサービスを指定します

  • この例では、 target-v2-0-rst でエラーとなった場合に staging/target-v1-0:80 にフォールバックする設定となります

kubectl apply -f cb-nsm-smi/nsm-smi-cb.yaml -n staging

正しく設定が反映されていることを確認します

kubectl get cb -n staging
実行結果サンプル
1NAMESPACE   NAME              AGE
2staging     circuit-breaker   11s

3. 動作確認

動作を確認します

curl -v -H "Host: webapp.example.com" http://localhost/
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host: webapp.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Server: nginx/1.21.6
12< Date: Wed, 28 Dec 2022 04:41:15 GMT
13< Content-Type: text/plain
14< Content-Length: 12
15< Connection: keep-alive
16< X-Mesh-Request-ID: d684b305ee38d3e78e938b548e67ee53
17< X-Mesh-Request-ID: 6b1ef6c1416917345b4e6d0522347ebd
18<
19target v1.0
20* Connection #0 to host localhost left intact

10行目で 200 OK が応答されており、19行目で フォールバックにより target v1.0 が確認できます

4. 不要設定の削除

不要な設定を削除します

## cd ~/f5j-nginx-k8s-apigw-lab/example
kubectl delete -f cb-nsm-smi/nic-vs-cb.yaml -n staging
kubectl delete -f cb-nsm-smi/nsm-smi-cb.yaml -n staging
kubectl delete --force -f sample-app/target-v2.0-fail.yaml -n staging
kubectl apply -f sample-app/target-v2.0-successful.yaml -n staging
kubectl delete --force -f sample-app/webapp-gw-targetv2.yaml -n staging
kubectl apply -f sample-app/webapp-gw.yaml -n staging