CDK Pipeline とは
CDK Pipeline とは CDK を用いて Lambda や API Gateway といった AWS リソースのみならず、それらを CI/CD でデプロイするためのパイプラインも CDK アプリケーション側で定義するためのものです。初めの一回だけ cdk deploy
したらあとはリポジトリにプッシュすれば必要に応じてパイプラインそのものを変更しつつ、パイプライン内でインフラストラクチャのデプロイ等も行うといったものになります。
今回はひとまず 前回 まで扱っていた HitCounter 及び TableViewer を用いて CDK Pipeline の構築を試みます。
Pipeline スタックの作成
まずはパイプラインを含むスタックを作成するところから始めます。cdk_workshop ディレクトリ配下に pipeline_stack.py を作成します。
from constructs import Construct
from aws_cdk import (
Stack
)
class WorkshopPipelineStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
次に app.py でデプロイのエントリポイントを変更します。
#!/usr/bin/env python3
import aws_cdk as cdk
from cdk_workshop.pipeline_stack import WorkshopPipelineStack
#from cdk_workshop.cdk_workshop_stack import CdkWorkshopStack
app = cdk.App()
#CdkWorkshopStack(app, "cdk-workshop")
WorkshopPipelineStack(app, "WorkshopPipelineStack")
app.synth()
リポジトリの作成
次は CDK アプリケーションのソースコードを格納するリポジトリを用意します。今回は GitHub リポジトリを使用するので、GitHub 側で OAuth トークンを予め控えておき、それを SecretManager のシークレットとして登録しておきます。
また、これまでの CDK アプリケーションの内容をプッシュしておきます。
パイプラインを定義する
リポジトリの作成ができたら、再び pipeline_stack.py をいじっていきます。なお、ソースに GitHub リポジトリを指定する方法は こちら を参考にしました。マネジメントコンソールで認証してからその Connection ARN を利用する方法が推奨されていますが、今回は OAuth トークンを利用します。
実際の pipeline_stack.py は以下の通りです。
from constructs import Construct
from aws_cdk import (
Stack,
SecretValue,
pipelines as pipelines,
)
class WorkshopPipelineStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
pipeline = pipelines.CodePipeline(
self,
"Pipeline",
synth=pipelines.ShellStep(
"Synth",
input=pipelines.CodePipelineSource.git_hub('U-PIN/cdk-pipeline-tutorial', 'main',
authentication=SecretValue.secrets_manager('GitHubOAuthToken')
),
commands = [
"npm install -g aws-cdk",
"pip install -r requirements.txt",
"npx cdk synth"
]
)
)
ここまでできたら $ cdk synth
と $ cdk deploy
を実行しましょう。
$ cdk synth
...
$ cdk deploy
...
✅ WorkshopPipelineStack
Stack ARN:
arn:aws:cloudformation:us-west-2:xxxxxxxxxxxx:stack/WorkshopPipelineStack/8a162f90-62c2-11ec-81ab-0a8a4e933019
以下のように CodePipeline 側でパイプラインが作成・実行されれば OK です。Build ステージには Synth 、UpdatePipeline ステージには SelfMutate というアクションがそれぞれ定義されているのがわかりますね。
CDK アプリケーション用ステージの追加
ここまでで、変更があった際に自分自身のアップデートを行う CDK Pipeline の構築が完了しました。なので、ここからは実際に CDK アプリケーションをパイプラインに組み込む作業に入ります。
まずは cdk_workshop ディレクトリ配下に pipeline_stage.py を以下の内容で作成します。
from constructs import Construct
from aws_cdk import (
Stage
)
from .cdk_workshop_stack import CdkWorkshopStack
class WorkshopPipelineStage(Stage):
def __init__(self, scope: Construct, id: str, **kwargs):
super().__init__(scope, id, **kwargs)
service = CdkWorkshopStack(self, 'WebService')
あとは Deploy ステージとして pipeline_stage.py で作成した Stage を pipeline_stack.py で追加するだけです。
from constructs import Construct
from aws_cdk import (
Stack,
SecretValue,
pipelines as pipelines,
)
from .pipeline_stage import WorkshopPipelineStage
class WorkshopPipelineStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
pipeline = pipelines.CodePipeline(
self,
"Pipeline",
synth=pipelines.ShellStep(
"Synth",
input=pipelines.CodePipelineSource.git_hub('U-PIN/cdk-pipeline-tutorial', 'main',
authentication=SecretValue.secrets_manager('GitHubOAuthToken')
),
commands = [
"npm install -g aws-cdk",
"pip install -r requirements.txt",
"npx cdk synth"
]
)
)
deploy = WorkshopPipelineStage(self, "Deploy")
deploy_stage = pipeline.add_stage(deploy)
さらに requirements.txt に cdk-dynamo-table-view を追加しておきます。これは CodeBuild のビルドプロジェクト内で pip install -r requirements.txt
をした際に cdk-dynamo-table-view も同時にインストールさせるためです。
aws-cdk-lib==2.1.0
constructs>=10.0.0,<11.0.0
cdk-dynamo-table-view==0.2.26
あとはリポジトリにプッシュします。
$ git add .
$ git commit -m "Added deploy stage to pipeline" && git push
[main f0909ac] Added deploy stage to pipeline
12 files changed, 5150 insertions(+), 206 deletions(-)
create mode 100644 cdk.out/assembly-WorkshopPipelineStack-Deploy/WorkshopPipelineStackDeployWebServiceC0F2BFAD.assets.json
create mode 100644 cdk.out/assembly-WorkshopPipelineStack-Deploy/WorkshopPipelineStackDeployWebServiceC0F2BFAD.template.json
create mode 100644 cdk.out/assembly-WorkshopPipelineStack-Deploy/cdk.out
create mode 100644 cdk.out/assembly-WorkshopPipelineStack-Deploy/manifest.json
rewrite cdk_workshop/__pycache__/pipeline_stack.cpython-39.pyc (99%)
create mode 100644 cdk_workshop/__pycache__/pipeline_stage.cpython-39.pyc
create mode 100644 cdk_workshop/pipeline_stage.py
Enumerating objects: 26, done.
Counting objects: 100% (26/26), done.
Delta compression using up to 8 threads
Compressing objects: 100% (17/17), done.
Writing objects: 100% (17/17), 21.97 KiB | 3.66 MiB/s, done.
Total 17 (delta 11), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (11/11), completed with 8 local objects.
To https://github.com/U-PIN/cdk-pipeline-tutorial.git
857f966..f0909ac main -> main
CodePipeline パイプラインに対して、以下のように Assets 及び Deploy ステージが追加されていれば成功です。
エンドポイントを出力させる
これまででパイプラインを用いて CDK アプリケーションのデプロイが完了しましたが、TableViewer や Rest API のエンドポイントを出力として得るためにもう少し弄ります。これで例えばフロントのアプリがあった時に API のエンドポイントを REACT_APP_*
のような環境変数に入れてビルドする、みたいなことも可能になりそうな気がしてます。
cdk_workshop/cdk_workshop_stack.py を以下のように書き換えましょう。
from constructs import Construct
from aws_cdk import (
Stack,
CfnOutput,
aws_lambda as _lambda,
aws_apigateway as apigw,
)
from cdk_dynamo_table_view import TableViewer
from .hitcounter import HitCounter
class CdkWorkshopStack(Stack):
@property
def hc_endpoint(self):
return self._hc_endpoint
@property
def hc_viewer_url(self):
return self._hc_viewer_url
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
my_lambda = _lambda.Function(
self, 'HelloHandler',
runtime=_lambda.Runtime.PYTHON_3_9,
code=_lambda.Code.from_asset('lambda'),
handler='hello.handler',
)
hello_with_counter = HitCounter(
self, 'HelloHitCounter',
downstream=my_lambda,
)
gateway = apigw.LambdaRestApi(
self, 'Endpoint',
handler=hello_with_counter._handler
)
tv = TableViewer(
self, 'ViewHitCounter',
title='Hello Hits',
table=hello_with_counter.table,
)
self._hc_endpoint = CfnOutput(
self, 'GatewayUrl',
value=gateway.url
)
self._hc_viewer_url = CfnOutput(
self, 'TableViewerUrl',
value=tv.endpoint
)
あとはリポジトリにプッシュすればパイプラインにより変更がデプロイされます。
$ git add .
$ git commit -m "Added CfnOutput" && git push
パイプライン実行後、Deploy-WebService という CloudFormation スタックで以下のような出力があれば成功です。
キー | 値 |
---|---|
GatewayUrl | https://xxxxxxxxxx.execute-api.us-west-2.amazonaws.com/prod/ |
TableViewerUrl | https://yyyyyyyyyy.execute-api.us-west-2.amazonaws.com/prod/ |
Validation Test を加える
ここまでで CDK Pipeline 及び TableViewer や API のデプロイが継続的に行えるようになりましたが、さらに TableViewer と API のテストを行うステージを追加しましょう。
まずは、Deploy ステージがそれぞれのエンドポイントをプロパティとして保持できるように、cdk_workshop/pipeline_stage.py を以下のように書き換えます。
from constructs import Construct
from aws_cdk import (
Stage
)
from .cdk_workshop_stack import CdkWorkshopStack
class WorkshopPipelineStage(Stage):
@property
def hc_endpoint(self):
return self._hc_endpoint
@property
def hc_viewer_url(self):
return self._hc_viewer_url
def __init__(self, scope: Construct, id: str, **kwargs):
super().__init__(scope, id, **kwargs)
service = CdkWorkshopStack(self, 'WebService')
self._hc_endpoint = service.hc_endpoint
self._hc_viewer_url = service.hc_viewer_url
次に、テスト用のアクションを追加するために cdk_workshop/pipeline_stack.py を以下のように書き換えます。
from constructs import Construct
from aws_cdk import (
Stack,
SecretValue,
pipelines as pipelines,
)
from .pipeline_stage import WorkshopPipelineStage
class WorkshopPipelineStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
pipeline = pipelines.CodePipeline(
self,
"Pipeline",
synth=pipelines.ShellStep(
"Synth",
input=pipelines.CodePipelineSource.git_hub('U-PIN/cdk-pipeline-tutorial', 'main',
authentication=SecretValue.secrets_manager('GitHubOAuthToken')
),
commands = [
"npm install -g aws-cdk",
"pip install -r requirements.txt",
"npx cdk synth"
]
)
)
deploy = WorkshopPipelineStage(self, "Deploy")
deploy_stage = pipeline.add_stage(deploy)
deploy_stage.add_post(
pipelines.ShellStep(
"TestViewerEndpoint",
env_from_cfn_outputs={
"ENDPOINT_URL": deploy.hc_viewer_url
},
commands=["curl -Ssf $ENDPOINT_URL"],
)
)
deploy_stage.add_post(
pipelines.ShellStep(
"TestAPIGatewayEndpoint",
env_from_cfn_outputs={
"ENDPOINT_URL": deploy.hc_endpoint
},
commands=[
"curl -Ssf $ENDPOINT_URL",
"curl -Ssf $ENDPOINT_URL/hello",
"curl -Ssf $ENDPOINT_URL/test",
],
)
)
あとはプッシュしましょう。
$ git add .
$ git commit -m "Added validation tests" && git push
パイプライン実行後、以下のように Validation Test 用のアクションが Deploy ステージに追加されていれば OK です。
なお、例えば API テスト用のビルドログを CodeBuild コンソールから見ると以下のようにテストが行われていることが確認できます。
[Container] 2021/12/22 07:05:35 Entering phase BUILD
[Container] 2021/12/22 07:05:35 Running command curl -Ssf $ENDPOINT_URL
Hello, CDK! You have hit /
[Container] 2021/12/22 07:05:38 Running command curl -Ssf $ENDPOINT_URL/hello
Hello, CDK! You have hit /hello
[Container] 2021/12/22 07:05:38 Running command curl -Ssf $ENDPOINT_URL/test
Hello, CDK! You have hit /test
[Container] 2021/12/22 07:05:38 Phase complete: BUILD State: SUCCEEDED
また、実際に TableViewer にブラウザからアクセスすると、テスト時のリクエストがカウントされていることがわかりますね。