AWS CDK v2でECRのレポジトリを作ってコンテナイメージをpushし、Lambdaで実行してみた

2023.04.10

データアナリティクス事業本部の鈴木です。

Rancher Desktopをインストールしたローカル環境からAmazon ECRにコンテナイメージを準備して、AWS Lambdaで実行してみたので、試した内容をまとめてみました。

検証の内容

今回は簡単に、以下の3点を確認しました。

  1. AWS CDKでECRにレポジトリを作成する。
  2. dockerコマンドでECRのレポジトリにコンテナイメージをpushする。
  3. コンテナをLambdaで実行する。

ECRのレポジトリにコンテナイメージを上げておくと、Lambdaはもちろん、ECSやSageMakerなどのさまざまなサービスによりコンテナを利用することができます。

今回はその例の一つとして、簡単ではありますが、レポジトリの作成からLambdaでの実行までを試してみました。

環境

以下の環境で検証しました。

  • Rancher Desktop: Version: 1.8.1
  • AWS CDK: 2.73.0

特にRancher Desktopについては、以下の記事ような手順であらかじめインストールしておきました。

やってみる

1. ECRレポジトリの作成

まず、以下のようにプロジェクトを作成しました。

mkdir cdk_erc
cd cdk_erc
cdk init sample-app --language typescript

lib/cdk_erc-stack.tsは以下のようにしました。

lib/cdk_erc-stack.ts

import * as cdk from 'aws-cdk-lib';
import * as ecr from "aws-cdk-lib/aws-ecr";
import { Construct } from 'constructs';

export class CdkErcStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // ECRレポジトリ
    const lambdaEcrRepository = new ecr.Repository(this, 'Repository', {
      repositoryName: "cm-nayuts-sample-repo",
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteImages: true
    });
  }
}

今回はレポジトリを削除しやすいように、removalPolicyautoDeleteImagesに値を設定してみました。ただし、運用する際は間違って消えてしまう可能性もあるので注意が必要です。

cdk deployでデプロイします。私の環境ではconfigファイルで指定したプロファイルでAWS環境にアクセスするようにしているので、--profileオプションを付けました。

cdk deploy --profile プロファイル名

--profileオプションについては、AWS CDK Toolkit (cdk command) - AWS Cloud Development Kit (AWS CDK) v2Specifying Region and other configurationをご確認ください。

以下のように作成されました。

レポジトリ

リポジトリタグも設定されています。

レポジトリタグ

2. コンテナイメージのpush

プロジェクトのルートにdockerディレクトリとsrcディレクトリを作成しました。

以下のようになるよう、ファイルを配置しました。

cd docker
tree
# docker
# ├── docker_build.sh
# └── dockerfile

cd lambda
tree
# src
# ├── app.py
# └── requirements.txt

以下は作成したファイルの説明です。

なお、app.pyおよびdockerfileは、2023/4/9時点でAWS Lambdaデベロッパーガイドのコンテナイメージで Python Lambda 関数をデプロイするに記載の内容を使用させて頂きました。

docker_build.sh

これはdockerfileからコンテナイメージをビルドしてECRにpushするツールです。

docker_build.sh

#!/bin/bash

ACCOUNTID=${1}
REGION=${2}
ECR_REPO_NAME=${3}
AWS_PROFILE=${4}

ECR_REPO_URI="${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com/${ECR_REPO_NAME}"

echo "ECR_REPO_URI: ${ECR_REPO_URI}"

aws ecr get-login-password --region ${REGION} --profile ${AWS_PROFILE} | docker login --username AWS --password-stdin ${ACCOUNTID}.dkr.ecr.${REGION}.amazonaws.com
docker build -f ./docker/dockerfile -t ${ECR_REPO_NAME} .
docker tag ${ECR_REPO_NAME}:latest ${ECR_REPO_URI}:latest
docker push ${ECR_REPO_URI}:latest

dockerfile

