ALB のパスベースルーティングで固定レスポンスを返す構成を AWS CDK で作ってみた

ALB のパスベースルーティングで固定レスポンスを返す構成を AWS CDK で作ってみた

Clock Icon2024.08.25

こんにちは、製造ビジネステクノロジー部の若槻です。

アプリケーションレイヤーで機能する Elastic Load Balancing である Application Load Balancer (ALB) では、リスナールールを設定することによりリクエストに応じた柔軟なルーティングを実装可能です。

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-listeners.html#listener-rules

また、リスナーに固定レスポンスアクションを設定して、ターゲットグループの代わりに固定のレスポンスを返させることもできます。

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-listeners.html#fixed-response-actions

今回は、ALB のパスベースルーティングで固定レスポンスを返す構成AWS CDK で作ってみました。

デフォルトのルーティングのみ

まず、シンプルな実装としてデフォルトのルーティングのみを実装してみます。

実装

ApplicationListener ConstructdefaultAction を構成することにより、デフォルトの固定レスポンスを設定できます。

lib/cdk-sample-stack.ts
import {
  aws_ec2 as ec2,
  aws_elasticloadbalancingv2 as elbv2,
  Stack,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    /**
     * Application Load Balancer の作成
     */
    const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', {
      vpc: new ec2.Vpc(this, 'VPC'),
      internetFacing: true,
    });

    /**
     * リスナーの作成
     */
    alb.addListener('Listener', {
      port: 80,
      // デフォルトの固定レスポンスを設定
      defaultAction: elbv2.ListenerAction.fixedResponse(200, {
        contentType: 'text/plain',
        messageBody: 'Hello, CDK!',
      }),
    });
  }
}

上記を CDK デプロイして CDK スタックの Construct ツリーを見ると、ALB にリスナーが 1 つ追加されていることがわかります。

またロードバランサーのコンソール画面からはリスナールールがデフォルトとして追加いることと、そのルールのアクションの詳細が確認できます。

動作確認

作成されたロードバランサーの DNS 名にアクセスすると、デフォルトの固定レスポンスが返ってきます。

$ curl $ALB_DNS_NAME
Hello, CDK!

一方でパスベースルーティングは未実装なので、サブディレクトリにアクセスした場合でも同じレスポンスが返ってきます。

$ curl $ALB_DNS_NAME/thanks
Hello, CDK!

パスベースルーティングの追加

それでは、パスベースルーティングを追加してみます。

実装

次のようにリスナーに対して addAction メソッドを使って、パスベースルーティングを追加します。

lib/cdk-sample-stack.ts
import {
  aws_ec2 as ec2,
  aws_elasticloadbalancingv2 as elbv2,
  Stack,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    /**
     * Application Load Balancer の作成
     */
    const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', {
      vpc: new ec2.Vpc(this, 'VPC'),
      internetFacing: true,
    });

    /**
     * リスナーの作成
     */
    const listener = alb.addListener('Listener', {
      port: 80,
      defaultAction: elbv2.ListenerAction.fixedResponse(200, {
        contentType: 'text/plain',
        messageBody: 'Hello, CDK!',
      }),
    });

    /**
     * GET /thanks 時のルーティングルールの追加
     */
    listener.addAction('ThanksPath', {
      priority: 1,
      conditions: [
        elbv2.ListenerCondition.httpRequestMethods(['GET']),
        elbv2.ListenerCondition.pathPatterns(['/thanks']),
      ],
      action: elbv2.ListenerAction.fixedResponse(200, {
        contentType: 'text/plain',
        messageBody: 'Thank you!',
      }),
    });

    /**
     * GET /goodbye 時のルーティングルールの追加
     */
    listener.addAction('GoodbyePath', {
      priority: 2,
      conditions: [
        elbv2.ListenerCondition.httpRequestMethods(['GET']),
        elbv2.ListenerCondition.pathPatterns(['/goodbye']),
      ],
      action: elbv2.ListenerAction.fixedResponse(200, {
        contentType: 'text/plain',
        messageBody: 'Goodbye!',
      }),
    });
  }
}

CDK の差分を確認すると、リスナールールが 2 つ追加されていることがわかります。

$ cdk diff
Stack CdkSampleStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Resources
[+] AWS::ElasticLoadBalancingV2::ListenerRule ALB/Listener/ThanksPathRule ALBListenerThanksPathRule5ACB4065
[+] AWS::ElasticLoadBalancingV2::ListenerRule ALB/Listener/GoodbyePathRule ALBListenerGoodbyePathRuleC36CB36F

