Lambda Web Adapter でExpressアプリをサーバーレス化してみた
実際に手を動かして学ぶAWS Lambda + Express
はじめに
AWS Lambda Web Adapter(LWA)を使って、ExpressアプリをほぼそのままLambdaで動かしてみました。従来のサーバーレス開発では、Lambda固有の書き方を覚える必要がありましたが、LWAを使えば既存のWebアプリケーションをほとんど変更せずにサーバーレス化できます。
この記事では、新規でExpressアプリを作成し、Lambda Web Adapterを使ってAWS Lambdaにデプロイするまでの手順を詳しく解説します。
Lambda Web Adapterとは
Lambda Web Adapterは、AWS公式が提供するOSSで、VM やコンテナ用に実装されたウェブアプリを、ほとんどそのまま Lambda でも動かせるようにするツールです。
くわしくはこちら
従来の課題:
- Lambda固有のハンドラー関数の実装が必要
- フレームワークごとに個別のアダプターが必要
- Lambda専用のコードはローカルやECSで再利用できない
LWAの利点:
- たった1行の追加でLambda対応
- フレームワーク非依存
- 同じコンテナイメージでローカル・ECS・Lambda全てで動作
プロジェクトの作成
まず、新しいNode.jsプロジェクトを作成します。
mkdir lambda-express-hello
cd lambda-express-hello
npm init -y
package.jsonの設定
{
"name": "lambda-express-hello",
"version": "1.0.0",
"description": "Express app with Lambda Web Adapter",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.2"
},
"engines": {
"node": ">=18"
}
}
依存関係をインストール:
npm install
Expressアプリケーションの実装
server.js
ファイルを作成します。
const express = require('express');
const app = express();
// ポート番号はLWAのデフォルト(8080)を使用
const port = process.env.PORT || 8080;
// JSONパーサーを有効化
app.use(express.json());
// ヘルスチェック(LWAが使用)
app.get('/', (req, res) => {
res.status(200).json({
message: 'Hello World from Lambda Web Adapter!',
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV || 'development'
});
});
// 複数のエンドポイント例
app.get('/api/hello', (req, res) => {
const name = req.query.name || 'World';
res.json({
message: `Hello, ${name}!`,
from: 'Express on Lambda'
});
});
app.get('/api/info', (req, res) => {
res.json({
service: 'Lambda Express API',
version: '1.0.0',
node_version: process.version,
aws_region: process.env.AWS_REGION || 'N/A',
lambda_function_name: process.env.AWS_LAMBDA_FUNCTION_NAME || 'local'
});
});
app.post('/api/echo', (req, res) => {
res.json({
message: 'Echo endpoint',
received_data: req.body,
headers: req.headers
});
});
// 404ハンドラー
app.use('*', (req, res) => {
res.status(404).json({
error: 'Not Found',
path: req.originalUrl,
method: req.method
});
});
// エラーハンドラー
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'Internal Server Error',
message: err.message
});
});
app.listen(port, () => {
console.log(`Express server running on port ${port}`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});
ローカルでのテスト
npm start
以下のエンドポイントでテスト:
- http://localhost:8080/ - ヘルスチェック
- http://localhost:8080/api/hello?name=Lambda - パラメータ付きAPI
- http://localhost:8080/api/info - システム情報
Docker化(Lambda Web Adapter対応)
ここが最重要ポイント! LWAの導入はDockerfile
にたった1行追加するだけです。
FROM node:22-alpine
# Lambda Web Adapterをインストール(この1行だけ追加!)
COPY /lambda-adapter /opt/extensions/lambda-adapter
# 作業ディレクトリを設定
WORKDIR /usr/src/app
# パッケージファイルをコピーして依存関係をインストール
COPY package*.json ./
RUN npm ci --omit=dev && \
npm cache clean --force
# アプリケーションコードをコピー
COPY server.js ./
# ポート8080を公開(LWAのデフォルト)
EXPOSE 8080
# 本番環境に設定
ENV NODE_ENV=production
# アプリケーションを開始
CMD ["node", "server.js"]
.dockerignore
も作成:
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.DS_Store
Dockerでのローカルテスト
# イメージをビルド
docker build -t lambda-express-hello .
# コンテナを実行
docker run -p 8080:8080 lambda-express-hello
同じエンドポイントでテストして、Docker環境でも正常に動作することを確認します。
AWSへのデプロイ
1. ECRリポジトリの作成
AWSコンソールでAmazon ECRを開き:
- 「リポジトリを作成」をクリック
- リポジトリ名:
lambda-express-hello
- 「リポジトリを作成」をクリック
2. ECRへのイメージプッシュ
# AWS CLIでECRにログイン
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com
# イメージをビルド
docker build -t lambda-express-hello .
# タグ付け
docker tag lambda-express-hello:latest YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-express-hello:latest
# プッシュ
docker push YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-express-hello:latest
よくあるトラブル:プロキシエラー
Docker Desktopでプロキシが設定されている場合、以下のエラーが発生することがあります:
proxyconnect tcp: dial tcp 192.168.65.1:3128: i/o timeout
解決方法:
- Docker Desktopの設定を開く
- 「Resources」→「Proxies」
- 「Bypass proxy settings for these hosts & domains」に
*.amazonaws.com
を追加 - または一時的にプロキシを無効化してプッシュを実行
3. Lambda関数の作成
AWSコンソールでAWS Lambdaを開き:
- 「関数の作成」をクリック
- 「コンテナイメージ」を選択
- 関数名:
lambda-express-hello
- コンテナイメージURI:先ほどプッシュしたECRイメージのURI
- アーキテクチャ:
x86_64
- 「関数の作成」をクリック
4. Lambda設定の調整
基本設定:
- タイムアウト:30秒(デフォルトの3秒では不足の可能性)
- メモリ:512MB(必要に応じて調整)
環境変数(必要に応じて):
-
AWS_LWA_PORT
:8080 -
NODE_ENV
:production
5. Function URLの設定
- Lambda関数の「設定」タブ
- 左メニューの「関数 URL」
- 「関数 URL を作成」をクリック
- 認証タイプ:
NONE
(パブリックアクセス) - CORS設定(必要に応じて設定)
- 「保存」をクリック
テストと動作確認
Function URLが生成されたら、以下のエンドポイントをテスト:
https://YOUR_FUNCTION_URL.lambda-url.ap-northeast-1.on.aws/
https://YOUR_FUNCTION_URL.lambda-url.ap-northeast-1.on.aws/api/hello?name=Lambda
https://YOUR_FUNCTION_URL.lambda-url.ap-northeast-1.on.aws/api/info
結果
Lambda Web Adapterを使用することで:
✅ 成功したポイント:
- Dockerfileへの1行追加だけでLambda対応完了
- 同じコードでローカル・Docker・Lambdaで動作
- 既存のExpress知識をそのまま活用
- コールドスタートも許容範囲内の速度
📊 パフォーマンス:
- 初回実行(コールドスタート):約2-3秒
- 2回目以降(ウォームスタート):約100-300ms
- メモリ使用量:約50-80MB
監視とトラブルシューティング
CloudWatch Logsでの確認項目:
- アプリケーションの起動ログ
- Lambda Web Adapterの動作ログ
- リクエスト処理のログ
よくある問題と解決法:
- タイムアウトエラー → Lambda関数のタイムアウトを延長
- メモリ不足 → メモリ割り当てを増加
-
ポート設定エラー →
AWS_LWA_PORT
環境変数を確認
まとめ
Lambda Web Adapterは、既存のWebアプリケーションをサーバーレス化する際の強力なツールです。特に:
こんな場面におすすめ:
- 既存のExpressアプリをサーバーレス化したい
- Lambda特有の書き方を覚える時間がない
- 同じコードベースを複数の環境で動かしたい
- プロトタイプを素早くサーバーレス化したい
注意すべき点:
- コールドスタート時間はネイティブLambdaより長い
- メモリ使用量はやや多め
- 大量の同時リクエストには従来のコンテナの方が適している場合もある
Lambda Web Adapterにより、サーバーレスへの移行ハードルが大幅に下がりました。ぜひお手持ちのアプリケーションで試してみてください!
Discussion