ブログ | NGINX

NGINX チュートリアル: レート制限でKubernetes APIを保護する

ダニエレ・ポレンチッチ サムネイル
ダニエレ・ポレンチッチ
2022年3月21日公開

このチュートリアルは、2022 年 3 月の Microservices の概念を実践する 4 つのチュートリアルのうちの 1 つです。 Kubernetes ネットワーキング:

さらに多くの Kubernetes ネットワークユースケースで NGINX を使用するための詳細なガイダンスが必要ですか? 無料の電子書籍「NGINX を使用した Kubernetes トラフィックの管理」をダウンロードしてください。 実用ガイド

あなたの組織は、Kubernetes で最初のアプリと API をリリースしました。 トラフィック量が多くなることが予想されると言われていますが (NGINX Ingress Controller がトラフィックを迅速にルーティングできるように自動スケーリングがすでに実装されています)、API が悪意のある攻撃の標的になる可能性があるという懸念があります。 API が大量の HTTP リクエストを受信すると(ブルートフォース パスワード推測や DDoS 攻撃の可能性あり)、API とアプリの両方が過負荷状態になったり、クラッシュしたりする可能性があります。

でも、あなたは幸運です! レート制限と呼ばれるトラフィック制御技術は、受信リクエスト レートを実際のユーザーにとって標準的な値に制限する API ゲートウェイの使用例です。 NGINX Ingress Controller を構成してレート制限ポリシーを実装すると、リクエストが多すぎてアプリと API が過負荷になるのを防ぐことができます。 素晴らしい仕事です!

ラボとチュートリアルの概要

このブログは、マイクロサービス 2022 年 3 月のユニット 2 「Kubernetes での API の公開」のラボに付随するもので、複数の NGINX Ingress コントローラーをレート制限と組み合わせて、アプリと API が過負荷になるのを防ぐ方法を示しています。

チュートリアルを実行するには、次の条件を満たすマシンが必要です。

  • 2CPU以上
  • 2 GBの空きメモリ
  • 20 GB の空きディスク容量
  • インターネット接続
  • Docker、Hyperkit、Hyper-V、KVM、Parallels、Podman、VirtualBox、VMware Fusion/Workstation などのコンテナまたは仮想マシン マネージャー
  • minikubeがインストールされている
  • Helmがインストールされました
  • ブラウザ ウィンドウを起動できるようにする構成。 それが不可能な場合は、ブラウザ経由で関連サービスにアクセスする方法を検討する必要があります。

ラボとチュートリアルを最大限に活用するには、開始する前に次のことを実行することをお勧めします。

このチュートリアルでは次のテクノロジを使用します。

各チャレンジの手順には、アプリを構成するために使用される YAML ファイルの完全なテキストが含まれています。 GitHub リポジトリからテキストをコピーすることもできます。 各 YAML ファイルのテキストとともに GitHub へのリンクが提供されます。

このチュートリアルには 3 つの課題が含まれています。

  1. クラスター、アプリ、API、Ingress コントローラーをデプロイする
  2. アプリとAPIを圧倒する
  3. デュアルイングレスコントローラとレート制限でアプリとAPIを保護

課題1: クラスター、アプリ、API、Ingress コントローラーをデプロイする

このチャレンジでは、 Minikube クラスターをデプロイし、サンプル アプリと API としてPodinfo をインストールします。次に、 NGINX Ingress Controller をデプロイしトラフィック ルーティングを構成しIngress 構成をテストします

Minikube クラスターを作成する

minikubeクラスターを作成します。 数秒後、デプロイメントが成功したことを確認するメッセージが表示されます。

$ minikube start 🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default 

PodinfoアプリとPodinfo APIをインストールする

