4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CDKでサクッとCodeCommit+CodePipeline

Last updated at Posted at 2020-07-29

はじめに

本投稿は、AWS CDKの勉強を兼ねて試作してみたCodeCommit+CodePipelineでS3にファイルをアップロードする機構について紹介しています。
エンジニアチーム内で「触ってみたいですよね」という声があり興味があった所に、別部署のエンジニアの方からも「CDKいいっすよ〜」という評判を聞いたので触ってみました。

AWS CDKとは?

AWS CDKとは、AWS クラウド開発キット (AWS CDK) は、使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースをモデル化およびプロビジョニングするためのオープンソースのソフトウェア開発フレームワークだそうです。PythonやTypeScriptなんかでインフラの定義がかけて便利です。

背景

最初期

過去、制作用の環境としてCloud9が使用されていたのですが、その時の選定要件は、

  1. 同時編集できる
  2. 成果物をプレビューできる
  3. プレビューにBasic認証入れられる
  4. バージョン管理できる

だったので、Cloud9+Codecommit+Lambda+CloudFront+Lambda@edge+S3でCloud9からCodeCommit経由でS3にhtmlファイル・画像ファイルがアップロードされ、CloudFront経由でプレビューできるというものを作っていました。最初はうまく回っているように見えていました。

問題点

最初はうまく回っているように見えていたCloud9も徐々に問題点が出てきました。

  1. 一部ファイルがS3に上がっていない。(これはCodeCommit→S3をLambdaでやっている所が怪しいと睨んでいます)
  2. 案件が増えてきてCloud9の容量が圧迫されている(画像もりもりの制作物なので…)
  3. そもそも同時編集機能が活用されてない気配がある…

ということで、Cloud9である必要性がなくなっていました。であればローカルで作ってS3に上がればいいよねってことで作り直しの機運が高まりました。(実際にコーディングするメンバーはAWSを触る人たちではないので極力AWSと直接相対しない形を提供してあげたいという思いもありました。)

本題

作り直しにあたり、社内で噂を聞いたCDKを採用しました。下図の左側CodeCommit・CodePipeline・CodeBuildのリソース作成をCDKが担当します。(S3+CloudFront+Lambda@edgeは以前のものを流用しました。)

codecommit_diagram.png

Cloud9時代には、1Cloud9(1EC2インスタンス)=複数案件=1リポジトリだったものを、1案件=1リポジトリにしました。案件着手時にcdk deployすることで、CodeCommit上にリポジトリの作成とCodePipelineが整備されて、masterにpushするごとに、CodeBuildでS3にファイルがアップロードされるという状態です。

実装

ドキュメントに従って、

$ npm install -g aws-cdk
$ mkdir test
$ cd test
$ cdk init test --language python
$ source .env/bin/activate
$ pip install -r requirements.txt

としてスタートします。

app.py
#!/usr/bin/env python3
import os
from os.path import join, dirname
from dotenv import load_dotenv

from aws_cdk import core
from test.test_stack import TestStack


dotenv_path = join(dirname(__file__), '.env_file')
load_dotenv(dotenv_path)


app = core.App()
TestStack(app,
          "test",
          repo_name=os.environ["REPOSITORY_NAME"],
          env={"account": "xxxxxxxxxxxx", "region": "ap-northeast-1"})
app.synth()
test_stack.py
from aws_cdk import core
from aws_cdk import aws_codecommit as codecommit
from aws_cdk import aws_codebuild as codebuild
from aws_cdk import aws_codepipeline as codepipeline
from aws_cdk import aws_codepipeline_actions as codepipeline_actions
from aws_cdk import aws_iam as iam

class TestStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, repo_name: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # output先のバケット名
        s3_bucket_name = "test-bucket"
    
        # CodeCommitのRepository作成
        repo = codecommit.Repository(self,
                                     "Repository",
                                     repository_name=repo_name,
                                     description="test.")
        repository = codecommit.Repository.from_repository_arn(self, repo_name, repo.repository_arn)

        # CodePipelineの定義
        pipeline = codepipeline.Pipeline(self,
                                         id=f"test-pipeline-{repo_name}",
                                         pipeline_name=f"test-pipeline-{repo_name}")
        source_output = codepipeline.Artifact('source_output')

        # CodeCommitの追加
        source_action =  codepipeline_actions.CodeCommitSourceAction(repository=repository,
                                                                     branch='master',
                                                                     action_name='source_collect_action_from_codecommit',
                                                                     output=source_output,
                                                                     trigger=codepipeline_actions.CodeCommitTrigger.EVENTS)
        pipeline.add_stage(stage_name='Source', actions=[source_action])

        # CodeBuildの追加
        cdk_build = codebuild.PipelineProject(self,
                                              "CdkBuild",
                                              build_spec=codebuild.BuildSpec.from_object(dict(
                                                  version="0.2",
                                                  phases=dict(
                                                      build=dict(
                                                          commands=[f"aws s3 sync ./ s3://{s3_bucket_name}/"]
                                                      )
                                                  )
                                              )))
        cdk_build.add_to_role_policy(
            iam.PolicyStatement(
                resources=[f'arn:aws:s3:::{s3_bucket_name}', f'arn:aws:s3:::{s3_bucket_name}/*'],
                actions=['s3:*']
            )
        )

        build_output = codepipeline.Artifact("CdkBuildOutput")

        build_action = codepipeline_actions.CodeBuildAction(
                            action_name="CDK_Build",
                            project=cdk_build,
                            input=source_output,
                            outputs=[build_output])

        pipeline.add_stage(
            stage_name="Build",
            actions=[build_action]
        )

CodeBuild

今にして思うと、s3 syncしてるだけのCodeBuildってCodeDeployで良かったのでは感があります。
s3 syncする時にs3バケット側の階層を一段深くしたくてこうしていますが、良い解決策はないものでしょうか…

codebuildの部分
# CodeBuildの追加
cdk_build = codebuild.PipelineProject(self,
                                      "CdkBuild",
                                      build_spec=codebuild.BuildSpec.from_object(dict(
                                        version="0.2",
                                        phases=dict(
                                        build=dict(commands=[f"aws s3 sync ./ s3://{s3_bucket_name}/"])
                                      ))))

デプロイ

あとは

$ cdk deploy

としてやればデプロイできますが、使う人向けに以下のようにスクリプトにしておきました。

make.sh
#!/bin/bash

if [ $# -ne 1 ]; then
  echo "実行するには1個の引数が必要です。" 1>&2
  echo "引数にはプロジェクトコードを指定してください。(プレビュー時のURLに一部使用されます)"
  exit 1
fi
echo REPOSITORY_NAME=$1 > .env_file
cdk deploy test --profile xxxxxxx
# commitぶっこむ
git clone ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/$1 ../$1
mkdir ../$1/$1
touch ../$1/$1/.gitkeep
cd ../$1
git checkout -b master
git add -A
git commit -m "initial commit"
git push origin master

これで、作成するリポジトリ名を引数として

$ ./make.sh test_repository

とすればOKです。

懸念点

  • 今はCDKの中でCodeCommitのリポジトリを作成しているため、スタックを消してしまうとリポジトリごと消えてしまうというリスクがあります。
  • 実は初回のdeploy時にはCodeCommit側でmasterブランチが作成されていない(?)ためBuild時に失敗してしまいます。git pushしてあげると正しく動作するので現在のところは放置しています。(が少し気持ち悪い。)

最後に

今回はCDKを使ってCodeCommitからCodeBuild経由でS3にファイルをアップロードする機構を作成しました。
Cloud9時代に比べて評判がいいようなので、このまま何事もなく使ってもらえるといいなと思いつつフィードバックを待ちます。
CDK自体はCloudFormationと直に戦わなくていいので良いものだと思います。

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?