3
4

More than 1 year has passed since last update.

Windows&Docker開発環境で作ったImageをそのままAWS Lambdaにのせる方法

Last updated at Posted at 2023-09-06

はじめに

この記事では、ローカル環境(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 の最新バージョンを使用してインストールまたは更新を行う こちら

構成図

  • オレンジ色の線はshellで実行
  • 青色の線は手動実行
    構成図.jpg

VSCode拡張機能

下記を入れておく
VSCode拡張.png

フォルダ構成

プロジェクトルート(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する

  1. cloneする

    Git Bush
    git clone git@github.com:sasayakado/docker_lambda_s3.git
    
  2. VSCodeで開く
    image.png

  3. .env_sampleを.envにreneame

  4. .envに自分の環境変数を設定

    .env
    AWS_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
    
変数 設定内容
1 AWS_ACCESS_KEY AWS IAMより取得して設定
2 AWS_SECRET_ACCESS_KEY AWS IAMより取得して設定
3 AWS_ACCOUNT_ID AWS右上を参照。黄色のハイフンは除いた部分。image.png
4 AWS_DEFAULT_REGION リージョン。東京なら「ap-northeast-1」
5 LOG_FILE S3に保存するファイルの後ろの名前と拡張子。好きにつけてOK。例1:LOG_FILE=.log・例2:LOG_FILE=_s3.log
6 BUCKET_NAME S3のバケット名。Globalで一意である必要あり。例:docker-lambda-s3
7 IMAGE_TAG Imageのタグ。例:latest
8 DOCKER_IMAGE_NAME Docker Image名とECRのリポジトリ名。好きにつけてOK。例:docker_lambda_s3 ← 本当はlambda-image-s3にしようとして間違えたけどQiitaにのせるハードコピー取り直すの大変なのでこのままでいく
9 DOCKER_CONTAINER_NAME Docker Container名で、好きにつけてOK。 例:lambda-container-s3
10 LAMBDA_FUNC_NAME lambdaの関数名。例:lambda-s3

  ↓ こんな風になる

.env
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を作成する

構成図の緑の丸部分を作る
構成図_2.jpg

・S3作成

1.バケット作成

  • バケット名の例:docker-lambda-s3
    S3作成_1.jpg
    2..envの★6に環境変数を設定
    BUCKET_NAME=docker-lambda-s3

・ECR作成

1.リポジトリを作成
.envの★8で設定した名前:docker_lambda_s3
image.png

2.リポジトリの中を見てみる
image.png

今はimageがない
image.png

3.ローカル環境でS3にファイルをアップロードする

構成図の緑の部分を動かす
構成図_3.jpg

1.DevContainerをひらく

  • VSCode左下の「><」をクリックして、「コンテナーで再度開く」を選択
    devcon_1.png

しばらく待っていると、開発コンテナーが開く
devcon_2_2.png

  • Dockerを確認
    • Image
      image.png

    • Container
      image.png

2.実行する

  • 「lambda_funcion.py」を右クリックして「ターミナルでPythonファイルを実行する」をクリックする
    devcon_5.png

  • 正常に実行されて、ターミナルに「年月日_時間分秒 テスト ファイル だよ」と出る。
    devcon_6.png

3.S3を確認する

  • バケット「docker-lambda-s3」の中に、実行した日付「9月4日」のフォルダが作成されており、その中に「年月日_時間分秒.log」が作成されている
    s3_2.jpg
  • 「年月日_時間分秒.log」の中身を見てみる。
    s3_3.jpg

中身が、ターミナルに出ていた「年月日_時間分秒 テスト ファイル だよ」となっている。
s3_4.jpg

4.Docker ImageをECRにpushする

構成図の緑の部分を行う
構成図_4.jpg

1.開発コンテナーを閉じる
devcon_8.png

2.プロジェクトルートフォルダを開く
git bash_0.png

3.awscli.shの「ECRにプッシュしたイメージをlambdaに更新」から下をコメントアウト(範囲選択してCTL + /)
 該当箇所はlambdaにECRイメージを更新する部分で、lambdaはまだ未作成でエラーになるため、ここではコメントアウトする。

awscli.sh
# 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で実行する

Git bash
 source awscli.sh

5.実行結果が下記のようになって成功していることを確認する
image.png

6.ECRでイメージが作成されていることを確認する
ECR作成_4.jpg

5.Lambda作成&実行

構成図の緑の丸部分を動かす
image.png

1.関数作成

  • 関数の作成
    lambda_1.png
  • コンテナイメージを選択。関数名はLAMBDA_FUNC_NAME=★10に設定したものと同じにするので、「lambda-s3」
    lambda_2.png
  • ECRのリポジトリを選択する。イメージは、latestを選ぶ。
    lambda_4.png
    lambda_5.png
  • ロールは後で変更。とりあえず「基本的な~新しいロールを作成」を選んで、関数の作成
    lambda_6.png
  • Lambdaが選んだECRのイメージで作成されていることを確認する
    lambda_7.png

2.設定

  1. アクセス権限
  • S3への読み書き許可のポリシーを与える
    image.png

  • インラインポリシーを作成
    image.png

  • JSONでポリシーを記入。BUCKET_NAME=★6で設定した名前を使う

{
   "Version": "2012-10-17",
   "Statement": [
       {
           "Effect": "Allow",
           "Action": [
               "s3:GetObject",
               "s3:PutObject"
           ],
           "Resource": "arn:aws:s3:::docker-lambda-s3/*"
       }
   ]
}

image.png

  • ポリシーの作成(ポリシー名は適当。例:lambda-s3-get-put)
    image.png
    image.png

2.環境変数

  • 設定する。
    image.png
    .envのLOG_FILE=★5とBUCKET_NAME=★6をここに記入
    image.png

2.関数実行

  • テストを保存後、テストする
    image.png
  • 成功していることを確認する
    lambda_10.png

3.S3を確認

  • バケット「docker-lambda-s3」の中に、実行した日付「9月5日」のフォルダが作成されており、その中に「年月日_時間分秒.log」が作成されている
    image.png
  • 「年月日_時間分秒.log」の中身を見てみる。
    中身が、「年月日_時間分秒 テスト ファイル だよ」となっている。
    image.png

6.修正を反映してみる

構成図の流れ全部
image.png

1.ローカル

  • 開発コンテナでlambda_function.pyを修正
    例:「追加」を後ろにつける
lambda_function.py
log_content = f"{today_time_str} テスト ファイル だよ 追加"
  • 実行した結果は、「年月日_時間分秒 テスト ファイル だよ 追加」となっている
    devcon_9.png

  • S3確認

    • バケット「docker-lambda-s3」の中に、実行した日付「9月5日」のフォルダが作成されており、その中に「年月日_時間分秒.log」が作成されている
      image.png
    • 「年月日_時間分秒.log」の中身を見てみる。
      中身が、lambdaログに出ていた「年月日_時間分秒 テスト ファイル だよ 追加」となっている。
      image.png

2.開発コンテナ閉じる

3.プロジェクトルートフォルダを開く

4.AWS Cli
.awscli.shの「ECRにプッシュしたイメージをlambdaに更新」から下のコメントアウトを外す(範囲選択してCTL + /)

awscli.sh
# 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で実行する

Git bash
 source awscli.sh
  • Dockerイメージを作成&ECRにプッシュ&lambda更新が出来ているか確認する
    image.png

3.AWS

  • lambdaを実行する
    ブラウザを更新して、下記のように更新中の場合はまだ反映できていないので実行しない。
    image.png
    下記のように、更新されていると表示されていたり、何も表示されていない場合は実行してOK
    image.png
  • テスト実行
    image.png
    成功していることを確認する
    image.png
  • S3確認
     - バケット「docker-lambda-s3」の中に、実行した日付「9月6日」のフォルダが作成されており、その中に「年月日_時間分秒.log」が作成されている
     - 「年月日_時間分秒.log」の中身を見てみる。
    image.png
    中身が、「年月日_時間分秒 テスト ファイル だよ 追加」となっている。
    image.png

おしまい。

プログラム解説

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回以上テストしているぐらいの利用状況。)
image.png

まとめ

環境構築するまでが大変ですが、環境を作り終わった後は楽に改修&Lambdaに反映出来るようになるのが良いと思います。次回はこの仕組みを使って開発して、Lambdaを使ってWebScripingするという内容をまとめたいと思います。

3
4
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
3
4