🐷

AWS入門 - AWS CDK 使ってみる

2021/10/22に公開

AWSを使う必要が出てきたので、色々と試してみる。
ここでは、AWS CDK を試す。

AWS CDK

https://docs.aws.amazon.com/ja_jp/cdk/latest/guide/home.html

AWS CDK とは CloudFormation を通じて、リソースを定義できる仕組みである。
特にTypeScriptなどのプログラミング言語で、プログラムとして記述できる特徴がある。

CLI

AWS CDK を使ってアプリケーションリソースを定義してみるので、CLIをインストールする。

$ npm install aws-sdk -g
$ cdk --version
1.128.0 (build 1d3883a)

CDKアプリケーション

CDKアプリケーションを作成するため、インストールしたCLIから新規作成用のコマンドを実行する。
これによって、CDKでリソースを記述するための雛形が作成される。

$ cdk init app --language typescript
$ tree -L 1 .
.
├── README.md
├── bin
├── cdk.json
├── jest.config.js
├── lib
├── node_modules
├── package-lock.json
├── package.json
├── test
└── tsconfig.json

スタック

https://docs.aws.amazon.com/ja_jp/cdk/latest/guide/stacks.html

デプロイ可能な単位のことをCDKではStackと呼ぶらしい。
lib/ec2-chat-stack.tsにStackを記述できるので、追記していく。

内容はシンプルで、EC2インスタンスを立ち上げる、ために必要な情報を記述していく。

import * as cdk from '@aws-cdk/core';
import * as ec2 from "@aws-cdk/aws-ec2";            // Allows working with EC2 and VPC resources
import * as iam from "@aws-cdk/aws-iam";            // Allows working with IAM resources
import * as keypair from "cdk-ec2-key-pair";        // Helper to create EC2 SSH keypairs

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

    // デフォルトで用意されているVPCを選択
    const vpc = ec2.Vpc.fromLookup(this, "VPC", {
      isDefault: true,
    });
    
    // EC2用のSSH鍵を作成
    // --> Secrets Manager で作成・保存される
    const key = new keypair.KeyPair(this, "KeyPair", {
      name: "cdk-keypair",
      description: "Key Pair created with CDK Deployment",
    });
    key.grantReadOnPublicKey;

    // EC2用のセキュリティグループを作成
    // --> SSHとHTTPでアクセスするためのポートを許可する
    const securityGroup = new ec2.SecurityGroup(this, "SecurityGroup", {
      vpc,
      description: "Allow SSH and HTTP in",
      allowAllOutbound: true,
    });
    securityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(22),
      "Allow SSH Access"
    );
    securityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(80),
      "Allow HTTP Access"
    );

    // EC2用のロールを作成
    // --> EC2インスタンスから各AWSサービスにアクセス出来る権限を付与する
    // --> 細かい意味は別途確認してみる
    const role = new iam.Role(this, "ec2Role", {
      assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"),
    });
    role.addManagedPolicy(
      iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore")
    );

    // EC2用のインスタンスタイプ・マシンイメージ
    // --> t2.micro を指定
    // --> Amazon Linux 2 を指定
    // --> セキュリティグループ・SSH鍵・ロールも指定
    const ec2Instance = new ec2.Instance(this, "Instance", {
      vpc,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.MICRO
      ),
      machineImage: new ec2.AmazonLinuxImage({
        generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
        cpuType: ec2.AmazonLinuxCpuType.X86_64,
      }),
      securityGroup: securityGroup,
      keyName: key.keyPairName,
      role: role,
    });

    // ---------- //

    new cdk.CfnOutput(this, "IP Address", {
      value: ec2Instance.instancePublicIp,
    });
    new cdk.CfnOutput(this, "Download Key Command", {
      value: "aws secretsmanager get-secret-value --secret-id ec2-ssh-key/cdk-keypair/private --query SecretString --output text > cdk-key.pem && chmod 400 cdk-key.pem",
    });
    new cdk.CfnOutput(this, "ssh command", {
      value: `ssh -i cdk-key.pem -o IdentitiesOnly=yes ec2-user@${ec2Instance.instancePublicIp}`,
    });
  }
}

Bootstrap

https://docs.aws.amazon.com/ja_jp/cdk/latest/guide/bootstrapping.html

CDKで利用するアカウント情報を設定する。
bin/ec2-chat.tsでコメントアウトされている部分を解除する。
これで、AWS CLI で設定したIDとリージョンが使われるようになる。