Podinfo は、 「Kubernetes でマイクロサービスを実行するためのベスト プラクティスを紹介する、Go で作成された Web アプリケーション」です。 フットプリントが小さいため、サンプル アプリと API として使用しています。

  1. 任意のテキスト エディターを使用して、次の内容を含む1-apps.yamlという YAML ファイルを作成します (またはGitHub からコピーします)。 以下を含むデプロイメントを定義します。

    • HTMLページをレンダリングするWebアプリ( Podinfo Frontendと呼びます)
    • JSONペイロードを返すAPI( Podinfo API
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: api 
    spec: 
      selector: 
        matchLabels: 
          app: api 
      template: 
        metadata: 
          labels: 
            app: api 
        spec: 
          containers: 
            - name: api 
              image: stefanprodan/podinfo 
              ports: 
                - containerPort: 9898 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: api 
    spec: 
      ports: 
        - port: 80 
          targetPort: 9898 
          nodePort: 30001 
      selector: 
        app: api 
      type: LoadBalancer 
    --- 
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: frontend 
    spec: 
      selector: 
        matchLabels: 
          app: frontend 
      template: 
        metadata: 
          labels: 
            app: frontend 
        spec: 
          containers: 
            - name: frontend 
              image: stefanprodan/podinfo 
              ports: 
                - containerPort: 9898 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: frontend 
    spec: 
      ports: 
        - port: 80 
          targetPort: 9898 
          nodePort: 30002 
      selector: 
        app: frontend 
      type: LoadBalancer
    
  2. アプリと API をデプロイします。

    $ kubectl apply -f 1-apps.yamldeployment.apps/api created 
    service/api created 
    deployment.apps/frontend created 
    service/frontend created 
    
  3. STATUS列の値がRunning であることから、 Podinfo APIおよびPodinfo Frontendのポッドが正常にデプロイされたことを確認します。

    $ kubectl get pods  
    NAME                        READY   STATUS    RESTARTS  AGE 
    api-7574cf7568-c6tr6        1/1     Running   0         87s 
    frontend-6688d86fc6-78qn7   1/1     Running   0         87s 
    
    
    

NGINX Ingress Controller をデプロイする

NGINX Ingress Controller をインストールする最も速い方法は、 Helmを使用することです。

Helm を使用して、 NGINX Ingress Controller を別の名前空間 ( nginx ) にインストールします。

  1. 名前空間を作成します。

    $ kubectl create namespace nginx 
    
  2. NGINX リポジトリを Helm に追加します。

    $ helm repo add nginx-stable https://helm.nginx.com/stable 
    
  3. クラスターに NGINX Ingress Controller をダウンロードしてインストールします。

    $ helm install main nginx-stable/nginx-ingress \  
     --set controller.watchIngressWithoutClass=true \ 
     --set controller.ingressClass=nginx \ 
     --set controller.service.type=NodePort \ 
     --set controller.service.httpPort.nodePort=30010 \ 
     --set controller.enablePreviewPolicies=true \ 
     --namespace nginx 
    
  4. STATUS列の値がRunning であることで示されるように、NGINX Ingress Controller ポッドがデプロイされていることを確認します (読みやすくするために、出力は 2 行に分かれています)。

    $ kubectl get pods -namespace nginx NAME                                  READY   STATUS   ...
    main-nginx-ingress-779b74bb8b-d4qtc   1/1     Running  ...
    
          ... RESTARTS   AGE 
          ... 0          92s 
    

アプリへのトラフィックをルーティングする

  1. 任意のテキスト エディターを使用して、次の内容を含む2-ingress.yamlという YAML ファイルを作成します (またはGitHub からコピーします)。 トラフィックをアプリと API にルーティングするために必要な Ingress マニフェストを定義します。
  2. apiVersion: networking.k8s.io/v1 
    kind: Ingress 
    metadata: 
      name: first 
    spec: 
      ingressClassName: nginx 
      rules: 
        - host: "example.com" 
          http: 
            paths: 
              - backend: 
                  service: 
                    name: frontend 
                    port: 
                      number: 80 
                path: / 
                pathType: Prefix 
        - host: "api.example.com" 
          http: 
            paths: 
              - backend: 
                  service: 
                    name: api 
                    port: 
                      number: 80 
                path: / 
                pathType: Prefix
    
  3. Ingress リソースをデプロイします。
  4. $ kubectl apply -f 2-ingress.yaml ingress.networking.k8s.io/first created 
    

イングレス構成をテストする

  1. Ingress 構成が期待どおりに動作していることを確認するには、一時的なポッドを使用してテストします。 クラスター内で使い捨てのBusyBoxポッドを起動します。

    $ kubectl run -ti --rm=true busybox --image=busybox If you don't see a command prompt, try pressing enter.
    / #
    
  2. ホスト名api.example.comを使用して NGINX Ingress Controller ポッドにリクエストを発行し、 Podinfo API をテストします。 表示される出力は、API がトラフィックを受信していることを示しています。

    / # wget --header="Host: api.example.com" -qO- main-nginx-ingress.nginx { 
      "hostname": "api-687fd448f8-t7hqk", 
      "version": "6.0.3", 
      "revision": "", 
      "color": "#34577c", 
      "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif", 
      "message": "greetings from podinfo v6.0.3", 
      "goos": "linux", 
      "goarch": "arm64", 
      "runtime": "go1.16.9", 
      "num_goroutine": "6", 
      "num_cpu": "4" 
    } 
    
  3. 同じ BusyBox ポッドで次のコマンドを発行して、Web ブラウザーをシミュレートし、Web ページを取得して、 Podinfo フロントエンドをテストします。 表示される出力は、Web ページの開始部分の HTML コードです。
    / # wget --header="Host: example.com" --header="User-Agent: Mozilla" -qO- main-nginx-ingress.nginx <!DOCTYPE html> 
    <html> 
    <head> 
      <title>frontend-596d5c9ff4-xkbdc</title> 
      # ...
    
  4. 別のターミナルで、ブラウザで Podinfo を開きます。 podinfo ページからの挨拶は、 Podinfo が実行中であることを示します。

    $ minikube service podinfo
    

    おめでとう! NGINX Ingress Controller はリクエストを受信し、それをアプリと API に転送します。

  5. 元のターミナルで、BusyBox セッションを終了します。

    / # exit 
    $
    

