EKSのIngressにてターゲットタイプがipとinstanceの場合におけるALBのヘルスチェック挙動を調べてみた

2024.02.21

こんにちはカスタマーソリューション部のこーへいです!

今回はEKSのIngressにてALBのターゲットタイプにipとinstanceがあり、ヘルスチェックに細かい違いがあることが発覚したので検証結果をまとめました。

結論

ヘルスチェックの観点においては、ターゲットタイプにipを指定した方がpod単位でのヘルスチェックの結果確認や、失敗しているpodにもトラフィックを流さないのでおすすめ。

補足情報(追記)

Using IP registration also allows us to control the timing and configuration of the traffic directly against the backend Pods, rather than trying to manage connections through the NodePort rules as well.

(※機械翻訳)また、IP登録を使用することで、NodePortルールを通して接続を管理するのではなく、バックエンドのPodに対して直接トラフィックのタイミングと設定を制御することができます。

こちらのドキュメントに上記記述があり、推奨事項として基本ip指定で良さそうでした。

前提

  • EKS Cluster作成済み
  • AWS Load Balancer Controllerを使用できる状態

上記を前提といたします。今回は本題ではないので省略しますが、EKS初心者の方は下記記事による構築がすごくわかりやすかったです。

事前準備

AWS Load Balancer Controllerを使用できる状態まで完了していると、残りは以下のリソース作成が必要となります。

  • Ingress
  • Service
  • deployment

サンプルとはなってしまいますが、検証の際に自分が用いたyamlファイルを置いておきます(検証のためpod数は2つにしています)。

ターゲットタイプがipの場合のサンプルファイル

Ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing  # 外部からのトラフィックを許可
    alb.ingress.kubernetes.io/target-type: ip  # Ipをターゲットに設定
  name: sample-ingress
spec:
  ingressClassName: alb  # ingressのクラス名を定義
  rules:
  - http:  # httpルール
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sample-service  # 接続するService
            port:
              number: 80
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-service
spec:
  ports:  # Podへのアクセスに使用するport
    - port: 80
      targetPort: 80
      protocol: TCP
  type: ClusterIP  # クラスター内Pod通信
  selector:
    app: nginx
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app  # 作成するリソースの名前
spec:
  replicas: 2  # Deploymentが管理するPodのレプリカ数
  selector:  # 適用するリソースの選択
    matchLabels:  # ラベルの一致を条件とする
      app: nginx
  template:  # 作成するpodの定義
    metadata:  
      labels:
        app: nginx
    spec:
      containers:  # pod内のコンテナの仕様
        - name: nginx  # コンテナの名前
          image: public.ecr.aws/nginx/nginx:1.23  # コンテナイメージ
          ports:  # コンテナが公開するポート(内部向け)
            - name: tcp
              containerPort: 80

ターゲットタイプがinstanceの場合のサンプルファイル

ターゲットタイプがinstanceなため、サービスタイプをNodeportにしています。

このトラフィックモードを使用するには、Kubernetes サービスで NodePort または「LoadBalancer」 タイプを指定する必要があります。

参考:https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/alb-ingress.html

Ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing  # 外部からのトラフィックを許可
    alb.ingress.kubernetes.io/target-type: instance  # instanceをターゲットに設定
  name: sample-ingress
spec:
  ingressClassName: alb  # ingressのクラス名を定義
  rules:
  - http:  # httpルール
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sample-service  # 接続するService
            port:
              number: 80
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-service
spec:
  ports:
    - port: 80  # サービスポート
      targetPort: 80  # ポッドのポート
      protocol: TCP
  type: NodePort  # NodePortタイプのサービス
  selector:
    app: nginx  # このサービスにトラフィックをルーティングするPodのラベルセレクター
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app  # 作成するリソースの名前
spec:
  replicas: 2  # Deploymentが管理するPodのレプリカ数
  selector:  # 適用するリソースの選択
    matchLabels:  # ラベルの一致を条件とする
      app: nginx
  template:  # 作成するpodの定義
    metadata:  
      labels:
        app: nginx
    spec:
      containers:  # pod内のコンテナの仕様
        - name: nginx  # コンテナの名前
          image: public.ecr.aws/nginx/nginx:1.23  # コンテナイメージ
          ports:  # コンテナが公開するポート(内部向け)
            - name: tcp
              containerPort: 80

調べる

では早速それぞれのターゲットタイプの場合のヘルスチェックを見てみましょう。

