Amazon Web Services ブログ

AWS App Mesh と Kong を使って Amazon EKS 上でマイクロサービスを実行する

この記事は、Running microservices in Amazon EKS with AWS App Mesh and Kong を翻訳したものです。

本投稿は、Kong ソリューションエンジニアの Claudio Acquaviva、Kong アライアンスの Morgan Davies と共同で作成されたものです。

サービスメッシュはサービス間通信のための一般的なアーキテクチャパターンとなっている透過的なインフラストラクチャレイヤーです。Amazon EKSAWS App Mesh を組み合わせることでマイクロサービスのための強力なプラットフォームを形成し、ロードバランシング、サービスディスカバリ、可観測性、アクセスコントロール、トレース、ヘルスチェック、サーキットブレーカーなどサービス間通信で発生する技術的な要件に対応する事ができます。

モダンなエンタープライズソリューションでは、次のカテゴリの明確な管理制御が必要です。

  • API エンドポイントへの外部からのイングレストラフィックをカバリングする API 管理
  • 運用管理とサービスの状態に焦点を当てたサービス管理機能

サービスメッシュは主に 2 つ目のカテゴリに対応していますが、イングレストラフィックも同様に重要であり、スロットリング、アプリケーションとユーザの認証、リクエストログとトレース、データのキャッシングなどクラスタ全体のポリシーをサポートするソリューションから恩恵を受けることができます。これらのポリシーに加えてイングレスは使用状況の把握、課金システムの追加、運用上の閾値を超えたアラートの生成などの機能により、API のマネタイズを可能にするレイヤーです。

これらの機能はクラスタ外の周辺ツールを利用することで実現することも可能ですが、Kong for Kubernetes Ingress Controller は HPA、自己修復、RBAC、cert-manager などの Kubernetes の機能を活用して、アプリケーションと並行して稼働しているサービスメッシュを保護するソリューションを提供します。

この記事では、Amazon EKS、AWS App Mesh、Kong for Kubernetes を利用してサービスメッシュを安全に実装する方法について説明します。ここで取り上げる課題の範囲は API や外部トラフィックの管理だけでなく、より深い統合シナリオも含みます。イングレスを Kubernetes ネイティブな方法で処理するだけでなく、サービスメッシュの一部にすることで可観測性、セキュリティ及びトラフィック制御を向上させます。

Kubernetes Ingress Controller のための AWS App Mesh と Kong の設定

AWS App Mesh は、お客様がサービスメッシュを実装するために利用できるフルマネージド型サービスです。このサービスを利用することで複数タイプのコンピューティングインフラストラクチャに跨がり内部のサービス間通信を簡単に管理できます。Kong for Kubernetes はサービスメッシュを外部コンシューマに公開する ingress を通過するトラフィックを制御する為に、ingress に対してポリシーの定義、適用、実行を行います。

Kong for Kubernetes は次の機能をサポートしています。

  • スケーラビリティ: Kong for Kubernetes は Kong API ゲートウェイをベースに ingress を管理する役割を担います。アプリケーションのトラフィック量が大きく変動し ingress に影響を与えることはよくあることです。Kong for Kubernetes は、Horizontal Pod Autoscaler (HPA) などの標準の Kubernetes スケーラビリティコントロールを活用しており、需要に合わせてシームレスにスケーリングします。
  • セキュリティ: Kubernetes の名前空間ベースの RBAC モデルを活用し一貫したアクセス制御を実現します。これらの制御はソフトウェアのデリバリーや運用に於いてそれぞれの役割を担うプラットフォームチーム、API チーム、アプリケーションチームの間で責任を分離するために必要なものです。例えば、それぞれの名前空間に制限されているアプリケーションチームは ingress オブジェクトを定義することができますが、ingress コントローラと API 管理コンポーネントへのアクセスは専門チームに制限することができます。
  • 拡張性: 幅広いプラグインエコシステムはサービスメッシュを保護するためのさまざまなオプションを提供します。例えば、OpenID Connect や相互 TLS 認証と認可、レート制限、IP 制限、Kong Enterprise Developer Portal を介したセルフサービスのクレデンシャル登録などがあります。
  • 可観測性: Prometheus、Jaeger、Amazon CloudWatch などのモニタリング、トレース、ロギングツールと完全に統合できます。

