Closed8

CDK Pipelines のサンプル "aws-samples/cdk-pipelines-demo" が実際にどのような構成を作っているのか調べる

hassaku63hassaku63

cdk synth で出力した内容のうち、CodePipeline で何するかをうっすら見ていく。IAM Role なんかは気にしずデプロイするまでに必要な本質的な処理の把握に絞る

hassaku63hassaku63

CodePipeline ステージ構成

6 つのステージからなる。論理リソースID はひとまずそのままコピペ

なんとなくこのまとめを見れば各ステージで何しようとしているのかはわかる

(1) Stage = GitHub

GitHub source action
Output = Artifact_Source_GitHub

(2) Stage = Build

CodeBuild "PipelineBuildSynthCdkBuildProject6BEFA8E6"
Output = Artifact_Build_Synth

(3) Stage = UpdatePipeline

CodeBuild "PipelineUpdatePipelineSelfMutationDAA41400"

SelfMutation とあるように、このパイプライン自身の変更を実行しようとしているように見える。
この SelfMutation というコンセプト自体は v2 の pipelines にも存在するので把握しといてOK。

(4) Stage = Assets

CodeBuild "PipelineAssetsFileAsset185A67CB4"

(5) Stage = Pre-Prod

(5-1) WebService.Prepare (runOrder=1)
CloudFormation deploy "Pre-Prod-WebService"
ActionMode = CHANGE_SET_REPLACE

(5-2) WebService.Deploy (runOrder=2)
CloudFormation deploy "Pre-Prod-WebService"
ActionMode = CHANGE_SET_EXECUTE

(5-3) IntegrationTests (runOrder=3)
CodeBuild "PipelinePreProdIntegrationTestsProject84C5132E"

(6) Stage = Prod

(6-1) WebService.Prepare (runOrder=1)
CloudFormation deploy "Prod-WebService"
ActionMode = CHANGE_SET_REPLACE

(6-2) WebService.Deploy
CloudFormation deploy "Prod-WebService"
ActionMode = CHANGE_SET_EXECUTE


pipeline の実装は以下のようなノリ

const synthAction = pipelines.SimpleSynthAction.standardNpmSynth({
    sourceArtifact,
    cloudAssemblyArtifact,
    buildCommand: 'npm run build && npm test',
});

const pipeline = new pipelines.CdkPipeline(this, 'Pipeline', {
    cloudAssemblyArtifact,
    sourceAction,
    synthAction
});

// Pre-prod
//
const preProdApp = new WebServiceStage(this, 'Pre-Prod');
const preProdStage = pipeline.addApplicationStage(preProdApp);

preProdStage.addActions(...)

// Prod
//
const prodApp = new WebServiceStage(this, 'Prod');
const prodStage = pipeline.addApplicationStage(prodApp);

CodePipeline のステージとしては SelfMutation を行うステージが最初に来て、その後 addStage すると後ろに「私がパイプライン越しにデプロイしたいと思っている IaC テンプレのデプロイ」を行うステージが繋がる感じに見える。

hassaku63hassaku63

(2) Stage = Build

CodeBuild "PipelineBuildSynthCdkBuildProject6BEFA8E6"
Output = Artifact_Build_Synth

対応してる実装はこれ

const synthAction = pipelines.SimpleSynthAction.standardNpmSynth({
    sourceArtifact,
    cloudAssemblyArtifact,
    buildCommand: 'npm run build && npm test',
});

生成された CloudFormation に書いてある PipelineBuildSynthCdkBuildProject6BEFA8E6 の BuildSpec はこれ↓
cdk synth する手前のコマンドにカスタマイズの余地があるっぽい。

{
    "version": "0.2",
    "phases": {
    "pre_build": {
          "commands": [
          "npm ci"
        ]
    },
    "build": {
        "commands": [
          "npm run build && npm test",
          "npx cdk synth"
        ]
    }
    },
    "artifacts": {
      "base-directory": "cdk.out",
      "files": "**/*"
    }
}
hassaku63hassaku63

(3) Stage = UpdatePipeline

CodeBuild "PipelineUpdatePipelineSelfMutationDAA41400"

SelfMutation はデフォルトの振る舞いで自動生成される系のリソースなので CDK コード中に明示的にこれを生成している部分はない(はず)

自動生成された CodeBuild PipelineUpdatePipelineSelfMutationDAA41400 の BuildSpec はこれ↓

{
    "version": "0.2",
    "phases": {
        "install": {
            "commands": "npm install -g aws-cdk"
        },
        "build": {
            "commands": [
                "cdk -a . deploy PipelineStack --require-approval=never --verbose"
            ]
        }
    }
}