参考までに Git の差分は以下の通りです。

git diff
$ diff --git a/lib/cdk-sample-stack.ts b/lib/cdk-sample-stack.ts
index 0f79616..b60ac86 100644
--- a/lib/cdk-sample-stack.ts
+++ b/lib/cdk-sample-stack.ts
@@ -20,12 +20,42 @@ export class CdkSampleStack extends Stack {
     /**
      * リスナーの作成
      */
-    alb.addListener('Listener', {
+    const listener = alb.addListener('Listener', {
       port: 80,
       defaultAction: elbv2.ListenerAction.fixedResponse(200, {
         contentType: 'text/plain',
         messageBody: 'Hello, CDK!',
       }),
     });
+
+    /**
+     * GET /thanks 時のルーティングルールの追加
+     */
+    listener.addAction('ThanksPath', {
+      priority: 1,
+      conditions: [
+        elbv2.ListenerCondition.httpRequestMethods(['GET']),
+        elbv2.ListenerCondition.pathPatterns(['/thanks']),
+      ],
+      action: elbv2.ListenerAction.fixedResponse(200, {
+        contentType: 'text/plain',
+        messageBody: 'Thank you!',
+      }),
+    });
+
+    /**
+     * GET /goodbye 時のルーティングルールの追加
+     */
+    listener.addAction('GoodbyePath', {
+      priority: 2,
+      conditions: [
+        elbv2.ListenerCondition.httpRequestMethods(['GET']),
+        elbv2.ListenerCondition.pathPatterns(['/goodbye']),
+      ],
+      action: elbv2.ListenerAction.fixedResponse(200, {
+        contentType: 'text/plain',
+        messageBody: 'Goodbye!',
+      }),
+    });
   }
 }

上記を CDK デプロイして CDK スタックの Construct ツリーを見ると、ALB にリスナールールが 2 つ追加されていることがわかります。

ロードバランサーのコンソール画面からは、デフォルトルールより高い優先度でパスベースのリスナールールが追加されていることが確認できます。

動作確認

パスベースルーティングで定義したパスにアクセスすると、それぞれの固定レスポンスが返ってくることが確認できます。

$ curl $ALB_DNS_NAME/thanks
Thank you!

$ curl $ALB_DNS_NAME/goodbye
Goodbye!

またルートおよび未定義のパスにアクセスすると、デフォルトの固定レスポンスが返ってきます。

$ curl $ALB_DNS_NAME
Hello, CDK!

$ curl $ALB_DNS_NAME/hoge
Hello, CDK!

ここで、パスベースルーティングで定義したパスのサブディレクトリ へアクセスすると、デフォルトの固定レスポンスが返ってきます。

$ curl $ALB_DNS_NAME/thanks/sub
Hello, CDK!

/thanks/* へのアクセスが、/thanks のパスのルールにはマッチせず、デフォルトのルールにマッチしてしまっているためです。

定義したパスのサブディレクトリへの対応

パスベースルーティングで定義したパスのサブディレクトリも対応させてみます。

実装

任意のサブディレクトに対応させる場合は、既存のパスベースルーティングのリスナールールのパスパターンに /path/* を追加します。

lib/cdk-sample-stack.ts
import {
  aws_ec2 as ec2,
  aws_elasticloadbalancingv2 as elbv2,
  Stack,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    /**
     * Application Load Balancer の作成
     */
    const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', {
      vpc: new ec2.Vpc(this, 'VPC'),
      internetFacing: true,
    });

    /**
     * リスナーの作成
     */
    const listener = alb.addListener('Listener', {
      port: 80,
      defaultAction: elbv2.ListenerAction.fixedResponse(200, {
        contentType: 'text/plain',
        messageBody: 'Hello, CDK!',
      }),
    });

    /**
     * GET /thanks 時のルーティングルールの追加
     */
    listener.addAction('ThanksPath', {
      priority: 1,
      conditions: [
        elbv2.ListenerCondition.httpRequestMethods(['GET']),
        elbv2.ListenerCondition.pathPatterns(['/thanks', '/thanks/*']), // パスパターンを追加
      ],
      action: elbv2.ListenerAction.fixedResponse(200, {
        contentType: 'text/plain',
        messageBody: 'Thank you!',
      }),
    });

    /**
     * GET /goodbye 時のルーティングルールの追加
     */
    listener.addAction('GoodbyePath', {
      priority: 2,
      conditions: [
        elbv2.ListenerCondition.httpRequestMethods(['GET']),
        elbv2.ListenerCondition.pathPatterns(['/goodbye', '/goodbye/*']), // パスパターンを追加
      ],
      action: elbv2.ListenerAction.fixedResponse(200, {
        contentType: 'text/plain',
        messageBody: 'Goodbye!',
      }),
    });
  }
}