こちらが Kong for Kubernetes のアーキテクチャ図です。

Kong for Kubernetes アーキテクチャ

次の図は、Kong for Kubernetes アーキテクチャを示しています。

Kong for Kubernetes のポッドには、次の 2 つのコンテナが含まれています。

  • Kong Gateway コンテナは、API トラフィックの処理と Kong for Kubernetes で利用可能なプラグインによって定義されたポリシーの適用を担当するデータプレーンになります。
  • Controller コンテナは、Kubernetes マニフェストと CRD を Kong の構成に変換するコントロールプレーンになり、プロキシと Kubernetes の構成を別々に管理する必要がなくなります。

Kong for Kubernetes の前には、Kong Gateway を外部コンシューマーに公開する Classic Load Balancer (CLB) または Network Load Balancer (NLB) があります。さらに Kong for KubernetesはKubernetes クラスタ内で実行されている ClusterIP タイプのサービスや、クラスタ内で公開されている外部サービスなど後にあるすべてのサービスを保護しています。

前提条件

このプロセスを開始する前に、次の前提条件が整っていることを確認してください。

  • EKS 1.15 以降のクラスタが既にデプロイされている。この演習では、eksctl を使用しました。
  • Kubectl 1.15 以降がローカルにインストールされている
  • Helm V3
  • Curl またはその他の HTTP クライアント

ソリューションのデプロイ

今回のデプロイは DJ Service Mesh Application の拡張版であり、その上に ingress コントローラレイヤーを追加したものです。Kong for Kubernetes には認証、ログ処理、キャッシングなど多数のポリシーを実装するためのプラグインが豊富に提供されています。

まずは、API キーベースのセキュリティレイヤーとレート制限ポリシーを実装して、イングレスの使用量を制御してみましょう。

ステップ 1: DJ サービスメッシュアプリケーションをデプロイする

EKS ワークショップで説明されている次の手順に従って、DJ サービスメッシュアプリケーションをデプロイして下さい。

  1. DJ App のデプロイ
  2. App Mesh 統合をインストール
  3. DJ App を App Mesh に移植

これらの手順では、metal と jazz のマイクロサービス (それぞれ 2 つのバージョン) で構成されるシンプルなソリューションをインストールし、 CRD と App Mesh コントローラーを含む AWS App Mesh コンポーネントをインストールします。これらのコンポーネントはアドミッションコントローラーとして機能し、デプロイしたポッドには Envoy Proxy のサイドカーを挿入します。prod 名前空間は、サイドカーコンテナの自動挿入を可能にするように構成され、トラフィックを制御するために必要な抽象度を提供します。

アプリケーションのメッシュ化により、DJ ポッドと既存のマイクロサービス間の通信が抽象化されます。API は jazz や metal のマイクロサービスのエンドポイントに直接通信するのではなく、仮想サービスを経由して新しい仮想ルーターを使用してトラフィックを制御します。最終的な論理アーキテクチャはこのようになります。

始める前に、クラスタ内にどのような仮想サービス、仮想ルータ、仮想ノードがあるのかを確認してみましょう。

$ kubectl get virtualservices -n prod
NAME    ARN                                                                                              AGE
jazz    arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualService/jazz.prod.svc.cluster.local    2m39s
metal   arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualService/metal.prod.svc.cluster.local   2m38s

$ kubectl get virtualrouters -n prod
NAME           ARN                                                                                  AGE
jazz-router    arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualRouter/jazz-router_prod    2m54s
metal-router   arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualRouter/metal-router_prod   2m53s

