こんにちは、soraです。
今回は、CDK(Python)でCodePipelineを作ってECSにデプロイしてみたことについて書いていきます。
CDKのPythonコードメインで説明し、CodePipelineとはみたいな各サービスの説明は割愛します。
また、とりあえず動くものを作ることを目的としているため、ログ取得の有効化などはしていません。
今回やってみること
今回の構成は以下です。
CodeCommitリポジトリのコミットをトリガーとしてCodePipelineが動いて、ECSにデプロイする構成です。
CodeCommit・ECR・ECSは既に存在している状態として、他の部分をCDK(Python)で作っていきます。
環境
Cloud9上に構築した開発環境でCDK(Python)を使って構築します。
(.venv) $ python --version
Python 3.7.16
(.venv) $ cdk --version
2.63.0 (build 7f4e35e)
やってみた
それでは実装してテストしていきます。
app.py
基本的には、スタックを呼び出しているだけです。
import aws_cdk as cdk
from test_python.aws_cdk_cicd_stack import AwsCdkCicdStack
env = cdk.Environment(account="<AWSのアカウントID>", region="ap-northeast-1")
app = cdk.App()
AwsCdkCicdStack(app, "aws-cdk-cicd", env=env)
app.synth()
aws_cdk_cicd_stack.py
メインのコードです。
アーティファクト用のS3は、指定せずに自動で新しく作成されるものを使用します。
デプロイ前の既存のリソースを呼び出す部分は、型に気を付けないとエラーにはまります。(私は少しはまりました)
from constructs import Construct
from aws_cdk import(
Stack,
RemovalPolicy,
aws_iam as iam,
aws_codebuild as codebuild,
aws_codecommit as codecommit,
aws_codepipeline as codepipeline,
aws_codepipeline_actions as codepipeline_actions,
aws_ecs as ecs,
aws_ec2 as ec2
)
class AwsCdkCicdStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# CodePipelineの作成
# S3(artifact_bucket)は指定せずに新しく作成
pipeline = codepipeline.Pipeline(self, "Pipeline",
pipeline_name = "cicd_codepipeline"
)
# ============ source stage start =============
# 既存のCodeCommitリポジトリを取得
repository_arn = "<CodeCommitのARN>"
repository = codecommit.Repository.from_repository_arn(self, "cicd-repository", repository_arn)
# ソースアーティファクトの指定
source_output=codepipeline.Artifact("source_artifact")
# CodeCommitからソースを取得するアクションを定義
source_action=codepipeline_actions.CodeCommitSourceAction(
repository=repository,
branch="develop",
action_name="Source-CodeCommit",
output=source_output,
# デフォルト値のため、なくても良い
trigger=codepipeline_actions.CodeCommitTrigger.EVENTS
)
# CodePipelineにソースステージの追加
pipeline.add_stage(
stage_name="Source",
actions=[source_action]
)
# ============ source stage end ==============
# ============ build stage start =============
# CodeBuild用のロール作成
build_role=iam.Role(self, "Role",
role_name="codebuild-role",
assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com")
)
# CodeCommitへの読み込み権限・ECRへの書き込み権限の追加
build_role.add_managed_policy(iam.ManagedPolicy.from_aws_managed_policy_name("AWSCodeCommitReadOnly"))
build_role.add_managed_policy(iam.ManagedPolicy.from_aws_managed_policy_name("AmazonEC2ContainerRegistryPowerUser"))
# CodeBuildの作成
build_project = codebuild.PipelineProject(self, "cicd-build",
project_name="cicd_build",
# Dockerイメージを構築するための特権付与
environment=codebuild.BuildEnvironment(privileged=True),
role=build_role
)
# ビルドアーティファクトの指定
build_output = codepipeline.Artifact("build_output")
# CodeBuild
build_action = codepipeline_actions.CodeBuildAction(
action_name="Build-CodeBuild",
project=build_project,
input=source_output,
outputs=[build_output]
)
# CodePipelineにビルドステージの追加
pipeline.add_stage(
stage_name="Build",
actions=[build_action]
)
# ============ build stage end ===============
# ============ deploy stage start ============
# 既存のECSクラスターの取得
ecs_cluster=ecs.Cluster.from_cluster_arn(self, "cicd-cluster",
cluster_arn="<ECSクラスターのARN>"
)
# 既存のECSサービスの取得
cicd_fargate=ecs.FargateService.from_fargate_service_attributes(self, "cicd-fargate",
cluster=ecs_cluster,
service_arn="<ECSサービスのARN>"
)
# ECSへのデプロイアクション
deploy_ecs = codepipeline_actions.EcsDeployAction(
action_name="Deploy-ECS",
service = cicd_fargate,
input=build_output
)
# CodePipelineにデプロイステージの追加
pipeline.add_stage(
stage_name="Deploy",
actions=[deploy_ecs]
)
# ============ deploy stage end ==============
その他のファイル
CodeCommitのリポジトリ内のファイルは以下です。
ブランチ名はdevelopとして、Dockerfileとbuildspec.ymlを配置しています。
Dockerfile(1行)はnginxを拾ってきているだけです。
buildspec.ymlでの出力するjsonファイルの指定について、デプロイ方法によって異なります。
Amazon ECS 標準デプロイでは、デプロイアクションへの入力で imagedefinitions.json ファイルが必要です。Amazon ECS Blue/Green デプロイでは、デプロイアクションへの入力で imageDetail.json ファイルが必要です。
buildspec.yml
version: 0.2
env:
variables:
AWS_DEFAULT_REGION: ap-northeast-1
AWS_ACCOUNT_ID: <AWSアカウントのID>
IMAGE_REPO_NAME: <ECRのリポジトリ名>
IMAGE_TAG: latest
phases:
build:
commands:
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- docker build --no-cache -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- printf '[{"name":"nginx","imageUri":"%s"}]' $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
Dockerfile
FROM nginx:latest
実行
ソースコードができたため、差分を確認した後に実行します。
# (不要な場合は省略)仮想環境への切り替え
$ source .venv/bin/activate
# 実行時の差分確認
(.venv) $ cdk diff
# 実行
(.venv) $ cdk deploy
テスト
CodePipelineが作成されて、正常に完了していることを確認します。
画像は取れていませんが、ECSのサービスを確認するとリビジョンも上がっています。
参考にしたサイト
AWS CDK Python Reference
AWS CDKをデプロイするCodepipelineをCDK(Python)で構築する
最後に
今回は、CDK(Python)でCodePipelineを作ってECSにデプロイしてみたことについて紹介しました。
参考になれば幸いです。