こんにちわ!namipay(@kata_dev)です。最近はもっぱらGoogle Cloudをメインでやっており、AWSはほぼ触っていないのでリハビリがてらにLambda Web Adapterを触ってみました。AWSでアプリケーションを動かす方法は様々ありますが、最近ではECS(Elastic Container Service)やEKS(Elastic Kubernetes Service)などのコンテナベースのサービスを利用することが一般的になっているかと思います。
実はLambda Web Adapterというツールを使うことでAWS Lambdaでもコンテナアプリケーションを動かすことができます。実際にLambda Web Adapterを使用してコンテナアプリケーションを動かしてみたので、簡単に記載してみます。
Lambda Web Adapterとは?
Lambda Web Adapterは、コンテナ化したWebアプリケーションをAWS Lambda上で実行できるようにするための公式が提供しているアダプターです。
これを利用することで、従来のコンテナアプリケーションをLambdaで簡単に動かすことができます。ECSやEKSでコンテナアプリケーションを動かす場合はサーバを常時稼働させる必要がありますが、Lambdaは従量課金なのでリクエストが発生しない場合はコストも発生しません。そのため、開発環境・社内向けサービス・個人開発やスタートアップなどのサービスをホスティングすれば一定のコスト効率が見込めるかと思います。
Lambda Web Adapterの使い方
Lambda Web Adapterに使い方は非常に簡単で既存のDockerfileに対して、以下の1行を追加するだけでLambdaにデプロイして動かせるようになります。
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
Lambdaにデプロイしてみる
実際にLambda Web Adapterでコンテナアプリケーションを動かして観ましょう。今回は内容を簡単にするため、honoの初期テンプレートの内容をコンテナ化してLambdaにデプロイしてみます。honoの公式ドキュメントにDockerfileのサンプルがあるのでこちらを参考にしても良いです。
- honoのプロジェクトを作成
コマンドを実行してhonoプロジェクトを作成します。初期テンプレートで生成されるコードでは、
/
にGETリクエストするとHello Hono!
とレスポンスするだけのシンプルなAPIが作られます。
1 2 3 4 5 6 7 8 9 10 |
$ npm create hono@latest my-app create-hono version 0.13.0 ✔ Using target directory … my-app ? Which template do you want to use? nodejs ? Do you want to install project dependencies? yes ? Which package manager do you want to use? npm ✔ Cloning the template ✔ Installing project dependencies 🎉 Copied project files Get started with: cd my-app |
- package.json・tsconfig.jsonの修正
my-appディレクトリ内にコードや設定ファイルが配置されるので
tsconfig.json
とpackage.json
の中身を以下に修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "types": [ "node" ], "jsx": "react-jsx", "jsxImportSource": "hono/jsx", "outDir": "./dist", }, "exclude": ["node_modules"], } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "name": "hono", "type": "module", "scripts": { "dev": "tsx watch src/index.ts", "build": "npx tsc" }, "dependencies": { "@hono/node-server": "^1.12.2", "hono": "^4.5.11" }, "devDependencies": { "@types/node": "^20.11.17", "tsx": "^4.7.1", "typescript": "^5.5.4" } } |
- Dockerfileの作成
コンテナイメージのビルドで使用するDocerfileを作成します。builderとrunnerに分けてマルチステージビルドしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# syntax=docker/dockerfile:1 FROM node:current-bookworm-slim AS builder WORKDIR /app RUN --mount=type=bind,source=tsconfig.json,target=tsconfig.json \ --mount=type=bind,source=package.json,target=package.json \ --mount=type=bind,source=package-lock.json,target=package-lock.json,rw \ --mount=type=bind,source=src,target=src \ --mount=type=cache,target=/root/.npm \ npm ci && \ npm run build && \ npm prune --production FROM gcr.io/distroless/nodejs20-debian12 AS runner ENV NODE_ENV=production USER node COPY --from=builder --chown=node:node /app/node_modules /app/node_modules COPY --from=builder --chown=node:node /app/dist /app/dist COPY --link package.json /app/package.json # Copy Lambda Web Adapter COPY --link --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter EXPOSE 8080 CMD ["/app/dist/index.js"] |
ここまでできれば、コード周りの下準備はokなのでインフラ周りのリソースを作っていきます。コマンド実行時の認証周りは適宜指定してください。
- ECRを作成する
1 2 |
$ export NAME=lambda-web-adapter-example $ aws ecr create-repository --repository-name=${NAME} |
- ECRの認証を取得
1 2 |
$ export REPO_URL=$(aws ecr describe-repositories --repository-name=${NAME} | jq -r '.repositories[].repositoryUri') $ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $(echo $REPO_URL | cut -d'/' -f1) |
- イメージのBuildとPush
1 2 |
$ docker build --platform=linux/arm64/v8 --load --provenance=false -f Dockerfile my-app --target=runner -t $REPO_URL $ docker push $REPO_URL |
- LambdaのIAMロール作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ aws iam create-role --role-name LambdaCloudWatchRole --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' aws iam attach-role-policy \ --role-name LambdaCloudWatchRole \ --policy-arn arn:aws:iam::aws:policy/CloudWatchLogsFullAccess |
- Lambda用のCloudWatch Logsを作成
1 |
$ aws logs create-log-group --log-group-name=/aws/lambda/${NAME} |
- Lambda関数の作成
1 2 3 4 5 6 7 |
$ aws lambda create-function \ --function-name=${NAME} \ --package-type=Image \ --code=ImageUri=${REPO_URL}:latest \ --memory-size=256 \ --architectures arm64 \ --role=$(aws iam get-role --role-name LambdaCloudWatchRole | jq -r '.Role.Arn') |
- リソースベースのポリシーステートメントを作成
1 2 3 4 5 6 |
$ aws lambda add-permission \ --function-name=${NAME} \ --function-url-auth-type=NONE \ --statement-id=${NAME} \ --action=lambda:InvokeFunctionUrl \ --principal="*" |
- Lambda URLの作成
1 |
$ export URL=$(aws lambda create-function-url-config --function-name=${NAME} --auth-type=NONE | jq -r '.FunctionUrl') |
- cURLでアクセスしてみる
1 |
$ curl ${URL} |
Hello Hono!
とJSONレスポンスが返ってくればうまく動いています!めちゃ簡単ですね!いぇい!まとめ
Lambda Web Adapterを使用することで、既存のコンテナアプリケーションを簡単にLambdaで動かすことができます。Lambdaで利用することでインフラコストや運用コストを下げることが一定可能だと思います。今後AWSでコンテナを動かす場合はアーキテクチャの一つとして検討してみるのも有りだと思います!
公園でペットの散歩をしながらSREしてます。