$ kubectl get virtualnodes -n prod
NAME       ARN                                                                            AGE
dj         arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualNode/dj_prod         3m8s
jazz-v1    arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualNode/jazz-v1_prod    3m4s
jazz-v2    arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualNode/jazz-v2_prod    2m41s
metal-v1   arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualNode/metal-v1_prod   3m4s
metal-v2   arn:aws:appmesh:us-west-2:<AWS_ACCOUNT>:mesh/dj-app/virtualNode/metal-v2_prod   2m40s

DJ ノードはトラフィックをルーティングせず主にテスト目的で使用されるため、余剰なノードであることに注意してください。重要なのは、App Mesh を使用して jazz-v2 および metal-v2 サービスのカナリアリリースを実装し、全てのトラフィックの 5% を両方のサービスのバージョン 2 にルーティングしていることです。

$ kubectl describe virtualrouter jazz-router -n prod 
...
  Routes:
    Http Route:
      Action:
        Weighted Targets:
          Virtual Node Ref:
            Name:  jazz-v1
          Weight:  95
          Virtual Node Ref:
            Name:  jazz-v2
          Weight:  5
...

なぜそれが重要なのでしょうか? サービスの利用方法に関係なく外部入力、もしくは内部の他のサービスを通じて、全ての通信はカナリアリリースによりトラフィックの 5% を新しいバージョンに送信します。

それは素晴らしいことですが、開発プロセスの一環ではカナリアリリースをどのようにテストしますか? DJ ポッドから curl コマンドを実行できることは可能ですが、CI/CD パイプライン及び API のコンシューマーが外部でなければならない ような (実際の環境と同様) 適切な API テスト戦略では機能しません。

つまり、API を外部に公開することを計画しているのであれば、現状のソリューションを改善すべきであるという事実を浮き彫りにしています。適切な API 管理には、( API キーで識別される) API クライアント 、レート制限、そして最後には使用量の取得と請求に対する制御機能を追加する必要があります。

ステップ 2: Kong for Kubernetes Ingress Controller をデプロイする

このブログ記事では、Kong for Kubernetes で完全に置き換えることができる余剰な DJ ノードを置き換えます。すべてのサービスを外部コンシューマーに公開する ingress オブジェクトを定義します。

次の図は、最終的なトポロジを示しています。

Kong for Kubernetes 名前空間

Kong for Kubernetes コンポーネントが存在する Kubernetes 名前空間は既存のメッシュの一部にし、App Mesh サイドカーの自動挿入を適用するために適切なラベルを設定する必要があります

kubectl create namespace kong
kubectl label namespace kong mesh=dj-app appmesh.k8s.aws/sidecarInjectorWebhook=enabled

Kong for Kubernetes 仮想ノード

Kong for Kubernetes のための仮想ノード宣言:

apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
  name: kong
  namespace: kong
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/instance: kong
  listeners:
    - portMapping:
        port: 80
        protocol: http
  backends:
    - virtualService:
        virtualServiceRef:
          name: jazz
          namespace: prod
    - virtualService:
        virtualServiceRef:
          name: metal
          namespace: prod
  serviceDiscovery:
    dns:
      hostname: kong-kong-proxy.kong.svc.cluster.local

宣言は次の通りです。

  • インストールする Kong for Kubernetes ポッドを選択します。
  • Jazz および Metal サービスは許可されている唯一のイングレスポイントであるためバックエンドとして定義します。
  • Kong for Kubernetes のサービス FQDN を DNS サービスディスカバリとして設定します。

kubectl を使用して適用します。

kubectl apply -f https://raw.githubusercontent.com/Kong/aws-blogposts/master/K4K8S+AWSAppMesh/kongvirtualnode.yml

仮想ノードを再度確認してください。Kong for Kubernetes 固有のものは、「dj-app」メッシュに組み込まれていることを意味します。

