NGINX Ingress Controllerの柔軟なアプリケーション制御、具体的なユースケースと設定方法を理解する
はじめに
本連載では、NGINX(エンジンエックス)が求められている時代背景と、そのユースケース、具体的な設定手順について複数回に分けて解説致します。第2回目となる本稿では、NGINX Ingress Controllerの柔軟なアプリケーション制御について、具体的なユースケースと設定方法を交えて詳しく解説します。
本番環境でアプリケーションを動作させるにあたって
Kubernetes環境に求められる要件
第1回でもご紹介した通り、近年、皆様を取り巻くビジネス環境の競争は激化しており、その中でデジタル技術を最大限活用し顧客のニーズに合わせたサービスを実現することが業界問わず必須要件となっています。そのアプリケーションを、コンテナ技術を用いてデプロイし、サービスの伸縮性と継続的なアプリケーション開発を実現するために注目されているのがKubernetesです。
各社より様々なKubernetes プラットフォームが提供されており、それらをアプリケーションの要件に合わせて自由に選択することが可能です。これらを用いて本番環境でアプリケーションをデプロイし、顧客へのサービスを開始すると開発環境では見られなかった問題が発生します。今回はNGINX Plusを用いたNGINX Ingress Controllerによりそれらの問題を解決する方法をご紹介いたします。
本題に入る前にそれぞれのフェーズで発生する課題について見ていきましょう(図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)。
インフラ担当者が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より取得します。
# cd ~/ # git clone https://github.com/nginxinc/kubernetes-ingress/ # cd ~/kubernetes-ingress # git checkout v1.12.0
Dockerイメージを作成します。今回はNGINX PlusとF5が提供するWAFモジュールであるApp ProtectがインストールされたDockerイメージを作成するため「debian-image-nap-plus」を選択します。
# cp ~/nginx-repo* . # ls nginx-repo.* nginx-repo.crt nginx-repo.key # make debian-image-nap-plus PREFIX=myregistry.example.com/nginxplus-ingress-nap TARGET=container TAG=1.12.0 # docker images | grep nginxplus-ingress-nap myregistry.example.com/nginxplus-ingress-nap 1.12.0 c99dfd32b43b 19 seconds ago 576MB
正しくDockerイメージが作成できたことを確認し、皆様の環境で利用するコンテナレジストリへDockerイメージをアップロードしてください。
NGINX Ingress Controller環境の
セットアップ
Kubernetes環境のセットアップを行います。この記事ではkubeadmで構築した環境でコマンドを実行しています。各コマンドの詳細については手順を確認してください。
先程の手順で取得したGitHubのフォルダへ移動し、必要となるManifestをデプロイします。
# cd ~/kubernetes-ingress/deployments # kubectl apply -f common/ns-and-sa.yaml # kubectl apply -f rbac/rbac.yaml # kubectl apply -f rbac/ap-rbac.yaml # kubectl apply -f common/default-server-secret.yaml # kubectl apply -f common/nginx-config.yaml # kubectl apply -f common/ingress-class.yaml # kubectl apply -f common/crds/k8s.nginx.org_virtualservers.yaml # kubectl apply -f common/crds/k8s.nginx.org_virtualserverroutes.yaml # kubectl apply -f common/crds/k8s.nginx.org_transportservers.yaml # kubectl apply -f common/crds/k8s.nginx.org_policies.yaml # kubectl apply -f common/crds/k8s.nginx.org_globalconfigurations.yaml # kubectl apply -f common/crds/appprotect.f5.com_aplogconfs.yaml # kubectl apply -f common/crds/appprotect.f5.com_appolicies.yaml # 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を参照してください。
# vi deployment/nginx-plus-ingress.yaml ** 省略 ** spec: serviceAccountName: nginx-ingress containers: - image: myregistry.example.com/nginxplus-ingress-nap:1.12.0 # 対象のレジストリを指定してください imagePullPolicy: IfNotPresent name: nginx-plus-ingress ** 省略 ** args: - -nginx-plus - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret - -enable-app-protect # App Protectを有効にします #- -v=3 # Enables extensive logging. Useful for troubleshooting. #- -report-ingress-status #- -external-service=nginx-ingress #- -enable-prometheus-metrics #- -global-configuration=$(POD_NAMESPACE)/nginx-configuration - -enable-preview-policies # OIDCに必要となるArgsを有効にします - -enable-snippets # OIDCで一部設定を追加するためsnippetsを有効にします
修正したマニフェストを指定しPodを作成します。
# kubectl apply -f deployment/nginx-plus-ingress.yaml deployment.apps/nginx-ingress created # kubectl get pods --namespace=nginx-ingress NAME READY STATUS RESTARTS AGE 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の接続を待ち受ける設定とします。
apiVersion: v1 kind: Service metadata: name: nginx-ingress namespace: nginx-ingress spec: type: NodePort ports: - port: 80 targetPort: 80 protocol: TCP name: http - port: 443 targetPort: 443 protocol: TCP name: https selector: app: nginx-ingress
NodePortをデプロイします。
# kubectl apply -f service/nodeport.yaml service/nginx-ingress created # kubectl get svc -n nginx-ingress NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-ingress NodePort 10.101.189.3180:30286/TCP,443:32353/TCP 7s
この手順を完了し、現在の状態は以下の図となります(図3)。
最後に、この環境へクライアントからアクセスするため、HTTP(TCP/80)、HTTPS(TCP/443)を待ち受け、それぞれNodePortで公開するポート番号へ転送するLBを用意します。
今回のラボ環境では同Linux Host上にNGINX Plusをインストールし以下nginx.confとしました。NGINX OSSでも同様の設定で問題ありません。NGINX PlusのInstall手順はInstalling NGINX Plusを参照してください。
# cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf- # vi /etc/nginx/nginx.conf user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } # TCP/UDP load balancing # stream { upstream tcp80_backend { server localhost:30286; } upstream tcp443_backend { server localhost:32353; } server { listen 80; proxy_pass tcp80_backend; } server { listen 443; proxy_pass tcp443_backend; } } # nginx -s reload
現在の状態は以下となり、サービスを外部に公開する準備が完了しました(図4)。
CRD(Custom Resource Definition)を
利用した通信制御と権限管理
それではアプリケーションをデプロイしてみましょう。まず1つ目はNGINX Ingress Controllerの柔軟な設定と権限管理を実現するVirtualServer/VirtualServerRouteを利用します。
対象となるGitHubのフォルダへ移動します。ブラウザでGitHub上の手順ご覧になられる場合にはCross-Namespace Configurationを参照してください。
# cd ~/kubernetes-ingress/examples-of-custom-resources/cross-namespace-configuration/
このアプリケーションは「cafe」というアプリケーション配下に「tea」と「coffee」というそれぞれのサービスがあり、その制御をVirtualServer/VirtualServerRouteで実施する構成となります。まずcafe-virtual-server.yamlの内容を見てみましょう。
apiVersion: k8s.nginx.org/v1 kind: VirtualServer # 作成するオブジェクトとしてVirtualServerを指定 metadata: name: cafe namespace: cafe # cafe VirtualServerをcafe namespaceに作成 spec: host: cafe.example.com # hostnameを指定 tls: secret: cafe-secret routes: - path: /tea # /tea 宛の通信を、tea namespaceのteaにルーティング route: tea/tea - path: /coffee # /coffee 宛の通信を、coffee namespaceのcoffeeにルーティング route: coffee/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を見てみましょう。
apiVersion: k8s.nginx.org/v1 kind: VirtualServerRoute # 作成するオブジェクトとしてVirtualServerRouteを指定 metadata: name: tea namespace: tea # tea VirtualServerRouteをtea namespaceに作成 spec: host: cafe.example.com upstreams: - name: tea service: tea-svc port: 80 subroutes: - path: /tea # /tea 宛の通信を、upstream teaに転送 action: pass: tea
tea VirtualServerRouteと同じ構成となりますが、coffee-virtual-server-route.yamlを見てみましょう。
apiVersion: k8s.nginx.org/v1 kind: VirtualServerRoute metadata: name: coffee namespace: coffee # coffee VirtualServerRouteをcoffee namespaceに作成 spec: host: cafe.example.com upstreams: - name: coffee service: coffee-svc port: 80 subroutes: - path: /coffee action: pass: coffee
このように、VirtualServer、VirtualServerRouteを適切なNamespaceやKubernetesのRBACで組み合わせる事により、サービスで対象とすべきURL Pathとサービスの管理権限を分けることができ、相互に誤った変更をすることが無い、適切な管理を行うことができるようになります。
それでは、実際に設定を反映し、結果を確認します。
Namespaceを作成します。新たに、cafe、coffee、teaが作成されていることが確認できます。
# kubectl apply -f namespaces.yaml namespace/cafe created namespace/tea created namespace/coffee created # kubectl get ns NAME STATUS AGE cafe Active 3s coffee Active 3s default Active 3d22h kube-node-lease Active 3d22h kube-public Active 3d22h kube-system Active 3d22h nginx-ingress Active 3d22h tea Active 3s
アプリケーションをデプロイします。
# kubectl apply -f tea.yaml # kubectl apply -f tea-virtual-server-route.yaml # kubectl apply -f coffee.yaml # kubectl apply -f coffee-virtual-server-route.yaml # kubectl apply -f cafe-secret.yaml # kubectl apply -f cafe-virtual-server.yaml
デプロイしたPodの情報の確認、及びコマンドラインで疎通を確認した結果を示します。5行目を環境に合わせて修正してください。
# kubectl get pods -n coffee -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coffee-6f4b79b975-2mv47 1/1 Running 0 60m 10.244.0.8 ubuntu# curl -H "Host: cafe.example.com" http://localhost/coffee Server address: 10.244.0.8:8080 Server name: coffee-6f4b79b975-2mv47 Date: 17/Aug/2021:08:27:34 +0000 URI: /coffee Request ID: 5ec6bd7996060d9d0b3e74e3207e0818
PATH /coffeeを指定し、cafe.example.comにアクセスすると、対象のサーバのIPアドレスを含む情報が応答されたことを確認できました。PATH /teaもデプロイされておりますので合わせてご確認ください。
現在の状態は以下の図となります(図5)。
NGINX Ingress ControllerでListenするポートを設定情報では明示していませんが、IngressやVirtualServerで設定を行った場合は、記述内容に合わせてHTTP(TCP/80)、HTTPS(TCP/443)が処理される状態となります。
この例ではNGINX Ingress Controllerの設定となるVirtualServerやVirtualServerRouteのリソースはそれぞれ別のNamespaceで管理しております。このようにアプリケーションデプロイとIngress Controllerで管理する設定範囲を適切に管理する事により、各チームで自由にデプロイすることが可能となり、Kubernetes環境でよりスムーズなアプリケーションの公開が可能となります。
それでは、最後に作成したアプリケーションを削除します。
# kubectl delete -f tea.yaml # kubectl delete -f tea-virtual-server-route.yaml # kubectl delete -f coffee.yaml # kubectl delete -f coffee-virtual-server-route.yaml # kubectl delete -f cafe-secret.yaml # kubectl delete -f cafe-virtual-server.yaml
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- クラウドネイティブな環境でKeycloakによるシングルサインオンを実現
- KubernetesのDiscovery&LBリソース(その2)
- Oracle Cloud Hangout Cafe Season7 #1「Kubnernetes 超入門」(2023年6月7日開催)
- Oracle Cloud Hangout Cafe Season5 #3「Kubernetes のセキュリティ」(2022年3月9日開催)
- KubernetesのConfig&Storageリソース(その1)
- Kubernetesの基礎
- コンテナ上のマイクロサービスの認証強化 ~StrimziとKeycloak~
- コンテナ上のマイクロサービスの認証強化 ~IstioとKeycloak~
- コンテナ上のマイクロサービスの認証強化 ~QuarkusとKeycloak~
- kustomizeやSecretを利用してJavaアプリケーションをデプロイする