はじめに
AWS Lambdaが好きです(告白)
Honoでサーバーを実装してFunction URLでデプロイする手軽さは他の追随を許さず、深く考えなくてもスケーラビリティは高く、コストも安い。最高だな!
そんなLambdaですが、最近GitHub Actionsで手軽にデプロイする手段が提供されるようになったとか。
これを試しつつ、Lambdaをデプロイするいくつかのパターンをおさらいしてみようと思います。
「4選」としていますが、これが全てという訳ではありません
e.g.) Serverless Framework, AWS SAM, Terraform...
共通: Honoアプリケーションを実装する
TypeScriptの優れた開発体験・さまざまなJavaScriptランタイムのサポートでたいへん流行しているHonoの、AWS Lambdaのサポートを利用します。
npm create hono@latest lambda-deploy-methods
> aws-lambda
import { Hono } from 'hono'
import { handle } from 'hono/aws-lambda'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export const handler = handle(app)
index.handler
がエントリーポイントとなります。Lambdaのリクエスト・レスポンスをHonoがいい感じに処理してくれます。このコードをデプロイすることを考えます。
Honoのデフォルトのデプロイ手順
HonoをAWS Lambda向けに初期化すると、package.json
にデプロイのためのコマンドが定義されています。
{
...
"scripts": {
"build": "esbuild --bundle --outfile=./dist/index.js --platform=node --target=node20 ./src/index.ts",
"zip": "zip -j lambda.zip dist/index.js",
"update": "aws lambda update-function-code --zip-file fileb://lambda.zip --function-name hello",
"deploy": "run-s build zip update"
},
...
}
TypeScriptのままではもちろん動作しないので、「ビルド」する必要があります。LambdaにコードをアップロードするにはZIP圧縮する必要があります。CLIを利用して、コードをアップロードして既存の関数を更新することができます。Honoはプロジェクト作成時点でこれらの手順がまとまっています、親切。
また、上記のコードをデプロイするだけではアプリケーションを動作させることは出来ず、HTTPリクエストでLambdaを動作させる仕組みが必要です。つまりAPI GatewayかLambdaのFunction URLs(関数URL)を追加する必要があります。本記事では以後、動作確認の際には断りなくFunction URLsを用いています。
たとえばHonoアプリケーションをLambda関数としてデプロイし、Function URLsを有効化した場合、下記のようなHTTPリクエストにより動作を確認することが出来ます。
curl https://45ngtrlalc6v4fa4brzmkvxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/
# Hello Hono!
1. マネジメントコンソールで編集・デプロイする
前述のHonoデフォルトんお手順ではCLIで関数を更新していましたが、LambdaはAWSのマネジメントコンソール上で編集・デプロイすることが出来ます。
コンソール上でVSCodeのUIを用いてコードを直接編集・デプロイすることが出来ます。
ちょっとした検証や決まりきったコードを実装するときは、使えるかもしれません。一方、今回のHonoアプリケーションはビルドの工程が必要なので、もしコンソールで直接編集するとしたら、ビルド成果物(dist/index.ts
をコピペして貼り付けることになりますが、普通はやりません。
1.1. VSCodeで編集・デプロイする
コンソール上のVSCodeでコードを編集できることがわかりましたが、最近はユーザーのマシン上のVSCodeを利用することが出来るようになったらしいです。
確かに「Open Visual Studio Code」というボタンがあります。クリックするとVSCodeが開き、(入っていなければ)AWS ToolkitなるExtensionのインストールを求められます。
加えて、クレデンシャルを取得したりリージョンを設定したり…。するとVSCodeで関数のコードを表示・編集できて、保存時にデプロイも出来ました。
開発体験的にどうかというとそこまで優れている感じはしませんが、ブラウザ上のVSCodeと比べると、手元のVSCodeはGenAIなどの開発支援を受けやすいという点が優位かもしれません。
2. ZIPファイルをアップロードする
Honoのデフォルトのデプロイ方法は、ビルド→ZIP化→アップロード・関数の更新という手順でしたが、ZIP化まで出来ていればコンソールからZIPをアップロードして関数を更新することも出来ます。
# Honoプロジェクトのルートで
npm run build # TypeScriptプロジェクトをesbuildでバンドル
# dist/index.jsというファイルが得られる
npm run zip # dist/index.jsをアーカイブしたZIPファイルが得られる
# lambda.zip
このZIPファイルをコンソールからアップロードすることが出来ます。
esbuildでバンドルされる際に難読化されているので、もはや直接コードを見たり編集する状態ではなくなっています。
3. GitHub Actionsでデプロイする
続いて、今回リリースされたGitHub Actionsによるデプロイを試します。以下の記事がわかりやすいです。
3.1 下準備(OIDCでGitHub Actionsを信頼するIAMポリシー・ロールを作成する)
GitHub ActionsでAWSリソースの操作を行うためには、Actionが何らかの権限を持つ必要があります。OIDCを用いてGitHubリポジトリを信頼するというのが定番なので、その操作を行なっておきます。
上記手順に沿って作成されるIAMロールを用いてActionを実行します。
3.2 ワークフローの定義
前述のリリースされたActionでは、ソースコードのビルドは関心外で、ビルド済みのコード(=フォルダ)をLambdaにデプロイする機能を提供しています。つまりworkflowを記述する際は事前にビルドしておく必要があります。ということで下記のようなワークフローとなります。
name: Deploy to AWS Lambda
on:
push:
branches: ['main']
permissions:
id-token: write
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v1
with:
node-version: '22.x'
- name: install
run: npm ci
- name: build
run: npm run build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ap-northeast-1
- name: Deploy Lambda Function
uses: aws-actions/aws-lambda-deploy@v1
with:
function-name: lambda-deploy-action
code-artifacts-dir: dist
handler: index.handler
runtime: nodejs22.x
architectures: arm64
role: ${{ secrets.AWS_LAMBDA_EXECUTION_ROLE_ARN }}
ポイント
- 関数を新規作成する場合(=
function-name
で指定した名前の関数が存在しない場合)、role
(Lambdaの実行ロール)が未定義だとエラーになる - オプションが色々あるけども、Function URLsは未サポートの模様
IAMロールを作成してワークフローを記述するだけでLambdaをデプロイ出来るのは結構手軽だとは思います。ただしLambdaだけあれば良いケースというのはあまり多くないでしょうから、他のリソースが登場すると厳しい気はする。
4. AWS CDKでデプロイする
AWS CDK自体の説明は省きますが、もちろんLambda専用の道具ではありません。他のAWSリソースとの連携などを宣言的に記述できるCDKはやはり優れた選択肢です。
加えて、NodejsFunction
はTypeScriptコードのバンドル機能を内包しているので簡潔にリソースを記述でき、これがたいへん強力です。たとえば今回のHonoアプリケーションは、エントリーポイントであるsrc/index.ts
を用いて下記のように書けます。関数URLも有効化しておきます。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const nodejsFunction = new cdk.aws_lambda_nodejs.NodejsFunction(
this,
'NodejsFunction',
{
runtime: cdk.aws_lambda.Runtime.NODEJS_22_X,
entry: '../src/index.ts',
handler: 'handler',
architecture: cdk.aws_lambda.Architecture.ARM_64,
},
);
nodejsFunction.addFunctionUrl({
authType: cdk.aws_lambda.FunctionUrlAuthType.NONE,
});
}
}
このコードだけで、Honoアプリケーションが関数URLつきでデプロイされるのはたいへん体験がよいです。S3などをくっつけたくなっても簡単に拡張できるのもCDKの良いところです。これがもう少し複雑な記述が求められたりすると、今回リリースされたActionを利用するのも良いかなという気持ちになるかもしれませんが、いかんせんCDKでもシンプルに済む…。
このCDKは下記のようなワークフローでデプロイ出来ます。TypeScriptコードのバンドルにDockerイメージが利用されますが、CPUアーキテクチャが一致していないとエラーとなるので、クロスビルドするための細工が含まれています。
name: Deploy with AWS CDK
on:
push:
branches: ['main']
permissions:
id-token: write
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v4
with:
node-version: 22
- name: install dependencies
run: npm ci
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: linux/arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ap-northeast-1
- name: Run CDK
working-directory: ./cdk
run: |
npm ci
npm run cdk -- deploy --require-approval never
おわりに
AWS Lambda単体のデプロイを考えると少しでもシンプルな方法が望まれますが、結局のところ一番シンプルなのはCDKだと思います(関数URLのことを考慮したり)。HonoアプリケーションをCDKでデプロイする体験のよさたるや。また、Lambdaだけあれば良いアプリケーションというのも稀で、つまり普通は他のAWSリソースとの連携が必要となるでしょう、その場合にもCDKなら拡張が容易です。
今回の記事で用いたコードは下記で公開しています