東京ガス内製開発チーム Tech Blog

東京ガス内製開発チームのTech Blogです!

Amazon EKS の Ingress を考える ~ AWS Load Balancer Controller + Istio ~

みなさんこんにちは、杉山です。花粉症に必死に抗う毎日ですが、気づいたら3月も残りわずかですね。同士のみなさん、がんばって乗り越えましょう!

さて今回は、私の前回記事:Amazon EKS 導入に続いて、少しだけ深堀りして Ingress の部分について書いてみたいと思います。

Amazon EKS の Ingress を考える

クラウドベンダーが提供するマネージド Kubernetes をやるということは、 Ingress を考えるということだ ━

このような言葉を、偉大なる先人の方に聞いたことがあります。(ないです)

EKS の構築について、当初は AWS Load Balancer Controller のみ利用して進めていました。その後、2023年末頃に KubernetesGateway API が GA されたこともあって、そちらに舵を切ろうと考えていた時期もあり、実際 Ingress のことを考えている時間は長かったような気がします。

しかし、AWS での Gateway API 実装でもある VPC Lattice は、クラスター同士の通信に使うことを主たる用途としており、外部からのトラフィックを受ける Ingress としては活用できなさそうでした。(2024年3月現在)*1

個人的には、GKE が提供しているようなロール間の責任が明確に分離される構成にしたかったのですが、AWS ではちょっとまだ早い…ということで、他の手段を考えることに。参考までに、以下が GKE の提供しているものです。とても分かりやすいなーって思います。

引用元:https://cloud.google.com/kubernetes-engine/docs/concepts/gateway-api?hl=ja#platform-managed_gateway_per_namespace

cloud.google.com


さて、そんな紆余曲折を経て、最終的には AWS Load Balancer Controller (以下 ALBC) を利用したうえで、柔軟なトラフィックコントロールが可能となる Istio を利用することになりました。前述のとおり、ALBC は最初から利用してはいたのですが、マイクロサービスが追加される際に Namespace の分割を行うと、 Ingress の標準仕様では Namespace を跨いだルーティング設定が出来ません。なので「じゃあ Ingress を Namespace ごとに作成しよう」なんてことをすると、ALBC によって Ingress 単位で ALB が作成されてしまいます。

e.g. Ingress マニフェスト

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  # ここで指定した namespace 内のサービスのみパスベースルーティングに指定可能
  namespace: service-A
  name: service-A-ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/healthcheck-path: /
    alb.ingress.kubernetes.io/wafv2-acl-arn: arn:aws:wafv2:ap-northeast-1:123456789012:regional/webacl/xxxx-web-acl-ap-northeast-1-dev/xxxx-xxxx
spec:
  # ここで alb と定義することで ALB が作成される
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: service-A
                port:
                  number: 80

こういった背景もありながら、さらに今後のサービス拡大も見据えたとき、サービス間通信も発生するでしょうし、このタイミングで Service Mesh の恩恵を受けられるようにしておくのがベストだと考え、Istio を導入することにしました。Istio にした理由は、過去に運用経験のあるメンバーがいたり、最近認定試験が出たりデファクトスタンダードになっていると感じているのもありますが、最後は「SRE としてお互いにがんばっていこう!」と会話しながら決めたのを覚えています。(脳内補正が入ってるかも…) 話し合いと納得感、大事ですね!私は毎日、色々と教えてもらっており、日々感謝です。

「あれ、AWS にしたのに、なんで App Mesh 使わないの?」という点については、最後まで悩んだのですが、ちょっとここでは書きづらいので、どこかでお話できたらと…苦笑

ALBC + Istio 全体構成

簡単な構成図は以下のとおりです。

ALBC + Istio 構成図

全体構成としては、ALB Ingress から Istio Ingress Gatewy をターゲットとして構成し、ここまでを Gateway レイヤーとして定義して、アプリケーションへのルーティングには、 Istio Virtual Service を使用しています。 Virtual Service から各マイクロサービスの Service にルーティングさせることで、一つの ALB から各サービスへ流れていくような構成となりました。以下のドキュメントにある Shared gateway のパターンです。

istio.io

はじめは Istio Ingress Gateway をそのままプロビジョニングしたため、 Service type が LoadBalancer になっており、 Internal な NLB が出来ていました…これもメンバーから「杉山さん、ナンデスカコレ」とありがたい指摘をいただき、急いで Istio Ingress Gateway を定義している Argo CD の values.yaml に以下を追記…!持つべきものは頼れる仲間ですね。

# 一部抜粋...
replicaCount: 2
autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 4
# 急いで追記っっ
service:
  type: NodePort

色々調べているとき、AWS 公式のブログも非常に参考になりましたので、こちらで紹介したいと思います。

aws.amazon.com

aws.amazon.com

構築してみる - ALBの作成 -

だいぶ細かくなってしまうのですが、実際のマニフェストから抜粋しつつ、続きを書いてみます。まず ALBC に ALB を作ってもらうため、以下のマニフェストを作成します。なお、ALBC 自体は Argo CD + Helm でインストールしています。(一部抜粋しているため、そのまま使うと動かない可能性があります)

