CDK(Python)でCodePipelineを作ってECSにデプロイしてみた

2023.02.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、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 ファイルが必要です。

イメージ定義ファイルのリファレンス - AWS CodePipeline

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にデプロイしてみたことについて紹介しました。
参考になれば幸いです。