Kubernetesのイングレス/エグレストラフィック管理を簡素化する方法

NGINX | 2021年6月28日

One of the ways a サービスメッシュをKubernetes環境でIngressコントローラーとは別に設定する必要がある場合、環境の管理がかえって複雑になることがあります。個別の設定は、単に時間がかかるだけではありません。適切なトラフィックルーティングを妨げる設定エラーが発生する可能性が高まり、悪意のある第三者がアクセス制限されたアプリに侵入するといったセキュリティ上の脆弱性や、許可されたアプリにお客さまがアクセスできないといったユーザー体験の低下につながることさえあります。個別の設定に時間がかかるだけでなく、エラーのトラブルシューティングにもさらに時間を費やすことになります。

これらの問題を回避して時間を節約するには、NGINX PlusベースのNGINX Ingress ControllerとNGINX Service Meshを統合し、受信と送信の両方のmTLSトラフィックを制御します。この動画デモでは、その手順を最初から最後までご紹介します。

関連ドキュメントについては、以下のセクションを参照してください。

前提条件(0:18)

実際のデモを開始する前に、以下の前提条件を満たしました。

  1. KubernetesクラスターにNGINX Server Meshコントロールプレーンをインストールし、サービスメッシュ用にmTLSとstrictポリシーを設定しました。
  2. Installed NGINX PlusベースのNGINX Ingress ControllerをKubernetesクラスターにDaemonSetではなくDeploymentとしてインストールし、egressを有効化して、タイプがLoadBalancerのサービスとして公開しました。注:このデモは、NGINX Open SourceベースのNGINX Ingress Controllerでは動作しません。読みやすくするため、以降の本ブログでは、NGINX PlusベースのNGINX Ingress Controllerを単に「NGINX Ingress Controller」と表記します。
  3. F5の手順に従ってサンプルbookinfoアプリをダウンロードし、NGINX Service Meshサイドカーを挿入して、アプリをデプロイしました。

ステップ1で作成したstrictポリシーにより、メッシュ外のクライアントからbookinfoアプリへのリクエストはサイドカーで拒否されます。デモでは、まず次のコマンドを実行してポートフォワーディングを設定し、この動作を示します。

> kubectl port-forward svc/product-page 9080Forwarding from 127.0.0.1:9080 -> 9080
Forwarding from [::1]:9080 -> 9080
Handling connection for 9080

アプリにアクセスしようとすると、ローカルマシンがサービスメッシュに含まれていないため、ステータスコード 503 が返されます:

> curl localhost:9080503

NGINX Service Meshを使用したNGINX Ingress Controllerのデプロイ(1:50)

アプリを公開するプロセスの最初の段階では、NGINX Ingress Controllerインスタンスをデプロイします。対応する手順については、チュートリアル「Deploy with NGINX Plus Ingress Controller for Kubernetes」をご覧ください。

この目的のために、NGINXではDeploymentとDaemonSetの両方のマニフェストを提供しています。このデモでは、Deploymentマニフェストの「nginx-plus-ingress.yaml」を使用します。このマニフェストには、受信トラフィックと送信トラフィックの両方を同じNGINX Ingress Controllerインスタンス経由でルーティングするためのアノテーションが含まれています。

コード例を読み込み中…

このマニフェストにより、NGINX Ingress ControllerをNGINX Service Meshの認証局(CA)であるSpireと直接統合できるため、NGINX Service MeshサイドカーをNGINX Ingress Controllerに挿入する必要がなくなります。代わりに、NGINX Ingress ControllerはSpire CAから証明書と鍵を直接取得し、メッシュ内のPodとのmTLSに使用します。このマニフェストでは、Spireエージェントのアドレスを指定します。

コード例を読み込み中…

さらに、SpireエージェントのUNIXソケットをNGINX Ingress Controllerポッドにマウントします。

コード例を読み込み中…

