インフラ

CDK + CodepipelineでEC2へのシンプルなデプロイフローを作る

CDK Codepipline

こんにちは、ちゃりおです。

最近、CDKでEC2にデプロイするCodepipeline作りました。

初めてCDKでCodeシリーズを書いたのですが、Cloudformationと比べてかなり楽に
かけて感動したので記事にします。

Contents

概要

以下概要です。

  • ソースはgithub
  • githubのWebhookをトリガーにCodepipelineを発火
  • フローはCodepipeline>Codebuil>CodeDeploy

バージョン

試したときのバージョンです。

  • node: v14.5.0
  • cdk: 1.60.0

事前準備

以下の事前準備が必要です。

  • nodeのインストール
  • CDKのインストール
  • githubトークンの取得
    • Settings>Developper settings>Personal access tokens
    • repoのフルアクセスがあればよいはず

CDK

デプロイフロー作成のCDK

最低限だとこんな感じになると思います。
本番で使うなら、環境ごとに作れるようにしたり、セキュアな情報はパラメータストアを使うなどが必要になってくると思います。

import * as cdk from '@aws-cdk/core';
import * as codebuild from '@aws-cdk/aws-codebuild';
import * as codedeploy from '@aws-cdk/aws-codedeploy';
import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions';
import * as codepipeline from '@aws-cdk/aws-codepipeline';

export class DeployflowStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string ) {
    super(scope, id);

    // === CodeBuild ===
    const project = new codebuild.PipelineProject(this, 'CodeBuild', {
      projectName: 'simple-ec2-deployflow-project',
    });

    // === CodeDeploy ===
    const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, "DeploymentGroup", {
      deploymentGroupName: 'simple-ec2-deployflow-group',
      ec2InstanceTags: new codedeploy.InstanceTagSet(
        {
          Deploy: ['simple-ec2-deployflow'],
        }
      ),
      deploymentConfig: codedeploy.ServerDeploymentConfig.ALL_AT_ONCE
    });

    // === Artifact ===
    const sourceOutput = new codepipeline.Artifact();
    const buildOutput = new codepipeline.Artifact();

    // === Code Pipeline Action ===
    const sourceAction = new codepipeline_actions.GitHubSourceAction({
      actionName: 'Github',
      owner: '<github owner>',
      repo: '<github repo>',
      output: sourceOutput,
      // トークンはssmとかで渡したほうが良さそう
      oauthToken: cdk.SecretValue.plainText('<github token>'),
      branch: 'master',
      trigger: codepipeline_actions.GitHubTrigger.WEBHOOK,
    });

    const buildAction = new codepipeline_actions.CodeBuildAction({
      actionName: "CodeBuild",
      input: sourceOutput,
      project,
      outputs: [buildOutput] 
    });

    const deployAction = new codepipeline_actions.CodeDeployServerDeployAction({
      actionName: 'CodeDeploy',
      input: buildOutput,
      deploymentGroup: deploymentGroup,
    });

    // === Code Pipeline ===
    const pipeline = new codepipeline.Pipeline(this, 'CodePipeline', {
      pipelineName: 'simple-ec2-deployflow-pipeline',
      stages: [
        {
          stageName: 'Source',
          actions: [sourceAction]
        },
        {
          stageName: 'Build',
          actions: [buildAction]
        },
        {
          stageName: 'Deploy',
          actions: [deployAction]
        }
      ]
    });
 }
}

注意点

EC2にデプロイする場合、EC2のIAMロールをKMSのアクセスポリシーで許可する必要があります。

許可しない状態でやると、デプロイ時に権限のエラーがでてデプロイできないです。
(デプロイアーティファクトをKMSで復号化できないため)

おまけ: 生成されるCloudformationをみてみる

Cloudformationは、1000行くらいになっていました。
JSONだからというのもあると思いますが、yamlで手動で書いても300行以上になる気がします。

今回のCDKだと80行くらいなので、かなり抑えられています。

