AWS_Lambdaを使って日次ジョブを仕込んでみる

やること

OCIの課金情報をメールに送付するスクリプトを作成したのでAWSのサーバレス環境でジョブ化してみる
主に2つのサービスを使う

  • AWS Lambda: サーバーレス実行環境
  • AWS EventBride Schedule: ジョブスケジューラ

AWS Lambda

この辺にあったLambda
image-120.png
Create a funcation
image-121.png

3パターンある。Use a blueprintってのはテンプレから作成するやつ。見てみたけど他のAWSサービスと連携する用みたいなのがいろいろ。
今回はAuthor from scratchで作ってPython用のコードをアップロードすることにする。
image-122.png

この辺にアップロード用のzip作成手順が載ってる
https://docs.aws.amazon.com/lambda/latest/dg/python-package.html

https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html

とりあえず側だけ(サンプルコード付き)作って、あとからコードアップロードするっぽい

資材の準備

スクリプトはPyCharmで書いてるので、
PyCharm
Tools > Sync Pythons requirements で requirements.txtを生成

依存ライブラリもろもろカレントに詰め込んでzipに固めてアップロードすればいいらしい
依存ライブラリの取得はスクリプトに包んだ

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ cat ../build_lambda_fun.sh
#!/usr/bin/env bash
set -euo pipefail

PROJECT_ROOT=$(pwd)
BUILD_DIR="${PROJECT_ROOT}/build"
rm -rf "$BUILD_DIR" && mkdir -p "$BUILD_DIR"

# 依存を確定
#uv lock
#uv export --frozen --no-dev -o requirements.txt

docker run --rm \
  --entrypoint /bin/bash \
  -e PIP_DEFAULT_TIMEOUT=1200 \
  -v "${PROJECT_ROOT}":/var/task \
  -w /var/task \
  public.ecr.aws/lambda/python:3.13 \
  -eu -c '
    PM=dnf; command -v yum >/dev/null && PM=yum
    $PM -y install gcc python3-devel || echo "skip gcc"
    python -m pip install --upgrade pip
    python -m pip install --only-binary=:all: \
           -r requirements.txt \
           --target build/python \
           --progress-bar off
  '
# entrypointがない 開発・ビルド専用イメージ public.ecr.aws/sam/build-python3.13 もあるらしい

sudo chown -R "$(id -u):$(id -g)" "$BUILD_DIR"

echo "依存ライブラリを build/python に展開しました"

docker@ubuntu24:~/lambda_build/multicloud_billing_report$

実行する

# ディレクトリ作る
mkdir ~/lambda_build/multicloud_billing_report
cd ~/lambda_build/multicloud_billing_report

# 作成したソースコード類を配置する
→scp 等で配置

# 依存ライブラリをダウンロードしてzipにまとめる
bash ../build_lambda_fun.sh
cd build/python
zip -r ../lambda-package.zip .

# ソースコード類もzipにまとめる
cd ../..
zip -g build/lambda-package.zip *.py config oci_api_key.pem 

資材アップロード

Lambdaのファンクション詳細画面からUpload
→失敗した
image-137.png

どうやらzipサイズは50MB以下である必要があるらしい。
S3を経由すればいけるらしいけど、そもそも解凍後250MB以下である必要があるらしく全然ダメだった。

