記事概要
本記事では、CDKで以下の構成をデプロイします。
以下の記事で、S3にアップロードしたソースコードからDockerイメージを作成しECRにプッシュする、というプロセスを自動化するCodePipelineを作成しました。
S3バケットやECRリポジトリ、CodeBuildやCodePipelineなど、マネジメントコンソールで作成してきたAWSリソースを一発で作成できるCDKコードを作成したので、デプロイ方法を紹介します。
CDKの練習を兼ねて作成したコードのため、とりあえずデプロイできるというクオリティです。可読性、保守性などを踏まえて後々ブラッシュアップしたいと思います…
CDKでデプロイされるリソース
CDKをデプロイすると、以下が作成されます。
- ECRプライベートリポジトリ
- 作成されたDockerイメージのpush先となるリポジトリ
- S3バケット
- DockerイメージのもとになるDockerfileやその他のソースコードのアップロード先
- ソースコード類はまとめてzipファイルにし、作成したS3バケットに直接配置する構成になっています
- CodeBuildプロジェクト
- buildspecをコマンドで挿入しているため、今回buildspec.ymlファイルの準備は不要です
- CodePipelineのパイプライン
- ソースステージで作成したS3バケットをプロバイダーとして指定
- ビルドステージで作成したCodeBuildプロジェクトをプロバイダーとして指定
S3にアップロードするzipファイルは自分で用意してS3バケットにアップロードします。アップロードのトリガーにパイプラインが実行されます。
※ buildspec.ymlはzipファイルに含める必要はありません
CDKのデプロイ方法
動作環境
今回はCloudShellで実行したため、そのままCDKコマンドが実行できます。それ以外の環境で実行する場合、AWS CDK の前提条件を確認のうえ、必要なセットアップを行ってください。
CDKプロジェクトの作成
以下のようにディレクトリを1つ作成し、そのディレクトリ内で cdk init
コマンドによりプロジェクトの初期化を行います。ディレクトリ名に合わせてCDKコードが生成されるため、 本記事のCDKを実行する場合はディレクトリ名を cdk
にすることをおすすめします。
mkdir cdk
cd cdk
cdk init app --language typescript
実行すると、bin
ディレクトリや lib
ディレクトリ、cdk.json
ファイルなどが作成されます。
CDKコードの挿入
今回はすでに作成されている /lib/cdk-stack.ts
を下記のCDKコードに書き換えます。必要に応じて一部カスタマイズしてください。
CloudShellだと編集しづらいため、ローカル環境でファイルを用意してから、CloudShellへアップロードすることをおすすめします。
CDKコードのカスタマイズ
- アップロードするzipファイルを変更したい場合は、以下の
'artifact.zip'
を変更してください。
※ 作成したS3バケット直下にzipファイルを配置する構成になっています
const Resourcepath = 'artifact.zip'
- リソースを作成するリージョンを変更したい場合は、以下の
"ap-northeast-1"
を変更してください。
"AWS_DEFAULT_REGION": "ap-northeast-1",
変数部分は cdk.json
など別ファイルに外だしするのが一般的だと思いますが、追々アップデートしていきたいと思います。
CDKブートストラップ・デプロイ
CDKでAWSリソースをデプロイするためのブートストラップとして、S3やIAMロールなどを作成するプロセスとしてブートストラップを行います。作成したディレクトリ(今回だとcdk
ディレクトリ)で以下のコマンドを実行します。
cdk bootstrap
ブートストラップはCDKプロジェクト作成時に一度だけ実行します
最後に、以下のコマンドでデプロイを行います。以後、CDKコードを変更しその内容をデプロイしたい場合は、以下のコマンドだけ実行すればOKです。
cdk deploy
デプロイ前に、必要なIAMリソースを作成してよいか聞かれるため y
を入力してください。
CDKコード
/lib/cdk-stack.ts
の内容を以下に書き換えます。
import * as cdk from 'aws-cdk-lib';
import * as ecr from "aws-cdk-lib/aws-ecr";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as cb from "aws-cdk-lib/aws-codebuild";
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline';
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import { Construct } from 'constructs';
export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// ECRプライベートリポジトリ
const EcrRepository = new ecr.Repository(this, 'Repository', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
imageTagMutability: ecr.TagMutability.MUTABLE,
emptyOnDelete: true
});
// S3バケット
const ResourceBucket = new s3.Bucket(this, 'CreateBucket' ,{
versioned: true,
});
const Resourcepath = 'artifact.zip'
//buildspecオブジェクト
const buildSpecObject = {
"version": "0.2",
"env": {
"variables": {
"AWS_DEFAULT_REGION": "ap-northeast-1",
"DOCKER_USER": "AWS",
"REPOSITORY_URI": EcrRepository.repositoryUri,
"IMAGE_NAME": EcrRepository.repositoryName,
"ACCOUNT_ID": cdk.Stack.of(this).account
}
},
"phases": {
"pre_build": {
"commands": [
"echo Logging in to Amazon ECR...",
"aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username $DOCKER_USER --password-stdin $REPOSITORY_URI"
]
},
"build": {
"commands": [
"echo Building the Docker image...",
"docker build -t \"$REPOSITORY_URI:latest\" ."
]
},
"post_build": {
"commands": [
"echo Pushing the Docker image...",
"docker push \"$REPOSITORY_URI:latest\""
]
}
}
}
//CodeBuildプロジェクト
const CodebuildProject = new cb.Project(this, 'Ci-Project', {
source: cb.Source.s3({
bucket: ResourceBucket,
path: Resourcepath
}),
buildSpec: cb.BuildSpec.fromObject(buildSpecObject),
});
// パイプラインの作成
const pipeline = new codepipeline.Pipeline(this, 'S3TriggeredPipeline', {
});
// ソースステージの追加
const sourceOutput = new codepipeline.Artifact();
const sourceAction = new codepipeline_actions.S3SourceAction({
actionName: 'S3Source',
bucket: ResourceBucket,
bucketKey: Resourcepath,
output: sourceOutput,
});
pipeline.addStage({
stageName: 'Source',
actions: [sourceAction],
});
// ビルドステージの追加
const buildAction = new codepipeline_actions.CodeBuildAction({
actionName: 'CodeBuild',
project: CodebuildProject,
input: sourceOutput,
outputs: [new codepipeline.Artifact()],
});
pipeline.addStage({
stageName: 'Build',
actions: [buildAction],
});
// EventBridgeルールの作成
const rule = new events.Rule(this, 'S3ObjectCreatedRule', {
eventPattern: {
source: ['aws.s3'],
detailType: ['Object Created'],
detail: {
bucket: {
name: [ResourceBucket.bucketName]
},
object: {
key: [Resourcepath]
}
}
}
});
// EventBridgeルールのターゲットとしてパイプラインを追加
rule.addTarget(new targets.CodePipeline(pipeline));
}
};
本記事はここまでとなります。