はじめに
本連載では、NGINX(エンジンエックス)が求められている時代背景と、そのユースケース、具体的な設定手順について複数回に分けて解説致します。第2回目となる本稿では、NGINX Ingress Controllerの柔軟なアプリケーション制御について、具体的なユースケースと設定方法を交えて詳しく解説します。
本番環境でアプリケーションを動作させるにあたって
Kubernetes環境に求められる要件
第1回でもご紹介した通り、近年、皆様を取り巻くビジネス環境の競争は激化しており、その中でデジタル技術を最大限活用し顧客のニーズに合わせたサービスを実現することが業界問わず必須要件となっています。そのアプリケーションを、コンテナ技術を用いてデプロイし、サービスの伸縮性と継続的なアプリケーション開発を実現するために注目されているのがKubernetesです。
各社より様々なKubernetes プラットフォームが提供されており、それらをアプリケーションの要件に合わせて自由に選択することが可能です。これらを用いて本番環境でアプリケーションをデプロイし、顧客へのサービスを開始すると開発環境では見られなかった問題が発生します。今回はNGINX Plusを用いたNGINX Ingress Controllerによりそれらの問題を解決する方法をご紹介いたします。
本題に入る前にそれぞれのフェーズで発生する課題について見ていきましょう(図1)。
図1:アプリケーションコンテナ化における課題
コンテナ・Kubernetesの利用を開始した際には、まずそれぞれのテクノロジーが持つ特徴を理解し、組織やチームが求めるゴールに向かってアプリケーションの開発を進めていきます。その後、本番環境でのアプリケーションの稼働が開始します。外部との接続により様々な通信が流れ始めますので、プラットフォームを安定させるための制御やセキュリティの実装、可視化が必要になり、これらの制御を各チームが並行して柔軟に実施する必要があります。そしてサービスが拡大すると、デプロイするアプリケーションが増え様々な要件に対応しながらも、プラットフォーム全体として高い安定性とセキュリティを実現する必要があります。
Kubernetesを使いこなすことが企業の原動力となり、高い競争力を得られることは間違いありませんが、これらの課題を解決するためには今までの手法ではなくKubernetesに最適な形で実装することが求められます。運用負荷を大きく増やすことなくこれらの課題に柔軟に対応する方法が、NGINX Ingress Controllerを用いた通信制御なのです。
NGINX Ingress Controllerが提供する
高度なトラフィック制御と権限管理
旧来のモノリシックな環境ではネットワーク機器や負荷分散機器の運用を熟達した担当者が行い、チームでのミスを事前に防ぐ作業レビューにより障害を未然に回避していました。Kubernetes環境においても、アプリケーションを外部公開する際には同等の品質を担保しなければいけません。例えば、負荷分散の役割を持つIngress Controllerが想定外の設定となりアプリケーションの通信が停止する場合があります。
しかし、アジャイル開発では複数のチームが有機的に連携しながら高速なアプリケーションリリースを実現することが必要不可欠です。旧来のモノリシックな環境で行っていた、開発とインフラチームが別れ分業化された運用では、柔軟なアプリケーションリリースができず、競合他社に出し抜かれる可能性があります。Ingress Controllerが本来持つ通信制御の役割をそのままに、これらの問題を解決するにはどうしたらいいのでしょうか?
それを解決するのが、NGINX PlusによるNGINX Ingress Controllerです。VirtualServer / VirtualServerRouteという新たに拡張したリソース(CRD)により、Ingress ControllerのManifestで必要となっていた多くのAnnotationを用いること無く、比較的NGINX Configurationに近い方式で柔軟な設定を行うことが可能です。また、VirtualServer、VirtualServerRoute、Policyというリソースで表現される設定をKubernetesのRBACと組み合わせる事により、NGINX Ingress Controllerの設定をそれぞれ適切なユーザ・チームでの変更・管理を行うことにより、変更による影響範囲を適切に管理し、想定外の動作による影響をサービス全体に波及させることのないようにすることが可能です。また、設定を変更する人にとっては、自分の設定行為で影響する範囲が限定されているので、自身が担当するアプリケーションデプロイに関わる機能に集中することができ、自由に設定の反映や更新を行うことが可能となります。
拡張したリソース(CRD)を用いた設定のイメージが以下となります(図2)。
図2:NGINXが提供するCRDと各チームによる管理
インフラ担当者がVirtualServerというリソースを書式に則って記述します。この例では、2つのサービスがありますが、それぞれに該当するURIを各サービスのVirtualServerRouteに紐付けます。また、これらのサービスが動作する環境は本番環境となりますので、別途セキュリティ担当者が定義したセキュリティポリシーをインフラ担当者が管理するVirtualServerで参照しています。それぞれの担当は担当外の設定を意識する必要は無く、またセキュリティポリシーなどは別のチームが作成したポリシーを要件に応じて選択・参照することで要件を満たすことが可能となります。
Manifestを用いた
NGINX Ingress Controller環境のセットアップ
それではまず環境のセットアップを行います。こちらの記事は執筆時点に記載されている手順で行っております。最新の機能や新たなKubernetes Versionへの対応が必要な場合には、最新の操作手順(Installation with Manifests)の内容をご確認ください。
NGINX Ingress ControllerのDocker Imageを作成します。手順はこちらの記事(Building the Ingress Controller Image)を参照してください。
事前作業
今回はNGINX Plusの機能をご紹介いたしますので、予めNGINX Ingress Controllerの無料トラアイルよりライセンスファイルを取得してください。また、作成したDockerイメージをコンテナレジストリに登録して利用する場合、対象のコンテナレジストリにログインが可能であることを確認してください。
NGINX Plus/NGINX App Protect Ingress Controllerの
Dockerイメージ作成
それでは早速セットアップを進めていきましょう。Dockerイメージ作成に必要な各種ファイルやKubernetes環境セットアップに用いるファイル群をGitHubより取得します。
3 | # cd ~/kubernetes-ingress |
Dockerイメージを作成します。今回はNGINX PlusとF5が提供するWAFモジュールであるApp ProtectがインストールされたDockerイメージを作成するため「debian-image-nap-plus」を選択します。
3 | nginx-repo.crt nginx-repo.key |
4 | # make debian-image-nap-plus PREFIX=myregistry.example.com/nginxplus-ingress-nap TARGET=container TAG=1.12.0 |
5 | # docker images | grep nginxplus-ingress-nap |
6 | myregistry.example.com/nginxplus-ingress-nap 1.12.0 c99dfd32b43b 19 seconds ago 576MB |
正しくDockerイメージが作成できたことを確認し、皆様の環境で利用するコンテナレジストリへDockerイメージをアップロードしてください。
NGINX Ingress Controller環境の
セットアップ
Kubernetes環境のセットアップを行います。この記事ではkubeadmで構築した環境でコマンドを実行しています。各コマンドの詳細については手順を確認してください。
先程の手順で取得したGitHubのフォルダへ移動し、必要となるManifestをデプロイします。
01 | # cd ~/kubernetes-ingress/deployments |
02 | # kubectl apply -f common/ns-and-sa.yaml |
03 | # kubectl apply -f rbac/rbac.yaml |
04 | # kubectl apply -f rbac/ap-rbac.yaml |
05 | # kubectl apply -f common/default-server-secret.yaml |
06 | # kubectl apply -f common/nginx-config.yaml |
07 | # kubectl apply -f common/ingress-class.yaml |
08 | # kubectl apply -f common/crds/k8s.nginx.org_virtualservers.yaml |
09 | # kubectl apply -f common/crds/k8s.nginx.org_virtualserverroutes.yaml |
10 | # kubectl apply -f common/crds/k8s.nginx.org_transportservers.yaml |
11 | # kubectl apply -f common/crds/k8s.nginx.org_policies.yaml |
12 | # kubectl apply -f common/crds/k8s.nginx.org_globalconfigurations.yaml |
13 | # kubectl apply -f common/crds/appprotect.f5.com_aplogconfs.yaml |
14 | # kubectl apply -f common/crds/appprotect.f5.com_appolicies.yaml |
15 | # kubectl apply -f common/crds/appprotect.f5.com_apusersigs.yaml |
NGINX Ingress Controllerの実行
NGINX Ingress Controllerのpodを実行します。DeploymentとDaemonSetによる実行が可能ですが、のこの記事ではDeploymentで実行します。DaemonSetで実行したい場合にはマニュアルを参照して適切に読み替えて進めてください。
Deploymentのマニフェストを修正します。7、15、21、22行目を環境に合わせて修正してください。argsで指定するパラメータの詳細はCommand-line Argumentsを参照してください。
01 | # vi deployment/nginx-plus-ingress.yaml |
05 | serviceAccountName: nginx-ingress |
07 | - image: myregistry.example.com/nginxplus-ingress-nap:1.12.0 # 対象のレジストリを指定してください |
08 | imagePullPolicy: IfNotPresent |
09 | name: nginx-plus-ingress |
13 | - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config |
14 | - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret |
15 | - -enable-app-protect # App Protectを有効にします |
16 | #- -v=3 # Enables extensive logging. Useful for troubleshooting. |
17 | #- -report-ingress-status |
18 | #- -external-service=nginx-ingress |
19 | #- -enable-prometheus-metrics |
20 | #- -global-configuration=$(POD_NAMESPACE)/nginx-configuration |
21 | - -enable-preview-policies # OIDCに必要となるArgsを有効にします |
22 | - -enable-snippets # OIDCで一部設定を追加するためsnippetsを有効にします |
修正したマニフェストを指定しPodを作成します。
1 | # kubectl apply -f deployment/nginx-plus-ingress.yaml |
2 | deployment.apps/nginx-ingress created |
4 | # kubectl get pods --namespace=nginx-ingress |
5 | NAME READY STATUS RESTARTS AGE |
6 | nginx-ingress-5787f47959-4vrwb 1/1 Running 0 5s |
STATUSが正しく「Running」になっていることが確認できます。正しいSTATUSとならない場合には、「kubectl describe pod <pod name> (-n <namespace>)」コマンド、「kubectl logs <pod name> (-n <namespace>)」コマンドの結果を参考に原因の調査を行ってください。
NGINX Ingress Controller の外部への公開
NGINX Ingress Controller PodをNode Portで外部へ公開します。各Public CloudのKubernetesを利用している場合にはマニュアルの各手順を参照してください。
以下のnodeport.yamlの内容を適用します。本稿では外部からHTTP、HTTPSの接続を待ち受ける設定とします。
05 | namespace: nginx-ingress |
NodePortをデプロイします。
1 | # kubectl apply -f service/nodeport.yaml |
2 | service/nginx-ingress created |
4 | # kubectl get svc -n nginx-ingress |
5 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
6 | nginx-ingress NodePort 10.101.189.31 <none> 80:30286/TCP,443:32353/TCP 7s</none> |
この手順を完了し、現在の状態は以下の図となります(図3)。
図3:NodePortデプロイ後の構成
最後に、この環境へクライアントからアクセスするため、HTTP(TCP/80)、HTTPS(TCP/443)を待ち受け、それぞれNodePortで公開するポート番号へ転送するLBを用意します。
今回のラボ環境では同Linux Host上にNGINX Plusをインストールし以下nginx.confとしました。NGINX OSSでも同様の設定で問題ありません。NGINX PlusのInstall手順はInstalling NGINX Plusを参照してください。
01 | # cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf- |
02 | # vi /etc/nginx/nginx.conf |
06 | error_log /var/log/nginx/error.log notice; |
07 | pid /var/run/nginx.pid; |
11 | worker_connections 1024; |
15 | # TCP/UDP load balancing |
18 | upstream tcp80_backend { |
19 | server localhost:30286; |
21 | upstream tcp443_backend { |
22 | server localhost:32353; |
27 | proxy_pass tcp80_backend; |
31 | proxy_pass tcp443_backend; |
現在の状態は以下となり、サービスを外部に公開する準備が完了しました(図4)。
図4:NGINX LBデプロイ後の構成
CRD(Custom Resource Definition)を
利用した通信制御と権限管理
それではアプリケーションをデプロイしてみましょう。まず1つ目はNGINX Ingress Controllerの柔軟な設定と権限管理を実現するVirtualServer/VirtualServerRouteを利用します。
対象となるGitHubのフォルダへ移動します。ブラウザでGitHub上の手順ご覧になられる場合にはCross-Namespace Configurationを参照してください。
1 | # cd ~/kubernetes-ingress/examples-of-custom-resources/cross-namespace-configuration/ |
このアプリケーションは「cafe」というアプリケーション配下に「tea」と「coffee」というそれぞれのサービスがあり、その制御をVirtualServer/VirtualServerRouteで実施する構成となります。まずcafe-virtual-server.yamlの内容を見てみましょう。
01 | apiVersion: k8s.nginx.org/v1 |
02 | kind: VirtualServer # 作成するオブジェクトとしてVirtualServerを指定 |
05 | namespace: cafe # cafe VirtualServerをcafe namespaceに作成 |
07 | host: cafe.example.com # hostnameを指定 |
11 | - path: /tea # /tea 宛の通信を、tea namespaceのteaにルーティング |
13 | - path: /coffee # /coffee 宛の通信を、coffee namespaceのcoffeeにルーティング |
2行目でkind: VirtualServerを指定しています。こちらは、環境のセットアップでデプロイしたCRDとなります。VirtualServerを利用する事により、より直感的にIngress Controllerの設定を行うことが可能となります。
今回の例では、通信を待ち受けるVirtualServerと各サービス、tea、 coffeeでそれぞれ別のVirtualServerRouteを設定し、各チームがそれぞれの権限で設定を管理することを想定したシナリオとなります。このため、VirtualServerは別途作成するcafeというnamespaceを指定します。
cafe.example.com宛のHTTP/HTTPSリクエストを受け取った後、routesのpathの内容に従って、/teaの場合にはtea namespaceのteaサービスへ、/coffeeの場合にはcoffee namespaceのcoffeeサービスへルーティングがされます。
では、次にteaサービスで利用するtea-virtual-server-route.yamlを見てみましょう。
01 | apiVersion: k8s.nginx.org/v1 |
02 | kind: VirtualServerRoute # 作成するオブジェクトとしてVirtualServerRouteを指定 |
05 | namespace: tea # tea VirtualServerRouteをtea namespaceに作成 |
07 | host: cafe.example.com |
13 | - path: /tea # /tea 宛の通信を、upstream teaに転送 |
tea VirtualServerRouteと同じ構成となりますが、coffee-virtual-server-route.yamlを見てみましょう。
01 | apiVersion: k8s.nginx.org/v1 |
02 | kind: VirtualServerRoute |
05 | namespace: coffee # coffee VirtualServerRouteをcoffee namespaceに作成 |
07 | host: cafe.example.com |
このように、VirtualServer、VirtualServerRouteを適切なNamespaceやKubernetesのRBACで組み合わせる事により、サービスで対象とすべきURL Pathとサービスの管理権限を分けることができ、相互に誤った変更をすることが無い、適切な管理を行うことができるようになります。
それでは、実際に設定を反映し、結果を確認します。
Namespaceを作成します。新たに、cafe、coffee、teaが作成されていることが確認できます。
01 | # kubectl apply -f namespaces.yaml |
04 | namespace/coffee created |
11 | kube-node-lease Active 3d22h |
12 | kube-public Active 3d22h |
13 | kube-system Active 3d22h |
14 | nginx-ingress Active 3d22h |
アプリケーションをデプロイします。
1 | # kubectl apply -f tea.yaml |
2 | # kubectl apply -f tea-virtual-server-route.yaml |
3 | # kubectl apply -f coffee.yaml |
4 | # kubectl apply -f coffee-virtual-server-route.yaml |
5 | # kubectl apply -f cafe-secret.yaml |
6 | # kubectl apply -f cafe-virtual-server.yaml |
デプロイしたPodの情報の確認、及びコマンドラインで疎通を確認した結果を示します。5行目を環境に合わせて修正してください。
01 | # kubectl get pods -n coffee -o wide |
02 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES |
03 | coffee-6f4b79b975-2mv47 1/1 Running 0 60m 10.244.0.8 ubuntu <none> <none> |
06 | Server address: 10.244.0.8:8080 |
07 | Server name: coffee-6f4b79b975-2mv47 |
08 | Date: 17/Aug/2021:08:27:34 +0000 |
10 | Request ID: 5ec6bd7996060d9d0b3e74e3207e0818</none></none> |
PATH /coffeeを指定し、cafe.example.comにアクセスすると、対象のサーバのIPアドレスを含む情報が応答されたことを確認できました。PATH /teaもデプロイされておりますので合わせてご確認ください。
現在の状態は以下の図となります(図5)。
図5:Cross Name Spaceサンプルデプロイ後の構成
NGINX Ingress ControllerでListenするポートを設定情報では明示していませんが、IngressやVirtualServerで設定を行った場合は、記述内容に合わせてHTTP(TCP/80)、HTTPS(TCP/443)が処理される状態となります。
この例ではNGINX Ingress Controllerの設定となるVirtualServerやVirtualServerRouteのリソースはそれぞれ別のNamespaceで管理しております。このようにアプリケーションデプロイとIngress Controllerで管理する設定範囲を適切に管理する事により、各チームで自由にデプロイすることが可能となり、Kubernetes環境でよりスムーズなアプリケーションの公開が可能となります。
それでは、最後に作成したアプリケーションを削除します。
1 | # kubectl delete -f tea.yaml |
2 | # kubectl delete -f tea-virtual-server-route.yaml |
3 | # kubectl delete -f coffee.yaml |
4 | # kubectl delete -f coffee-virtual-server-route.yaml |
5 | # kubectl delete -f cafe-secret.yaml |
6 | # kubectl delete -f cafe-virtual-server.yaml |