チャレンジ2: アプリとAPIを圧倒する

このチャレンジでは、オープンソースの負荷生成ツールであるLocust をインストールし、それを使用して、API を圧倒しアプリをクラッシュさせるトラフィックの急増をシミュレートします

Locustをインストールする

  1. 任意のテキスト エディターを使用して、次の内容を含む3-locust.yamlという YAML ファイルを作成します (またはGitHub からコピーします)。

    ConfigMapオブジェクトは、正しいヘッダーを備えたポッドに送信されるリクエストを生成するlocustfile.pyというスクリプトを定義します。 トラフィックはアプリと API の間で均等に分散されていません。リクエストはPodinfo APIに偏っており、5 件のリクエストのうち 1 件だけがPodinfo Frontendに送られます。

    Deployment オブジェクトServiceオブジェクトは Locust ポッドを定義します。

    apiVersion: v1 
    kind: ConfigMap 
    metadata: 
      name: locust-script 
    data: 
      locustfile.py: |- 
        from locust import HttpUser, task, between 
    
        class QuickstartUser(HttpUser): 
            wait_time = between(0.7, 1.3) 
    
            @task(1) 
            def visit_website(self): 
                with self.client.get("/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("Frontend failed") 
                    else: 
                        response.success() 
      
            @task(5) 
            def visit_api(self): 
                with self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("API failed") 
                    else: 
                        response.success() 
    --- 
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: locust 
    spec: 
      selector: 
        matchLabels: 
          app: locust 
      template: 
        metadata: 
          labels: 
            app: locust 
        spec: 
          containers: 
            - name: locust 
              image: locustio/locust 
              ports: 
                - containerPort: 8089 
              volumeMounts: 
                - mountPath: /home/locust 
                  name: locust-script 
          volumes: 
            - name: locust-script 
              configMap: 
                name: locust-script 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: locust 
    spec: 
      ports: 
        - port: 8089 
          targetPort: 8089 
          nodePort: 30015 
      selector: 
        app: locust 
      type: LoadBalancer
    
  2. ローカストを展開:

    $ kubectl apply -f  3-locust.yaml configmap/locust-script created 
    deployment.apps/locust created 
    service/locust created 
    
  3. Locust のデプロイメントを確認します。 次のサンプル出力では、検証コマンドはkubectl applyコマンドのわずか数秒後に実行されたため、 STATUSフィールドの Locust ポッドの値がContainerCreating であることからわかるように、インストールはまだ進行中です。 次のセクションに進む前に、値が実行中になるまで待ちます。 (読みやすくするために、出力は 2 行に分かれています。)
    $ kubectl get pods
    NAME                        READY   STATUS            ...           api-7574cf7568-c6tr6        1/1     Running           ...
    frontend-6688d86fc6-78qn7   1/1     Running           ...            locust-77c699c94d-hc76t     0/1     ContainerCreating ...
    
          ... RESTARTS   AGE 
          ... 0          33m
          ... 0          33m
          ... 0           4s
    

トラフィックの急増をシミュレートする

  1. ブラウザで Locust を開きます。

    $ minikube service locust
    
  2. フィールドに次の値を入力します。

    • ユーザー数1000
    • 出現率30
    • ホストhttp://main-nginx-ingress
  3. Start swarming」ボタンをクリックして、トラフィックをPodinfo APIPodinfo Frontendに送信します。 Locustチャート障害タブでトラフィック パターンを観察します。

    • グラフ– API リクエストの数が増えると、 Podinfo API の応答時間が悪化します。
    • 障害Podinfo APIPodinfo Frontend はIngress コントローラーを共有しているため、API リクエストの数が増えるとすぐに Web アプリがエラーを返すようになります。

これは、API を使用する 1 人の悪意のある人物が API だけでなく、NGINX Ingress Controller によって提供されるすべてのアプリを停止できるため、問題となります。

課題3: デュアルイングレスコントローラとレート制限でアプリとAPIを保護

最後のチャレンジでは、2 つの NGINX Ingress Controller をデプロイして、以前のデプロイの制限を排除し、それぞれに個別の名前空間を作成しPodinfo FrontendPodinfo APIに個別の NGINX Ingress Controller インスタンスをインストールし、アプリと API のトラフィックをそれぞれの NGINX Ingress Controller に送信するように Locust を再構成してレート制限が有効であることを確認します。 まず、アーキテクチャ上の問題にどのように対処するかを見てみましょう。 前回のチャレンジでは、API リクエストで NGINX Ingress コントローラーが過負荷になり、アプリにも影響が出ました。これは、単一の Ingress コントローラーが Web アプリ ( Podinfo Frontend ) と API ( Podinfo API ) の両方へのトラフィックのルーティングを担当していたために発生しました。

各サービスに対して個別の NGINX Ingress Controller ポッドを実行すると、過剰な API リクエストによってアプリが影響を受けるのを防ぐことができます。 これは必ずしもすべてのユースケースに必要なわけではありませんが、私たちのシミュレーションでは、複数の NGINX Ingress コントローラーを実行することの利点を簡単に確認できます。

Podinfo API が過負荷になるのを防ぐソリューションの 2 番目の部分は、NGINX Ingress Controller を API ゲートウェイとして使用してレート制限を実装することです。

 

レート制限とは何ですか?

レート制限は、ユーザーが特定の期間内に実行できるリクエストの数を制限します。 たとえば、DDoS 攻撃を軽減するには、レート制限を使用して、受信リクエスト レートを実際のユーザーの標準的な値に制限できます。 NGINX でレート制限が実装されている場合、リクエストを多数送信するクライアントはエラー ページにリダイレクトされるため、API に悪影響を与えることはありません。この仕組みについては、 NGINX Ingress Controller のドキュメントを参照してください。

API ゲートウェイとは何ですか?

API ゲートウェイは、クライアントからの API リクエストを適切なサービスにルーティングします。 この単純な定義の大きな誤解は、API ゲートウェイが独自のテクノロジーであるという点です。 そうではありません。 むしろ、「API ゲートウェイ」は、さまざまな種類のプロキシ (最も一般的なのは ADC またはロード バランサとリバース プロキシですが、Ingress コントローラやサービス メッシュも増えています) を介して実装できる一連のユース ケースを表します。 レート制限は、API ゲートウェイをデプロイする場合の一般的な使用例です。 KubernetesでのAPIゲートウェイのユースケースの詳細については、 どうやって選べばいいですか? API ゲートウェイ vs. Ingress コントローラー vs. サービスメッシュ 私たちのブログで。

クラスターを準備する

新しいアーキテクチャとレート制限を実装する前に、以前の NGINX Ingress Controller 構成を削除する必要があります。

  1. NGINX Ingress Controller 構成を削除します。

     

    $ kubectl delete -f 2-ingress.yaml 
    ingress.networking.k8s.io "first" deleted 
    
    
    
  2. Podinfo Frontend用にnginx‑webという名前空間を作成します。

    $ kubectl create namespace nginx-web 
    namespace/nginx-web created 
    
    
    
  3. Podinfo API用にnginx‑apiという名前空間を作成します。

    $ kubectl create namespace nginx-api 
    namespace/nginx-api created 
    
    
    

Podinfo フロントエンド用の NGINX Ingress コントローラーをインストールする

  1. NGINX Ingress Controller をインストールします。

    $ helm install web nginx-stable/nginx-ingress  
      --set controller.ingressClass=nginx-web \ 
      --set controller.service.type=NodePort \ 
      --set controller.service.httpPort.nodePort=30020 \ 
      --namespace nginx-web
    
    
    
  2. Podinfo Frontend用の4-ingress-web.yamlという Ingress マニフェストを作成します (またはGitHub からコピーします)。

    apiバージョン: k8s.nginx.org/v1 種類: ポリシーメタデータ: 名前: レート制限ポリシー 仕様: rateLimit: レート: 10r/s キー:${binary_remote_addr}ゾーンサイズ: 10M --- apiバージョン: k8s.nginx.org/v1 種類: 仮想サーバーのメタデータ: 名前: api-vs 仕様: ingressClassName: nginx-api ホスト: api.example.com ポリシー: - 名前: rate-limit-policy アップストリーム: - 名前: api サービス: api ポート: 80 ルート: - パス: / アクション: パス: api
  3. 新しいマニフェストをデプロイします。

    $ kubectl apply -f 4-ingress-web.yaml 
    ingress.networking.k8s.io/frontend created  
    
    
    

ローカストを再構成する

次に、Locust を再構成し、次のことを確認します。

  • Podinfo API はオーバーロードされません。
  • Podinfo APIにいくつリクエストが送信されても、 Podinfo Frontendには影響はありません。

次の手順を実行します。

  1. Locust スクリプトを次のように変更します。

    • Podinfo Frontendへのすべてのリクエストは、 http://web-nginx-ingress.nginx-webにあるnginx-web NGINX Ingress Controllerに送信されます。
    • Podinfo APIへのすべてのリクエストは、 http://api-nginx-ingress.nginx-apinginx‑api NGINX Ingress コントローラに送信されます。

    Locust はダッシュボードで 1 つの URL のみをサポートするため、次の内容の YAML ファイル6-locust.yamlを使用して Python スクリプトに値をハードコードします (またはGitHub からコピーします)。 各タスクの URL をメモします。

    
    apiVersion: v1 
    kind: ConfigMap 
    metadata: 
      name: locust-script 
    data: 
      locustfile.py: |- 
        from locust import HttpUser, task, between 
    
        class QuickstartUser(HttpUser): 
            wait_time = between(0.7, 1.3) 
    
            @task(1) 
            def visit_website(self): 
                with self.client.get("http://web-nginx-ingress.nginx-web/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("Frontend failed") 
                    else: 
                        response.success() 
      
    
            @task(5) 
            def visit_api(self): 
                with self.client.get("http://api-nginx-ingress.nginx-api/", headers={"Host": "api.example.com"}, timeout=0.2) as response: 
                    if response.request_meta["response_time"] > 200: 
                        response.failure("API failed") 
                    else: 
                        response.success() 
    --- 
    apiVersion: apps/v1 
    kind: Deployment 
    metadata: 
      name: locust 
    spec: 
      selector: 
        matchLabels: 
          app: locust 
      template: 
        metadata: 
          labels: 
            app: locust 
        spec: 
          containers: 
            - name: locust 
              image: locustio/locust 
              ports: 
                - containerPort: 8089 
              volumeMounts: 
                - mountPath: /home/locust 
                  name: locust-script 
          volumes: 
            - name: locust-script 
              configMap: 
                name: locust-script 
    --- 
    apiVersion: v1 
    kind: Service 
    metadata: 
      name: locust 
    spec: 
      ports: 
        - port: 8089 
          targetPort: 8089 
          nodePort: 30015 
      selector: 
        app: locust 
      type: LoadBalancer 
    
    
    
  2. 新しい Locust 構成をデプロイします。 出力では、スクリプトが変更されたが、他の要素は変更されていないことが確認できます。

    $ kubectl apply -f 6-locust.yaml 
    configmap/locust-script configured 
    deployment.apps/locust unchanged 
    service/locust unchanged
    
    
    
  3. 新しい ConfigMap を強制的にリロードするには、Locust ポッドを削除します。 削除するポッドを識別するために、 kubectl delete podコマンドの引数は、すべてのポッドのリストから Locust ポッドを選択するパイプ コマンドとして表現されます。

    $ kubectl delete pod `kubectl get pods | grep locust | awk {'print $1'}` 
    
    
    
  4. Locust がリロードされたことを確認します ( AGE列の Locust ポッドの値は数秒のみです)。

    $ kubectl get pods
    NAME                        READY   STATUS   ...           api-7574cf7568-jrlvd        1/1     Running  ...
    frontend-6688d86fc6-vd856   1/1     Running  ...            locust-77c699c94d-6chsg     0/1     Running  ...
    
          ... RESTARTS   AGE 
          ... 0        9m57s
          ... 0        9m57s
          ... 0           6s
    
    
    

レート制限を確認する

  1. Locust に戻り、次のフィールドのパラメータを変更します。

    • ユーザー数400
    • 出現率10
    • ホストhttp://main-nginx-ingress
  2. Start swarming」ボタンをクリックして、トラフィックをPodinfo APIPodinfo Frontendに送信します。

    左上の Locust タイトル バーで、 STATUS列のユーザー数が増加すると、 FAILURES列の値も増加することがわかります。 ただし、API に設定されたレート制限により過剰なリクエストが拒否されるため、エラーはPodinfo フロントエンドからではなく、 Podinfo APIから発生するようになりました。 右下のトレースを見ると、NGINXがメッセージを返していることがわかります。503サービスは一時的に利用できません。これはレート制限機能の一部であり、カスタマイズ可能です。 API はレート制限されており、Web アプリケーションは常に利用可能です。 よくやった!

次のステップ

現実の世界では、レート制限だけでは、アプリや API を悪意のある行為者から保護するのに十分ではありません。 Kubernetes アプリ、API、インフラストラクチャを保護するには、次の方法のうち少なくとも 1 つまたは 2 つを実装する必要があります。

  • 認証と承認
  • Web アプリケーション ファイアウォールと DDoS 保護
  • エンドツーエンドの暗号化とゼロトラスト
  • 業界規制への準拠

これらのトピックとその他のトピックについては、「マイクロサービス 2022 年 3 月 – Kubernetes のマイクロサービス セキュリティ パターン」のユニット 3 で取り上げます。 NGINX Plus および NGINX App Protect を搭載した NGINX Ingress Controller for Kubernetes を試すには、今すぐ30 日間の無料トライアルを開始するか、お問い合わせの上、ユースケースについてご相談ください。 NGINX Open Source で NGINX Ingress Controller を試すには、リリース ソース コードを入手するか、 DockerHubからビルド済みコンテナーをダウンロードします。


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 q。"