0
1

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書き始め用の個人的メモ

Posted at

CDK自体はいじったことがあったのだが、APIリファレンスの追い方をちょっと前に整理したものをここに保存。
後は、サンプルがどうもVPCを新規に作るものが多いような気がするので「既存のサブネットを使う」コードを保存しておきたかったという思いもある。
数年後に忘れても思い出せますように。
自分のためのメモだが、ついでに初学者の人のヒントになれば嬉しい。

記事の目的

  • APIリファレンスの読み方
  • そのほかサンプルやテスト方法などについて参考リンクのメモ
  • ALB + EC2 を既存サブネットに起動するコードメモ

APIリファレンスの見方

importの書き方

リファレンスの細かい見方はここが参考になった。

例として、ApplicationLoadBalancerの場合には以下のようにタイプが書いてある。

aws-cdk-lib » aws_elasticloadbalancingv2 » ApplicationLoadBalancer

この場合importの仕方と使い方はアンダーバー(_)をハイフン(-)に変えて以下のようになる。

import * as elbv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
...
    const alb = new elbv2.ApplicationLoadBalancer(this, "ALB", { 
    ...

このimportの書き方は以下のページなどで使われている書き方である。

他にもimportの書き方は複数あり、以下のページではClassic importBarrel importの2通りを紹介している。

それぞれ以下。
Classic import

import { Stack, App, aws_s3 as s3 } from 'aws-cdk-lib';
...
new s3.Bucket(stack, 'TestBucket');

Barrel import

import { Bucket } from 'aws-cdk-lib/aws-s3';
...
new Bucket(stack, 'TestBucket');

また、上記含めた色々なimport方法を比較してくれている記事もあった。好きな方法で書けば良い。

リファレンスの追い方

「ALB作成時に既存のサブネットを指定したい」という状況を例にとってみていく。
ApplicationLoadBalancerのページを見る

vpcSubnetsプロパティはSubnetSelectionというタイプなので、SubnetSelectionタイプでどうやってサブネットを指定できるのか、プロパティを見る。
SubnetSelectionでサブネットを指定できそうなのはsubnetsプロパティで、これはISubnet[]タイプである。そこでさらにISubnetのリファレンスを見ると以下のようなメソッドから取得できることがわかる。

Obtainable from Subnet.fromSubnetAttributes(), Subnet.fromSubnetId()

なので、こんな感じで既存サブネットを指定したSubnetSelectionを取得できる(fromSubnetAttributesとfromSubnetIdの使い分けはパッとわからなかったので置いておく)

import * as ec2 from "aws-cdk-lib/aws-ec2";

const subnetIds = [
      {
        "subnetId":"subnet-xxxxx",
        "az":"ap-northeast-1a"
        
      },
      {
        "subnetId":"subnet-yyyyy",
        "az":"ap-northeast-1b"
      }
    ]

        const subnets: ec2.SubnetSelection = {
      subnets:subnetIds.map(subnet => {
        // fromSubnetAttributesはISubnetタイプを返す
        return ec2.Subnet.fromSubnetAttributes(this, "subnet-"+subnet.subnetId, {
          "subnetId":subnet.subnetId,
          "availabilityZone": subnet.az
        })
      })
    }

バージョン管理について

CDK固有のものではない情報も多いがこれを機にメモ。

  • CDKバージョンを上げたいときはaws-cdkだけではなく、package.jsonaws-cdk-libを見る必要がある。
  • グローバルでcdkをインストールするとバージョンの不整合が起こりうるので、npm install aws-cdkでローカルインストールしてnpx cdkで使うのがバージョンの管理はしやすそう
  • 以下のコマンドで、全部のパッケージを最新にできる。この記事がバージョン管理について考察してくれている

npm install npm-check-updates
npx ncm -u
npm install

コンストラクタの作り方

この辺りが参考になりそう(読んだわけではない)。

L2コンストラクトで対応していないプロパティを変えるエスケープハッチ

L1コンストラクトを取り出してそのプロパティを変更する。

// Get the CloudFormation resource
const cfnBucket = bucket.node.defaultChild as s3.CfnBucket;

// Change its properties
cfnBucket.analyticsConfiguration = [
  { 
    id: 'Config',
    // ...        
  } 
];

テスト方法

aws-cdk-lib/assertionsライブラリを使って単体テストができそう

AWS CDK Immersion Day ワークショップ

サンプル

  • https://github.com/aws-samples
    色々なサンプルが置いてあるレポジトリだが、cdk + 使いたいサービス名で検索するとCDKアプリケーションもあったりする

  • AWS Solutions Constructs
    CDKチームではないがAWSの人が開発しているらしい。「API Gateway + Lambda」とか「Fargate + SQS」とか、2サービスくらいを組み合わせてまとめたものが多そう。単品でこれを作るのは面倒だよね、というレベルのものはないが、シンプルが故にパーツとしては使いやすそう。

  • https://cdkpatterns.com/
    CDKサンプルをオープンソースで公開するプロジェクトのよう
    例えば以下のLambda + RDS (RDS Proxy)など、しっかりした構成のサンプル
    https://github.com/cdk-patterns/serverless/tree/main/the-rds-proxy/typescript

ALB + EC2 を既存サブネットに起動するコード例

やはりどこかに雛形を置いておきたいためこの記事に入れる。
あとデフォルトでALB作ろうとすると同じAZにノード二つ配置しようとしてエラーになる気がする。

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";
import * as elbv2target from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets';
import * as asg from 'aws-cdk-lib/aws-autoscaling';

// 既存のVPCとサブネットを指定する
// ALBはinternet facingで80ポートフルオープン
// **EC2インスタンスはSSHポートを開けていないのでそこは追記する**
// ALBにアクセスすると固定文字列が返されるだけのシンプルなApacheサーバー

//既存VPCを選択
const VPCID = "vpc-aaa";

// 既存のパブリックサブネットとそのID。AZは別のサブネットを選ぶこと。
const subnetIds = [
  {
    "subnetId": "subnet-xxxxx",
    "az": "ap-northeast-1a"

  },
  {
    "subnetId": "subnet-yyyyy",
    "az": "ap-northeast-1b"
  }
]

//既存キーペア名
const ec2KeyPairName = "<EC2インスタンス接続用の既存キーペアの名前>";

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

    // 既存のVPC, subnetを取得
    const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { vpcId: VPCID })

    const subnets: ec2.SubnetSelection = {
      subnets: subnetIds.map(subnet => {
        return ec2.Subnet.fromSubnetAttributes(this, "subnet-" + subnet.subnetId, {
          "subnetId": subnet.subnetId,
          "availabilityZone": subnet.az
        })
      })
    }

    // ALBを作成
    //// SG for ALB
    const sgForAlb = new ec2.SecurityGroup(this, "vpcForAlb", {
      vpc,
      description: "SG for ALB. created by CDK"
    })

    sgForAlb.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80))

    //// ALB
    const alb = new elbv2.ApplicationLoadBalancer(this, "ALB", {
      vpc,
      vpcSubnets: subnets,
      internetFacing: true,
      securityGroup: sgForAlb
    });

    // EC2インスタンス用のSG
    const sgForEc2 = new ec2.SecurityGroup(this, "vpcForEc2", {
      vpc,
      description: "SG for EC2. created by CDK"
    })

    sgForEc2.addIngressRule(ec2.Peer.securityGroupId(sgForAlb.securityGroupId), ec2.Port.tcp(80))
    // SSH用に22番ポートを開けるルールも書く
    // sgForEc2.addIngressRule(ec2.Peer.prefixList(prefixListId), ec2.Port.tcp(22))
    // sgForEc2.addIngressRule(ec2.Peer.ipv4("172.0.0.0/8"), ec2.Port.tcp(22))

    // ターゲットグループ作成
    const targetGroup = new elbv2.ApplicationTargetGroup(this, "TargetGroup", {
      vpc,
      port: 80,
      targetType: elbv2.TargetType.INSTANCE,
    });

    // リスナーを作成してALBにアタッチ
    alb.addListener("Listener", {
      port: 80,
      defaultTargetGroups: [targetGroup]
    });


    //autoscaling
    ////起動テンプレート
    const userData = ec2.UserData.forLinux();
    userData.addCommands(
      "sudo dnf install httpd -y",
      'sudo echo "test page" |sudo tee /var/www/html/index.html',
      "sudo service httpd restart"
    );

    const launchTemplate = new ec2.LaunchTemplate(this, 'LaunchTemplate', {
      launchTemplateName: "ec2LaunchTemplateByCdk",
      machineImage: ec2.MachineImage.latestAmazonLinux2023(),
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.MICRO
      ),
      securityGroup: sgForEc2,
      keyPair: ec2.KeyPair.fromKeyPairName(this, "keyPair", ec2KeyPairName),
      userData
    });

    //起動テンプレートを指定してオートスケーリンググループ作成 & ターゲットグループにアタッチ
    const autoScalingGroup = new asg.AutoScalingGroup(this, "AutoScalingGroup", {
      vpc,
      autoScalingGroupName: "asgByCdk",
      launchTemplate,
      vpcSubnets: subnets
    });
    autoScalingGroup.attachToApplicationTargetGroup(targetGroup)
  }
}

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?