直接記述しなくても、IAMロールとかS3バケットの作成とかよしなにやってくれます。

自動生成されるCloudformationを抜粋しつつ見てみます。
「cdk.out/<スタック名>.template.json」が生成されたCloudFormationです。(マネジメントコンソールからも見れます。)

ArtifactBucketの定義です。
CDKでは一行ですが、以下のS3バケットの記述があります。(new codepipeline.Artifact();)
KMSで暗号化しているみたいです。

    "CodePipelineArtifactsBucket11111": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketEncryption": {
          "ServerSideEncryptionConfiguration": [
            {
              "ServerSideEncryptionByDefault": {
                "KMSMasterKeyID": {
                  "Fn::GetAtt": [
                    "CodePipelineArtifactsBucketEncryptionKey11111",
                    "Arn"
                  ]
                },
                "SSEAlgorithm": "aws:kms"
              }
            }
          ]
        },

探してみると、KMSの定義もありました。

    "CodePipelineArtifactsBucketEncryptionKey1111": {
      "Type": "AWS::KMS::Key",
      "Properties": {
        "KeyPolicy": {
          "Statement": [
            {
              "Action": [
                "kms:Create*",
                "kms:Describe*",
                "kms:Enable*",
                "kms:List*",
                "kms:Put*",
                "kms:Update*",
                "kms:Revoke*",

KMSもう少しみてみると、IAMロールに許可を与えているのがわかります。
CodePipelineとCodebuildのIAMロールに復号化などの許可を行っています。

              "Action": [
                "kms:Decrypt",
                "kms:DescribeKey",
                "kms:Encrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*"
              ],
              "Effect": "Allow",
              "Principal": {
                "AWS": {
                  "Fn::GetAtt": [
                    "CodePipelineRole1111",
                    "Arn"
                  ]
                }
              },

CodepipelineのIAMロール・ポリシーもありました。
CDK側ではなにも指定しませんでしたが、最小限の権限が割り当てられているようです。

    "CodePipelineRoleB3A660B4": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                "Service": "codepipeline.amazonaws.com"
              }
            }
          ],
          "Version": "2012-10-17"
        }
      },
      "Metadata": {
        "aws:cdk:path": "DeployflowStack/CodePipeline/Role/Resource"
      }
    },
    "CodePipelineRoleDefaultPolicy8D520A8D": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyDocument": {
          "Statement": [
            {
              "Action": [
                "s3:GetObject*",
                "s3:GetBucket*",
                "s3:List*",
                "s3:DeleteObject*",
                "s3:PutObject*",
                "s3:Abort*"
              ],

このように、色々自動生成してくれます。

便利な反面どういったリソースが作られるか理解できていないと
共通化できるのに、必要以上にリソースを作ってしまうことがあります。

まとめ

今回は、CDKでCodepipelineのデプロイフローを作ってみました。
Cloudformationで数百行書く必要があったものが、CDK L2 Libraryを使えば百行以下でかけました。

便利な反面、慣れていないととコード上からはどういったリソースが作られているかわかないこともあります。
(KMSが作成されるの見落として、EC2にデプロイするときにハマりました。)

生成されるCloudformationも確認しながら、CDK使っていきたいです。

実践AWS CDK
CDKの基礎を「実践 AWS CDK – TypeScript でインフラもアプリも!」で学ぼうこんにちは、ちゃりおです。 以前から気になっていた「実践 AWS CDK – TypeScript でインフラもアプリも!」を読みまし...
codebuildキャッシュ
composer install時にCodeBuildのS3キャッシュを使うこんにちは、ちゃりおです。 今回はCodeBuildのS3キャッシュを試してみます。 S3キャッシュを使用すれば、ビルド時間の短縮がで...
aws dev
業務で開発経験なしでも取れるAWS 認定デベロッパーアソシエイトの勉強法こんにちは、ちゃりおです。 AWS 認定デベロッパー – アソシエイトの勉強法についてまとめてみます。 私は、普段インフラを主にやっ...