マニフェストについて最後に注目すべき点は、-enable-internal-routes CLI引数です。この引数により、egressサービスへのルーティングが可能になります。

コード例を読み込み中…

デモを開始する前に、kubectl apply -f nginx-plus-ingress.yamlコマンドを実行してNGINX Ingress Controllerをインストールしました。ここでは、nginx-ingress名前空間のデプロイを確認します。以下の出力のREADY列に示すように、NGINX Ingress ControllerポッドにはNGINX Service Meshサイドカーを挿入していないため、コンテナは1つだけです。

また、NGINX Ingress Controllerの外部IPアドレス(ここでは35.233.133.188)をクラスター外に公開するため、タイプがLoadBalancerのサービスもデプロイしました。このIPアドレスを使用して、サンプルbookinfoアプリケーションにアクセスします。

> kubectl get pods --namespace=nginx-ingressNAME                                 READY   STATUS    RESTARTS   AGE
pod/nginx-ingress-867f954b8f0fzdrm   1/1     Running   0          3d3h

NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP      ...
service-nginx-ingress   LoadBalancer   10.31.245.207   35.233.133.188   ...

      ... PORT(S)                      AGE
      ... 80:31469/TCP,443:32481/TCP   4d2h
...

標準のKubernetes Ingressリソースを使用してアプリを公開する(3:55)

次に、メッシュ内の「bookinfo」アプリを公開します。これには、bookinfo-ingress.yamlで定義されている標準のKubernetes Ingressリソースを使用します。対応する手順については、チュートリアル「NGINX Plus Ingress Controllerを使用したアプリケーションの公開」をご覧ください。

コード例を読み込み中…

このリソースは、10行目でbookinfoアプリのKubernetes Secretを参照し、bookinfo.example.comへのリクエストをproductpageサービスに送信するよう指定したルーティングルール(11~18行目)を含みます。このSecretはbookinfo-secret.yamlで定義されています。

コード例を読み込み中…

次のコマンドを実行して、キーと証明書を読み込みます。デモでは自己署名証明書を使用します。

> kubectl apply -f bookinfo-secret.yamlsecret/bookinfo-secret unchanged

Ingressリソースを有効化します:

> kubectl apply -f bookinfo-ingress.yamlingress.networking.k8s.io/bookinfo-ingress deleted

出力の末尾にあるイベントを確認し、Ingress Controllerがリソースで定義されたルートを追加したことを検証します。

> kubectl describe ingress bookinfo-ingress...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  5s    nginx-ingress-controller  Configuration for ...
        ...default/bookinfo-ingress was added or updated