ターゲットタイプがIPの場合

上記の通り、ワーカーノード(EC2インスタンス)に存在するpodのプライベートIPアドレスがターゲットとして登録されています(EC2インスタンスのIPアドレスではないです)。

ヘルスチェックに関するパラメータは上記となります。ここではパスがルートディレクトリであることを把握しておきます。

今回サンプルアプリケーションとしてnginxのコンテナを利用しており、nginx側のデフォルト設定としてルートディレクトリにアクセスした場合は、設定されたパス(/usr/share/nginx/html/)に格納されたindex.htmlを返すようになっています。

特にpod内でnginxの設定は変更していないので、ヘルスチェックも上記の通り成功し、直接ブラウザからアクセスした場合もnginxのページが表示されました。

片方のpodのヘルスチェックを失敗にしてみる

片方のpodに入り、index.htmlを削除します。

k get pods -n default -o wide # pod名を確認
k exec -it [pod名] -- /bin/bash # podにログイン
rm /usr/share/nginx/html/index.html # podのindex.htmlを削除

index.htmlを削除後、しばらくすると削除したpodのヘルスチェックが失敗に切り替わりました。

ALBはunhealthyにはトラフィックを流さないので、引き続き正常なpodでのアクセスは可能です。

ターゲットタイプがinstanceの場合

ターゲットタイプがinstanceの場合は、予想通りpodではなくワーカーノード(EC2インスタンス)が登録されています。

ヘルスチェックもパスはルートディレクトリで設定しています。

今回の場合も特にpod内でnginxの設定は変更していないので、ヘルスチェックも上記の通り成功し、直接ブラウザからアクセスした場合もnginxのページが表示されました。

2つあるpod両方のindex.htmlを削除する

今回は2つあるpod両方に入り、index.htmlを削除します。

k get pods -n default -o wide # pod名を確認
k exec -it [pod名] -- /bin/bash # podにログイン
rm /usr/share/nginx/html/index.html # podのindex.htmlを削除

2つのpodのindex.htmlを削除している様子。

ヘルスチェックが失敗に切り替わりましたね、ターゲットタイプがinstanceの場合でもALBのヘルスチェックはpodに対して実行されていることがわかりました。

ノードに対してヘルスチェックしている可能性も考えましたが違ったようですね(index.htmlを削除する前はヘルスチェックに成功しており、インスタンス自体はwebサーバーではないので元々予想できた)。

片方のpodのindex.htmlを削除する

今回一番気にしていたのがターゲットタイプがinstanceの場合、複数あるpodのうち1つだけindex.htmlを削除(unhealthyに)した場合、ヘルスチェックの表示はどうなるのか?でした。

同様の手順でpodの1つのみindex.htmlを削除し、ヘルスチェックを確認したところ成功扱いになっていました。

ブラウザからアクセスすると、403エラーのページと正常なnginxのページが交互に開かれました。

podのログを確認しても、unhealtyのpod(index.htmlを削除したpod)に流れていることがわかります。

10.0.1.250 - - [21/Feb/2024:04:46:49 +0000] "GET / HTTP/1.1" 403 153 "-" "ELB-HealthChecker/2.0" "-"
2024/02/21 04:46:51 [error] 30#30: *64 directory index of "/usr/share/nginx/html/" is forbidden, client: 10.0.1.250, server: localhost, request: "GET / HTTP/1.1", host: "k8s-default-samplein-xxxx-xxxx.ap-northeast-1.elb.amazonaws.com"
10.0.1.250 - - [21/Feb/2024:04:46:51 +0000] "GET / HTTP/1.1" 403 555 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" "[MyIpAddress]"

この場合のヘルスチェックの表示やトラフィックの流れなどもう少し細かく調査することもできそうでしたが、少なくともヘルスチェックの観点においては、「ターゲットタイプにipを指定した方がpod単位でのヘルスチェックの結果確認や、失敗しているpodにもトラフィックを流さないのでおすすめ」という冒頭の結論に至ると感じました。

まとめ

ターゲットタイプがinstance時の細かい仕様や挙動までは確認できませんでした(ドキュメントにも記載なさそう)が、基本ターゲットタイプはipを指定するのが吉かなと思います。

もしこの辺り補足情報があれば嬉しいです。

メトリクス挙動について

メトリクスの挙動についても調べてみましたので、気になる方はこちらの記事を参照ください。

その他参考