PipelineStack はサンプルコードで定義してる最上位の Stack 要素。

ここに含まれてるのはパイプラインのスタックで、プロダクトコードに相当する部分の Stack は含んでいない(これを自分は少し混同していた)

hassaku63hassaku63

(4) Stage = Assets

CodeBuild "PipelineAssetsFileAsset185A67CB4"

PipelineAssetsFileAsset185A67CB4 の BuildSpec は以下。

{
    "version": "0.2",
    "phases": {
        "install": {
            "commands": "npm install -g cdk-assets"
        },
        "build": {
            "commands": [
                "cdk-assets --path \"assembly-PipelineStack-Pre-Prod/PipelineStackPreProdWebService366DDB3C.assets.json\" --verbose publish \"d13603a1f070cfad016ec67b8286a6e0043af96b03f4c43d8575400184acb608:current_account-current_region\"",
                "cdk-assets --path \"assembly-PipelineStack-Prod/PipelineStackProdWebServiceFF6D621D.assets.json\" --verbose publish \"d13603a1f070cfad016ec67b8286a6e0043af96b03f4c43d8575400184acb608:current_account-current_region\""
            ]
        }
    }
}

cdk-assets というコマンドがあるのは初めて知った。CDK の本体レポジトリにあるらしい。

https://www.npmjs.com/package/cdk-assets

A tool for publishing CDK assets to AWS environments.

と説明文にある通り、デプロイ対象のアカウントにアセットをアップロードするツールに見える。

synth した後生成されている Cloud Assembly をインプットとするコマンドらしいが、いったん理解を棚上げして先に進むことにする。

cdk-assets requires an asset manifest file called assets.json, in a CDK CloudAssembly (cdk.out/assets.json). It will take the assets listed in the manifest, prepare them as required and upload them to the locations indicated in the manifest.

hassaku63hassaku63

5,6 は共通していて、このへんは CodePipeline で Cfn Provider のアクションを実行するもの。

ActionMode という概念が出てくるが、だいたい見ればニュアンスは把握できるので省略する。
それぞれのステージも名前を見ればおおよそやってることの想像はつくので省略。

ActionMode の仕様は以下のドキュメントを参照

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-action-reference.html

(5) Stage = Pre-Prod

(5-1) WebService.Prepare (runOrder=1)

CloudFormation deploy "Pre-Prod-WebService"
ActionMode = CHANGE_SET_REPLACE

(5-2) WebService.Deploy (runOrder=2)

CloudFormation deploy "Pre-Prod-WebService"
ActionMode = CHANGE_SET_EXECUTE

(5-3) IntegrationTests (runOrder=3)

CodeBuild "PipelinePreProdIntegrationTestsProject84C5132E"

PipelinePreProdIntegrationTestsProject84C5132E の BuildSpec は以下。

{
    "version": "0.2",
    "phases": {
        "build": {
            "commands": [
                "set -eu",
                "export SERVICE_URL=\"$(node -pe 'require(process.env.CODEBUILD_SRC_DIR_Artifact_PipelineStackPreProdWebService366DDB3C_Outputs + \"/outputs.json\")[\"url\"]')\"",
                "npm install",
                "npm run build",
                "npm run integration"
            ]
        }
    }
}

対応する PreProd のステージ実装は以下。

// Pre-prod
//
const preProdApp = new WebServiceStage(this, 'Pre-Prod');
const preProdStage = pipeline.addApplicationStage(preProdApp);
const serviceUrl = pipeline.stackOutput(preProdApp.urlOutput);

preProdStage.addActions(new pipelines.ShellScriptAction({
    actionName: 'IntegrationTests',
    runOrder: preProdStage.nextSequentialRunOrder(),
    additionalArtifacts: [
        sourceArtifact
    ],
    commands: [
        'npm install',
        'npm run build',
        'npm run integration'
    ],
    useOutputs: {
        SERVICE_URL: serviceUrl
    }
}));

npm scripts に npm run integration 対応する実装は書いてある。このサンプルでは雛形のみで実際に意味のある処理はしていない様子だったが、API Gateway のエンドポイントを実際に叩いてレスポンスを確かめるようなものを意図している様子だった。

(6) Stage = Prod

(6-1) WebService.Prepare (runOrder=1)

CloudFormation deploy "Prod-WebService"
ActionMode = CHANGE_SET_REPLACE

(6-2) WebService.Deploy

CloudFormation deploy "Prod-WebService"
ActionMode = CHANGE_SET_EXECUTE

hassaku63hassaku63

全体的なパイプラインの流れを図示するとこんな感じになった。

このスクラップは2023/06/28にクローズされました