はじめに
この記事では、ローカル環境(Windows&VSCode&Docker開発環境&Python)で開発したImageをそのままAWS Lambdaで動かせるようにする方法を載せています。
例として、S3にファイルをアップロードすることを目指します。
Windows環境で確認しています。
未確認ですが、前提条件以外の部分は多分Macでも同じかと思ってます。
背景
- ローカル環境のWindows環境&Pythonで動いても、それをAWS Lamdbaにのせるのが大変。それに無事にAWS Lamdbaにのせることが出来ても、改修しにくい。
⇒ ローカルで出来たことをLambdaにそのまま反映したい!! - プログラムによってはzipしてAWS Lambda Layer作らないといけないのが大変
⇒ Layerとか作りたくない!!
そんなわけで、希望が叶う手順を作りました。
環境を作るまではちょっと大変です。
目次
環境
-
ローカル
OS Windows11 Docker Dockerデスクトップ 開発ツール Visual Studio Code 言語 Python3.8 -
クラウド:AWS
- Amazon Simple Storage Service (S3)
- AWS Lambda
- Amazon Elastic Container Registry(ECR)
前提条件
- Dockerがインストール済みであること(参考サイト①)
- VSCodeがインストール済みであること(参考サイト②)
- Gitがインストール済みであること(参考サイト②)
- VSCodeでdev containaerを設定済みであること(参考サイト③)
- AWS IAMユーザにS3とLambdaとECRへの権限が付与されていること(参考サイト④)
- AWS CLI認証情報を設定済みであること(参考サイト⑤)
参考サイト
私は下記のサイトを参考に環境構築しました。
分かりやすくて大変有難かったです!!
NO | 環境構築 | 参考サイト | URL |
---|---|---|---|
① | Windows&Dockerデスクトップ | 【Docker Desktop】Windowsにインストール(WSL2) | こちら |
② | VSCode&Dev Containers | 【VS Code】Dockerコンテナの環境でリモート開発【Win/Mac】 | こちら |
③ | VSCode&Dev Containers | VS CodeでDocker開発コンテナを便利に使おう | こちら |
④ | AWS IAM アクセス許可 | IAM ID のアクセス許可の追加および削除 | こちら |
⑤ | AWS CLI | AWS CLI の最新バージョンを使用してインストールまたは更新を行う | こちら |
構成図
VSCode拡張機能
フォルダ構成
プロジェクトルート(D:\src\docker_lambda_s3)
├── .devcontainer
│ ├── devcontainer.json
│ └── settings.json
├── app
│ ├──__init__.py
│ ├── lambda_function.py
│ ├── log_manager.py
│ └── s3_uploader.py
├── .dockerignore
├── .env(.env_sampleをリネーム)
├── .gitignore
├── awscli.sh
├── docker-compose.yml
├── Dockerfile
├── README.md
└── requirements.txt
GitHub
手順
1.GitHubよりcloneする
-
cloneする
Git Bushgit clone git@github.com:sasayakado/docker_lambda_s3.git
-
.env_sampleを.envにreneame
-
.envに自分の環境変数を設定
.envAWS_ACCESS_KEY=★1 AWS_SECRET_ACCESS_KEY=★2 AWS_ACCOUNT_ID=★3 AWS_DEFAULT_REGION=★4 LOG_FILE=★5 BUCKET_NAME=★6 IMAGE_TAG=★7 DOCKER_IMAGE_NAME=★8 DOCKER_CONTAINER_NAME=★9 LAMBDA_FUNC_NAME=★10
↓ こんな風になる
AWS_ACCESS_KEY=★自分のAWS_ACCESS_KEY
AWS_SECRET_ACCESS_KEY=★自分のAWS_SECRET_ACCESS_KEY
AWS_ACCOUNT_ID=★自分のAWS_ACCOUNT_ID
AWS_DEFAULT_REGION=ap-northeast-1
LOG_FILE=.log
BUCKET_NAME=docker-lambda-s3
IMAGE_TAG=latest
DOCKER_IMAGE_NAME=docker_lambda_s3
DOCKER_CONTAINER_NAME=lambda-container-s3
LAMBDA_FUNC_NAME=lambda-s3
2.AWSでS3&ECRを作成する
・S3作成
1.バケット作成
・ECR作成
1.リポジトリを作成
.envの★8で設定した名前:docker_lambda_s3
3.ローカル環境でS3にファイルをアップロードする
1.DevContainerをひらく
2.実行する
3.S3を確認する
- バケット「docker-lambda-s3」の中に、実行した日付「9月4日」のフォルダが作成されており、その中に「年月日_時間分秒.log」が作成されている
- 「年月日_時間分秒.log」の中身を見てみる。
中身が、ターミナルに出ていた「年月日_時間分秒 テスト ファイル だよ」となっている。
4.Docker ImageをECRにpushする
3.awscli.shの「ECRにプッシュしたイメージをlambdaに更新」から下をコメントアウト(範囲選択してCTL + /)
該当箇所はlambdaにECRイメージを更新する部分で、lambdaはまだ未作成でエラーになるため、ここではコメントアウトする。
# ECRにプッシュしたイメージをlambdaに更新
# aws lambda update-function-code --function-name $LAMBDA_FUNC_NAME --image-uri $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$DOCKER_IMAGE_NAME:$IMAGE_TAG > /dev/null 2> /tmp/aws_lambda_error.log
# if [ $? -eq 0 ]; then
# echo "lambda update success"
# else
# # エラーメッセージを表示する
# cat /tmp/aws_lambda_error.log
# return 1
# fi
# # 一時ファイルを削除
# rm -f /tmp/aws_lambda_error.log
4.awscli.shをGit bashで実行する
source awscli.sh
5.Lambda作成&実行
1.関数作成
- 関数の作成
- コンテナイメージを選択。関数名はLAMBDA_FUNC_NAME=★10に設定したものと同じにするので、「lambda-s3」
- ECRのリポジトリを選択する。イメージは、latestを選ぶ。
- ロールは後で変更。とりあえず「基本的な~新しいロールを作成」を選んで、関数の作成
- Lambdaが選んだECRのイメージで作成されていることを確認する
2.設定
- アクセス権限
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::docker-lambda-s3/*"
}
]
}
2.環境変数
2.関数実行
3.S3を確認
- バケット「docker-lambda-s3」の中に、実行した日付「9月5日」のフォルダが作成されており、その中に「年月日_時間分秒.log」が作成されている
- 「年月日_時間分秒.log」の中身を見てみる。
中身が、「年月日_時間分秒 テスト ファイル だよ」となっている。
6.修正を反映してみる
1.ローカル
- 開発コンテナでlambda_function.pyを修正
例:「追加」を後ろにつける
log_content = f"{today_time_str} テスト ファイル だよ 追加"
-
S3確認
2.開発コンテナ閉じる
3.プロジェクトルートフォルダを開く
4.AWS Cli
.awscli.shの「ECRにプッシュしたイメージをlambdaに更新」から下のコメントアウトを外す(範囲選択してCTL + /)
# ECRにプッシュしたイメージをlambdaに更新
aws lambda update-function-code --function-name $LAMBDA_FUNC_NAME --image-uri $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$DOCKER_IMAGE_NAME:$IMAGE_TAG > /dev/null 2> /tmp/aws_lambda_error.log
if [ $? -eq 0 ]; then
echo "lambda update success"
else
# エラーメッセージを表示する
cat /tmp/aws_lambda_error.log
return 1
fi
# 一時ファイルを削除
rm -f /tmp/aws_lambda_error.log
4.awscli.shをGit bashで実行する
source awscli.sh
3.AWS
- lambdaを実行する
ブラウザを更新して、下記のように更新中の場合はまだ反映できていないので実行しない。
下記のように、更新されていると表示されていたり、何も表示されていない場合は実行してOK
- テスト実行
成功していることを確認する
- S3確認
- バケット「docker-lambda-s3」の中に、実行した日付「9月6日」のフォルダが作成されており、その中に「年月日_時間分秒.log」が作成されている
- 「年月日_時間分秒.log」の中身を見てみる。
中身が、「年月日_時間分秒 テスト ファイル だよ 追加」となっている。
おしまい。
プログラム解説
Dockerfile
Dockerコンテナイメージの構築手順を定義するファイル。
ファイル名はこれじゃないとダメ。
- public.ecr.aws/lambda/python:3.8というAWS Lambdaと互換性のあるPython 3.8のベースイメージを利用。
FROM public.ecr.aws/lambda/python:3.8
- 作業ディレクトリを/var/taskに設定。これはLambda関数のデフォルトの作業ディレクトリと一致。
# ワーキングディレクトリを設定
WORKDIR /var/task
- Pythonのライブラリを追加する場所として/var/task/appを利用するため、PYTHONPATHの環境変数を設定。
# PYTHONPATH の設定
ENV PYTHONPATH="/var/task/app"
- 依存関係のファイルrequirements.txtをコピー。
yumコマンドでシステムを更新し、tarやgzipといった必要なパッケージをインストール。pipを最新版に更新し、requirements.txtにリストされたPythonのライブラリをインストール。
# 依存関係のコピー
COPY requirements.txt .
# yumを更新し、必要なパッケージをインストール
RUN yum update -y && \
yum install -y tar gzip
# 依存関係のインストール
RUN pip install --upgrade pip && \
pip install -r requirements.txt
- appディレクトリの内容をコピー。
# appディレクトリを/var/task/app/へコピー
COPY app/ /var/task/app/
- AWS Lambdaが呼び出す関数としてlambda_function.lambda_handlerを指定。
CMD ["lambda_function.lambda_handler"]
docker-compose.yml
Dockerコンテナの設定や連携を定義するファイル。
ファイルの名前を変更するとコマンド実行時にファイル名を指定する必要が出てくるので、基本的にこのファイル名。
- docker-composeのバージョンを指定。
version: "3"
- services: devという名前のサービスを定義
- build: Dockerfileを基にコンテナイメージを構築。
- image & container_name: 環境変数によって指定されるイメージ名とコンテナ名を使用。
- working_dir: コンテナ内の作業ディレクトリを指定。
- volumes: ホストとコンテナ間でのディレクトリの共有を指定。これにより、ローカルの変更が即座にコンテナに反映。
- ports: ホストとコンテナのポートをマッピング。例として、ホストの9000番ポートとコンテナの8080番ポートを結びつけ。
environment & env_file: AWSの認証情報などの環境変数を設定。
services:
dev:
build:
context: .
dockerfile: Dockerfile
image: $DOCKER_IMAGE_NAME
container_name: $DOCKER_CONTAINER_NAME
working_dir: /var/task
volumes:
- ./:/var/task
ports:
- "9000:8080"
environment:
AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY
AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
env_file:
- ./.env
devcontainer.json
VSCodeのRemote - Containers拡張のための設定ファイル。
ファイル名はこれじゃないとダメ。
- name:コンテナの名前を指定。
- dockerComposeFile:使用するdocker-composeのファイルパスを指定。
- service:docker-compose.yml内で使用するサービスを指定。
- workspaceFolder:開発コンテナでの作業ディレクトリをコンテナ内の/var/taskに指定。
- settings:開発コンテナでの使用するターミナルのシェルをbashに指定。
- customizations:開発コンテナでの使用するVSCode拡張機能のリストを指定。
- remoteUser:コンテナ内で操作する際のユーザーをrootとして指定。
- shutdownAction:コンテナのシャットダウンアクションとして、docker-composeの停止を指定。
{
"name": "Lambda Dev Container",
"dockerComposeFile": "../docker-compose.yml",
"service": "dev",
"workspaceFolder": "/var/task",
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"customizations": {
"vscode": {
"extensions": [
"amazonwebservices.aws-toolkit-vscode",
"ms-python.python",
"njqdev.vscode-python-docstring",
"magicstack.MagicPython",
"ms-python.vscode-pylance",
"kevinrose.vsc-python-indent",
"esbenp.prettier-vscode",
"microsoft.vscode-docker",
"njpwerner.autodocstring"
]
}
},
"remoteUser": "root",
"shutdownAction": "stopCompose"
}
.dockerignore
特定のファイルやディレクトリをDockerビルドから除外するための設定ファイル。
ファイル名はこれじゃないとダメ。
コンテナでは色々なファイルが見れると助かるけど、Imageでは軽量化したかったり、セキュリティ的に含めたくないものをImageにしてECRにpushしないように、不要なものを設定。
.gitignore
特定のファイルやディレクトリをGitリポジトリの追跡から除外するための設定ファイル。
ファイル名はこれじゃないとダメ。
.env
環境変数の設定を行うファイル。AWSの認証情報などの重要な情報を含むので、取扱注意!
ファイル名はこれじゃないとダメ。
awscli.sh
AWS CLIのコマンドをまとめて実行するためのスクリプト。
requirements.txt
Pythonプロジェクトで使用する外部ライブラリやそのバージョンをリスト化するためのファイル。
ファイル名はこれじゃないとダメ。
このファイルを使用することで、プロジェクトに必要な依存関係を一元管理し、他の環境やサーバーでの再現性を高める。
具体的には、pip install -r requirements.txtコマンドで一括で依存関係をインストール可能。
appフォルダの中身
-
__init__.py
このファイルは、appディレクトリをPythonのパッケージとして扱うための特殊なファイル。
ファイル名はこれじゃないとダメ。
具体的な実行内容は無いが、このファイルが存在することで、他のPythonファイルからapp内のモジュールや関数をインポートできるようになる。 -
lambda_function.py
AWS Lambdaで動作開始時に呼び出されるスクリプト。
ファイル名は今回の構成ではこれじゃないとダメ。
ログを生成、一時ファイルへ書き込んでS3にログファイルをアップロードする処理をコーディング。 -
log_manager.py
ログに関する処理を定義しているファイル。 -
s3_uploader.py
Amazon S3にファイルをアップロードするための処理を定義しているファイル。
指定の一時ログファイルを読み込み、S3の指定のキーにアップロード。
アップロード後、一時ログファイルは削除。
料金
なんでEC2じゃなくてLambdaで作りたいのかと言うと、驚くほど安い!!からなわけです。
正確なお値段は各自の利用状況で調べて頂きたいのですが、私の「lambda&S3&ECR」は、月$1にも満たない感じです。
(20回以上ECRにpushして、lambdaも20回以上テストしているぐらいの利用状況。)
まとめ
環境構築するまでが大変ですが、環境を作り終わった後は楽に改修&Lambdaに反映出来るようになるのが良いと思います。次回はこの仕組みを使って開発して、Lambdaを使ってWebScripingするという内容をまとめたいと思います。