Open4

EC2でcdkとかの開発ボックスを用意するとか

モッモッ

ここではとりあえずspotインスタンスを使う

lib/cdk-dev-box-ec2-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3assets from 'aws-cdk-lib/aws-s3-assets';
import { SpotInstance } from 'cdk-ec2-spot-simple';
import * as fs from 'fs';
import * as path from 'path';

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

    // 設定ファイルを読み込み
    const configPath = path.join(__dirname, '../config.json');
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));

    // カスタムVPCを作成
    const vpc = new ec2.Vpc(this, 'CustomVPC', {
      ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
      maxAzs: 2,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'Public',
          subnetType: ec2.SubnetType.PUBLIC,
        }
      ],
      natGateways: 0
    });

    // キーペア参照を作成
    const keyPair = ec2.KeyPair.fromKeyPairName(this, 'KeyPair', config.keyPairName);

    // セキュリティグループを作成
    const securityGroup = new ec2.SecurityGroup(this, 'SpotInstanceSecurityGroup', {
      vpc,
      description: 'Security group for CDK development box spot instance',
      allowAllOutbound: true
    });

    // SSH アクセスを許可
    securityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(22),
      'Allow SSH access'
    );

    // ユーザーデータスクリプトを作成
    const userData = ec2.UserData.forLinux();
    userData.addCommands(
      'apt-get update -y',
      'apt-get install -y curl unzip git jq python3',
      // AWS CLIをインストール
      'curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"',
      'unzip awscliv2.zip',
      'sudo ./aws/install',
      // Node.jsとnpmをインストール
      'curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -',
      'apt-get install -y nodejs',
      // AWS CDKをグローバルにインストール
      'npm install -g aws-cdk',
      // OpenAI Codexをインストール
      'npm install -g @openai/codex'
    );

    // 永続的なスポットインスタンスを作成
    const spotInstance = new SpotInstance(this, 'PersistentSpotInstance', {
      vpc,
      vpcSubnets: {
        subnetType: ec2.SubnetType.PUBLIC
      },
      instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.SMALL),
      machineImage: ec2.MachineImage.lookup({
        name: 'debian-12-arm64-*',
        owners: [config.debianAmiOwner],
        filters: {
          'architecture': ['arm64'],
          'virtualization-type': ['hvm'],
          'root-device-type': ['ebs']
        }
      }),
      keyPair,
      securityGroup,
      userData,
      spotOptions: {
        interruptionBehavior: ec2.SpotInstanceInterruption.STOP,
        requestType: ec2.SpotRequestType.PERSISTENT
      }
    });

    // インスタンスロールにCloudFormation権限を追加
    const role = spotInstance.role;
    role.addToPrincipalPolicy(new iam.PolicyStatement({
      actions: [
        "cloudformation:DescribeStacks",
        "cloudformation:DescribeStackResources",
        "cloudformation:GetTemplate",
        "cloudformation:ListStacks",
        "cloudformation:ListStackResources"
      ],
      resources: ["*"]
    }));

    // インスタンスIDとパブリックIPを出力
    new cdk.CfnOutput(this, 'SpotInstanceId', {
      value: spotInstance.instanceId,
      description: 'CDK development box spot instance ID'
    });

    new cdk.CfnOutput(this, 'SpotInstancePublicIP', {
      value: spotInstance.instancePublicIp,
      description: 'CDK development box spot instance public IP'
    });

  }
}
bin/app.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { CdkDevBoxEc2Stack } from '../lib/cdk-dev-box-ec2-stack';
import * as fs from 'fs';
import * as path from 'path';

const app = new cdk.App();

// 設定ファイルから環境情報を読み込み
const configPath = path.join(__dirname, '..', 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));

// ここでデプロイ先を固定(ENVに無ければ設定ファイルの既定値を使う)
const env: cdk.Environment = {
  account: process.env.CDK_DEFAULT_ACCOUNT ?? config.account,
  region:  process.env.CDK_DEFAULT_REGION  ?? config.region,
};

new CdkDevBoxEc2Stack(app, 'CdkDevBoxEc2Stack', { env });
config.json
{
  "account": "your_aws_account",
  "region": "ap-northeast-1",
  "debianAmiOwner": "136693071363",
  "keyPairName": "your_key_pair"
}

デプロイ

cdk deploy
モッモッ

ポイント

権限

    // インスタンスロールにCloudFormation権限を追加
    const role = spotInstance.role;
    role.addToPrincipalPolicy(new iam.PolicyStatement({
      actions: [
        "cloudformation:DescribeStacks",
        "cloudformation:DescribeStackResources",
        "cloudformation:GetTemplate",
        "cloudformation:ListStacks",
        "cloudformation:ListStackResources"
      ],
      resources: ["*"]
    }));

