AWS Lambda × Dockerでサーバーレスアプリ構築

はじめに

AWS Lambdaは、サーバーの管理不要でコードを実行できるサーバーレスコンピューティングサービスとして広く利用されています。2020年12月からは、Lambda上でDockerコンテナを実行できるようになり、さらに柔軟なアプリケーション開発が可能になりました。

本記事では、AWS LambdaとDockerを組み合わせたサーバーレスアプリケーションの構築方法について、基礎から実践まで詳しく解説します。

AWS Lambda × Dockerの魅力

従来のLambdaでは、Node.js、Python、Java、Goなどの言語をサポートしていましたが、コンテナイメージのサポートにより、以下のような大きなメリットが生まれました:

  1. 言語や依存関係の自由度向上:任意の言語やライブラリを利用可能
  2. 既存のDockerスキルやツールの活用:Docker開発環境をそのまま流用可能
  3. コンテナサイズの柔軟性:最大10GBのコンテナイメージをサポート
  4. ローカル開発とテストの容易さ:Dockerを使った開発環境との一貫性
  5. 既存コンテナアプリケーションのLambda移行:コンテナ化済みアプリの再利用

前提条件

この記事を進めるにあたって、以下のツールが必要です:

  • AWSアカウント
  • AWS CLI(設定済み)
  • Docker
  • エディタ(VSCode等)

Lambda用Dockerイメージの基本

Lambda用のDockerイメージには以下の2つの選択肢があります:

  1. AWS提供のベースイメージを使用する:Lambda実行環境に最適化されたイメージ
  2. カスタムイメージを作成する:Lambda Runtime API互換のイメージを自作

初めての方は、AWS提供のベースイメージを使うのがおすすめです。ここでは、Python用のベースイメージを使った例を紹介します。

実践編:Dockerイメージを使ったLambda関数の構築

手順1:プロジェクト構成の作成

まず、プロジェクトディレクトリを作成します:

mkdir lambda-docker-demo
cd lambda-docker-demo

以下のファイル構成で進めていきます:

lambda-docker-demo/
├── app.py          # Lambdaハンドラーコード
├── Dockerfile      # Dockerイメージ定義
├── requirements.txt # Pythonの依存関係
└── deploy.sh       # デプロイスクリプト

手順2:アプリケーションコードの作成

シンプルなPythonアプリケーションを作成します。app.pyに以下のコードを記述します:

import json
import requests
from datetime import datetime

def handler(event, context):
    """サンプルLambda関数"""
    
    # 外部APIからデータ取得のサンプル
    try:
        response = requests.get('https://api.ipify.org?format=json', timeout=3)
        ip_data = response.json()
        ip_address = ip_data['ip']
    except Exception as e:
        ip_address = f"APIリクエストエラー: {str(e)}"
    
    # 現在時刻の取得
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    # イベント情報の取得
    path = event.get('path', 'パスなし')
    method = event.get('httpMethod', 'メソッドなし')
    headers = event.get('headers', {})
    query_params = event.get('queryStringParameters', {})
    
    # レスポンスの構築
    result = {
        'message': 'AWS Lambda with Docker',
        'timestamp': current_time,
        'serverIp': ip_address,
        'request': {
            'path': path,
            'method': method,
            'userAgent': headers.get('User-Agent', 'Unknown'),
            'params': query_params
        }
    }
    
    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json'
        },
        'body': json.dumps(result, ensure_ascii=False)
    }

requirements.txt ファイルに依存関係を記述します:

requests==2.28.1

手順3:Dockerfileの作成

次に、Lambdaでコンテナを実行するためのDockerfileを作成します:

# AWS提供のLambda Python 3.9ベースイメージを使用
FROM public.ecr.aws/lambda/python:3.9

# 作業ディレクトリを/varに設定
WORKDIR ${LAMBDA_TASK_ROOT}

# 依存関係ファイルをコピー
COPY requirements.txt .

# 依存関係のインストール
RUN pip install -r requirements.txt

# アプリケーションコードをコピー
COPY app.py .

