AWS CDKで作るEC2 ー 初歩の初歩
初歩の記事が一本あった方がいろいろいいなという事でハンズオン形式で書いてみた。
AWS CDKとは
これを読んでいきなり理解するのも結構難しいとは思うんだけど、要するにCDK
というのはAWSリソースをプログラムコードで管理するものだ。例えば何も考えずにEC2
を起動するとDefault VPC
というネットワークが共有され何も考えずネットワークが利用可能となる。一方でプロジェクト向けに独立したネットワーク空間を、新たなVPCを作成して定義することも可能である。これによりネットワーク空間を完全に分離する事で他のリソースによる干渉を制限する事ができるわけなのだが、ここで独自のEC2
をカスタムVPCで1つ起動しようと思うと、手作業でやる場合結構しんどい思いをする事になるだろう(このしんどさを理解する意味ではCDK
を学ぶ前に、一度手積みのAWS環境を構築する事を推奨するのではあるが)。
EC2
を1つインターネットに出すだけでそこそこのネットワーク構成が必要となるし、そこに至るまでの設定が多いと何をどう設定したのかを忘れがちになってAWSリソースが結構汚れるという経験を手積みで設定するとそのような事になりがちだろう。これをある程度解消するのがCDK
などのIaC(Infrastructure as Code)
であり、プログラムコードでインフラ環境を構築させる仕組みである。IaC
に関してはCDK
の他にterraform
といったもの等が存在するが、AWS
に限ってIaC
するのであれば現状ではCDK
を最初に触れてみる事がAWSを主に利用する環境においてはROI投資対効果
の点からも有益と筆者は考える。
CDKの特徴
CDK
はtypescript
やpython
といった比較的モダンな軽量言語で記述が可能である。これにおいて、typescript
やpython
の深い知識(要するに大規模アプリケーション設計など)は特に必要としないが、基本的な制御構造くらいは理解している必要がある。さらに、CDK
というのはCloudFormation
というAWSの機能をトランスパイルするものに過ぎないという点もまた重要だ。従ってプログラムレベルで全てインフラを自由自在に構築できるわけではなく、あくまでCloudFormation
の「スタック」とよばれるものを排出する中間レイヤーであるから、最終的なCloudFormation
スタックの形を意識して作成しなくてはならない事が学習あるいは体験が深まると多々登場する事となるはずだ。ただ、最初は軽量のスタックを作成し破壊する事で検知が深まると思われるので、次のセクションからハンズオン形式で学習してみよう。
CDK Bootstrapの必要性
CDK
を使用してインフラストラクチャをデプロイするには、まず最初にCDK Bootstrapping
が必ず必要となる、これはCDK
を実行するために必要なAWS
リソースを一元的に作成するものであり、細かい事は沢山あるのだが、とにかく1アカウント1リージョンごと一度は実行しなければならない。
これをもっとも簡単に行う方法はAdministratorAccess
権限のアカウントでAWSにログインした画面でCloudShell
を使う事である。
CloudShellの開き方とcdk bootstrapの実行
CloudShell
は各webコンソールの画面の左下に簡易アクセスアイコンが用意されているので、そちらから起動するのがわかりやすいだろう。
CloudShellへの動線をクリックしてシェルを開いた画面
ここで、それぞれのアカウントに応じたシェル環境が起動するのだが、aws
コマンドはもちろんcdk
も事前にシェル内の環境に組み込まれている。
type cdk
とcdk --version
の実行結果
以上を確認したら以下のように入力してみる
cdk bootstrap aws://<アカウントID>/ap-northeast-1
bootstrappingが初まり各種AWSリソースの作成が始まる
ここでのbootstrapはそこそこの量のリソースが作成されるのだが、ここで何が行われているかちょっと見てみよう。これはAWSのメニューよりCloudFormation
→ スタックを見る
CDKToolkit
スタックが作成されている
このCDKToolkit
スタックが作成したリソースが全てである。
具体的に作成されたリソースを見る
CloudFormation
に慣れていない場合は、ここで作成されたリソースの確認方法について学習しておこう、といっても当該スタックの中に入ってリソース
のタブを見るだけだ。
CloudFormation
のリソース
タブを確認している
ここにおいて、作成されているのはほとんどロール
やポリシー
である事が確認できるだろう。一部S3
が含まれるため、微量ではあるが、Bootstrapにて課金が発生する可能性があるが、ただし、無視できるような課金量である(あるいはほとんどの場合は無料枠内で収まる)
具体的なスタックの記述
ここから具体的にスタックを記述してく。新規VPC
ネットワークを作成し、表題の通りEC2
を一本起動してみるが、まず進め方を確認しておこう。
どこでコードを書くのか
もちろん、CloudShell
の中でshell上のエディターを使って書く猛者も居るだろうが、これはあまりにも一般的ではない。通常はgitリポジトリーを経由する事になるはずだ。これにおいては、github
でもgltlab
でもいいのでインフラ専用のリポジトリーを用意するのが好ましいだろう。このケースにおいては以下のような開発フローになるのではないだろうか
まあこれは実際悪くないフローではあるがcloudshellにpullとかして実行しないと即座に結果がわからないというのはある。その場合手元で差分確認やデプロイする方法も確かにあるのだが、その辺の環境構築は慣れてから各自考えてみるのがよろしいのだろうと思う。
どうやってコードを書くのか
これは最初はAI出力でほぼ事足りるはずだ、というかAIの時代になって、よりIaC
が効くようになった(とくにCDK) と感じる。AWS
内で細かな設定1つやるにせよ手動で行わずIaC
でやった方がよいまである。
そしてこのコードについては冒頭述べたように大規模アプリケーションの開発をするわけではないのでコードレベルでの複雑な設計はほとんど発生しないので、いわば書き捨てのようなコードの積み重ねで動作する(どのようなリソース変更があるのかは注意深く見る必要がもちろんある)。設計という意味で重要になるのは 「スタック設計」 につきる。いかにスタックを分割し、再現性を取ったり取らなかったりするのかというのはある程度経験が必要となるだろうから、ここは何度かトライアンドエラーで掴んでいくしかない(スタック設計は個々の環境に極めて依存するので、最終的なスタック設計をこうすればいいというベストな戦略は存在しない事がほとんどである)
EC2でsshする場合は事前にキーペアが必要
これは最初に導入しておいて欲しい。詳細は割愛。まあここまで読めてる人なら問題ないと思うが。
cdk initで初期プロジェクトの雛形を作成する
ここでいよいよCDK
によるコードの作成だ。最初にイニシャライズが必要となるが、これはcdk
コマンドが簡単に使えるcloudshellで行っちゃうのがいいかもしれない。もちろん手元でも環境を構築すれば実行する事はできるのだが。
ここではcdktest
というプロジェクトで行う事にする。以下のようにcdktest
ディレクトリを作成し、cdk init
を行う。
mkdir cdktest
cd cdktest/
cdk init --language=typescript # typescriptをここで指定している。
cdk init
の時点でgitリポジトリが作成されている
デフォルトでcdk init
すると、gitリポジトリーが作成されている。IaC
はバージョン管理で強力に効果を発揮するという意味もあり最初からgitリポジトリーの生成を行うという親切設計なのだろう。
さて、initializeが完成し、git status
を入力すると以下のようになった。
cdktest $ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitignore
new file: .npmignore
new file: README.md
new file: bin/cdktest.ts
new file: cdk.json
new file: jest.config.js
new file: lib/cdktest-stack.ts
new file: package.json
new file: test/cdktest.test.ts
new file: tsconfig.json
Untracked files:
(use "git add <file>..." to include in what will be committed)
package-lock.json
このようなファイル一式が作成されgit add
された状態となっている
重要なファイルの確認
このままそっくりコミットしてしまってもいいのだが、その前にここで一度現在のファイル構成を確認しておこう。
- .gitignore (プロジェクト管理用)
- .npmignore (プロジェクト管理用)
- README.md (プロジェクト管理用)
- bin/cdktest.ts
- cdk.json
- jest.config.js (テスト用)
- lib/cdktest-stack.ts
- package.json (ライブラリ管理用)
- test/cdktest.test.ts (テスト用)
- tsconfig.json
ここでテストとプロジェクト管理を除くと以下のようになる
- bin/cdktest.ts
- cdk.json
- lib/cdktest-stack.ts
- tsconfig.json
ここで太字で強調したファイルを主に変更していくことになるのであるが、まずその2ファイルの内容を見てみよう
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { CdktestStack } from '../lib/cdktest-stack';
const app = new cdk.App();
new CdktestStack(app, 'CdktestStack', {
/* 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. */
/* Uncomment the next line to specialize this stack for the AWS Account
* and Region that are implied by the current CLI configuration. */
// 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 */
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
export class CdktestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// The code that defines your stack goes here
// example resource
// const queue = new sqs.Queue(this, 'CdktestQueue', {
// visibilityTimeout: cdk.Duration.seconds(300)
// });
}
}
このように最初はexampleが入っているだけであるので、基本的にlib/cdktest-stack.tsは不要である。そしてbin/cdktest.tsは良くみればlib/cdktest-stack.tsを呼び出しているだけの「エントリーポイント」に過ぎない。またbin/cdktest.tsという名前はディレクトリに応じて自動生成されるため、これを変更したくなるかもしれない、が、いずれにせよ最初はこれをgitリポジトリーに転送し、その後で作業してみることにする。
# 初回コミット
git commit -m "Initial commit: CDK TypeScript app initialized"
# githubに転送
git branch -M main
git remote add origin https://github.com/catatsumuri/cdktest.git
git push -u origin main
作業環境でclone
というわけで作業環境として好ましいところ、つまりvscode
など好きなエディターが利用できる編集環境等々でcloneしよう。ここでまず、最初にやるのはlib/cdktest-stack.tsを捨てることだ。git rmするとlibが空になるので作り直している
cd cdktest
npm install
git rm lib/cdktest-stack.ts
mkdir lib
npm install
はしておくこと。これにより編集環境でもcdk
コマンドなどが手に入る(ただしnpx cdk
で起動したりはあるかもしれない)
vpc-stack.tsを作成する
ここでlib/vpc-stack.tsを作成してみよう
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
export class VpcStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// NATなし、パブリックサブネットのみのVPC
const vpc = new ec2.Vpc(this, 'MyVpc', {
maxAzs: 2, // 2つのAZに配置
natGateways: 0, // NAT Gatewayは作らない
subnetConfiguration: [
{
subnetType: ec2.SubnetType.PUBLIC,
name: 'PublicSubnet',
cidrMask: 24,
},
],
});
}
}
こういうのはAIに雛形を作ってもらえばよいのだが、重要なのは
maxAzs: 2, // 2つのAZに配置
natGateways: 0, // NAT Gatewayは作らない
AZ
の数とnatGateway
である。AZはap-northeast-1
なら
ap-northeast-1a
ap-northeast-1c
ap-northeast-1d
の最大3つ作れるが、ここでは2
とした。1
だと分散構成にするとき等ちょっと面倒な事になるので何もアイデアが無い場合は2
をセットしよう。さらにnatGateway
はここでは指定しない。これはプライベートネットを作成するときのみ利用する「ことがある」。ただし高価になりがちなのでこのリソース作成に関してはコスト面で注意が必要となるため、このチュートリアルでは作成しない。それ以外VPCを作成するにあたっては追加の費用は必要ない。
さらにbin/以下を変更する。現在bin/cdktest.tsとなっていてファイル名が微妙と感じられた場合はbin/app.tsとかにしてもよい。というかここではそのようにする
git mv bin/cdktest.ts bin/app.ts
同時にbin/app.tsを変更し、vpc-stackを呼び出せるようにしておこう。ここでは以下のようにimport
しnew
する
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { VpcStack } from '../lib/vpc-stack';
const app = new cdk.App();
new VpcStack(app, 'VpcStack', {
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
結局ts書くならvscode
が楽だよなって話...
env
の中は、まあここではコメント付きでそのままにしといた。
さらにcdk.jsonを変更し、呼び出しをbin/app.tsに変更した事を伝える必要がある。
@@ -1,5 +1,5 @@
{
- "app": "npx ts-node --prefer-ts-exts bin/cdktest.ts",
+ "app": "npx ts-node --prefer-ts-exts bin/app.ts",
この環境で現状確認出来る事
この環境ってのはcloneしてきた編集作業環境だが、結局コードをcloneしてきた場所に過ぎないので、cdk
コマンドを現状で打っても権限が足りず何もできないため、やれる事はほとんどない。tsが間違ってないか確認するとかくらいなもんだろう。これは以下のようにtsc --noEmit
で確認するとよいだろう(もちろんvscode
を使っているならエディターの支援によりエラーを潰すとかはある)
npx tsc --noEmit
コンパイルエラーなし
エラーが無い事を確認したら、この段階でcommit、pushする
git add .
git commit
git push
CloudShellでpullして確認する
cloudshell側でpullしたら、以下の2コマンドが確認のためによく用いられる
cdk synth
cdk diff
synth
はCloudStackのYAMLに変換するコマンドである
膨大なYAML
が出力される
出力が膨大なのと、人が読んでも微妙にわかり辛いのでCloudformationの最終形態を確認するにはいいのかもしれないが実際にはcdk diff
で確認する事の方が多いだろう。これを実行すると
cdktest $ cdk diff
current credentials could not be used to assume 'arn:aws:iam::****:role/cdk-hnb659fds-lookup-role-****-ap-northeast-1', but are for the right account. Proceeding anyway.
Lookup role arn:aws:iam::****:role/cdk-hnb659fds-lookup-role-****-ap-northeast-1 was not assumed. Proceeding with default credentials.
Lookup role arn:aws:iam::****:role/cdk-hnb659fds-lookup-role-****-ap-northeast-1 was not assumed. Proceeding with default credentials.
Stack VpcStack
IAM Statement Changes
┌───┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────┬───────────────────────────────────┬────────────────────────────────────────────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼───────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role.Arn} │ Allow │ sts:AssumeRole │ Service:lambda.amazonaws.com │ │
├───┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼───────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤
│ + │ arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/${MyVpcF9F0CA6F.DefaultSecurityGroup} │ Allow │ ec2:AuthorizeSecurityGroupEgress │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ │
│ │ │ │ ec2:AuthorizeSecurityGroupIngress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupEgress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupIngress │ │ │
└───┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴────────┴───────────────────────────────────┴────────────────────────────────────────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ {"Fn::Sub":"arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"} │
└───┴────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Parameters
(略)
Conditions
(略)
Resources
[+] AWS::EC2::VPC MyVpc MyVpcF9F0CA6F
[+] AWS::EC2::Subnet MyVpc/PublicSubnetSubnet1/Subnet MyVpcPublicSubnetSubnet1Subnet60D1320D
[+] AWS::EC2::RouteTable MyVpc/PublicSubnetSubnet1/RouteTable MyVpcPublicSubnetSubnet1RouteTable00654ADB
[+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PublicSubnetSubnet1/RouteTableAssociation MyVpcPublicSubnetSubnet1RouteTableAssociation2CCE9CDC
[+] AWS::EC2::Route MyVpc/PublicSubnetSubnet1/DefaultRoute MyVpcPublicSubnetSubnet1DefaultRoute2D379878
[+] AWS::EC2::Subnet MyVpc/PublicSubnetSubnet2/Subnet MyVpcPublicSubnetSubnet2Subnet122ADB1B
[+] AWS::EC2::RouteTable MyVpc/PublicSubnetSubnet2/RouteTable MyVpcPublicSubnetSubnet2RouteTableC647F413
[+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PublicSubnetSubnet2/RouteTableAssociation MyVpcPublicSubnetSubnet2RouteTableAssociation7AF8666E
[+] AWS::EC2::Route MyVpc/PublicSubnetSubnet2/DefaultRoute MyVpcPublicSubnetSubnet2DefaultRouteAFC76296
[+] AWS::EC2::InternetGateway MyVpc/IGW MyVpcIGW5C4A4F63
[+] AWS::EC2::VPCGatewayAttachment MyVpc/VPCGW MyVpcVPCGW488ACE0D
[+] Custom::VpcRestrictDefaultSG MyVpc/RestrictDefaultSecurityGroupCustomResource MyVpcRestrictDefaultSecurityGroupCustomResourceA4FCCD62
[+] AWS::IAM::Role Custom::VpcRestrictDefaultSGCustomResourceProvider/Role CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0
[+] AWS::Lambda::Function Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E
✨ Number of stacks with differences: 1
(略)
このような形でどのリソースが変更されるか見易くなっている
cdk diff
のよく使う実行例
コマンド例 | 説明・用途 |
---|---|
cdk diff |
プロジェクトの 全スタック の差分を確認。単一スタック構成ではこれでOK。 |
cdk diff VpcStack |
複数スタック構成で、特定スタックだけ を比較。 |
cdk diff VpcStack --exclusively |
依存スタックを含めず、完全にそのスタックだけ を比較。ノイズ削減に便利。 |
cdk diff --security-only |
セキュリティ関連の変更のみ を表示。IAMポリシーの権限拡張などを事前に確認。 |
cdk diff --fail |
差分があったら終了コード1 を返す。CI/CDで差分検出時に処理を止めたい場合に有効。 |
- 日常の確認は
cdk diff
またはcdk diff スタック名
が基本。- 要するにstackを引数にするか、しないかの違い
- 本番前やCIパイプラインでは
--security-only
や--fail
の組み合わせがよく使われる。 - マルチスタック構成では
--exclusively
を使うと差分出力がすっきりする。
deployしてみる
diffの出力に納得したらいよいよcdk deploy
する。これはスタックが1つの時は何も考えずシンプルに実行してok
cdk deploy
作成されるリソース最終確認が行われる
これを適用しよう。
作成されたリソースを目視で確認する
作成されたリソースをCloudFormation
スタックから確認
冒頭で触れたようにCDK
はCloudFormation
スタックを作って適用している「だけ」なので、まずはCloudFormation
スタックから確認する。ここではVpcStack
というネーミングとなっている。
また、作成されたVPC
を実際に確認するのも忘れずに行っておこう。VPC
の画面からはリソースマップ
を確認することができるので、これは便利だから一度目視で必ず確認しておくこと。
識別子とリソースマップの確認
VpcStack/MyVpc
とかいう命名がまた微妙なことになっている気がする点も気になるかもしれないが、ここでは割愛。このハンズオンでは名前よりもまずはリソースマップをよく確認してネットワーク構成を吟味しておくとよいだろう。
また内部IPアドレスとして 10.0.0.0/16 が利用されている点にも注目しておこう。これは192.168.0.0/16とかでもいいっちゃいいんだけどdefaultでは10の方が0
が揃って拡張性が一番高いという意味で10
が使われているだけ。プライベートIPはcidr
指定によって変更する事もできるはず。
ともあれネットワークが作成できたので
ここで1本「インターネットにアクセス可能な」パブリックサブネットが作成された。ここにEC2
を配置すればEC2をインターネットに剥き出しになるはずだ。つまりINもOUTも設定によってやり放題という事になる。
EC2を配置する
今、VpcStack
を用意したので、次はEc2Stack
を作成する事になる。ここで、Ec2はVpcで作成されたネットワークにのっかるのだから、ここは「依存」関係となるはずだ。これをプログラムでどう解決していくのかをまず注目しよう。
まず作成したvpcをエントリポイントで受けとれるようにする
これにおいては以下のようにlib/vpc-stack.tsを変更する
@@ -3,11 +3,13 @@ import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
export class VpcStack extends cdk.Stack {
+ public readonly vpc: ec2.Vpc;
+
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// NATなし、パブリックサブネットのみのVPC
- const vpc = new ec2.Vpc(this, 'MyVpc', {
+ this.vpc = new ec2.Vpc(this, 'MyVpc', {
maxAzs: 2, // 2つのAZに配置
natGateways: 0, // NAT Gatewayは作らない
subnetConfiguration: [
このようにec2.Vpc
の結果を自身のプロパティーにreadonly
で格納し、エントリポイントで取り出せるようにする。
@@ -3,6 +3,12 @@ import * as cdk from 'aws-cdk-lib';
import { VpcStack } from '../lib/vpc-stack';
const app = new cdk.App();
-new VpcStack(app, 'VpcStack', {
+const vpcStack = new VpcStack(app, 'VpcStack', {
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
+
+/* Ec2Stackの呼び出し例
+new Ec2Stack(app, 'Ec2Stack', {
+ vpc: vpcStack.vpc,
+});
+*/
というようにEc2Stack(... vpc: vpcStack.vpc)
的な依存関係が作れるというわけだ。
vpcを受け取ってEc2を起動するStack
そしたらこれを記述していく
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
interface Ec2StackProps extends cdk.StackProps {
vpc: ec2.IVpc;
}
export class Ec2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props: Ec2StackProps) {
super(scope, id, props);
const instance = new ec2.Instance(this, 'WebServer', {
vpc: props.vpc,
instanceType: new ec2.InstanceType('t3.micro'),
machineImage: ec2.MachineImage.latestAmazonLinux2023(),
keyName: 'your-keypair', // <--------------- **これは修正すること**
});
instance.connections.allowFromAnyIpv4(ec2.Port.tcp(22), 'SSH');
instance.connections.allowFromAnyIpv4(ec2.Port.tcp(80), 'HTTP');
}
}
最終的なbin/app.tsの形も置いておこう
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { VpcStack } from '../lib/vpc-stack';
import { Ec2Stack } from '../lib/ec2-stack';
const app = new cdk.App();
const vpcStack = new VpcStack(app, 'VpcStack', {
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
new Ec2Stack(app, 'Ec2Stack', {
vpc: vpcStack.vpc,
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
これをcommit
git add .
git commit
git push
これが完了したらCloudShellでpullすること
cdk diffとdeploy
CloudShellでプルした後
cdk diff
の結果
Stack Ec2Stack
IAM Statement Changes
┌───┬───────────────────────────────┬────────┬────────────────┬───────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼───────────────────────────────┼────────┼────────────────┼───────────────────────────┼───────────┤
│ + │ ${WebServer/InstanceRole.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
└───┴───────────────────────────────┴────────┴────────────────┴───────────────────────────┴───────────┘
Security Group Changes
┌───┬────────────────────────────────────────────┬─────┬────────────┬─────────────────┐
│ │ Group │ Dir │ Protocol │ Peer │
├───┼────────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${WebServer/InstanceSecurityGroup.GroupId} │ In │ TCP 22 │ Everyone (IPv4) │
│ + │ ${WebServer/InstanceSecurityGroup.GroupId} │ In │ TCP 80 │ Everyone (IPv4) │
│ + │ ${WebServer/InstanceSecurityGroup.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)
Parameters
[+] Parameter SsmParameterValue:--aws--service--ami-amazon-linux-latest--al2023-ami-kernel-6.1-x86_64:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61x8664C96584B6F00A464EAD1953AFF4B05118Parameter: {"Type":"AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>","Default":"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64"}
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
(略)
Resources
[+] AWS::EC2::SecurityGroup WebServer/InstanceSecurityGroup WebServerInstanceSecurityGroup044089BE
[+] AWS::IAM::Role WebServer/InstanceRole WebServerInstanceRoleEEE3F4CD
[+] AWS::IAM::InstanceProfile WebServer/InstanceProfile WebServerInstanceProfile7A5DA8F6
[+] AWS::EC2::Instance WebServer WebServer99EDD300
✨ Number of stacks with differences: 2
ここでStack2つに変更がかかることが確認できた。これをdeployするのだが、このようなとき
cdk deploy
とすると
Since this app includes more than a single stack, specify which stacks to use (wildcards are supported) or specify `--all`
Stacks: VpcStack · Ec2Stack
など、どちらをdeployするのか聞かれる。stackを引数で指定してもよいし、指示のように --allを付けてもよい。ここでは --all を付けた
接続確認とwebサーバーのインストール、疎通確認
パブリックIPが付いたEC2。ロールも適当に作成されている点にも注目
これでパブリックIPに向けてssh接続を行う。AmazonLinuxなのでec2-user
でログインすること
sshログインできた
このようにログインが可能となっているだろう。さらにwebサーバー(apache)を導入して疎通確認を行う。
sudo dnf update -y
sudo dnf install -y httpd
sudo systemctl enable httpd
sudo systemctl start httpd
ここでパブリックIPにhttp接続すると
web(http)アクセスを確認
以上のようにapacheのdefaultページが見えれば成功だ。
破壊する
ここまででEC2を起動し、webサーバーの疎通を確認したので本稿の趣旨はほぼ完了しているのだが、最後に作成したリソースを片付けることにしよう。CDK
内でのスタックは2つ作っただけ、なのにもかかわらず作成されたリソースは結構な数に登る、これを全て削除するにはcdk destroy
する
(stack単位で指定して破壊する場合はcdk destroy <stack>
する
destroy中
なお、デストロイの様子はcloudformationの画面でも見る事ができる
cloudformation管理画面からみたスタックの破壊
つまり、cdk destroy
は実のところはスタックを手動削除しているのと大して変わらないのだが、cdk deploy
は複数の依存stackをまとめてdestroyするという指示をしているということになる。実際にここではVpcStack
とEc2Stack
の2つを作成したのであるがこれを手作業で消してもほぼ同じような効果になる。というかcdkのコマンドラインから削除に失敗することも割と普通にあるので、その場合はCloudFormationのstackに移動し手動で削除するという事を試みることになるだろう。
次回
さらなるEC2のカスタム(sshを使わないEC2とか)とかチューニングとか行ってみるかも。ここまでのボリュームにはならないようにしたいですねえ...
Discussion