$ kubectl get virtualnodes --all-namespaces
NAMESPACE NAME  ARN  AGE
kong  kong  arn:aws:appmesh:us-west-2::mesh/dj-app/virtualNode/kong_kong  62s
prod  dj  arn:aws:appmesh:us-west-2::mesh/dj-app/virtualNode/dj_prod  22m
prod  jazz-v1 arn:aws:appmesh:us-west-2::mesh/dj-app/virtualNode/jazz-v1_prod 22m
prod  jazz-v2 arn:aws:appmesh:us-west-2::mesh/dj-app/virtualNode/jazz-v2_prod 21m
prod  metal-v1 arn:aws:appmesh:us-west-2::mesh/dj-app/virtualNode/metal-v1_prod 22m
prod  metal-v2 arn:aws:appmesh:us-west-2::mesh/dj-app/virtualNode/metal-v2_prod 21m

Kong for Kubernetes のインストール

今回は Helm を使って Kong の chart リポジトリを追加し、Kong for Kubernetes をインストールしていきます。

helm repo add kong https://charts.konghq.com 
helm repo update 
helm install -n kong kong kong/kong --version "1.11.0" --set ingressController.installCRDs=false

デフォルトでは、App Mesh はメッシュで明示的に定義されているノード以外からの egress を許可しません。その為、ingress コントローラコンテナが API サーバと通信できなくなるので Kong ポッドのデプロイは失敗します。この問題に対処するため App Mesh の機能を使用して、 UID 1337 (デフォルト) のセキュリティコンテキストで実行されているコンテナの egress フィルタリングをバイパスします。ingress コントローラにこのセキュリティコンテキストオプションを設定することで API サーバとの通信が可能になりますが、サービスメッシュ内の他のノードはメッシュ内で定義されたノードとのみ通信するように制限されています。

kubectl patch deploy -n kong kong-kong -p '{"spec":{"template":{"spec":{"containers":[{"name":"ingress-controller","securityContext":{"runAsUser": 1337}}]}}}}'

パッチを適用したらデプロイを検証してすべてのコンテナが実行されていることを確認してみましょう。

$ kubectl get pods -n kong
NAME READY STATUS RESTARTS AGE
kong-kong-5b4499bc4-rgxnd 3/3 Running 0 13m
ingress でプロビジョニングされたサービスは “type: LoadBalancer” なので ELB インスタンスも一緒に取得されます。
$ kubectl get service -n kong
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kong-kong-proxy LoadBalancer 10.100.18.222 adf320d20effa44d4b49ca2cf279e0b8-240857585.{region}.elb.amazonaws.com 80:32445/TCP,443:32024/TCP 5d19h

ポッド内のすべてのコンテナとイメージをリストアップして、Kong for Kubernetes ポッドに Envoy サイドカーが挿入されていることを確認します。

$ kubectl get po -n kong -o jsonpath='{range .items[*]}{"pod: "}{.metadata.name}{"\n"}{range .spec.containers[*]}{"\tname: "}{.name}{"\n\timage: "}{.image}{"\n"}{end}'
pod: kong-kong-6f784b6686-qlrvp
name: ingress-controller
image: kong-docker-kubernetes-ingress-controller.bintray.io/kong-ingress-controller:1.0
name: proxy
image: kong:2.1
name: envoy
image: 840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.15.1.0-prod

ステップ 3: サービスメッシュを公開および保護するための Ingress を定義する

サービス構成

Kong for Kubernetes は対応する Kubernetes のエンドポイントに基づいてターゲットを構成します。つまり、Kong 自体が直接ポッドと通信し、エンドポイントの検出メカニズムとしてサービスの抽象化を行なっているということです。これにより、Kong は kube-proxy を通過する余分なホップをバイパスし、最適化された負荷分散アルゴリズムでバランシングすることができます。

AWS App Mesh の場合、サービスへの通信はサービスの完全修飾ドメイン名を介してのみ許可されることに注意が必要です。例えば、 jazz.prod.svc.cluster.local へのルーティングは許可されていますが、サービスをホスト名で直接呼び出すことはできません (例: curl jazz:9080 ) 。ホスト名でサービスを呼び出す機能は AWS App Mesh のロードマップにあるので将来的にはこの設定はもっとシンプルになるでしょう。

