AWS CDKとは
AWSリソースをアプリケーションコード (TypeScript / Python / Java / C#) で定義できる,AWS公式のInfrastructre as a Code (IaC)ツールです.
構造化ファイル(YAML / JSON)を使ってリソース定義を行うCloudFormationと比較すると,
- 制御構文や抽象化を使うことで,比較的少ないコード量でリソース定義しやすい
- エディタによるエラーチェック・サジェスチョン・ドキュメント参照がしやすい
等のメリットがあります.
概要・特色を知りたい方は,Black Belt Online Seminarの資料がオススメです.
また,試してみたい方は,CDK Workshopから入るのがオススメです.
CDK Docker Image Assetsとは
CDKでは,各種AWSリソースとともに,Dockerfileからコンテナイメージをビルドして,cdk deploy
コマンドで一緒にデプロイすることができます.
これを用いると,例えば,ECS環境とアプリケーションのコンテナイメージを1つのリポジトリで管理でき,負担軽減される場面もあるかも?と思います.
ちょうど業務でそのような場面があり使ってみた,のですが...
API Referenceを読むと,それらしきクラスやメソッドが色々なところに存在し,ぱっと見どれを使えば良いか分からない!どのリポジトリにpushされるかもよく分からない!
そこで,簡単なテストスタックを作って,どのようにコンテナイメージがpushされるか確認してみました.
検証環境
- CDK 1.59.0 (2020/08/17時点での最新バージョン1)
- TypeScriptで実装
CDKは頻繁にアップデートが行われ,2,3日でバージョンが上がることもザラにあります.この記事に関わる部分も破壊的変更が行われる可能性もあり,こまめに最新情報をチェックするのをオススメします.
方法1 aws-ecr-assets moduleを使う
はじめに見つけた方法です.下記のコード抜粋のように,Dockerfileがあるディレクトリを指定して,DockerImageAssetを作ってあげるだけでOKです.
import * as cdk from '@aws-cdk/core';
import assets = require('@aws-cdk/aws-ecr-assets');
export class AssetsStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new assets.DockerImageAsset(this, `docker-image`, {
directory: path.join(__dirname, 'containers'), // Dockerfileがあるディレクトリを指定
});
}
}
しかし,この方法,ECRリポジトリやコンテナイメージのタグを定義してないけど,どこへpushされるのだろう???
スタックをデプロイすると,aws-cdk/assets
というECRリポジトリが自動的に作成され,その中にビルドしたイメージがpushされていました.イメージタグはCDKが自動的に採番するようです.
お手軽で良いのだけど,特定のECRリポジトリにpushしたい場合や,イメージタグを指定したい場合はどうするのだろう…? DockerImageAssetのコンストラクタには,
- repositoryName (push先のリポジトリを指定)
- extraHash (イメージタグを指定)
という引数が存在するのですが,既に非推奨となっており,将来的に削除される可能性があります.
また,本検証では,これらの引数を指定すると,イメージがpushされませんでした.
ということで,push先のリポジトリ・イメージタグを指定するには,方法2を使う必要があります.
方法2 StackSynthesizerを使う
下記のコード抜粋のように,Stack.synthesizer.addDockerImageAsset
を使って定義します.
import * as cdk from '@aws-cdk/core';
import assets = require('@aws-cdk/aws-ecr-assets');
export class AssetsStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
this.synthesizer.addDockerImageAsset({
directoryName: path.join(__dirname, 'app1'), // Dockerfileがあるディレクトリを指定,
repositoryName: "my-ecr-repos", // ECRリポジトリ名を指定
sourceHash: "image-tag", // イメージタグを指定.
});
}
所望のリポジトリにコンテナイメージがpushされています.
方法2の注意点
-
Dockerfileを書き換えても,sourceHashを変更しなければ,同じタグのイメージが新たなイメージで上書きされるようです.前のバージョンも残しておきたい場合は,都度sourceHashを変更する必要があります.私は,gitのコミットハッシュからイメージタグを決定し,CDK context valueを通してsourceHashを与えるようにしました.
-
同一スタック内で複数回
addDockerImageAsset
を行った場合,最後に行った指定のみが有効になりました.複数のリポジトリに色々なコンテナイメージをpushしたい場合に注意が必要です.
import * as cdk from '@aws-cdk/core';
import assets = require('@aws-cdk/aws-ecr-assets');
export class AssetsStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 1つ目のイメージ../app1/Dockerfileを使って,my-repos1へtestタグでpushしたい.
this.synthesizer.addDockerImageAsset({
directoryName: path.join(__dirname, 'app1'),
repositoryName: "my-repos1",
sourceHash: "test",
});
// 2つ目のイメージ../app2/Dockerfileを使って,my-repos2へtestタグでpushしたい.
this.synthesizer.addDockerImageAsset({
directoryName: path.join(__dirname, 'app2'),
repositoryName: "my-repos2",
sourceHash: "test",
});
// この場合,2つ目のイメージだけがpushされます.
// 両方ともpushしたい場合,スタックを分ければOK.
// 同一スタック内での複数イメージpushは未検証 (イメージ1, 2でsourceHashを変えればよいのか…?)
}
ソースコード
パイプライン(CodeBuild)でデプロイする場合の注意点
CodeBuildでは,都度ビルド用コンテナ環境が作成され,その中でbuildspec.yml
に記述されたコマンドが実行されます.コンテナイメージのビルドでは,docker-in-docker
を行うため,Privilegedの有効化が必要です.
まとめ
方法1 (aws-ecr-assets module)は,現状EXPERIMENTAL
指定となっており,非推奨機能も多いようです.
今後は,方法2 (Stack.synthesizer)に移行していきそうな雰囲気がありました.
-
2020/08/22時点では,ver1.60.0がリリースされていました. ↩