ここによりcdk diffに必要な権限がEC2に与えられる

ユーザーデーター

    const userData = ec2.UserData.forLinux();
    userData.addCommands(
      'apt-get update -y',
      'apt-get install -y curl unzip git jq python3',
      // AWS CLIをインストール
      'curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"',
      'unzip awscliv2.zip',
      'sudo ./aws/install',
      // Node.jsとnpmをインストール
      'curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -',
      'apt-get install -y nodejs',
      // AWS CDKをグローバルにインストール
      'npm install -g aws-cdk',
      // OpenAI Codexをインストール
      'npm install -g @openai/codex'
    );

ここで以下のツールがインストールされている

AWS関連ツール

  • AWS CLI v2 - AWSサービスの操作
  • AWS CDK CLI - CDKプロジェクトの管理
  • SSM Agent - Systems Manager Session Managerでの接続サポート

ユーティリティツール

  • jq - JSONデータの処理・整形
  • curl - HTTPクライアント
  • unzip - アーカイブ展開ツール
  • @openai/codex - OpenAI Codex CLI
  • aws-cdk - CDK CLI

ここをユーザーごとの環境にセットアップしていくのだが、userdataが長くなってくるとこの方式では辛いのでscriptに切り出してs3に置いた方がいいかも

lib/cdk-dev-box-ec2-stack.ts
+ import * as s3assets from 'aws-cdk-lib/aws-s3-assets';
@@ -46,23 +46,21 @@ export class CdkDevBoxEc2Stack extends cdk.Stack {
       'Allow SSH access'
     );
 
-    // ユーザーデータスクリプトを作成
+    // S3アセットとしてuserdataスクリプトを作成
+    const userdataAsset = new s3assets.Asset(this, 'UserdataAsset', {
+      path: path.join(__dirname, '../assets/userdata.sh'),
+    });
+
+    // ユーザーデータを作成してS3からスクリプトをダウンロード・実行
     const userData = ec2.UserData.forLinux();
-    userData.addCommands(
-      'apt-get update -y',
-      'apt-get install -y curl unzip git jq python3',
-      // AWS CLIをインストール
-      'curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"',
-      'unzip awscliv2.zip',
-      'sudo ./aws/install',
-      // Node.jsとnpmをインストール
-      'curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -',
-      'apt-get install -y nodejs',
-      // AWS CDKをグローバルにインストール
-      'npm install -g aws-cdk',
-      // OpenAI Codexをインストール
-      'npm install -g @openai/codex'
-    );
+    userData.addS3DownloadCommand({
+      bucket: userdataAsset.bucket,
+      bucketKey: userdataAsset.s3ObjectKey,
+      localFile: '/tmp/userdata.sh',
+    });
+    userData.addExecuteFileCommand({
+      filePath: '/tmp/userdata.sh',
+    });
 
     // 永続的なスポットインスタンスを作成
     const spotInstance = new SpotInstance(this, 'PersistentSpotInstance', {
assets/userdata.sh
#!/bin/bash
set -e

# システムの更新
apt-get update -y
apt-get install -y curl unzip git jq python3

# AWS CLI をインストール
curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Node.js と npm をインストール
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
apt-get install -y nodejs

# AWS CDK をグローバルにインストール
npm install -g aws-cdk

# OpenAI Codex をインストール
npm install -g @openai/codex

# ログ出力
echo "Setup completed successfully" >> /var/log/userdata.log
モッモッ

userdataをコネる

結局開発環境はどれだけuserdataをキメるかってところでもあるけどキメすぎると逆にわけがわからんくなることもあり...

たとえばclaudecodeも導入し、cdkのデモプロジェクトをinitする場合

assets/userdata.sh
@@ -3,7 +3,7 @@ set -e
 
 # システムの更新
 apt-get update -y
-apt-get install -y curl unzip git jq python3
+apt-get install -y curl unzip git jq python3 ripgrep
 
 # AWS CLI をインストール
 curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"
@@ -20,5 +20,14 @@ npm install -g aws-cdk
 # OpenAI Codex をインストール
 npm install -g @openai/codex
 
-# ログ出力
-echo "Setup completed successfully" >> /var/log/userdata.log
\ No newline at end of file
+# Claude Code をインストール
+npm install -g @anthropic-ai/claude-code
+
+
+# CDKテストプロジェクト作成用のディレクトリを作成
+mkdir -p /home/admin/cdk-test
+cd /home/admin/cdk-test
+cdk init app --language typescript
+chown admin /home/admin -R

起動てuserdataが完了したら

cd cdk-test
cdk deploy

などを引いてみればよい


問答無用再生成するには

cdk destroy --force && cdk deploy --require-approval never
モッモッ

あとはうまいことvscodeとかで繋ぐ