まとめ
まとめを先に書きます。
CDKを用いたIaCプロジェクトから、コンテナイメージとしてLambdaをデプロイする事ができます。実際に試すことができるミニマムなコードを紹介します。
AWS LambdaにおけるCDKとコンテナの位置づけ
本記事のサンプルが狙うポイントの紹介ですが、既にご存じの方は次セクションへスキップしてください。
コンテナを利用する利点
カスタマイズ性が高いLambdaを開発できることと、イメージサイズの上限が高いことが挙げられます。Zipパッケージでは250MBが上限であるところ、イメージでは10GB1まで利用できます。大きめのpythonパッケージ2を使う場合には250MBは厳しい制約ですが、10GBであればOSレイヤーのサイズも含むとはいえ十分な自由度が得られます。
CDKを利用する利点
AWSのサービスを用いたワークロードのIaC3が上手にできるツールです。著名なプログラミング言語(TypescriptやPythonなど)でワークロード全体 (アプリケーションやインフラなど) を記述し、コードの実行によりデプロイや更新できます4。つまり、Python言語でLambdaそのものとLambda上で実行されるプログラムの両方を記述し、git上で一様に管理することができます。これにより、一例としてプログラムはきちんと変更管理をしているのに、実際にデプロイされたLambdaの設定変更は管理されていなかった…といった分断が予防されます。
利点の用例
次のようなケースをまるっとコードで取り扱えます。
- cx-Oracle を入れて5Oracle DBにアクセスするLambdaを開発するぞ。アクセス先DBも込みで!
- openCV に独自の学習モデルを与えて6物体認識させるLambdaを開発するぞ。認識結果の出力先ストレージも込みで!
開発環境
当記事の作成環境です。
- Ubuntu Linux 22.x
- Python 3.11
- Docker Server 20.x - https://docs.docker.com/engine/install/ubuntu/
- node v18.x - https://github.com/nvm-sh/nvm
- Poetry 1.7.x - https://python-poetry.org/
cdkコマンドをインストール
$ npm install -g aws-cdk
$ cdk --version
2.114.1 (build 02bbb1d)
$
AWS Cliのプロファイル作成などの前提は下記デベロッパーガイドをご覧ください。
プロジェクト作成
CDKのサンプルアプリを新規作成します。ミニマムな例を示したいので、サンプルコードを改変する流れで紹介していきます。
$ cdk init sample-app --language=python
...
✅ All done!
$
初期状態では、以下のようファイル一式が作成されます。
$ ls
README.md cdk.json requirements-dev.txt sample
app.py requirements.txt tests source.bat
$
Poetryを使う
当記事はPoetryを利用した状況での説明内容になっていますので、requirementsをPoetry経由でインストールします。
$ poetry init
$ poetry add $( cat requirements.txt )
$ poetry add --dev $( cat requirements-dev.txt )
今回、requirementsは使いませんので削除しておきます。
$ rm requirements.txt
$ rm requirements-dev.txt
Python仮想環境に入る
pythonパッケージをインストールし、仮想環境に入ります。aws-cdk-libがインストールされている事も確認しておきます。
$ poetry install
$ poetry shell
$ poetry show aws-cdk-lib
name : aws-cdk-lib
version : 2.110.0
...
$
Dockerfileを書く
Lambda のベースコンテナイメージが Amazon ECR リポジトリで公開されていますので利用します。ほかにも多数のバージョンや開発言語向けのイメージがありますので、ご興味がある方は次のドキュメントをご覧ください。
FROM public.ecr.aws/lambda/python:3.11
# Install poetry
RUN curl -sSL https://install.python-poetry.org \
| POETRY_HOME=/opt/poetry python \
&& cd /usr/local/bin \
&& ln -s /opt/poetry/bin/poetry \
&& poetry config virtualenvs.create false
COPY src/ ${LAMBDA_TASK_ROOT}/
COPY pyproject.toml poetry.lock ${LAMBDA_TASK_ROOT}/
# Install python packages without dev pkgs
RUN poetry install --no-root --no-dev
# CMD [ "sample.handler" ] # ★1
★1の CMD
はコンテナイメージが起動対象とするLambdaのモジュールと関数名を指定する行ですが、次セクションでCDKのStack定義から指定しますので、Dockerfile内では無指定でOKです。
Lambdaのコード
処理は本記事の主題ではないのでこんにちは世界。
def handler(event, context):
return {"message": "Hello World!"}
Stack定義を書く
一つのLambda関数リソースだけを定義したシンプルなCDKスタック定義です。
class SampleStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# コンテナイメージを定義
image_asset = aws_lambda.DockerImageCode.from_image_asset(
cmd=["sample.handler"],
directory=".",
)
# コンテナイメージ方式のLambda関数を定義し、利用イメージを指定
aws_lambda.DockerImageFunction(
scope=self, id="SampleFunction", code=image_asset
)
aws_lambda.DockerImageCode.from_image_asset
メソッドでは、どのLambdaで使うコンテナイメージのAsset一式を指定します。directory
パラメータに与える値は、docker buildコマンドのPATHを指定することに対応します。cmd
パラメータに与える値は、Dockerfileにおける CMD
の指定に対応しており、先のDockerfile内でCOPYした src/
配下の sample.py
handler
関数が実行対象プログラムであることを示します。docker run コマンドの COMMAND オプション を指定することに対応します。
aws_lambda.DockerImageFunction
メソッドでは、AWS上にプロビジョニングするLambdaリソースを定義しています。先のコンテナイメージを利用させる指定をしていますが、LambdaのTimeoutやメモリサイズなどLambdaリソースへのセッティングができます。
いずれのメソッドも、指定可能なパラメータ数が豊富ですね。
これは私感ですが、CDKの仕様からは、AWSサービスを広くモデリングして、AWSでできること全体をCDKでもIaCできるようにしようという意思を感じます。実際、CDKはそのカバー範囲の広さがAPIドキュメントからよく分かりますし、更新頻度やコントリビューションの活発さもあって頼もしいツールです。
Bootstraping Stackの作成
予め、bootstrap コマンドを一度だけ実行しておきます。
$ cdk bootstrap
✅ Environment aws://ACCOUNT_NO/REGION bootstrapped.
$
CDKを介してインフラやアプリケーションをAWS環境にデプロイするには、ブートストラッピングスタックと呼ばれる、リソースプロビジョニングを行ってくれる仕組みが介在します。デプロイ先リージョンのCloudformationスタックとして一度作成しておく操作が bootstrap コマンドです。
デプロイ
いよいよ、Lambda関数をCDK経由でデプロイします。
$ cdk deploy
...
✨ Synthesis time: 4.1s
SampleStack: start: Building
...
Step 1/4 : FROM public.ecr.aws/lambda/python:3.11
---> be5c629c78a6
...
1:21:11 AM | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | SampleStack
...
✨ Total time: 70.32s
$
デプロイ結果の確認
Cloudformation Stacks
Cloudformation Stack Sample Stack
としてデプロイされました。
ECR
ECR リソースが、 CDKToolkit スタックのリソースとして作られ、デプロイ対象のLambdaイメージの管理に利用されています。
LambdaのコンテナイメージはローカルPC上のDocker Serverでビルドされ、ECRにpushされてからLambda関数としてデプロイされているわけですが、CDKのAPIが一連のプロビジョニングからデプロイまでのプロセスを、Stack定義の記述だけで済ませられるようラップしてくれている点がGoodです。
Lambda
イメージデプロイされたLambdaが存在する事も確認できました。
以後、DockerfileやStack定義を育てる過程で、前述の利点を享受し続けることになります。
余談&紹介
本記事は、私が所属しているAWSサービスプロバイダーの社内Slackで、アドベントカレンダーやろうぜ!と募集があり楽しげでしたので参加する形で執筆しました。カレンダーにはAWS関連の記事が主に投稿されていますので、ご興味がありましたらぜひ覗いてみてください。
余談まで読んでくださりありがとうございます。良い年末をお過ごしください。
-
ただしZipパッケージとは異なり、イメージの場合はOSやミドルウェアレイヤーのサイズを含む上限です。Lambdaのクォータに関する仕様 - https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html ↩
-
たとえばNumPyやSciPyのパッケージサイズは250MBを超えます - https://avilpage.com/2020/02/reduce-python-package-footprint.html ↩
-
IaCに関するAWSのホワイトペーパー https://aws.amazon.com/jp/what-is/iac/ ↩
-
デベロッパーガイドの冒頭 https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html ↩
-
環境要件にOracle Instant Clientのインストールが要求されるので、イメージデプロイが向いている ↩
-
extraモジュールや独自の分類器もインストールして使いたい場合なども、イメージデプロイが向いている ↩