0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python DashアプリをAWS Lambdaにデプロイ

Last updated at Posted at 2025-03-22

Python DashアプリをAWS Lambdaにデプロイする完全ガイド

このガイドでは、ローカル環境で動作しているPython Dashアプリケーションを、AWS Lambdaを使用してサーバーレスアプリケーションとしてデプロイする方法を詳しく説明します。このガイドに従うことで、多くのユーザーがアプリケーションを利用できるようになります。

目次

  1. はじめに
  2. デプロイオプションの比較
  3. Zappaを使用したデプロイ方法
  4. AWS SAMを使用したデプロイ方法
  5. AWS CLIを使用したデプロイ方法
  6. API Gatewayの設定
  7. トラブルシューティング
  8. ベストプラクティス
  9. まとめ

はじめに

Python Dashは、データ分析や可視化のためのウェブアプリケーションを簡単に作成できるフレームワークです。通常、Dashアプリケーションは常時稼働するサーバー上で実行されますが、AWS Lambdaを使用することで、サーバーレスアーキテクチャとしてデプロイすることができます。

サーバーレスアーキテクチャの主な利点は以下の通りです:

  • インフラストラクチャの管理が不要
  • 使用した分だけ課金される(アイドル時のコストが発生しない)
  • 自動的にスケールする
  • 高可用性が確保される

ただし、Dashアプリケーションをサーバーレス環境で実行するには、いくつかの調整が必要です。このガイドでは、その方法を詳しく説明します。

デプロイオプションの比較

Python DashアプリをAWS Lambdaにデプロイする方法として、主に以下の3つのアプローチがあります:

  1. Zappa: PythonのWSGIアプリケーションをLambdaにデプロイするためのツール
  2. AWS SAM (Serverless Application Model): AWSのサーバーレスアプリケーション開発フレームワーク
  3. AWS CLI: AWSのコマンドラインインターフェースを直接使用する方法

各アプローチの比較は以下の通りです:

機能 Zappa AWS SAM AWS CLI
セットアップの容易さ ★★★★★ ★★★☆☆ ★★☆☆☆
カスタマイズ性 ★★★☆☆ ★★★★★ ★★★★★
AWS統合 自動 明示的 手動
デプロイ速度 速い やや遅い 遅い
ローカルテスト 限定的 充実 非常に限定的
学習曲線 緩やか やや急
ドキュメント 中程度 充実 充実

選択基準:

  • Zappa: 迅速にデプロイしたい場合や、Pythonのみで完結させたい場合
  • AWS SAM: AWS環境との深い統合が必要な場合や、複雑なインフラストラクチャが必要な場合
  • AWS CLI: 完全なコントロールが必要な場合や、既存のCI/CDパイプラインに統合する場合

Zappaを使用したデプロイ方法

前提条件

  • Python 3.6以上
  • AWS CLIがインストールされ、設定済み
  • AWSアクセスキーとシークレットキーが設定済み

手順

1. 仮想環境の構築

# 仮想環境の作成
python3 -m venv venv

# 仮想環境のアクティベート
source venv/bin/activate

2. 必要なパッケージのインストール

# DashとZappaのインストール
pip install dash plotly pandas zappa

3. Dashアプリケーションの作成

以下のような内容でapp.pyを作成します:

import plotly.express as px
import dash
from dash import html, dcc
from dash.dependencies import Input, Output

# サンプルデータを読み込む
df = px.data.gapminder()

# アプリケーションを作成する
app = dash.Dash(__name__)

# グラフを作成する関数
def create_graph(data, x_col, y_col):
    fig = px.scatter(data, x=x_col, y=y_col, color='continent',
                     log_x=True, size_max=60, title=f"{y_col} vs {x_col}")
    return fig

