🦉

NLBとALBの直接接続する際の注意点

2021/12/15に公開

AWSのNLBとALBを直接接続した構成を構築する機会があった。せっかくなので検証した内容などを記録しておく。

なお、この機能の利用方法は下記ドキュメントに記載されている。
Application Load Balancers as targets

背景

最近、下記要件を満たすようにELBを構成する必要があった。

  1. ホストベースルーティング機能が必要
  2. 固定IPである必要がある

これら要件を満たすには単純にALBやNLBを1つ立てればよいとはならない。
というのも1の要件を満たすにはALB、2の要件を満たすにはNLBを利用する必要がある。

以前も同様の要件で構築したことがあるが、そのときは下記構成で構築した。
固定IP化をNLBで行い、リクエストをnginxを用いたプロキシサーバを用いてALBへパススルーすることで実現した。

しかし今回は、下記構成で構築した。

この構成は9月末に発表された機能によって実現できるようになった。以前の構成と見比べてもらえばすぐに分かるが、プロキシサーバなしでNLBとALBを直接接続している。これにより、プロキシサーバを構築する必要なく(運用対象の削減!)、NLBの機能をALBの機能をどちらも同時に利用する事が可能になった。

NLBとALB直接接続の利点

まず最初に、この機能の利点について。
NLBとALBの機能を同時に利用できるようになったこと、主にNLBの機能であった下記機能をALBで利用可能になったのがうれしいところ。

  • ALBのIPの固定化が可能
  • ALBをNLBを介してPrivateLinkで公開可能

今回はIPの固定化のために利用しているが、PrivateLinkをALBで利用したいときもこの構成が使える。
なお、PrivateLinkを用いてALBを別VPCに公開する方法についてはAWSのブログで紹介されているので気になる方はそちらへ。
Network Load BalancerのターゲットグループにApplication Load Balancerを設定する

HTTPS通信で利用する場合

HTTPS通信を受けたい場合は注意すべき点が1つある。
それは、TLSの終端をALBにする必要がある点。

NLBとALBを接続する機能はNLBのTLSリスナではサポートしておらず、TCPリスナを利用する必要がある。そのため、必然的にALBを側でHTTPSリスナを用意し、TLSを終端する必要がある。
ちなみに、この制約によりNLBではログが取得できない。(NLBはTLSリスナしかログを収集できない)

ヘルスチェックの注意点

NLBからALBへのリクエスト転送はNLBのターゲットグループのヘルスチェックに合格しないと開始されない。

この機能を利用する際のヘルスチェックの仕様として注意すべき点は下記。

  • ヘルスチェックの多くの設定は変更不可
  • 送信元IPはNLBのローカルIPとなる
  • プロトコルはHTTPかHTTPSのみ利用可能
  • ALB側のレスポンスコードは200~399の範囲である必要がある

ヘルスチェックの多くの設定は変更不可

ALBとターゲットとした場合、ターゲットグループのヘルスチェック設定の多くは設定変更不可。基本的にデフォルトの値で利用することになる。
変更不可な設定は下記。

  • Unhealthyとみなすヘルスチェック失敗の閾値
  • ヘルスチェック失敗とみなすタイムアウト時間
  • ヘルスチェック間隔時間
  • ヘルスチェック成功とするステータスコード

こちらはドキュメントに記載されている。

For Advanced health check settings, you cannot modify Unhealthy threshold, Timeout, Interval, and Success codes.

Application Load Balancers as targets - Step 2: Create the target group with the Application Load Balancer as the target

ちなみにCloudFormationのデフォルト設定で構築した際の設定は下記となる。

その時利用したコードは下記。

InternetNLBTargetToALB:
  Type: AWS::ElasticLoadBalancingV2::TargetGroup
  Properties:
    Port: 80
    Protocol: TCP
    Targets:
      - Id: !Ref InternalALB
        Port: 80
    TargetType: alb
    VpcId: !Ref VPC

送信元IPはNLBのローカルIP

ALBへ行われるヘルスチェックの送信元IPはNLBのローカルIPとなる。(サブネットのCidrから採番されたIP)
そのため、ALB側のSecurityGroupでNLBのローカルIPからの通信を許可しておく必要がある。