デベロッパーガイド記載の、Python関数のコンテナイメージ作成用のdockerfileです。

dockerfile

FROM public.ecr.aws/lambda/python:3.8

# Install the function's dependencies using file requirements.txt
# from your project folder.

COPY ./src/requirements.txt  .
RUN  pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

# Copy function code
COPY ./src/app.py ${LAMBDA_TASK_ROOT}

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "app.handler" ]

app.py

デベロッパーガイド記載の、Python関数で実行するスクリプトです。

app.py

import sys
def handler(event, context):
    return 'Hello from AWS Lambda using Python' + sys.version + '!'

requirements.txt

コンテナイメージのビルド時にインストールするPythonライブラリの一覧です。今回は特に必要がないので空ファイルにしておきました。

ここまでで、プロジェクトは以下のようなディレクトリ構成になっています。

tree -L 1
# .
# ├── README.md
# ├── bin
# ├── cdk.json
# ├── docker
# ├── jest.config.js
# ├── lib
# ├── node_modules
# ├── package-lock.json
# ├── package.json
# ├── src
# ├── test
# └── tsconfig.json

プロジェクトのルートから、以下のようにツールを実行して、コンテナイメージをECRのレポジトリにpushしました。

./docker_build.sh 自分のAWSアカウントID ap-northeast-1 レポジトリ名 プロファイル名

このようにイメージがpushされました。

pushされたイメージ

3. Lambdaでの実行

pushしたコンテナイメージを、Lambdaでコンテナとして実行してみました。Lambdaは別のCDKのプロジェクトを作ってデプロイしました。

mkdir cdk_lambda
cd cdk_lambda
cdk init sample-app --language typescript

lib/cdk_lambda-stack.tsは以下のようにしました。

lib/cdk_erc-stack.ts

import * as cdk from 'aws-cdk-lib';
import * as ecr from "aws-cdk-lib/aws-ecr";
import * as lambda from "aws-cdk-lib/aws-lambda";
import { Construct } from 'constructs';

export class CdkLambdaStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // ECRリポジトリARNを指定して、Lambda関数用カスタムコンテナイメージのリポジトリを取得する。
    const lambdaEcrRepository = ecr.Repository.fromRepositoryArn(
      this,
      id,
      this.node.tryGetContext("ecrRepoArn")
    );

    // Python関数
    const sampleLambda = new lambda.Function(this, "cm-nayuts-sample-container-lambda", {
      code: lambda.Code.fromEcrImage(lambdaEcrRepository, {
        cmd: ["app.handler"],
        tag: "latest",
      }),
      functionName: "cm-nayuts-sample-container-lambda",
      runtime: lambda.Runtime.FROM_IMAGE,
      handler: lambda.Handler.FROM_IMAGE,
      timeout: cdk.Duration.seconds(3)
    });
  }
}

ECRリポジトリのARNは、cdk.jsoncontextに、ecrRepoArnのバリューとして設定しておきました。

そして、以下のコマンドでデプロイしました。

cdk deploy --profile プロファイル名

以下のように関数が作成されました。

作成された関数

テストから実行してみると、app.pyが実行されていることが分かりました。

実行結果

後片付け

それぞれのCDKプロジェクトで、スタックを削除しておきました。

cdk destroy --profile プロファイル名

ECRのレポジトリもリポジトリタグの効果でスタックを削除すると綺麗に削除されました。検証時は便利ですが、運用時は間違って消される可能性もありそうなのでそこは注意ですね。

レポジトリの削除

最後に

Rancher Desktopをインストールしたローカル環境から、Amazon ECRにコンテナイメージを準備してAWS Lambdaで実行してみたので、試した内容をまとめてみました。

簡単な例ではありましたが、Lambdaで実行するコンテナを変えればさまざまなことが実現できますし、ECRにpushする方法まで分かったのでSageMakerのProcessing Jobを使うときなどにも応用ができますね。

参考