# Lambdaハンドラーを設定
CMD [ "app.handler" ]

手順4:Dockerイメージのビルド

ローカルでDockerイメージをビルドします:

docker build -t lambda-docker-demo .

手順5:ローカルでのテスト

Lambda実行環境をエミュレートするために、dockerコマンドでコンテナを実行し、テストリクエストを送信できます:

# Lambda実行環境のエミュレーション
docker run -p 9000:8080 lambda-docker-demo

# 別のターミナルでテストリクエストを送信
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{
  "path": "/test",
  "httpMethod": "GET",
  "headers": {"User-Agent": "curl/test"},
  "queryStringParameters": {"param1": "value1"}
}'

手順6:Amazon ECRにイメージをプッシュ

Lambda関数でコンテナイメージを使用するには、まずAmazon ECR(Elastic Container Registry)にイメージをプッシュする必要があります:

# ECRリポジトリの作成
aws ecr create-repository --repository-name lambda-docker-demo --region ap-northeast-1

# ECRへのログイン
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com

# イメージにタグ付け
docker tag lambda-docker-demo:latest <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-docker-demo:latest

# ECRへのプッシュ
docker push <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-docker-demo:latest

手順7:Lambda関数の作成

AWS CLIを使用してLambda関数を作成します:

aws lambda create-function \
  --function-name lambda-docker-function \
  --package-type Image \
  --code ImageUri=<アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-docker-demo:latest \
  --role arn:aws:iam::<アカウントID>:role/lambda-execution-role \
  --region ap-northeast-1

注意: lambda-execution-roleは事前に作成しておく必要があります。基本的なLambda実行ロールにはAWSLambdaBasicExecutionRoleポリシーがアタッチされている必要があります。

手順8:Lambda関数のテスト

AWS CLIまたはAWSコンソールからLambda関数をテストできます:

AWS CLIの場合:

aws lambda invoke \
  --function-name lambda-docker-function \
  --payload '{"path": "/test", "httpMethod": "GET", "headers": {"User-Agent": "aws-cli"}, "queryStringParameters": {"param1": "value1"}}' \
  --region ap-northeast-1 \
  response.json

cat response.json

Lambda × Dockerの実践的なユースケース

1. カスタムランタイムを使用したアプリケーション

Lambda標準環境では対応していない言語(RustやC++など)や、特定のバージョンが必要な場合にDockerイメージを活用できます。

Rustの例(Dockerfile):

FROM rust:1.53 as builder
WORKDIR /usr/src/app
COPY . .
RUN cargo build --release

FROM public.ecr.aws/lambda/provided:al2
COPY --from=builder /usr/src/app/target/release/lambda-rust-app /var/task/bootstrap
CMD [ "bootstrap" ]

2. 大きな依存関係を持つアプリケーション

機械学習モデルや大規模なライブラリを含むアプリケーションもDockerイメージで簡単にパッケージ化できます。

機械学習モデルの例(Dockerfile):

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

# 依存関係のインストール
COPY requirements.txt .
RUN pip install -r requirements.txt

# モデルファイルをコピー
COPY ./model /var/task/model
COPY app.py .

CMD [ "app.handler" ]

3. マイクロサービスとしてのAPI実装

API Gateway、Lambda、Dockerを組み合わせて、フルマネージドなマイクロサービスAPIを構築できます。

以下のAWS CLIコマンドでAPI Gatewayと連携できます:

# Lambda関数のリソースベースのポリシー追加
aws lambda add-permission \
  --function-name lambda-docker-function \
  --statement-id apigateway \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-arn "arn:aws:execute-api:<リージョン>:<アカウントID>:<API ID>/*/*/*"

# API Gatewayの作成コマンド(概要)
aws apigateway create-rest-api --name lambda-docker-api
# リソースやメソッドの設定
# ...
# デプロイ
aws apigateway create-deployment --rest-api-id <API ID> --stage-name prod

パフォーマンス最適化のヒント

Lambda×Dockerを使用する際のパフォーマンス最適化について、いくつかのヒントを紹介します:

1. イメージサイズの最適化

  • マルチステージビルドを活用する
  • 不要なファイルを含めない
  • Alpine LinuxベースのイメージやDistrolessイメージを検討

最適化したDockerfileの例:

FROM public.ecr.aws/lambda/python:3.9-slim as builder

WORKDIR /var/task
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt -t python/

FROM public.ecr.aws/lambda/python:3.9-slim
WORKDIR ${LAMBDA_TASK_ROOT}
COPY --from=builder /var/task/python ./
COPY app.py .
CMD [ "app.handler" ]

2. コールドスタート時間の短縮

  • 依存関係を最小限に保つ
  • 初期化コードを最適化する
  • Provisioned Concurrencyを活用する

3. ローカル開発環境の整備

AWS SAM(Serverless Application Model)CLIを使用して、ローカル環境でLambda関数をテストできます:

# template.yamlの例
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  DockerFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      ImageUri: lambda-docker-demo:latest
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /test
            Method: GET

# ローカル実行
sam local start-api

本番運用のベストプラクティス

1. CIパイプラインの構築

GitHub ActionsやAWS CodePipelineを使用して、継続的なビルドとデプロイを自動化できます:

GitHub Actionsの例(.github/workflows/deploy.yml):

name: Deploy Lambda Docker

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-1
    
    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    
    - name: Build and push image to ECR
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: lambda-docker-demo
      run: |
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }} .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
    
    - name: Update Lambda Function
      run: |
        aws lambda update-function-code \
          --function-name lambda-docker-function \
          --image-uri ${{ steps.login-ecr.outputs.registry }}/lambda-docker-demo:${{ github.sha }}

2. モニタリングとログ管理

CloudWatchとX-Ray統合で、Lambda関数のパフォーマンスと問題を追跡できます:

# X-Ray統合の例(app.pyに追加)
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all

patch_all()

def handler(event, context):
    # 関数の処理
    ...

依存関係にX-Rayを追加:

aws-xray-sdk==2.11.0

3. セキュリティ対策

  • ECRイメージスキャンを有効化
  • 最小権限の原則に従ったIAMロールの設定
  • 機密情報はAWS Systems Manager Parameter StoreやSecrets Managerで管理
# ECRイメージスキャンの有効化
aws ecr put-image-scanning-configuration \
  --repository-name lambda-docker-demo \
  --image-scanning-configuration scanOnPush=true \
  --region ap-northeast-1

Lambda×Dockerの制限事項と注意点

  1. イメージサイズ制限: 最大10GBまで(解凍後)
  2. 実行時間制限: 最大15分(通常のLambdaと同じ)
  3. メモリ設定: 最大10,240MB
  4. コールドスタート: コンテナイメージはZIP形式のデプロイよりも起動時間が長くなる傾向がある
  5. 料金: 実行時間とメモリ使用量に基づく(通常のLambdaと同じ課金モデル)

まとめ

AWS LambdaとDockerの組み合わせにより、サーバーレスコンピューティングの柔軟性と開発の容易さが大きく向上しました。本記事では、基本的なセットアップから本番運用のベストプラクティスまで、Lambda×Dockerを活用したサーバーレスアプリケーション構築について解説しました。

この技術を活用することで、特定の言語やライブラリに縛られることなく、幅広いアプリケーションをサーバーレスアーキテクチャで実現できます。また、既存のDockerベースの開発フローを活かして、スムーズにサーバーレス環境へ移行することも可能です。

Lambda×Dockerの組み合わせは、特に以下のようなケースで威力を発揮します:

  • カスタム依存関係や特殊なランタイムが必要なアプリケーション
  • 既存のコンテナ化されたアプリケーションのサーバーレス化
  • 複雑な設定や大規模なライブラリを含むアプリケーション

最後に、AWS Lambda×Dockerは比較的新しい技術であり、今後も進化していくことが期待されます。公式ドキュメントを定期的にチェックして、最新情報を入手することをお勧めします。

参考リソース

コメント

タイトルとURLをコピーしました