幸いなことに、Kong は両方の制約に対処する設定を提供することができます。以下のコマンドは両方の処理を行い、Kong ingress にアップストリームを設定し、ルーティングにサービスのホスト名ではなく FQDN を有効にする設定を使用するように指示します

kubectl apply -f https://raw.githubusercontent.com/Kong/aws-blogposts/master/K4K8S%2BAWSAppMesh/fqdn-service-routing.yaml

Kong for Kubernetes Ingress

次のステップは、Kong が提供する適切なセキュリティとトラフィック制御機能を使用して、jazz と metal の両方の仮想サービスを外部に公開するルーティングルールを持つ Kubernetes の ingress オブジェクトを定義します。ingress オブジェクトは標準の Kubernetes オブジェクトです。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: djingress
  namespace: prod
  annotations:
    konghq.com/strip-path: "true"
    kubernetes.io/ingress.class: kong
    konghq.com/override: do-not-preserve-host
spec:
  rules:
  - http:
      paths:
        - path: /dj/jazz
          backend:
            serviceName: jazz
            servicePort: 9080
        - path: /dj/metal
          backend:
            serviceName: metal
            servicePort: 9080
kubectl create -f https://raw.githubusercontent.com/Kong/aws-blogposts/master/K4K8S+AWSAppMesh/dj_ingress.yml
  • アノテーション konghq.com/strip-path: "true" は、ターゲットの仮想サービスに送信する前にリクエストから “/dj/jazz” などの拡張パスを削除します。
  • アノテーション konghq.com/override: do-no-preserve-host は、リクエスト元のホストを削除する構成オブジェクトを参照します。サービスに適用されている FQDN アノテーションと組み合わせることで、サイドカーが適切な権限に基づいてリクエストをルーティングできるようになります。

ingress 定義で参照される構成オプション「do-not-preserve-host」は、以下を参照しています。

apiVersion: configuration.konghq.com/v1
kind: KongIngress
metadata:
  name: do-not-preserve-host
  namespace: prod
route:
  preserve_host: false

この構成オプションを prod 名前空間の ingress に適用します。

kubectl create -f https://raw.githubusercontent.com/Kong/aws-blogposts/master/K4K8S+AWSAppMesh/kongingress-dontpreservehost.yml

kubectl で ingress もチェックします。この ingress は Kong proxy 用にプロビジョニングされたものと同じロードバランサーを使用していることに注意してください。

$ kubectl get ingress -n prod
NAME     HOSTS   ADDRESS  PORTS AGE
djingress *      {name.region}.elb.amazonaws.com 

指定した外部アドレスを使用して ingress を利用します。

curl {your ingress address}/dj/jazz
["Astrud Gilberto","Miles Davis"]

ループを実行して、カナリアリリースの動作を確認します。

while [ 1 ];
  do curl http://{your ingress address}/dj/jazz/
  echo
done

["Astrud Gilberto","Miles Davis"]
["Astrud Gilberto","Miles Davis"]
["Astrud Gilberto","Miles Davis"]
["Astrud Gilberto","Miles Davis"]
["Astrud Gilberto (Bahia, Brazil)","Miles Davis (Alton, Illinois)"]
["Astrud Gilberto","Miles Davis"]
["Astrud Gilberto","Miles Davis"]

URL パスを /dj/metal に変更して、メタルサービスにも同じことを行うことができます。

このステップでは、カナリア機能を維持したままサービスを外部に公開しました。さらにサービスメッシュ上に ingress と API 管理のための重要なレイヤーを得ました。ingress は基礎となる仮想サービスへのトラフィックをルーティングする DJ の機能を完全に実装しており、DJ 仮想ノードを削除することができます。

ステップ4:レート制限ポリシーを適用する

ingress が設定されたらその消費量を制御するためのポリシーを定義する必要があります。最初のものはレート制限です。ingress にポリシーを適用するプロセスは非常にシンプルです。

  • ポリシーを宣言及び作成する
  • ingress にアノテーションでパッチを当てる

