はじめに
みなさんこんにちは、seshimoと申します。
今回は、AWS公式のワークショップでCDKに関するハンズオンを実施しましたので、手順や躓いたところを共有します。
本記事ではCDKを用いてLambda関数とAPI GatewayをAWSアカウント上にデプロイし、パブリックにアクセス可能な状態にするとこまで確認します。なお、今回のCDKではboto3 (Python) を使用して記述していきます。
基本的にこちらのワークショップに沿って進めていきます。
Pythonワークショップ
環境構築
前提
WSL2にUbuntu 24.04.1 LTSをインストールし、VSCode上から接続しています。
AWS CLIのインストール
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html
こちらのドキュメントに沿ってAWS CLIをインストールしていきます。
自環境にはunzipが入っていなかったのでインストールしました。
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo apt install unzip
unzip awscliv2.zip
sudo ./aws/install
アクセスキーの発行と認証情報の設定
aws configure
aws configureを入力すると、アクセスキー、シークレットキー、リージョンの入力を求められるので、マネコン上でアクセスキーを発行し、そこからコピペしてきます。また、リージョンについては今回はリージョンをアイルランドとしoufput formatはデフォルト(空欄)にしました。
Node.jsのインストール
AWS CDKはNode.jsを使用するため、インストールする必要があります。AWS CDK は Node.js 14.15.0 以降を使用するようですが、今回は最新のバージョンをインストールします。
Node.jsをインストールするために、Node.jsのパッケージ管理ツールであるnvmをインストールします。
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
nvmがインストールされていることを確認します。
nvm --versoin
(インストール直後にnvmコマンドを試そうとしたところnvmコマンドを認識してくれなかったので、一度VS Codeを起動し直し、再度試したところ認識してくれました。)
無事nvmがインストールされていることを確認したら以下コマンドで最新のNode.jsを取得します。
nvm install --lts
node -v
インストールされていることを確認するため、バージョン確認を行います。今回使用するバージョンは”v22.11.0”となります。
CDKのインストール
Node.jsをインストールしたのでnpmコマンドが使えるようになっているはずです。早速npmコマンドを使ってAWS CDK Toolkitをインストールしましょう!
npm install -g aws-cdk
cdk --version
インストールしたら確認のためにバージョンを見ます。今回使用するものは”2.165.0 (build 00f70f1)”でした。
最初のCDKプロジェクトの作成
cdkの始め方
まずは作業用にディレクトリを作成します。
mkdir cdk_workshop && cd cdk_workshop
cdk initを使用して、新しいPython CDKプロジェクトを作成します。今回はpythonを使用しますが、CDKではTypeScripeやJava, Goなどの言語も使用できます。
cdk init sample-app --language python
Virtualenvのアクティべート
システムのPythonに影響を及ばさないよう、仮想環境を作成してそこで作業していきます。activateコマンドを入力すると仮想環境が立ち上がります。
sudo apt install python3-venv
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -r requirements.txt
で必要なモジュールを仮想環境にインストールします。
この時点で、ディレクトリ構造は以下のようになっていることが確認できます。
├── README.md
├── app.py
├── cdk.json
├── cdk_workshop
│ ├── __init__.py
│ └── cdk_workshop_stack.py
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
├── __init__.py
└── unit
├── __init__.py
└── test_cdk_workshop_stack.py
今回のワークショップでは、SQSやSNSのリソースを作成するものがインストールされます。
メインスタック
生成されたファイル群に”cdk_workshop/cdk_workshop_stack.py”というファイルがあることを確認できます。こちらがアプリケーションの肝となる部分であり、以下のようになっています。
from constructs import Construct
from aws_cdk import (
Duration,
Stack,
aws_sqs as sqs,
aws_sns as sns,
aws_sns_subscriptions as subs,
)
class CdkWorkshopStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
queue = sqs.Queue(
self, "CdkWorkshopQueue",
visibility_timeout=Duration.seconds(300),
)
topic = sns.Topic(
self, "CdkWorkshopTopic"
)
topic.add_subscription(subs.SqsSubscription(queue))
cdk synth
cdk synthというコマンドを打つことによって、CDKアプリケーションを生成することができます。結果としてCloudFormationテンプレートが出力されることが確認できます。(出力結果は長いので割愛。SQSのキュー、SNSトピック、サブスクリプションなどが生成されます。)
cdk bootstrap
cdk bootstrapというコマンドを使うことにより、デプロイに必要なリソースを作成してくれる。これには、
- S3バケット(アセット保存用)
- IAMロール(デプロイメント用)
- CloudFormationの実行ロール
が含まれます。
cdk bootstrap
実際にcdk boorstrapを入力し、マネジメントコンソール上を見ると、S3バケットやIAMロールがCDKによって作成されていることが確認できます。
cdk deploy
では、CDKを用いてデプロイしていきたいと思います。デプロイするにはcdk deployというコマンドを使います。
cdk deploy
実際に実行すると、マネジメントコンソール上から以下のリソースが作成されていることを確認できます。
CloudForamation, sqs, snsトピック, サブスクリプションの生成確認
Hello, CDK!
次に、CDKのコードを記述して、Lambda関数とAPI Gatewayエンドポイントをデプロイしていきます。エンドポイントのURLにアクセスし、Lambda関数から返事をもらうようなコードを記述していきます。
CDKcdk_workshop/cdk_workshop_stack.pyの編集を行います。
from constructs import Construct
from aws_cdk import (
Stack
)
class CdkWorkshopStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# Nothing here!
次にcdk diffを行うと既存のデプロイされているリソースとの差分を示してくれます。
この状態でcdk deployを行うと、先ほど作成されたSQSやSNSのトピック、サブスクリプションが削除されていることを確認できます。
Hello Lambda
プロジェクトツリーのルートにlambdaディレクトリを作成し、lamhda/hello.pyファイルを追加します。
その後、cdk_workshop_stack.pyにて、ラムダ関数を定義します。
from constructs import Construct
from aws_cdk import (
Stack,
aws_lambda as _lambda,
)
class CdkWorkshopStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# Defines an AWS Lambda resource
my_lambda = _lambda.Function(
self, 'HelloHandler',
runtime=_lambda.Runtime.PYTHON_3_8,
code=_lambda.Code.from_asset('lambda'),
handler='hello.handler',
)
my_lambdaの定義について簡単に説明します。
- code=_lambda.Code.from_asset(‘lambda’)
- Lambda関数のソースコードの場所を指定しており、ルートディレクトリからの相対パスを指定しています。
- handler=’hello.handler’
- ‘hello.handler’はhello.pyのhandler関数を呼び出すことを示しています。
cdk diffというコマンドを入力することにより、cdk deployを実行したときに何が起こるかを確認することができます。
cdk diff
出力結果
では、cdk deployでデプロイしていきましょう。
cdk deploy
lambda関数がデプロイされていることを確認し、lambda関数を開きテストタブを開きます。イベント名にtest、テンプレートにAPI Gateway AWS Proxyを選択し、テストを実行します。
“詳細”を展開すると、実行中の関数が成功していることがわかります。
ホットスワップデプロイ
例えばLambdaコードだけを変更したい場合、cdk deployを使うとCloudFormationスタックを更新してしまいます。そこで、変更されたリソースのみを更新するホットスワップデプロイという機能があります。
cdk deploy --hotswap
を使用すると、CloudFormationによるデプロイの代わりにホットスワップデプロイを実行できるかどうか評価します。ここで、lambda関数のコードをHello, CDK!から別の文言に変更し、上のコマンドを打ちデプロイ時間の比較を行います。
- 先ほどのデプロイ✨ Total time: 57.06s
- ホットスワップデプロイ✨ Total time: 19.89s
すると、3倍近い差が出ることを確認できました。しかし、実際にLambda関数のコードは生成されているのでしょうか…?確認してみましょう。
しっかりと変更されていますね!
CDK Watch
CDK Watchはコードとアセットの変更を監視し、変更が検出されれると自動的にデプロイを実行しようとします。デフォルトではホットスワップを行うようになっており、ホットスワップを無効にするにはcdk watch —no-hotswapと呼び出す必要があります。
監視するファイルはcdk.jsonファイルのwatch設定によって決まります。試しにcdk.jsonファイルの中身を見てみましょう
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"python/__pycache__",
"tests"
]
},
では、cdk watchを呼び出したいと思います。
コマンドを入力すると、最初のデプロイがトリガーされ、その後cdk.jsonファイルで指定したファイルの監視が行われます。
早速lambda関数のコードを変更するとすぐにデプロイが開始されることが確認できると思います。今回の実行時間は
✨ Total time: 19.2s でした。
cdk watchの使用を停止するにはctrl + cコマンドを入力します。
API Gateway
次に、関数のフロントにAPI Gatewayを追加します。これにより、インターネット上のだれでもアクセスできるようパブリックなHTTPエンドポイントを公開します。
from constructs import Construct
from aws_cdk import (
Stack,
aws_lambda as _lambda,
aws_apigateway as apigw,
)
class CdkWorkshopStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# Defines an AWS Lambda resource
my_lambda = _lambda.Function(
self, 'HelloHandler',
runtime=_lambda.Runtime.PYTHON_3_8,
code=_lambda.Code.from_asset('lambda'),
handler='hello.handler',
)
apigw.LambdaRestApi(
self, 'Endpoint',
handler=my_lambda,
)
コード最上部でapi gatewayをインポートします。また、新たにAPI Gatewayの定義をコード下部で行っています。handler=my_lambdaでLambda関数との統合を設定しています。
では、cdk diffでデプロイ時の変更点を見ていきます。
変更点を確認したところで、cdk deployによってデプロイしたいと思います。
デプロイが完了すると以下のようなURLが生成されるので、アクセスしてみますと、Lambda関数のコードに記述したものが表示されます。
Outputs:
CdkWorkshopStack.Endpoint8024A810 = https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/prod/
無事Lambda関数に記述したコードが表示されることを確認できました!
おわりに
CDKを利用することにより、少ない記述量でリソースの生成が簡単にできることがわかりました。また、ホットスワップデプロイにより場合によっては短時間でのデプロイも利用できることを確認しました。まだハンズオンは続くので、次回はコンストラクトの作成などを行っていきたいと思います!