# レイアウトを定義する
app.layout = html.Div([
    html.H1("Dash App on AWS Lambda"),
    html.Div([
        dcc.Graph(id='graph-1')
    ]),
    html.Div([
        dcc.Dropdown(
            id='x-column',
            options=[{'label': i, 'value': i} for i in df.columns],
            value='gdpPercap'
        )
    ], style={'width': '25%', 'display': 'inline-block'}),
    html.Div([
        dcc.Dropdown(
            id='y-column',
            options=[{'label': i, 'value': i} for i in df.columns],
            value='lifeExp'
        )
    ], style={'width': '25%', 'display': 'inline-block'}),
])

# コールバックを定義する
@app.callback(
    Output('graph-1', 'figure'),
    [Input('x-column', 'value'),
     Input('y-column', 'value')])
def update_graph(x_col, y_col):
    fig = create_graph(df, x_col, y_col)
    return fig

# Zappaで使用するWSGIアプリケーションインスタンス
dapp = app.server.wsgi_app

if __name__ == '__main__':
    app.run_server(debug=True, host="0.0.0.0")

4. Zappaの設定ファイルの作成

プロジェクトのルートディレクトリにzappa_settings.jsonを作成します:

{
    "dev": {
        "app_function": "app.dapp",
        "project_name": "dash-lambda",
        "aws_region": "ap-northeast-1",
        "runtime": "python3.9",
        "s3_bucket": "your-deployment-bucket-name",
        "environment_variables": { 
            "DASH_REQUESTS_PATHNAME_PREFIX": "/dev/" 
        },
        "memory_size": 512,
        "timeout_seconds": 30,
        "keep_warm": false
    }
}

5. デプロイ

# デプロイの実行
zappa deploy dev

デプロイが完了すると、以下のようなURLが表示されます:

https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev

6. 更新

アプリケーションを更新する場合は、以下のコマンドを実行します:

zappa update dev

7. 削除

アプリケーションを削除する場合は、以下のコマンドを実行します:

zappa undeploy dev

AWS SAMを使用したデプロイ方法

前提条件

  • AWS SAM CLIがインストールされていること
  • AWS CLIがインストールされ、設定済みであること
  • Docker(ローカルテスト用、オプション)

手順

1. プロジェクト構造の作成

mkdir -p dash-lambda-sam/frontend/assets
cd dash-lambda-sam

2. SAMテンプレートの作成

プロジェクトのルートディレクトリにtemplate.ymlを作成します:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Api:
    Function:
      Timeout: 30
      MemorySize: 1024
    BinaryMediaTypes:
      - "*/*"

Resources:
  FrontendFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: frontend/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        RootIntegration:
          Type: Api
          Properties:
            Path: "/"
            Method: ANY
        ProxyIntegration:
          Type: Api
          Properties:
            Path: "/{proxy+}"
            Method: ANY

Outputs:
  DashApi:
    Description: "API Gateway endpoint URL for Dash application"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
  DashFunction:
    Description: "Lambda Function ARN"
    Value: !GetAtt FrontendFunction.Arn

3. Dashアプリケーションの作成

frontend/dash_app.pyを作成します:

from dash import Dash, html, dcc, Input, Output, callback
import plotly.express as px

def build_app(dash_kwargs: dict = None) -> Dash:
    dash_kwargs = dash_kwargs or {}
    
    app = Dash(
        name=__name__,
        **dash_kwargs,
    )
    
    # サンプルデータを読み込む
    df = px.data.gapminder()
    
    app.layout = html.Div(
        children=[
            html.H1(children="Dash App on AWS Lambda"),
            html.P("This is a dash app running on a serverless backend."),
            html.Div([
                dcc.Graph(id='graph-1')
            ]),
            html.Div([
                dcc.Dropdown(
                    id='x-column',
                    options=[{'label': i, 'value': i} for i in df.columns],
                    value='gdpPercap'
                )
            ], style={'width': '25%', 'display': 'inline-block'}),
            html.Div([
                dcc.Dropdown(
                    id='y-column',
                    options=[{'label': i, 'value': i} for i in df.columns],
                    value='lifeExp'
                )
            ], style={'width': '25%', 'display': 'inline-block'}),
        ]
    )
    
    @app.callback(
        Output('graph-1', 'figure'),
        [Input('x-column', 'value'),
         Input('y-column', 'value')])
    def update_graph(x_col, y_col):
        fig = px.scatter(df, x=x_col, y=y_col, color='continent',
                         log_x=True, size_max=60, title=f"{y_col} vs {x_col}")
        return fig
    
    return app