プロトコルはHTTPかHTTPSのみ利用可能

ターゲットグループのポート設定はTCPしか許可されていないため、勘違いしやすいポイント。
ヘルスチェックプロトコルはHTTPかHTTPSのみ利用可能であり、TCPなどは利用不可となっている。

For Health checks, choose HTTP or HTTPS as the Health check protocol.

Application Load Balancers as targets - Step 2: Create the target group with the Application Load Balancer as the target

なお、CloudFormationで作成した場合、NLB用のターゲットグループのヘルスチェックポートはデフォルトだとTCPになるが、ALBをターゲットにした場合のみHTTPとなるので注意が必要。

※この情報はドキュメントが追いついていないので記載されていない。

ALB側のレスポンスコードは200~399

ヘルスチェックに対するレスポンスのステータスコードは200〜399である必要がある。
ALBのリスナルールでNLBからのリクエストは上記ステータスコードでレスポンスするように設定する必要がある。

下記はCloudFormationで構築する際の例。NLBのローカルIPからのリクエストに対して200番を返す設定としている。

ListenerRuleForNLBHealthCheck:
  Type: AWS::ElasticLoadBalancingV2::ListenerRule
  Properties:
    Actions:
      - Type: fixed-response
        FixedResponseConfig:
          StatusCode: '200'
    Conditions:
      - Field: source-ip
        SourceIpConfig:
          Values:
            - !Ref NLBCidr # NLBが存在するサブネットのCidr
    ListenerArn: !Ref InternalALBListener
    Priority: 1

おまけ:検証に利用したCloudFormationテンプレート

最後に検証で利用したCloudFormationテンプレートを記載しておく。これをデプロイすることで、NLBとALBをHTTPで接続した環境が構築される。(VPCなどは記載されていないので、別途作成する必要があり)

AWSTemplateFormatVersion: '2010-09-09'

Description: This template is for ELB connection test.
  It deploys internal Application Load Balancer and internet facing Network Load Balancer.

Parameters:
  PublicSubnets:
    Type: List<AWS::EC2::Subnet::Id>

  PrivateSubnets:
    Type: List<AWS::EC2::Subnet::Id>

  VPC:
    Type: AWS::EC2::VPC::Id

  NLBCidr:
    Type: String

Resources:
  InternalALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internal
      SecurityGroups:
        - !Ref SecurityGroupForInternalALB
      Subnets: !Ref PrivateSubnets
      Type: application

  InternalALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: fixed-response
          FixedResponseConfig:
            StatusCode: '403'
            ContentType: application/json
            MessageBody: '{"message":"NG"}'
      LoadBalancerArn: !Ref InternalALB
      Port: 80
      Protocol: HTTP

  ListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: fixed-response
          FixedResponseConfig:
            StatusCode: '200'
            ContentType: application/json
            MessageBody: '{"message":"OK"}'
      Conditions:
        - Field: source-ip
          SourceIpConfig:
            Values:
              - !Ref NLBCidr
      ListenerArn: !Ref InternalALBListener
      Priority: 1

  SecurityGroupForInternalALB:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: It is for internal ALB
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - CidrIp: !Ref NLBCidr
          IpProtocol: tcp
          FromPort: 80
          ToPort: 80
        - CidrIp: 0.0.0.0/0
          IpProtocol: tcp
          FromPort: 80
          ToPort: 80

  InternetNLB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      Subnets: !Ref PublicSubnets
      Type: network

  InternetNLBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref InternetNLBTargetToALB
      LoadBalancerArn: !Ref InternetNLB
      Port: 80
      Protocol: TCP

  InternetNLBTargetToALB:
    DependsOn:
      - InternalALBListener
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckProtocol: HTTP # The supported value is 'HTTP' or 'HTTPS' if the target type is ALB
      Port: 80
      Protocol: TCP
      Targets:
        - Id: !Ref InternalALB
          Port: 80
      TargetType: alb
      VpcId: !Ref VPC

Discussion