そして、cdk bootstrapコマンドを実行すると、CDKを使用するための下準備が行われる。
関連ファイルを保存するためのS3を用意したり、必要なIAMを設定したりしてくれるらしい。

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { Ec2ChatStack } from '../lib/ec2-chat-stack';

const app = new cdk.App();
new Ec2ChatStack(app, 'Ec2ChatStack', {
  /* If you don't specify 'env', this stack will be environment-agnostic.
   * Account/Region-dependent features and context lookups will not work,
   * but a single synthesized template can be deployed anywhere. */

  env: {
    account: process.env.CDK_DEFAULT_ACCOUNT,
    region: process.env.CDK_DEFAULT_REGION,
  },

  /* Uncomment the next line if you know exactly what Account and Region you
   * want to deploy the stack to. */
  // env: { account: '123456789012', region: 'us-east-1' },

  /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
});

デプロイ

そして、cdk deployコマンドを実行すると、記述した内容をもとにリソースが作成される。

$ cdk deploy
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬──────────────────────────────────┬────────┬────────────────┬──────────────────────────────┬───────────┐
│   │ Resource                         │ Effect │ Action         │ Principal                    │ Condition │
├───┼──────────────────────────────────┼────────┼────────────────┼──────────────────────────────┼───────────┤
│ + │ ${EC2-Key-Pair-Manager-Role.Arn} │ Allow  │ sts:AssumeRole │ Service:lambda.amazonaws.com │           │
├───┼──────────────────────────────────┼────────┼────────────────┼──────────────────────────────┼───────────┤
│ + │ ${ec2Role.Arn}                   │ Allow  │ sts:AssumeRole │ Service:ec2.amazonaws.com    │           │
└───┴──────────────────────────────────┴────────┴────────────────┴──────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬──────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                     │ Managed Policy ARN                                                             │
├───┼──────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${EC2-Key-Pair-Manager-Role}${EC2-Key-Pair-Manager-Policy}                                                 │
│ + │ ${EC2-Key-Pair-Manager-Role} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │
├───┼──────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${ec2Role}                   │ arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore             │
└───┴──────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
Security Group Changes
┌───┬──────────────────────────┬─────┬────────────┬─────────────────┐
│   │ Group                    │ Dir │ Protocol   │ Peer            │
├───┼──────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${SecurityGroup.GroupId} │ In  │ TCP 22     │ Everyone (IPv4) │
│ + │ ${SecurityGroup.GroupId} │ In  │ TCP 80     │ Everyone (IPv4) │
│ + │ ${SecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │
└───┴──────────────────────────┴─────┴────────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? y
Ec2ChatStack: deploying...
[0%] start: Publishing afe67465ec62603d27d77795221a45e68423c87495467b0265ecdadad80bb5e2:current
[100%] success: Published afe67465ec62603d27d77795221a45e68423c87495467b0265ecdadad80bb5e2:current
Ec2ChatStack: creating CloudFormation changeset...








 ✅  Ec2ChatStack

Outputs:
Ec2ChatStack.DownloadKeyCommand = aws secretsmanager get-secret-value --secret-id ec2-ssh-key/cdk-keypair/private --query SecretString --output text > cdk-key.pem && chmod 400 cdk-key.pem
Ec2ChatStack.IPAddress = 54.178.111.86
Ec2ChatStack.sshcommand = ssh -i cdk-key.pem -o IdentitiesOnly=yes ec2-user@54.178.111.86

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:111111111111:stack/Ec2ChatStack/f6763df0-332a-11ec-b117-0e64b831f3c9

Secrets Manager

作成したSSH鍵はSecrets Managerに登録されている。
ec2-ssh-key/cdk-keypair/privateという名前で登録されている。

これをAWS CLIとかで参照して、SSH時に指定すればインスタンス内にアクセスできる。

$ aws secretsmanager get-secret-value --secret-id ec2-ssh-key/cdk-keypair/private --query SecretString --output text > cdk-key.pem

リソース削除

作成したリソースはcdk destroyコマンドを実行すると、削除できる。

$ cdk destroy
Are you sure you want to delete: Ec2ChatStack (y/n)? y
Ec2ChatStack: destroying...





 ✅  Ec2ChatStack: destroyed
****************************************************
*** Newer version of CDK is available [1.129.0]  ***
*** Upgrade recommended (npm install -g aws-cdk) ***
****************************************************

まとめ

プログラムとして定義できるのは良い。
特に開発・検証・本番のように、複数かつ同等のAWS環境を構築するのが非常に楽になるはず。

Amazon EC2 でウェブアプリケーションをデプロイする

https://aws.amazon.com/jp/getting-started/guides/deploy-webapp-ec2/

Discussion