4. Lambda関数ハンドラーの作成

frontend/app.pyを作成します:

import json
from apig_wsgi import make_lambda_handler
from dash_app import build_app

# Dashアプリケーションの構築
app = build_app()
wsgi_app = app.server

# Lambda関数ハンドラー
lambda_handler = make_lambda_handler(wsgi_app, binary_support=True)

5. 依存関係ファイルの作成

frontend/requirements.txtを作成します:

dash==2.14.0
plotly==5.18.0
pandas==2.1.1
apig-wsgi==2.18.0

6. ビルドとデプロイ

# SAMでビルド
sam build

# デプロイ
sam deploy --guided

初回デプロイ時は--guidedオプションを使用して対話的に設定を行います。以降のデプロイでは単にsam deployを実行します。

AWS CLIを使用したデプロイ方法

前提条件

  • AWS CLIがインストールされ、設定済みであること
  • Python 3.6以上がインストールされていること
  • 必要なIAM権限が設定されていること

手順

1. IAMロールの作成

# IAMロール用のポリシードキュメントを作成
cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

# IAMロールを作成
aws iam create-role \
  --role-name DashLambdaRole \
  --assume-role-policy-document file://trust-policy.json

# 基本的なLambda実行ポリシーをアタッチ
aws iam attach-role-policy \
  --role-name DashLambdaRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

# CloudWatchログへのアクセス権限を追加(オプション)
aws iam attach-role-policy \
  --role-name DashLambdaRole \
  --policy-arn arn:aws:iam::aws:policy/CloudWatchLogsFullAccess

2. デプロイパッケージの作成

# プロジェクトディレクトリの作成
mkdir -p dash-lambda-project
cd dash-lambda-project

# 仮想環境の作成とアクティベート
python3 -m venv venv
source venv/bin/activate

# 必要なパッケージのインストール
pip install dash plotly pandas mangum

# app.pyファイルの作成(内容は前述のコードを参照)

# 依存関係ファイルの作成
pip freeze > requirements.txt

# パッケージディレクトリの作成
mkdir -p package

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

# アプリケーションコードのコピー
cp app.py package/

# Zipファイルの作成
cd package
zip -r ../dash-lambda-deployment.zip .
cd ..

3. S3バケットの作成とパッケージのアップロード

# S3バケットの作成
aws s3 mb s3://your-dash-lambda-bucket

# デプロイパッケージのアップロード
aws s3 cp dash-lambda-deployment.zip s3://your-dash-lambda-bucket/

4. Lambda関数の作成

# Lambda関数の作成
aws lambda create-function \
  --function-name dash-app \
  --runtime python3.9 \
  --role arn:aws:iam::<YOUR_ACCOUNT_ID>:role/DashLambdaRole \
  --handler app.handler \
  --code S3Bucket=your-dash-lambda-bucket,S3Key=dash-lambda-deployment.zip \
  --timeout 30 \
  --memory-size 512

# 環境変数の設定(オプション)
aws lambda update-function-configuration \
  --function-name dash-app \
  --environment "Variables={DASH_REQUESTS_PATHNAME_PREFIX=/prod/}"

5. Lambda関数の更新(既存関数を更新する場合)

# コードの更新
aws lambda update-function-code \
  --function-name dash-app \
  --s3-bucket your-dash-lambda-bucket \
  --s3-key dash-lambda-deployment.zip

# 設定の更新
aws lambda update-function-configuration \
  --function-name dash-app \
  --timeout 30 \
  --memory-size 512

API Gatewayの設定

Lambda関数をデプロイした後、API Gatewayを設定してウェブからアクセスできるようにします。

1. REST APIの作成

# REST APIの作成
aws apigateway create-rest-api \
  --name "Dash-App-API" \
  --description "API for Dash application" \
  --endpoint-configuration "{ \"types\": [\"REGIONAL\"] }"

# 作成したAPIのIDを取得
API_ID=$(aws apigateway get-rest-apis \
  --query "items[?name=='Dash-App-API'].id" \
  --output text)

# ルートリソースのIDを取得
ROOT_RESOURCE_ID=$(aws apigateway get-resources \
  --rest-api-id $API_ID \
  --query "items[?path=='/'].id" \
  --output text)

2. プロキシリソースの作成

# プロキシリソースの作成(すべてのパスをキャプチャ)
aws apigateway create-resource \
  --rest-api-id $API_ID \
  --parent-id $ROOT_RESOURCE_ID \
  --path-part "{proxy+}"

# プロキシリソースのIDを取得
PROXY_RESOURCE_ID=$(aws apigateway get-resources \
  --rest-api-id $API_ID \
  --query "items[?path=='/{proxy+}'].id" \
  --output text)

3. Lambda関数との統合

# Lambda関数のARNを設定
LAMBDA_ARN="arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:dash-app"

# ルートパスにANYメソッドを追加
aws apigateway put-method \
  --rest-api-id $API_ID \
  --resource-id $ROOT_RESOURCE_ID \
  --http-method ANY \
  --authorization-type NONE

# Lambda関数との統合を設定
aws apigateway put-integration \
  --rest-api-id $API_ID \
  --resource-id $ROOT_RESOURCE_ID \
  --http-method ANY \
  --type AWS_PROXY \
  --integration-http-method POST \
  --uri "arn:aws:apigateway:<REGION>:lambda:path/2015-03-31/functions/${LAMBDA_ARN}/invocations"

# プロキシパスにANYメソッドを追加
aws apigateway put-method \
  --rest-api-id $API_ID \
  --resource-id $PROXY_RESOURCE_ID \
  --http-method ANY \
  --authorization-type NONE

# Lambda関数との統合を設定
aws apigateway put-integration \
  --rest-api-id $API_ID \
  --resource-id $PROXY_RESOURCE_ID \
  --http-method ANY \
  --type AWS_PROXY \
  --integration-http-method POST \
  --uri "arn:aws:apigateway:<REGION>:lambda:path/2015-03-31/functions/${LAMBDA_ARN}/invocations"

4. Lambda関数に権限を付与

# ルートパスからの呼び出し権限を付与
aws lambda add-permission \
  --function-name dash-app \
  --statement-id api-gateway-root \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-arn "arn:aws:execute-api:<REGION>:<ACCOUNT_ID>:${API_ID}/*/ANY/"

# プロキシパスからの呼び出し権限を付与
aws lambda add-permission \
  --function-name dash-app \
  --statement-id api-gateway-proxy \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-arn "arn:aws:execute-api:<REGION>:<ACCOUNT_ID>:${API_ID}/*/ANY/{proxy+}"

5. バイナリメディアタイプの設定

# バイナリメディアタイプの設定
aws apigateway update-rest-api \
  --rest-api-id $API_ID \
  --patch-operations '[
    {
      "op": "replace",
      "path": "/binaryMediaTypes/*~1*",
      "value": "*/*"
    }
  ]'

6. APIのデプロイ

# ステージの作成とAPIのデプロイ
aws apigateway create-deployment \
  --rest-api-id $API_ID \
  --stage-name prod

# デプロイされたAPIのURLを取得
API_URL="https://${API_ID}.execute-api.<REGION>.amazonaws.com/prod"