# alb-istio-ingress-gateway.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: alb-istio-ingress-gateway
  namespace: istio-ingress
  annotations:
    # 一部抜粋...
    alb.ingress.kubernetes.io/group.name: istio-ingressgateway
    # istio ingress gateway service をターゲットとしてヘルスチェックを実施
    alb.ingress.kubernetes.io/healthcheck-path: /healthz/ready
    alb.ingress.kubernetes.io/healthcheck-port: "15021"
    alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:123456789012:certificate/xxxxxxxx-xxxx-xxxxxxxxx
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
    alb.ingress.kubernetes.io/wafv2-acl-arn: arn:aws:wafv2:ap-northeast-1:123456789012:regional/webacl/xxx-wafv2-web-acl-ap-northeast-1/xxxxxxxx-xxxx-xxxxxxxxx
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
          - path: /*
            pathType: ImplementationSpecific
            backend:
              service:
                name: istio-ingressgateway
                port:
                  number: 80
Istio コアコンポーネントのインストール

次に、Istio のコアコンポーネントをインストールします。具体的には以下の3種類です。

  1. istio-base
  2. istiod
  3. istio-ingress

これらも Argo CD + Helm で導入します。注意点としては、istio-ingress のデプロイ時に istio-baseistiod が確実にデプロイされていないとエラーになることがある、というところでしょうか。Argo CD で 3種類のコンポーネントを一気に AutoSync させてしまい、依存関係が考慮されず、以下の Issues にあるように派手に転びました。

github.com

これで Istio Ingress Gateway のサービスが作成されます。このサービスを前述した Ingress で指定している形となります。( name: istio-ingressgateway のところです)

Gateway リソースの作成

続いて Gateway リソースを作成します。これは Helm を利用せず、以下のようなマニフェストを定義します。ここでは Istio Ingress Gateway の Pod とバインディングされるよう、selector にて istio: ingressgateway を定義してあげます。

# sample-gateway.yaml
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: sample-gateway
  namespace: istio-ingress
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - "*"
      port:
        name: http
        number: 80
        protocol: HTTP

作成したリソースは kubectl get gateway.networking.istio.io -n istio-ingress のコマンドで見つけることが可能です。次は Virtual Service にトラフィックを転送させていく必要がありますが、以下のドキュメントにも記載されているように、Virtual Service が Gateway にバインドされることで、トラフィック転送を制御することが可能になっています。

istio.io

The Gateway specification above describes the L4-L6 properties of a load balancer. A VirtualService can then be bound to a gateway to control the forwarding of traffic arriving at a particular host or gateway port.

実際の構築時には、ここまで構築するのに結構な時間が掛かってしまいました…Gateway のところが理解が難しく、ドキュメントを読んでいてもイマイチしっくり来なかったので、Virtual Service まで実際に作ってしまい、 Kiali を使って可視化しながら理解していきました。実際に手を動かして作ってしまうのも大切ですね…!

というわけで、ここまでで、以下のオレンジ枠内までが作成されました。最後は Virtual Service の作成です。

Gateway Layer

Virtual Service の作成

Virtual Service は各アプリケーション用マニフェストの方に定義しています。具体的には以下のような感じです。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: service-A-virtual-service
  namespace: service-A
spec:
  hosts:
    - "*"
  gateways:
    # ここでGatewayを指定
    - istio-ingress/sample-gateway
  http:
    - match:
        - uri:
            prefix: /api/v1/service-A
      route:
        - destination:
            host: service-A.service-A.svc.cluster.local
            port:
              number: 80

前述のとおり、Virtual Service を Gateway にバインドする必要があります。これは上記コメント箇所で Gateway を指定することで、バインドすることが可能です。ここまでで、無事にサービスまでトラフィックが流れるようになりました!

Virtual Service と Gateway のバインド

実際にやってみて

理解するまでは本当に大変でしたが、実際に構築して動かしていると、こんなに便利なものはない!というのが率直なところです。多くのサービスで Istio が利用されているのも納得しました。ただ、Kubernetes と違うサイクルでのバージョンアップ(半年ほど)があったり、Istio 自身も Gateway API の実装がベータ版で提供されていたりするので、これで終わりと満足せず、常にアップデートを追いかけ続けないといけないな、と感じています。ちょうど今、KubeCon + CloudNativeCon Europe 2024 も開催されていますし、ブログを拝見して追いかけているところです!

さいごに

今回は内製開発チームの Amazon EKS Ingress について紹介しました。ここは本当に肝になるところなので、SRE チームとしても Priority を上げて対応していきたいです!年内には Istio Gateway API 実装もベータから正式バージョンになりそうですし、今のうちに検証しておきたいですね。


私たちのチームは現在、積極的な採用活動を行っています!ご興味のある方は、ぜひ以下をご覧ください。

www.wantedly.com

それでは、最後までお読みいただき、ありがとうございました!また次回の投稿でお会いしましょう!

*1:ご相談に乗っていただいた SA の方にはいつも感謝しかありません!ありがとうございます!