以下に示すレート制限ポリシーでは、1 分間に 3 回のリクエストが許可されます。

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: rl-by-minute
  namespace: prod
config:
  minute: 3
  policy: local
plugin: rate-limiting

こちらのポリシーを適用します。

kubectl create -f https://raw.githubusercontent.com/Kong/aws-blogposts/master/K4K8S+AWSAppMesh/ratelimiting.yml

一度作成したポリシーは、それを必要とする ingress に適用することができます。

kubectl patch ingress djingress -n prod -p '{"metadata":{"annotations":{"konghq.com/plugins":"rl-by-minute"}}}'

1 分間に 3 回以上サービスを利用しようとするとエラーが発生します。Kong Ingress を使用すると、基礎実装の一部となる DJ サービスへのトラフィックを制御することができます (後で “classic” や “country” のようなサービスを追加することを想定しています) 。

while [ 1 ]
  do curl {your ingress address}/dj/jazz/
  echo
done
["Astrud Gilberto","Miles Davis"]
["Astrud Gilberto","Miles Davis"]
["Astrud Gilberto","Miles Davis"]
{
"message":"API rate limit exceeded"
}

こちらのページを参考に Kong が提供しているプラグインを確認してログ処理、リアルタイム監視、トレースツールなどの豊富なリストと統合することができます。

ステップ 5:API キーのセキュリティポリシーを定義する

レート制限ポリシーと同様、最初にポリシーを作成してから、このポリシーを ingress に適用する必要があります。

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: apikey
  namespace: prod
plugin: key-auth
kubectl create -f https://raw.githubusercontent.com/Kong/aws-blogposts/master/K4K8S+AWSAppMesh/apikey.yml

ingress に別のアノテーションを追加して API キーポリシーを適用します。今回は両方のポリシーを適用していることに注目してください。

kubectl patch ingress djingress -n prod -p '{"metadata":{"annotations":{"konghq.com/plugins":"apikey, rl-by-minute"}}}'

これで、ingress を利用しようとするとエラーが表示されるようになりました。

curl {your ingress address}/dj/jazz
{
"message":"No API key found in request"
}

キーをプロビジョニングしてコンシューマーに関連付けます。

kubectl create secret generic consumerapikey -n prod --from-literal=kongCredType=key-auth --from-literal=key=kong-secret

コンシューマーを作成して、それを consumerapikey と関連付けましょう。

apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
  name: consumer1
  namespace: prod
  annotations:
    kubernetes.io/ingress.class: kong
username: consumer1
credentials:
- consumerapikey
kubectl apply -f https://raw.githubusercontent.com/Kong/aws-blogposts/master/K4K8S+AWSAppMesh/consumer.yml

コンシューマーを識別できるようになったので、API キーを付けて ingress を利用してみましょう。 (curl コマンドに渡されるヘッダーに注意してください) 。

curl {your ingress address}/dj/jazz -H 'apikey:kong-secret'
["Astrud Gilberto","Miles Davis"]

まとめ

Kong for Kubernetes と AWS App Mesh は、複数のプラットフォームにまたがって構築されたサービスに対して一貫した可視性とネットワークトラフィックの制御機能を提供することでサービスの実行を容易にします。AWS App MeshKong for Kubernetes の公式ドキュメントにて、このブログで紹介した製品についての詳細を学ぶことができます。

次の記事では可観測性の追加、トレース、モニタリング、及び AWS X-Ray と Elasticsearch を網羅したインテグレーションを通じて、AWS エコシステムとのより深い統合を紹介していきます。さらに、OIDC やAmazon Cognito を使用した認証・認可の側面にも焦点を当てていきます。

この記事で使用されているポリシーを自由に変更して、キャッシュ、ログ処理、OIDC ベースの認証、カナリア、GraphQL 統合など、Kong が提供する豊富なプラグインリストを使用してポリシーの実装を体験してみてください。

この記事で使用した宣言はすべて GitHub で公開されています。

ー Mikhail Shapirov
翻訳はソリューションアーキテクト黄 光川が担当しました。