7. Lambda関数の環境変数の更新

# 環境変数の更新
aws lambda update-function-configuration \
  --function-name dash-app \
  --environment "Variables={DASH_REQUESTS_PATHNAME_PREFIX=/prod/}"

トラブルシューティング

一般的な問題と解決策

  1. コールドスタートの遅延

    • 問題: 初回アクセス時にアプリケーションの起動が遅い
    • 解決策: Lambda関数のメモリサイズを増やす(512MB以上推奨)、定期的なウォームアップ戦略を実装する
  2. タイムアウトエラー

    • 問題: Lambda関数がタイムアウトする
    • 解決策: タイムアウト設定を増やす(最大900秒)、アプリケーションを最適化する
  3. バイナリコンテンツの問題

    • 問題: 画像やCSSなどのバイナリコンテンツが正しく表示されない
    • 解決策: API Gatewayのバイナリメディアタイプを設定する、binary_support=Trueを設定する
  4. パスの問題

    • 問題: アセットファイルのパスが正しく解決されない
    • 解決策: DASH_REQUESTS_PATHNAME_PREFIX環境変数をAPI Gatewayのステージ名に合わせて設定する
  5. デプロイパッケージのサイズ制限

    • 問題: デプロイパッケージが大きすぎる
    • 解決策: 不要な依存関係を削除する、Lambda Layersを使用する、パッケージを最適化する

ログの確認方法

CloudWatchログを確認して、エラーの詳細を把握することができます:

# CloudWatchログの取得
aws logs get-log-events \
  --log-group-name /aws/lambda/dash-app \
  --log-stream-name $(aws logs describe-log-streams \
    --log-group-name /aws/lambda/dash-app \
    --order-by LastEventTime \
    --descending \
    --limit 1 \
    --query "logStreams[0].logStreamName" \
    --output text)

ベストプラクティス

パフォーマンスの最適化

  1. メモリサイズの最適化

    • Dashアプリケーションは比較的メモリを消費するため、512MB以上を推奨
    • パフォーマンスとコストのバランスを考慮して設定
  2. コールドスタート対策

    • 定期的なウォームアップ関数を実装
    • プロビジョンドコンカレンシーの使用を検討
  3. 依存関係の最適化

    • 必要最小限の依存関係のみをインストール
    • 大きなデータセットは外部ストレージ(S3など)に保存

セキュリティの考慮事項

  1. IAM権限の最小化

    • 必要最小限の権限のみを付与
    • リソースベースのポリシーを使用
  2. API Gatewayの保護

    • APIキーの使用
    • WAF(Web Application Firewall)の設定
    • レート制限の設定
  3. 環境変数の暗号化

    • 機密情報はAWS Secrets Managerを使用
    • 環境変数の暗号化を有効化

コスト最適化

  1. アイドル時間の削減

    • サーバーレスアーキテクチャの利点を活かす
    • 不要なウォームアップを避ける
  2. リソースの適切なサイジング

    • 必要最小限のメモリサイズを設定
    • タイムアウト値を適切に設定
  3. モニタリングとアラート

    • 異常な使用パターンを検出するためのアラートを設定
    • コスト予算を設定

まとめ

このガイドでは、Python DashアプリケーションをAWS Lambdaにデプロイする方法について詳しく説明しました。Zappa、AWS SAM、AWS CLIという3つの異なるアプローチを紹介し、それぞれの手順を詳細に解説しました。

サーバーレスアーキテクチャを採用することで、インフラストラクチャの管理が不要になり、使用した分だけ課金されるため、コスト効率の良いデプロイが可能になります。また、自動的にスケールするため、ユーザー数の増加にも柔軟に対応できます。

ただし、コールドスタートの問題やメモリ制限などの課題もあるため、アプリケーションの特性に合わせて適切な設定を行うことが重要です。

created by Manus

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?