AWS CDK のWorkshopを試したら、ElasticIPアドレスの枯渇でコケたので対策した

クラウド

AWS CDK という、IaC が面白そうなので、今トライしています。

とりあえずハンズオンからということで、有名な Workshop をやってみています。

TypeScript の基礎から始める AWS CDK 開発入門
This workshop is about introduction TypeScript for using CDK.

ここで、ElasticIPアドレスに関してコケたので備忘録しておきます。

症状

現状、もろもろ理由で、アカウント内の ElasticIP アドレスは枯渇している(全て使用している)状態です。
その状況で、以下の typescript で書いた AWS CDK を cdk deploy するとエラーになりました。

(ちなみに対象のページは こちら

内容としては、パブリックサブネットにEC2インスタンスを起動するだけの超シンプルなやつです。

コード
import { Duration, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
// ec2 に関するパッケージを import
import * as ec2 from "aws-cdk-lib/aws-ec2";
// ファイルを読み込むためのパッケージを import
import { readFileSync } from "fs";

export class CdkWorkshopStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, "BlogVpc", {
      ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"),
      maxAzs: 2,
    });

    // EC2 インスタンスの宣言を準備
    const webServer1 = new ec2.Instance(this, "WordpressServer1", {
      vpc: vpc,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.SMALL
      ),
      machineImage: new ec2.AmazonLinuxImage({
        generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
      }),
      // EC2 インスタンスを配置するサブネットを指定
      vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
    });

    // user-data.sh を読み込み、変数に格納
    const script = readFileSync("./lib/resources/user-data.sh", "utf8");
    // EC2 インスタンスにユーザーデータを追加
    webServer1.addUserData(script);

    // port80, 全ての IP アドレスからのアクセスを許可
    webServer1.connections.allowFromAnyIpv4(ec2.Port.tcp(80));
  }
}

エラーは以下のようになりました。

5:06:15 PM | CREATE_FAILED        | AWS::EC2::EIP                         | BlogVpcPublicSubnet1EIPF2ED1F25
Resource handler returned message: "The maximum number of addresses has been reached. (Service: Ec2, Status Code: 400, Request ID: XXXXXXXXXXXXX)" (RequestToken: XXXXXXXXXXX, HandlerErrorCode: GeneralServiceException)

The maximum number of addresses has been reached ということで、EIPが上限に達したとのことです。

対策

結構大変でした。
ChatGPT や AI付きのBing に聞いてみましたが、妥当な解決策を提示してくれず・・・
ググっても情報出てこないので、
思い切って ChatGPT plus で ChatGPT4 に聞いてみると、さすが GPT4 ということで、適切な答えをくれました。

以下、修正後のソースコードです。
NATゲートウェイをなしに設定すると解決しました。
NATゲートウェイが悪さしてたんですね・・・

import { Duration, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
// ec2 に関するパッケージを import
import * as ec2 from "aws-cdk-lib/aws-ec2";
// ファイルを読み込むためのパッケージを import
import { readFileSync } from "fs";

export class CdkWorkshopStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, "BlogVpc", {
      ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/16"),
      maxAzs: 2,
+      natGateways: 0, // この行を追加
    });

    // EC2 インスタンスの宣言を準備
    const webServer1 = new ec2.Instance(this, "WordpressServer1", {
      vpc: vpc,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.SMALL
      ),
      machineImage: new ec2.AmazonLinuxImage({
        generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
      }),
      // EC2 インスタンスを配置するサブネットを指定
      vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
    });

    // user-data.sh を読み込み、変数に格納
    const script = readFileSync("./lib/resources/user-data.sh", "utf8");
    // EC2 インスタンスにユーザーデータを追加
    webServer1.addUserData(script);

    // port80, 全ての IP アドレスからのアクセスを許可
    webServer1.connections.allowFromAnyIpv4(ec2.Port.tcp(80));
  }
}

解説

今回のWorkshopでは、プライベートサブネットとパブリックサブネットをそれぞれ作るというものになります。( というか実はデフォルトで生成するとプライベートサブネットとパブリックサブネットをそれぞれ構築するらしい。そのつもりがなければ別途指定が必要とのこと> )

で、デフォルトでNATゲートウェイが付属されるらしいです。
そして、NATゲートウェイは ElasticIPアドレスが必須のようで、結果的にNATゲートウェイがつくからElasticIPアドレスでエラーになってたとのことです。

なので、NATゲートウェイをなくすことで解決しました。

補足

NATゲートウェイがないと困る場合は、NATインスタンスで代替可能とのことです。

以下、ChatGPTによる回答。

私の質問:

NATゲートウェイにElasticIPアドレスをアタッチせずに、プライベートサブネットからNATゲートウェイを経由してインターネットにアクセスする方法はないのですか?

ChatGPTの回答:

AWS NATゲートウェイは、デフォルトでElastic IPアドレスを必要とします。これは、NATゲートウェイがプライベートサブネット内のインスタンスからのリクエストをインターネットに転送する際、固定のパブリックIPアドレスが必要であるためです。

ただし、Elastic IPアドレスを使用せずにプライベートサブネットからインターネットにアクセスする方法として、NATインスタンスを検討するこ> とができます。NATインスタンスは、EC2インスタンスにNATソフトウェアをインストールして構成することで、NATゲートウェイと同様の機能を提供し> ます。NATインスタンスでは、Elastic IPアドレスの代わりに、一時的なパブリックIPアドレスを使用することができます。

NATインスタンスを使用する場合は、以下の手順を実行します。

NATインスタンス用のAmazon Linux 2 EC2インスタンスを作成します。
NATインスタンスにパブリックサブネットに配置し、一時的なパブリックIPアドレスを割り当てます。
NATインスタンスのセキュリティグループを設定して、プライベートサブネット内のインスタンスからのトラフィックを許可します。
NATインスタンスでIPマスカレードとフォワーディングを有効にするように、Amazon Linux 2の設定を変更します。
プライベートサブネットのルートテーブルを更新して、インターネットへのトラフィックをNATインスタンスに転送するように設定します。
ただし、NATインスタンスはマネージドサービスではないため、パフォーマンスや可用性を管理する責任がユーザーにあります。また、NATゲートウェイと比較してスケーラビリティや容量に制限がある場合があります。そのため、要件に応じてNATインスタンスとNATゲートウェイのどちらを使用するか慎重に検討してください。

コメント

タイトルとURLをコピーしました