このデモでは、ブラウザーを使用して、bookinfoアプリ(https://bookinfo.example.com/)にアクセスします。(前述のとおり、ローカルの/etc/hostsファイルには、Ingress ControllerサービスのIPアドレス(このデモでは35.233.133.188)とbookinfo.example.comのマッピングをあらかじめ追加しています。手順については、ドキュメントを参照してください。)ページのBook Reviewsセクションに表示される情報は、リクエストがbookinfo.yamlで定義されたreviewsサービスの3つのバージョンに順番に振り分けられるため、定期的に変わります(ダウンロード)。

次に、クラスターへの受信トラフィックを調査します。generate-traffic.shスクリプトを実行し、NGINX Ingress ControllerのパブリックIPアドレス経由でproductpageサービスにリクエストを送信します。その後、nginx-meshctlのtopコマンドを実行してトラフィックを監視します。

> nginxmesh-ctl top deploy/productpage-v1Deployment       Direction  Resource       Success Rate  P99    P90    P50   ...
productpage-v1  
                 To         details-v1     100.00%       3ms    3ms    2ms   
                 To         reviews-v2     100.00%       99ms   90ms   20ms
                 To         reviews-v3     100.00%       99ms   85ms   18ms
                 To         reviews-v1     100.00%       20ms   17ms   9ms
                 From       nginx-ingress  100.00%       192ms  120ms  38ms

      ... NumRequests 
      ... 14
      ... 5
      ... 5
      ... 12

NGINX VirtualServerリソースを使用してアプリを公開する(6:45)

次に、アプリを公開する別の方法として、NGINX VirtualServerリソースを使用する方法を紹介します。これはNGINX Ingress Controllerのカスタムリソースで、トラフィック分割やコンテンツベースルーティングなど、より複雑なトラフィック処理をサポートします。

まず、標準のIngressリソースを削除します。

> kubectl delete -f bookinfo-ingress.yamlingress.networking.k8s.io "bookinfo-ingress" deleted

bookinfo-vs.yamlファイルでは、bookinfo-ingress.yamlと同じSecretを使用してmTLSを構成しています(7~8行目)。9~12行目では、productpageサービスをアップストリームとして定義し、13~24行目では、GETメソッドでbookinfo.example.comに送信されたすべてのリクエストをそのアップストリームに転送するルートを定義しています。GET以外のHTTPメソッドに対しては、ステータスコード405を返します。

コード例を読み込み中…

リソースを適用します。

> kubectl apply -f bookinfo-vs.yamlvirtualserver.kubernetes.nginx.org/bookinfo-vs created

次に、Ingressリソースの場合と同じ手順を実行します。つまり、kubectl describeコマンドを実行して正しくデプロイされていることを確認し、ブラウザーでアプリにアクセスします。アプリが正常に動作していることは、POSTメソッドが拒否されることでも確認できます。

> curl -k -X POST https://bookinfo.example.com/Method not allowed

NGINX Ingress Controllerを使用したセキュアなエグレスルートの構成(8:44)

ここでは、NGINX Ingress Controllerを介してエグレストラフィックをルーティングする方法を説明します。チュートリアル「NGINX Plus Ingress Controllerを使用したセキュアなエグレスルートの構成」では、別のサンプルアプリを使用して、その手順を解説しています。

シンプルなbash Podをbash.yamlですでに定義し、リクエストの送信元となるdefault名前空間にデプロイしています。この出力のREADY列に示されているように、NGINX Service Meshサイドカーが挿入されています。

> kubectl get allNAME                        READY  STATUS    RESTARTS   AGE
pod/bash-6ccb678958-zsgm7   2/2    Running   0          77s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.31.240.1   <none>        443/TCP   4d2h
...

Pod内から、NGINX Service Meshに含まれないエンティティであるエグレスサービスへのリクエストを有効にするユースケースはいくつかあります。たとえば、次の場所にデプロイされたサービスです。

  • クラスター外
  • 別のクラスター上
  • 同じクラスター上にあるが、NGINX Service Meshサイドカーがインジェクトされていない

このデモでは、最後のユースケースについて説明します。アプリケーションはlegacy名前空間にデプロイされています。この名前空間はNGINX Service Meshの制御下になく、NGINX Service Meshサイドカーの自動インジェクションも無効になっています。このアプリケーションでは、1つのPodのみが稼働しています。

> kubectl get all --namespaces=legacyNAME                          READY  STATUS    RESTARTS   AGE
pod/target-5f7bcb96c6-km9lz   1/1    Running   0          27m

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/target-svc   ClusterIP   10.31.245.213   <none>        80/TCP,443/TCP   27m
...

NGINX Service Meshにはstrict mTLSポリシーが設定されているため、bash Podからターゲットサービスにリクエストを直接送信できないことに注意してください。両者間で相互に認証できないためです。送信を試みると、次に示すようにステータスコード503が返されます。

> kubectl exec -it bash-6ccb678958-zsgm7 -c bash -- curl target-svc.legacycurl: (56) Recv failure: connection reset by peer
503command terminated with exit code 56

解決するには、bash PodがNGINX Ingress Controller経由で外向きトラフィックを送信できるようにします。bash.yamlの14~15行目にあるアノテーションのコメントを解除します。

コード例を読み込み中…

次に、新しい設定を適用します。

> kubectl apply -f bash.yamldeployment.apps/bash configured

新しいbash Podが起動したことを確認します:

> kubectl get podsNAME                    READY  STATUS        RESTARTS   AGE
bash-678c8b4579-7sfml   2/2    Running       0          6s
bash-6ccb678958-zsgm7   2/2    Terminating   0          3m28s

ここで、前と同じkubectl execコマンドを実行し、bash Podからターゲットサービスにリクエストを送信すると、ステータスコードは503ではなく404になります。これは、bash PodからNGINX Ingress Controllerへのリクエスト送信には成功したものの、ルートが定義されていないため、NGINX Ingress Controllerが転送先を特定できないことを示しています。

legacy-route.yamlに次のIngressリソース定義を記述し、必要なルートを作成します。7行目の「internal-route」アノテーションは、ターゲットサービスがインターネットには公開されず、NGINX Service Mesh内のワークロードにのみ公開されることを意味します。

コード例を読み込み中…

新しいリソースを有効化し、NGINX Ingress Controllerがそのリソースで定義されたルートを追加したことを確認します。

> kubectl apply -f legacy-route.yamlingress.networking.k8s.io/target-internal-route created
> kubectl describe ingress target-internal-route -n legacy
...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  6s    nginx-ingress-controller  Configuration for ...
        ...legacy/target-internal-route was added or updated

ここで、kubectl execコマンドを実行すると、ターゲットサービスに到達できます。

{"req": {"method": "GET"                "url": "/",
                "host": "target-svc.legacy",
                "remoteAddr": "10.28.2.76:56086"}}

送信トラフィックをNGINX Ingress Controller経由でルーティングする利点は、クラスター内からアクセスできる外部サービスを厳密に制御できることです。アクセスできるのは、ルートを定義したサービスだけです。

デモの最後に、エグレス・トラフィックを監視する方法をご紹介します。まず、kubectl execコマンドを実行して複数のリクエストを送信し、次に以下のコマンドを実行します。

> nginxmesh-ctl top deploy/nginx-ingress -n nginx-ingressDeployment      Direction  Resource  Success Rate  P99  P90  P50  NumRequests
nginx-ingress  
                To         target    100.00%       1ms  1ms  1ms  9
                From       bash      100.00%       0ms  0ms  0ms  9

レイテンシーに「ノー」を — NGINX Service MeshとNGINX Ingress Controllerをお試しください

多くのサービスメッシュには、イングレスゲートウェイとエグレスゲートウェイのオプションがありますが、NGINXとの統合には、レイテンシーを低減できるというメリットもあります。ほとんどのメッシュでは、Ingressコントローラーにサイドカーを挿入する必要があるため、アプリケーションに到達するまでのトラフィックに余分なホップが発生します。わずかな遅延も重要です。この余分なホップによってデジタルエクスペリエンスが低下すると、顧客が他社のサービスを選ぶ可能性があります。NGINX Service Meshは、NGINX Ingress Controllerにサイドカーを挿入しないため、不要なレイテンシーを追加しません。代わりに、メッシュのCAであるSpireと直接統合することで、NGINX Ingress ControllerがNGINX Service Meshの一部になります。NGINX Ingress ControllerはSpireエージェントから証明書と鍵を取得し、それらを使用して、メッシュ化されたPodとのmTLS証明書交換に参加します。

NGINX Ingress Controller for Kubernetesには、NGINX Open SourceとNGINX Plusの2つのバージョンがあります。このブログで説明しているように、NGINX Ingress ControllerをNGINX Service Meshとともにデプロイするには、NGINX Plusバージョンを使用する必要があります。NGINX Plusは、30日間無料でお試しいただけます。

NGINX Service Meshは完全無料で、今すぐダウンロードできます。導入は10分未満で完了します。まずはドキュメントをご覧いただき、使用した感想をGitHubでお聞かせください。


共有
タグ: 2021, F5 NGINX, Tech

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります