1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS CDKでNext.jsをデプロイする【第3回】〜ALB構築〜

1
Last updated at Posted at 2026-03-03

はじめに

第2回では、ECS ClusterとFargate用Task Definitionを作成しました。

この時点では、

  • ECRにイメージはある
  • Task Definitionも定義済み
  • しかし、まだ外部からアクセスする入口が存在しない

という状態です。

第3回では、外部公開のためのALB(Application Load Balancer)を構築します。

本シリーズの構成

1. ECR編
2. ECS Task Definition編
3. ALB編(本記事)
4. ECS Service編
5. セキュリティ編

構成全体と今回の作業範囲

AWS全体構成の中で、本記事がどの範囲を扱うのかを確認します。

ALBとは?

ALB(Application Load Balancer)は、HTTP/HTTPS通信を処理するAWSのロードバランサーです。

ALBの主な役割は次のとおりです。

  • インターネットからのHTTP/HTTPSリクエストを受け取る
  • 登録されたターゲットへリクエストを転送する(負荷分散)
  • ヘルスチェックで正常なターゲットのみを利用する
  • パスやドメインに応じて転送先を切り替える

3-1. ALB用Stackを作成する

lib/alb-stack.ts を作成します。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';

interface AlbStackProps extends cdk.StackProps {
  vpc: ec2.IVpc;
}

export class AlbStack extends cdk.Stack {
  public readonly targetGroup: elbv2.ApplicationTargetGroup;

  constructor(scope: Construct, id: string, props: AlbStackProps) {
    super(scope, id, props);

    // ALB用Security Group
    // allowAllOutbound: trueを明示
    const albSg = new ec2.SecurityGroup(this, 'AlbSecurityGroup', {
      vpc: props.vpc,
      allowAllOutbound: true,
      description: 'Security group for public ALB',
    });

    // インターネットからのHTTP(80)を許可
    albSg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP from Internet');

    // ALB作成(作成したSGを適用)
    const alb = new elbv2.ApplicationLoadBalancer(this, 'LoadBalancer', {
      vpc: props.vpc,
      internetFacing: true, // インターネット公開
      securityGroup: albSg,
    });

    // Target Group作成(ECSを登録する場所)
    this.targetGroup = new elbv2.ApplicationTargetGroup(this, 'TargetGroup', {
      vpc: props.vpc,
      port: 3000,
      protocol: elbv2.ApplicationProtocol.HTTP,
      targetType: elbv2.TargetType.IP, // FargateはIP指定
      // ヘルスチェック設定
      healthCheck: {
        path: '/',
        healthyHttpCodes: '200',
      },
    });

    // Listener作成(HTTP:80)
    alb.addListener('HttpListener', {
      port: 80,
      defaultTargetGroups: [this.targetGroup],
    });

    new cdk.CfnOutput(this, 'AlbDnsName', {
      value: alb.loadBalancerDnsName,
    });
  }
}

ポイント解説

VPCについて

今回は、第2回でEcsStack内から参照したVPCをそのまま利用します。
そのため、AlbStackでは新たにVPCを取得せず、外部から渡されたVPCを使用する構成にします。

ALBの通信を制御するSecurity Groupの設定

const albSg = new ec2.SecurityGroup(this, 'AlbSecurityGroup', {
  vpc: props.vpc,
  allowAllOutbound: true,
  description: 'Security group for public ALB',
});

ALBがECS(ターゲット)へリクエストを転送するためには、ALB側のSecurity Groupでアウトバウンド通信が許可されている必要があります。
今回は検証構成として、ALBからのアウトバウンド通信をすべて許可しています。

ALBをインターネット向けに公開する設定

internetFacing: true

ALBをインターネット向けに公開するための設定です。
trueを指定すると、ALBはPublic Subnetに配置され、インターネットから直接アクセス可能になります。

Fargate構成に合わせたターゲットタイプの設定

targetType: elbv2.TargetType.IP

Fargateでは、コンテナはEC2インスタンス上で動作するのではなく、
タスクごとに作成されるENI(Elastic Network Interface)に紐付きます。
そのため、ALBが通信する相手は「EC2インスタンス」ではなく「IPアドレス」です。
よって、ターゲットタイプはINSTANCEではなくIPを指定します。

ターゲットの正常性を判定するHealthCheckの設定

healthCheck: {
  path: '/',
  healthyHttpCodes: '200',
},

ALBは設定したパスへ一定間隔でリクエストを送り、そのレスポンスをもとにターゲットの状態を判定します。
以下の条件を満たすと Healthy と判断されます。

  • healthyHttpCodes で指定したステータスコードが返る
  • かつ、接続がタイムアウトしない
  • これが連続して成功回数のしきい値を満たす

上記の設定により、/にアクセスしてHTTP 200を返す場合のみ正常と判定されます。
Next.jsのトップページは通常/200を返すため、基本的な構成としてはこの設定で問題ありません。

3-2. ecs-stackを修正する

第2回ではEcsStack内でVPCを取得していましたが、
他のStackから利用できるようpublic readonly vpcとして公開する形に修正します。

対象ファイル
lib/ecs-stack.ts

import * as ecs from 'aws-cdk-lib/aws-ecs';
//...

export class EcsStack extends cdk.Stack {
  public readonly vpc: ec2.IVpc; // 追加

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // VPC(デフォルトVPCを使用)
    this.vpc = ec2.Vpc.fromLookup(this, 'DefaultVpc', {
      isDefault: true,
    });

    // クラスター作成
    const cluster = new ecs.Cluster(this, 'Cluster', {
      vpc: this.vpc,
    });

    //...
  }
}

3-3. binにStackを追加する

対象ファイル
bin/cdk-nextjs-infra.ts

import { AlbStack } from '../lib/alb-stack';
//...
const ecsStack = new EcsStack(app, 'NextjsInfraEcsStack', { env });

new AlbStack(app, 'NextjsInfraAlbStack', {
  env,
  vpc: ecsStack.vpc,
});

3-4. デプロイ

cdk deploy NextjsInfraAlbStack --profile <プロファイル名>

成功すると、OutputsにALBのDNS名が表示されます。

例:

Outputs:
NextjsAlbStack.AlbDnsName = xxxxxxxxx.ap-northeast-1.elb.amazonaws.com

本記事で作成したスタックに含まれるAWSリソースは、削除するまで料金が発生します。
検証が不要になった場合は、以下のコマンドでスタックを削除してください。

cdk destroy <スタック名> --profile <プロファイル名>

AWSコンソールで確認

① EC2 → ロードバランサー

  • ALBが作成されているか
  • 状態がアクティブになっているか

② EC2 → ターゲットグループ

  • TargetGroupが作成されているか
    ※ まだECS Serviceを作成していないため、登録ターゲットは0です。

最終コード(今回追加・修正したファイル)

lib/alb-stack.ts

コード全体
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';

interface AlbStackProps extends cdk.StackProps {
  vpc: ec2.IVpc;
}

export class AlbStack extends cdk.Stack {
  public readonly targetGroup: elbv2.ApplicationTargetGroup;

  constructor(scope: Construct, id: string, props: AlbStackProps) {
    super(scope, id, props);

    // ALB用Security Group
    // allowAllOutbound: trueを明示
    const albSg = new ec2.SecurityGroup(this, 'AlbSecurityGroup', {
      vpc: props.vpc,
      allowAllOutbound: true,
      description: 'Security group for public ALB',
    });

    // インターネットからのHTTP(80)を許可
    albSg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP from Internet');

    // ALB作成(作成したSGを適用)
    const alb = new elbv2.ApplicationLoadBalancer(this, 'LoadBalancer', {
      vpc: props.vpc,
      internetFacing: true, // インターネット公開
      securityGroup: albSg,
    });

    // Target Group作成(ECSを登録する場所)
    this.targetGroup = new elbv2.ApplicationTargetGroup(this, 'TargetGroup', {
      vpc: props.vpc,
      port: 3000,
      protocol: elbv2.ApplicationProtocol.HTTP,
      targetType: elbv2.TargetType.IP, // FargateはIP指定
      // ヘルスチェック設定
      healthCheck: {
        path: '/',
        healthyHttpCodes: '200',
      },
    });

    // Listener作成(HTTP:80)
    alb.addListener('HttpListener', {
      port: 80,
      defaultTargetGroups: [this.targetGroup],
    });

    new cdk.CfnOutput(this, 'AlbDnsName', {
      value: alb.loadBalancerDnsName,
    });
  }
}

lib/ecs-stack.ts

コード全体
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import * as logs from 'aws-cdk-lib/aws-logs';

export class EcsStack extends cdk.Stack {
  public readonly vpc: ec2.IVpc; // 追加

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // VPC(デフォルトVPCを使用)
    this.vpc = ec2.Vpc.fromLookup(this, 'DefaultVpc', {
      isDefault: true,
    });

    // ECS Cluster
    const cluster = new ecs.Cluster(this, 'Cluster', {
      vpc: this.vpc,
    });

    const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDefinition', {
      cpu: 256,
      memoryLimitMiB: 512,
    });

    const repository = ecr.Repository.fromRepositoryName(
      this,
      'ImportedRepository',
      'my-nextjs-app'
    );

    const container = taskDefinition.addContainer('Container', {
      // ECRリポジトリからイメージを指定
      // ※本番は固定タグ推奨
      image: ecs.ContainerImage.fromEcrRepository(repository),
      // CloudWatch Logsへログを出力する設定
      logging: ecs.LogDrivers.awsLogs({
        streamPrefix: 'nextjs',
        logRetention: logs.RetentionDays.ONE_WEEK,
      }),
    });

    container.addPortMappings({
      containerPort: 3000,
    });
  }
}

bin/cdk-nextjs-infra.ts

コード全体
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { EcrStack } from '../lib/ecr-stack';
import { EcsStack } from '../lib/ecs-stack';
import { AlbStack } from '../lib/alb-stack';

const app = new cdk.App();

const env = {
  account: process.env.CDK_DEFAULT_ACCOUNT,
  region: process.env.CDK_DEFAULT_REGION,
};

new EcrStack(app, 'NextjsInfraEcrStack', { env });

const ecsStack = new EcsStack(app, 'NextjsInfraEcsStack', { env });

new AlbStack(app, 'NextjsInfraAlbStack', {
  env,
  vpc: ecsStack.vpc,
});

ここまでの成果

外部公開の入口となるALBを構築し、ECSを接続するための準備が整いました。

現時点ではタスクは起動していませんが、次回ECS Serviceを作成することでアプリが実際に表示される状態になります。

次回

次回はECS Serviceを作成し、ALBと接続します。
タスクを常時起動させ、ALB経由でNext.jsアプリが表示されるところまで進めます。

第4回:ECS Service編はこちら

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?