docker@ubuntu24:~/lambda_build/multicloud_billing_report/build$ du -sm ./*
123     ./lambda-package.zip
550     ./python
docker@ubuntu24:~/lambda_build/multicloud_billing_report/build$

ociがデカすぎる

docker@ubuntu24:~/lambda_build/multicloud_billing_report/build/python$ du -sm ./* | sort -n
1       ./bin
1       ./certifi
1       ./certifi-2025.4.26.dist-info
1       ./cffi
1       ./cffi-1.17.1.dist-info
1       ./circuitbreaker-2.1.3.dist-info
1       ./circuitbreaker.py
1       ./cryptography-44.0.3.dist-info
1       ./dateutil
1       ./dotenv
1       ./dotenv-0.9.9.dist-info
1       ./numpy-2.3.0.dist-info
1       ./OpenSSL
1       ./pandas-2.2.3.dist-info
1       ./__pycache__
1       ./pycparser-2.22.dist-info
1       ./pyOpenSSL-24.3.0.dist-info
1       ./python_dateutil-2.9.0.post0.dist-info
1       ./python_dotenv-1.1.0.dist-info
1       ./pytz-2025.2.dist-info
1       ./rust
1       ./six-1.17.0.dist-info
1       ./six.py
1       ./tzdata-2025.2.dist-info
2       ./_cffi_backend.cpython-313-x86_64-linux-gnu.so
2       ./pycparser
3       ./oci-2.153.0.dist-info
3       ./pytz
3       ./tzdata
13      ./cryptography
27      ./numpy.libs
43      ./numpy
75      ./pandas
379     ./oci
docker@ubuntu24:~/lambda_build/multicloud_billing_report/build/python$

コンテナイメージを使う方法に変更

コンテナイメージで実施
https://docs.aws.amazon.com/lambda/latest/dg/python-image.html

テンプレに沿ってDockerfileを作る

${LAMBDA_TASK_ROOT} →/var/taskらしい。
スクリプトからファイルの読みこむ場合はちょっと工夫しないといけないっぽい。

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ cat Dockerfile
FROM public.ecr.aws/lambda/python:3.13

# Copy requirements.txt
COPY requirements.txt ${LAMBDA_TASK_ROOT}

# Install the specified packages
RUN pip install -r requirements.txt

# Copy function code
COPY *.py   ${LAMBDA_TASK_ROOT}
COPY config ${LAMBDA_TASK_ROOT}
COPY *pem   ${LAMBDA_TASK_ROOT}

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "lambda_function.handler" ]
docker@ubuntu24:~/lambda_build/multicloud_billing_report$

ビルド
※キャッシュを利用しないでベースイメージも最新から引っ張ってくるコマンド:
docker buildx build --no-cache --pull --platform linux/amd64 --provenance=false -t multicloud_billing_report:latest .
→キャッシュでハマったことがあるので俺みたいなDocker初心者はキャッシュをバイパスする用にしたほうがいいかも。その分ビルドに時間がかかるのだが。

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ docker buildx build --platform linux/amd64 --provenance=false -t multicloud_billing_report:latest .
[+] Building 0.4s (12/12) FINISHED                                                                                   docker:default
 => [internal] load build definition from Dockerfile                                                                           0.0s
 => => transferring dockerfile: 579B                                                                                           0.0s
 => [internal] load metadata for public.ecr.aws/lambda/python:3.13                                                             0.0s
 => [internal] load .dockerignore                                                                                              0.0s
 => => transferring context: 2B                                                                                                0.0s
 => [1/7] FROM public.ecr.aws/lambda/python:3.13                                                                               0.0s
 => [internal] load build context                                                                                              0.2s
 => => transferring context: 315B                                                                                              0.2s
 => CACHED [2/7] COPY requirements.txt /var/task                                                                               0.0s
 => CACHED [3/7] RUN pip install -r requirements.txt                                                                           0.0s
 => CACHED [4/7] COPY *.py   /var/task                                                                                         0.0s
 => CACHED [5/7] COPY .env   /var/task                                                                                         0.0s
 => CACHED [6/7] COPY config /var/task                                                                                         0.0s
 => CACHED [7/7] COPY *pem   /var/task                                                                                         0.0s
 => exporting to image                                                                                                         0.0s
 => => exporting layers                                                                                                        0.0s
 => => writing image sha256:6f04cd85c01547f2a0ade8e81a8d6b7911f5b34f61cd638adcdb29716835c03a                                   0.0s
 => => naming to docker.io/library/multicloud_billing_report:latest                                                            0.0s
docker@ubuntu24:~/lambda_build/multicloud_billing_report$

ビルドが出来たらお試しにローカルで実行
→Ctrl+Cで止めるまでプロンプト戻ってこない

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ docker run --platform linux/amd64 -p 9000:8080 multicloud_billing_report:latest
14 Jun 2025 09:21:24,483 [INFO] (rapid) exec '/var/runtime/bootstrap' (cwd=/var/task, handler=)

別termからcurlで確認
→テンプレに沿うとこれでキックできる

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
{"status": "OK"}
docker@ubuntu24:~/lambda_build/multicloud_billing_report$

標準出力はdocker runを流したtermで出力される

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ docker run --platform linux/amd64 -p 9000:8080 multicloud_billing_report:latest
14 Jun 2025 10:55:19,462 [INFO] (rapid) exec '/var/runtime/bootstrap' (cwd=/var/task, handler=)
START RequestId: 43c245bc-8842-4b02-b307-dc2f5075d283 Version: $LATEST
14 Jun 2025 10:55:29,172 [INFO] (rapid) INIT START(type: on-demand, phase: init)
14 Jun 2025 10:55:29,172 [INFO] (rapid) The extension's directory "/opt/extensions" does not exist, assuming no extensions to be loaded.
14 Jun 2025 10:55:29,172 [INFO] (rapid) Starting runtime without AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN , Expected?: false
14 Jun 2025 10:55:29,899 [INFO] (rapid) INIT RTDONE(status: success)
14 Jun 2025 10:55:29,900 [INFO] (rapid) INIT REPORT(durationMs: 727.773000)
14 Jun 2025 10:55:29,900 [INFO] (rapid) INVOKE START(requestId: 2ce5b621-9824-4b31-819b-cec31ca29ce8)
raw event: {}
request id: 2ce5b621-9824-4b31-819b-cec31ca29ce8
remaining ms: 299271
date       | cost(小数点以下繰り上げ) | currency
2025-06-01 |                    140 | JPY
2025-06-02 |                     66 | JPY
2025-06-03 |                     78 | JPY
2025-06-04 |                     55 | JPY
2025-06-05 |                     55 | JPY
2025-06-06 |                     43 | JPY
2025-06-07 |                     43 | JPY
2025-06-08 |                     39 | JPY
2025-06-09 |                     37 | JPY
2025-06-10 |                     13 | JPY
2025-06-11 |                     13 | JPY
2025-06-12 |                     25 | JPY
2025-06-13 |                     18 | JPY
2025-06-14 |                      4 | JPY
=======================================
sum        |                    629 | JPY
END RequestId: 2ce5b621-9824-4b31-819b-cec31ca29ce8
REPORT RequestId: 2ce5b621-9824-4b31-819b-cec31ca29ce8  Init Duration: 0.04 ms  Duration: 3277.68 ms    Billed Duration: 3278 ms    Memory Size: 3008 MB    Max Memory Used: 3008 MB
14 Jun 2025 10:55:32,449 [INFO] (rapid) INVOKE RTDONE(status: success, produced bytes: 0, duration: 2548.942000ms)

Ctrl+Cで止める

メモ

デバッグ用
→通常イメージだとコンテナにログインできないがデバッグ用に用意されているイメージを使うとコンテナにログインできる

# Dockerfile

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ cat Dockerfile
#FROM public.ecr.aws/lambda/python:3.13
FROM public.ecr.aws/sam/build-python3.13 AS dev

# Copy requirements.txt
COPY requirements.txt ${LAMBDA_TASK_ROOT}

# Install the specified packages
RUN pip install -r requirements.txt

# Copy function code
COPY *.py   ${LAMBDA_TASK_ROOT}
COPY .env   ${LAMBDA_TASK_ROOT}
COPY config ${LAMBDA_TASK_ROOT}
COPY *pem   ${LAMBDA_TASK_ROOT}

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
#CMD [ "lambda_function.handler" ]
CMD ["python"]
docker@ubuntu24:~/lambda_build/multicloud_billing_report$

# ビルド
docker buildx build --platform linux/amd64 --provenance=false -t multicloud_billing_report:test .

# 実行&コンテナログイン
docker run --rm -it multicloud_billing_report:test /bin/bash

Amazon ECR にプッシュ

AWSのコンテナレジストリ
この辺にある

image-138.png

Create
image-139.png

名前を入力してCreate
image-141.png

できました
image-142.png

Repositoryの中はまだ空
image-143.png

view push commmandsを押すとアップロード用コマンドが表示されるので、ここからはCLI対応

まずawscliをインストールする
https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html

インストールコマンド

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

実行した

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ sudo ./aws/install
[sudo] password for docker:
You can now run: /usr/local/bin/aws --version
docker@ubuntu24:~/lambda_build/multicloud_billing_report$ aws --version
aws-cli/2.27.35 Python/3.13.3 Linux/6.8.0-48-generic exe/x86_64.ubuntu.24
docker@ubuntu24:~/lambda_build/multicloud_billing_report/aws/dist$ ldd aws
        linux-vdso.so.1 (0x00007ffee88b9000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007774854c1000)
        libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007774854a5000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007774854a0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000777485200000)
        /lib64/ld-linux-x86-64.so.2 (0x00007774854d8000)
docker@ubuntu24:~/lambda_build/multicloud_billing_report/aws/dist$

つぎにawscliからAWSに接続できるようにAccess Keyを発行する

IAM > Security credentials
Access keysのところからCreate accesss key
image-144.png

Root userで発行するなという警告がでる
image-146.png

できた
image-147.png

接続構成

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ aws configure
AWS Access Key ID [None]: <Access Keyをここに入力>
AWS Secret Access Key [None]: <Secret access Keyをここに入力>
Default region name [None]: us-east-1
Default output format [None]:
docker@ubuntu24:~/lambda_build/multicloud_billing_report$

ECRにログイン

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin xxxxxxxxxxxxxx.xxx.ecr.us-east-1.amazonaws.com
WARNING! Your password will be stored unencrypted in /home/docker/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores

Login Succeeded
docker@ubuntu24:~/lambda_build/multicloud_billing_report$

イメージにタグをつけてPush

docker@ubuntu24:~/lambda_build/multicloud_billing_report$ docker tag multicloud_billing_report:latest xxxxxxxxxxxxxx.xxx.ecr.us-east-1.amazonaws.com/multicloud_billing_report:latest
docker@ubuntu24:~/lambda_build/multicloud_billing_report$ docker push xxxxxxxxxxxxxx.xxx.ecr.us-east-1.amazonaws.com/multicloud_billing_report:latest
The push refers to repository [xxxxxxxxxxxxxx.xxx.ecr.us-east-1.amazonaws.com/multicloud_billing_report]
a58c0862a8be: Pushed
f7311e9d678c: Pushed
3062268dddc3: Pushed
9a865928ce17: Pushed
b1a54c892e52: Pushed
1349293713ba: Pushed
ce6a38e7833c: Pushed
d316c5099768: Pushed
6a9b57324378: Pushed
86fc9027353c: Pushed
6b702557483e: Pushed
0b81e7a3683d: Pushed
latest: digest: sha256:45ec3f9c22ff58ca8cb815506b40e17729be35dea0bfa2431c0b1d73fa66f689 size: 2828
docker@ubuntu24:~/lambda_build/multicloud_billing_report$

ここまでやってなんだけど、金かかりそうだから別の所に移すかも

入っとる。346MBらしい。これなら無料枠に収まるかも。
image-148.png

Lambda Function作成画面からPushしたイメージを読み込んでCreate functionする
image-149.png

Test OK
image-152.png

以下ハマった点

  • ECRにイメージアップロードしなおしたら、再度Deploy仕直さないとFunction側には反映されないらしい。ECRにtag上書きで保存しなおしてもイメージとしては元のやつが使われちゃう。
    image-150.png

  • Functionの実行タイムアウト設定できる。デフォルト3秒は世知辛い。
    image-151.png

Amazon EventBridge Schedule

作成したLambda Functionを日次で起動されるようにする

https://docs.aws.amazon.com/lambda/latest/dg/with-eventbridge-scheduler.html

この辺にあった
image-153.png

EventBrige Schedule
image-154.png

設定
毎日21:30実行
image-156.png

この辺はざっくり5分
image-157.png

Lambdaを選んで作ったfunctionを選ぶ
image-158.png

ここはとりあえずようわからんからDefault
image-159.png

最後確認画面が出てCreate Schedule

はい、出来ました。あとは実行されるのを待つ
image-160.png

CloudWatchってので見るのね

CloudWatch > Log groups にLambda FunctionのLoggroupができてた
標準出力はここにでるらしい
image-161.png

無事飛ぶようになりました

ちゃんとメール飛ぶようになったのでひとまずは完了
image-195.png

とりあえずで作ったのでOCIのAPIキーとかコンテナイメージに包んでしまったが、それはよろしくないのでAWSのシークレットマネージャーなるものに格納するようにしてみたいと思う。