shibatch's journey

日々考えていることをつらつら書くだけです

きみもAWS LoadBalancer Controller芸人になろう

AWSKubernetesingressコントローラーとしてAWS LoadbBanacer Controllerがあります。 これ、使うとALBが自動で起動されて使うことができます。つまり、ALBの仕様に完全に依存する形のIngress Controllerです。

ALBは高機能な反面、ingressとして使うと少々クセがある感じがしたので覚え書きとしてまとめていきます。 仕様に制限があるのでその制限を回避する方法も載せていきます。

なお、現時点での最新版、v2.7での情報です。

1. リスナールールを「条件」「アクション」に分けてアノテーションで記載する

ALBにはリスナールールという仕組みがあり、AWS LoadBalancer Controllerではこの仕組みを制御できます。

Application Load Balancer のリスナールール - Elastic Load Balancing

リスナールールはアクセスを「条件」と「アクション」に分けて記述する機能です。 AWS LoadBalancer Controllerを使うとIngressでこのリスナールールを表現することができます。

条件

条件はalb.ingress.kubernetes.io/conditions.${conditions-name} で、ここにSourceIPやらリクエストメソッドはGETやらの条件をつけることができます。

アクション

アクションはalb.ingress.kubernetes.io/actions.${action-name}で、ここにリダイレクトするなり、特定のKuernetes のservice resourceに転送するなりのアクションをつけることができます。

これは実際におこなった設定なのですが「password変更のRESTful APIを塞ぎたい」場合、以下のようなアノテーションingressに設定することになります。

    alb.ingress.kubernetes.io/conditions.deny-password-login: >
      [{"Field":"http-request-method", "httpRequestMethodConfig":{"Values":["POST"]}}]
    alb.ingress.kubernetes.io/actions.deny-password-login: >
      {"Type": "fixed-response", "fixedResponseConfig":{"statusCode":"403"}}

conditionでPOST、actionで403のレスポンスを返す、としています。conditionとactionで名前を合わせることで一対の設定となります。 これをingressのruleに設定します。以下のようにすれば、example.com/api/session に対して上記のアノテーションを設定することができます。

  rules:
  - host: example.com
    http:
      paths:
      - backend:
          service:
            name: deny-password-login  👈annotationで設定した名前を指定する
            port:
              name: use-annotation
        path: /api/session
        pathType: Exact

2.「条件」の個数制限に注意する

ALBの仕様として、「条件」は5つまでしか設定できません。

Application Load Balancer のクォータ - Elastic Load Balancing

この個数を超えるとingressを設定してもALBには設定は反映されません。AWS LoadbBanacer Controllerのログにはエラーが出力されますが、Ingress ControllerのPodやIngressは見かけ上異常なく見えるので注意が必要です。

3. pathTypeの仕様に注意する

IngressではpathTypeによって完全一致か前方一致かを指定できます。

Ingress | Kubernetes

  • ImplementationSpecific(実装に特有): このパスタイプでは、パスとの一致はIngressClassに依存します。Ingressの実装はこれを独立したpathTypeと扱うことも、PrefixExactと同一のパスタイプと扱うこともできます。
  • Exact: 大文字小文字を区別して完全に一致するURLパスと一致します。
  • Prefix: /で分割されたURLと前方一致で一致します。大文字小文字は区別され、パスの要素対要素で比較されます。パス要素は/で分割されたパスの中のラベルのリストを参照します。リクエストがパス p に一致するのは、Ingressのパス p がリクエストパス p と要素単位で前方一致する場合です。

簡単に言えばExactは完全一致、Prefixは前方一致で、AWS LoadbBanacer Controllerを使った場合でもその仕様は変わりません。

ただし、pathTypeはALBの「条件」に設定されるため、前述の個数制限は留意する必要があります。

たとえば、先ほど出した例は Exactを使いましたが、

  rules:
  - host: example.com
    http:
      paths:
      - backend:
          service:
            name: deny-password-login 
            port:
              name: use-annotation
        path: /api/session
        pathType: Exact  👈これね

この場合は自動的に2つ「条件」を使うことになります。

  • HTTPホストヘッダーが example.com
  • パスパターンが/api/session

そのため、他の条件(リクエストメソッドやソースIPなど)は3つつけることができます。

さて、Prefixはどうなるのでしょう?

  rules:
  - host: example.com
    http:
      paths:
      - backend:
          service:
            name: deny-password-login 
            port:
              name: use-annotation
        path: /api/session
        pathType: Prefix

この場合は3つ「条件」を使います。

  • HTTPホストヘッダーが example.com
  • パスパターンが/api/session
  • パスパターンが/api/session/*

そのため、他の条件(リクエストメソッドやソースIPなど)は2つになります。

いちばんわかりにくいImplementationSpecificの場合はどうなるのでしょうか? これは指定したそのままがALBに設定されます。

        path: /api/session
        pathType: ImplementationSpecific

↑たとえばこの場合はExactと同じように、2つ「条件」を使うことになります。example.com/api/session には適用されますが example.com/api/session/hoge には適用されない設定です。

        path: /api/session/*
        pathType: ImplementationSpecific

これはExactでもPrefixでもない指定になって、2つ「条件」を使うことになります。 example.com/api/session には適用されず、example.com/api/session/hoge には適用されます。

こういう感じなので、AWS LoadbBanacer Controller を使う場合は設定が思った通りに反映されているかALBを確認しながらやると良いです。

4. 制限は回避できる

ここまで条件の指定は5つまでしかない、ということを口すっぱく言ってきましたが、この仕様は回避することができます。 たとえばソースIPをたくさん条件に指定したい場合は以下のように複数アノテーションを作ればよいです。

    alb.ingress.kubernetes.io/actions.allow-from-certain-ips1: >
      {"Type": "forward", "ForwardConfig": {"TargetGroups": [{"ServiceName": "exampleservice", "ServicePort": "80"}]}}
    alb.ingress.kubernetes.io/conditions.allow-from-certain-ips1: >
      [{"Field":"source-ip", "sourceIpConfig": {"values":["8.8.4.0/24", "8.8.8.0/24", "8.34.208.0/20"]}}]
    alb.ingress.kubernetes.io/actions.allow-from-certain-ips2: >
      {"Type": "forward", "ForwardConfig": {"TargetGroups": [{"ServiceName": "exampleservice", "ServicePort": "80"}]}}
    alb.ingress.kubernetes.io/conditions.allow-from-certain-ips2: >
      [{"Field":"source-ip", "sourceIpConfig": {"values":["8.35.192.0/20", "23.236.48.0/20", "23.251.128.0/19"]}}]
  - host: example.jp
    http:
      paths:
      - backend:
          service:
            name: allow-from-certain-ips1
            port: 
              name: use-annotation
        path: /example/*
        pathType: ImplementationSpecific
  - host: example.jp
    http:
      paths:
      - backend:
          service:
            name: allow-from-certain-ips2
            port: 
              name: use-annotation
        path: /example/*
        pathType: ImplementationSpecific

これは特定のソースIPからexample.jp/example/*にアクセスした場合はそのままexampleserviceに通すことを意味しています。


どうでしょう?AWS LoadBalancer Controllerの深淵を少しは覗くことができたでしょうか??

なんだか難しそうには見えますが、慣れてしまうと割と高機能なALBの機能を活用できるので便利です。

誰か困っている人に届きますように……