はじめに
この記事では、AWS LambdaをAWS CLIを使ってデプロイするコードを紹介します。紹介するコードを使うと次のことを実現することができます。
- Dockerイメージを使ったLambdaの構築
- Amazon API Gatewayと統合
- LambdaにProvisioned Concurrencyをセット
- Lambdaの継続的な更新
ディレクトリ構成
次のディレクトリ構成に従ってコードを記述していきます。
├── Dockerfile
├── Makefile
├── deploy
│ ├── config
│ │ ├── policy.json
│ │ └── role.json
│ ├── push-ecr.sh
│ ├── provision-api-gateway.sh
│ ├── provision-ecr.sh
│ ├── provision-lambda.sh
│ └── update-lambda.sh
└── src/ # source code
環境変数の設定
次の環境変数を設定します。
AWS_ACCOUNT_ID
AWS_REGION
IMAGE_NAME
LAMBDA_ALIAS
PROVISIONED_CONCURRENCY
# AWS account ID
$ export AWS_ACCOUNT_ID=123456789123
# AWS region
$ export AWS_REGION=ap-northeast-1
# ECR image and lambda name
$ export IMAGE_NAME=my-program
# Lambda alias
$ export LAMBDA_ALIAS=prod
# Number of Lambda Provisioned Concurrency
$ export PROVISIONED_CONCURRENCY=1
ECR関連コード
ECRのレポジトリを作成するシェルスクリプトdeploy/provision-ecr.sh
を作成します。
deploy/provision-ecr.sh
#!/bin/sh
# Create ECR repository
aws ecr create-repository --repository-name ${IMAGE_NAME} --region ${AWS_REGION}
そして、以下を実行するシェルスクリプトdeploy/push-ecr.sh
を作成します。
- ECRにログイン
Dockerfile
からDockerイメージをビルド- ビルドしたDockerイメージをECRにプッシュ
deploy/push-ecr
#!/bin/bash
# ECR repository URI
ECR_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:latest
# ECR login
aws ecr get-login-password | docker login --username AWS --password-stdin https://$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
# Build docker image
docker build -t $ECR_URI .
# Push docker image to ECR
docker push $ECR_URI
Lambda関連コード
Lambdaに権限を付与するためのIAM RoleとPolicyのJSONファイルを作成します。
deploy/config/policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
deploy/config/role.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
そして、以下を実行するシェルスクリプトprovision-lambda.sh
を作成します。
- Lambda用のIAM Policyを
deploy/config/policy.json
から作成 - Lambda用のIAM Roleを
deploy/config/role.json
から作成 - RoleにPolicyをアタッチ
- Lambdaを作成(ソースはECRのイメージ)
- Lambdaのバージョンとエイリアスを作成(Provisioned Concurrencyで使うため)
- LambdaにProvisioned Concurrencyをセット
deploy/provision-lambda.sh
#!/bin/bash
# Create IAM policy for lambda
aws iam create-policy --policy-name AWSLambdaBasicExecutionRole-${IMAGE_NAME} --policy-document file://deploy/config/policy.json
# Create IAM role for lambda
aws iam create-role --role-name ${IMAGE_NAME}-lambda-role --assume-role-policy-document file://deploy/config/role.json
# Sleep for waiting provision of policy and role creation
sleep 10
# Attach policy to role
aws iam attach-role-policy \
--role-name ${IMAGE_NAME}-lambda-role \
--policy-arn arn:aws:iam::${AWS_ACCOUNT_ID}:policy/AWSLambdaBasicExecutionRole-${IMAGE_NAME}
# Sleep for waiting attachment
sleep 10
# Create lambda
aws lambda create-function \
--function-name ${IMAGE_NAME} \
--package-type Image \
--code ImageUri=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_NAME}:latest \
--timeout 600 \
--memory-size 10240 \
--ephemeral-storage Size=2048 \
--role arn:aws:iam::${AWS_ACCOUNT_ID}:role/${IMAGE_NAME}-lambda-role
# Wait for Lambda function to be created
while true; do
LAMBDA_STATUS=$(aws lambda get-function --function-name ${IMAGE_NAME} --query "Configuration.State" --output text)
if [[ ${LAMBDA_STATUS} == "Active" ]]; then
break
elif [[ ${LAMBDA_STATUS} == "Failed" ]]; then
echo "Lambda creation failed" >&2
exit 1
else
echo "Waiting for Lambda function to be created..."
sleep 10
fi
done
echo "Lambda function created."
# Create lambda version and alias
LAMBDA_VERSION=$(aws lambda publish-version --function-name ${IMAGE_NAME} --region ${AWS_REGION} --query 'Version' --output text)
aws lambda create-alias \
--function-name ${IMAGE_NAME} \
--name ${LAMBDA_ALIAS} \
--function-version ${LAMBDA_VERSION} \
--region ${AWS_REGION}
# Add provisioned concurrency to lambda
aws lambda put-provisioned-concurrency-config \
--function-name ${IMAGE_NAME} \
--qualifier ${LAMBDA_ALIAS} \
--provisioned-concurrent-executions "$PROVISIONED_CONCURRENCY" \
--region ${AWS_REGION}
# Wait for provisioned concurrency to be configured
while true; do
CONCURRENCY_STATUS=$(aws lambda get-provisioned-concurrency-config \
--function-name ${IMAGE_NAME} \
--qualifier ${LAMBDA_ALIAS} \
--query "Status" --output text)
if [[ ${CONCURRENCY_STATUS} == "READY" ]]; then
break
elif [[ ${CONCURRENCY_STATUS} == "FAILED" ]]; then
echo "Provisioned concurrency addition failed" >&2
exit 1
else
echo "Waiting for provisioned concurrency to be configured..."
sleep 10
fi
done
echo "Provisioned concurrency added to the Lambda function."
Lambdaを継続的に更新するためのシェルスクリプトupdate-lambda.sh
を作成します。update-lambda.sh
では次の内容が実行されます。
- 指定したECRイメージからLambdaを更新
- Lambdaのエイリアスを更新(エイリアスを更新するとProvisioned Concurrencyも更新される)
deploy/update-lambda.sh
# !/bin/bash
# Update lambda
aws lambda update-function-code --function-name ${IMAGE_NAME} --image ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_NAME}:latest
while true; do
LAST_UPDATE_STATUS=$(aws lambda get-function --function-name ${IMAGE_NAME} --region ${AWS_REGION} --query 'Configuration.LastUpdateStatus' --output text)
if [ ${LAST_UPDATE_STATUS} != "InProgress" ]; then
break
fi
echo "Waiting for Lambda function update to complete..."
sleep 10
done
NEW_LAMBDA_VERSION=$(aws lambda publish-version --function-name $IMAGE_NAME --region $AWS_REGION --query 'Version' --output text)
# Update lambda alias
aws lambda update-alias \
--function-name $IMAGE_NAME \
--name $LAMBDA_ALIAS \
--function-version $NEW_LAMBDA_VERSION \
--region $AWS_REGION
# Wait for provisioned concurrency to be configured
while true; do
CONCURRENCY_STATUS=$(aws lambda get-provisioned-concurrency-config \
--function-name ${IMAGE_NAME} \
--qualifier ${LAMBDA_ALIAS} \
--query "Status" --output text)
if [[ ${CONCURRENCY_STATUS} == "READY" ]]; then
break
elif [[ ${CONCURRENCY_STATUS} == "FAILED" ]]; then
echo "Provisioned concurrency addition failed" >&2
exit 1
else
echo "Waiting for provisioned concurrency to be configured..."
sleep 10
fi
done
echo "Provisioned concurrency added to the Lambda function."
API Gateway関連コード
API Gatewayをセットアップするシェルスクリプトprovision-api-gateway.sh
を作成します。provision-api-gateway.sh
は以下を実行します。
- REST APIを作成
- POSTメソッドを作成
- Lambdaと統合
- Lambdaのパーミッションを設定
- API Gatewayをデプロイ
deploy/provision-api-gateway.sh
#!/bin/bash
LAMBDA_FUNCTION_ARN=arn:aws:lambda:${AWS_REGION}:${AWS_ACCOUNT_ID}:function:${IMAGE_NAME}
# Create REST API
REST_API=$(aws apigateway create-rest-api --name $IMAGE_NAME)
REST_API_ID=$(echo $REST_API | python -c "import sys, json; print(json.load(sys.stdin)['id'])")
# Get root resource ID
RESOURCE=$(aws apigateway get-resources --rest-api-id $REST_API_ID)
RESOURCE_ID=$(echo $RESOURCE | python -c "import sys, json; print(json.load(sys.stdin)['items'][0]['id'])")
# Create POST method for the root resource
aws apigateway put-method \
--rest-api-id $REST_API_ID \
--resource-id $RESOURCE_ID \
--http-method POST \
--no-api-key-required \
--authorization-type NONE
# Create integration for Lambda function
aws apigateway put-integration \
--rest-api-id $REST_API_ID \
--resource-id $RESOURCE_ID \
--http-method POST \
--type AWS \
--integration-http-method POST \
--uri "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${LAMBDA_FUNCTION_ARN}:${LAMBDA_ALIAS}/invocations"
aws apigateway put-method-response \
--rest-api-id $REST_API_ID \
--resource-id $RESOURCE_ID \
--http-method POST \
--status-code 200 \
--response-models '{"application/json": "Empty"}'
aws apigateway put-integration-response \
--rest-api-id $REST_API_ID \
--resource-id $RESOURCE_ID \
--http-method POST \
--status-code 200 \
--response-templates '{"application/json": ""}'
# Add Lambda permission
aws lambda add-permission \
--function-name $IMAGE_NAME:${LAMBDA_ALIAS} \
--statement-id apigateway-access \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:${AWS_REGION}:${AWS_ACCOUNT_ID}:${REST_API_ID}/*/POST/"
# Deploy API Gateway
aws apigateway create-deployment \
--rest-api-id $REST_API_ID \
--stage-name prod
# Output API Gateway endpoint
echo "https://$REST_API_ID.execute-api.$AWS_REGION.amazonaws.com/prod"
Makefile
次のようなMakefileを作成します。
Makefile
.PHONY: deploy-init deploy
deploy-init:
sh deploy/provision-ecr.sh
sh deploy/push-ecr.sh
sh deploy/provision-lambda.sh
sh deploy/provision-api-gateway.sh
deploy:
sh deploy/push-ecr.sh
sh deploy/update-lambda.sh
デプロイ
次のコマンドを実行すると、Lambda、API Gatewayが初回デプロイされます。
$ make deploy-init
make deploy-init
では次の処理が行われています。
- ECRレポジトリを作成
- Dockerをビルドし、ECRにプッシュ
- Lambdaを作成
- API Gatewayを作成
Dockerイメージの更新のタイミングで次のコマンドを実行するとLambdaが更新されます。
$ make deploy
make deploy
では次の処理が行われています。
- Dockerをビルドし、ECRのプッシュ
- Lambdaを更新
AlloyDB
Amazon Cognito
Amazon EC2
Amazon ECS
Amazon QuickSight
Amazon RDS
Amazon Redshift
Amazon S3
API
Autonomous Vehicle
AWS
AWS API Gateway
AWS Chalice
AWS Control Tower
AWS IAM
AWS Lambda
AWS VPC
BERT
BigQuery
Causal Inference
ChatGPT
Chrome Extension
CircleCI
Classification
Cloud Functions
Cloud IAM
Cloud Run
Cloud Storage
Clustering
CSS
Data Engineering
Data Modeling
Database
dbt
Decision Tree
Deep Learning
Descriptive Statistics
Differential Equation
Dimensionality Reduction
Discrete Choice Model
Docker
Economics
FastAPI
Firebase
GIS
git
GitHub
GitHub Actions
Google
Google Cloud
Google Search Console
Hugging Face
Hypothesis Testing
Inferential Statistics
Interval Estimation
JavaScript
Jinja
Kedro
Kubernetes
LightGBM
Linux
LLM
Mac
Machine Learning
Macroeconomics
Marketing
Mathematical Model
Meltano
MLflow
MLOps
MySQL
NextJS
NLP
Nodejs
NoSQL
ONNX
OpenAI
Optimization Problem
Optuna
Pandas
Pinecone
PostGIS
PostgreSQL
Probability Distribution
Product
Project
Psychology
Python
PyTorch
QGIS
R
ReactJS
Regression
Rideshare
SEO
Singer
sklearn
Slack
Snowflake
Software Development
SQL
Statistical Model
Statistics
Streamlit
Tabular
Tailwind CSS
TensorFlow
Terraform
Transportation
TypeScript
Urban Planning
Vector Database
Vertex AI
VSCode
XGBoost