CDK の差分を確認すると、既存のパスベースルーティングのリスナールールのパスパターンが更新されていることがわかります。

$ cdk diff
Stack CdkSampleStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Resources
[~] AWS::ElasticLoadBalancingV2::ListenerRule ALB/Listener/ThanksPathRule ALBListenerThanksPathRule5ACB4065
 └─ [~] Conditions
     └─ @@ -11,7 +11,8 @@
        [ ]   "Field": "path-pattern",
        [ ]   "PathPatternConfig": {
        [ ]     "Values": [
        [-]       "/thanks"
        [+]       "/thanks",
        [+]       "/thanks/*"
        [ ]     ]
        [ ]   }
        [ ] }
[~] AWS::ElasticLoadBalancingV2::ListenerRule ALB/Listener/GoodbyePathRule ALBListenerGoodbyePathRuleC36CB36F
 └─ [~] Conditions
     └─ @@ -11,7 +11,8 @@
        [ ]   "Field": "path-pattern",
        [ ]   "PathPatternConfig": {
        [ ]     "Values": [
        [-]       "/goodbye"
        [+]       "/goodbye",
        [+]       "/goodbye/*"
        [ ]     ]
        [ ]   }
        [ ] }

✨  Number of stacks with differences: 1

参考までに Git の差分はは以下の通りです。

git diff
$ diff --git a/lib/cdk-sample-stack.ts b/lib/cdk-sample-stack.ts
diff --git a/lib/cdk-sample-stack.ts b/lib/cdk-sample-stack.ts
index b60ac86..d629dcd 100644
--- a/lib/cdk-sample-stack.ts
+++ b/lib/cdk-sample-stack.ts
@@ -35,7 +35,7 @@ export class CdkSampleStack extends Stack {
       priority: 1,
       conditions: [
         elbv2.ListenerCondition.httpRequestMethods(['GET']),
-        elbv2.ListenerCondition.pathPatterns(['/thanks']),
+        elbv2.ListenerCondition.pathPatterns(['/thanks', '/thanks/*']),
       ],
       action: elbv2.ListenerAction.fixedResponse(200, {
         contentType: 'text/plain',
@@ -50,7 +50,7 @@ export class CdkSampleStack extends Stack {
       priority: 2,
       conditions: [
         elbv2.ListenerCondition.httpRequestMethods(['GET']),
-        elbv2.ListenerCondition.pathPatterns(['/goodbye']),
+        elbv2.ListenerCondition.pathPatterns(['/goodbye', '/goodbye/*']),
       ],
       action: elbv2.ListenerAction.fixedResponse(200, {
         contentType: 'text/plain',

上記を CDK デプロイしてリスナールールのコンソール上での変更を確認すると、パスベースルーティングのリスナールールの Condition が更新されていることがわかります。

動作確認

パスベースルーティングで定義したパスおよびそのサブディレクトリにアクセスすると、それぞれの固定レスポンスが返ってくることが確認できます。

$ curl $ALB_DNS_NAME/thanks
Thank you!

$ curl $ALB_DNS_NAME/thanks/sub
Thank you!

$ curl $ALB_DNS_NAME/goodbye
Goodbye!

$ curl $ALB_DNS_NAME/goodbye/sub
Goodbye!

またルートおよび未定義のパスにアクセスすると、引き続きデフォルトの固定レスポンスが返ってきます。

$ curl $ALB_DNS_NAME
Hello, CDK!

$ curl $ALB_DNS_NAME/hoge
Hello, CDK!

おわりに

ALB のパスベースルーティングで固定レスポンスを返す構成を AWS CDK で作ってみました。

固定レスポンスを使用すると、開発中の API のモックや、メンテナンス中のサイト表示などの用途で利用できて便利です。またパスベースルーティングにより単一の ALB で複数のエンドポイントを管理できるため、運用の効率化にもつながります。ぜひ活用してみてください。

参考

https://dev.classmethod.jp/articles/alb-vpc-lambda-sample-cdk

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.