🙆

AWS Lambda の Container Runtime を GitHub Actions で E2E テストしたい時の備忘録

2024/08/04に公開

AWS Lambda の Container Runtime を GitHub Actions で E2E テストしたい時の備忘録

はじめに

機械学習のAPI提供として AWS Lambda の Container Runtime を使うことがよくあります。
pytest などを用いて開発環境で unit test はよく行いますが、さらにDocker Container としての成果物の E2E テストをすることでより安全性を高めることができます。
今回はこの E2E テストを GitHub Acitons 上で行うための備忘録です。

ほとんどの内容は以下のブログ記事を参考にしてます。(ありがとうございます 🙇 )

差分はpythonでテストを書いてることくらいだと思われます。

なので、詳細は引用してるブログ記事を参照してください。

想定読者

  • AWS LambdaのコンテナをGHA上でE2Eテストしたい未来の自分

GitHub ActionsでのTest Job

GtiHub ACtions上でのテストのワークフローの定義は以下のようになります。

name: test

on: push

jobs:
  test:
    name: Run E2E Test on Docker Containers
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Run E2E Test on Docker Containers through Docker Network
        run: docker compose up server -d && docker compose up test

ポイントは、AWS Lambda Container を docker compose up server -d で推論サーバーとしてバックプロセスで立ち上げてからテスト用のコンテナを立ち上げてるところです。
こうしないとうまく動きません。step 間では環境が分かれてるので、同一ステップ内でテストしたい推論サーバーを立てて、別のテスト用のコンテナを立ててテストしています。

pytest なら E2E 用のマーカーをテストにつけておくみたいな工夫もあってもいいかもしれません。

特別なことはしてませんが、機械学習の E2E テストはモデルのロードや推論など重めの処理が乗ることが多いので、develop ブランチに pushした時のみトリガーされるような設定を付け足しておくといいと思います

name: test

on: push

jobs:
  test:
    name: Run E2E Test on Docker Containers
    runs-on: ubuntu-latest
    # develop/mainブランチにpushした時のみトリガー
    if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Run E2E Test on Docker Containers through Docker Network
        run: docker compose up server -d && docker compose up test

各 Dockerfile と AWS Lambda のサンプルコード

推論用の Dockerfile

推論サーバーとしての Dockerfile は以下のようになります。

python の環境構築に rye を使ってるので rye で生成された lock ファイルを使って、LAMBDA_TASK_ROOT に必要なパッケージをインストールします。

FROM public.ecr.aws/lambda/python:3.10 as app

WORKDIR ${LAMBDA_TASK_ROOT}

# README.md,pyproject.tomlがないとinstall時にコケる
COPY README.md pyproject.toml requirements.lock "${LAMBDA_TASK_ROOT}"
RUN \
  python -m pip install --upgrade \
      'pip==24.2' \
      'wheel==0.43.0' \
      'setuptools==72.1.0' && \
  python -m pip install --no-cache-dir -r requirements.lock -t "${LAMBDA_TASK_ROOT}"
COPY . "${LAMBDA_TASK_ROOT}"
CMD [ "src.example_aws_lambda_e2e_test_on_gha.app.handler" ]

E2E テスト用の Dockerfile

E2E テスト用の Dockerfile は以下のようになります。

FROM python:3.10 AS builder

ENV ORIGIN=http://localhost:8080
WORKDIR /app

COPY . .
RUN \
    python -m pip install --upgrade \
      'pip==24.2' \
      'wheel==0.43.0' \
      'setuptools==72.1.0' && \
    pip install -r requirements.lock && \
    pip install -r requirements-dev.lock

CMD ["pytest"]

推論用コードとその E2E テストコード

推論用の lambda function のコードは今回何でもいいのでリクエストが来たら、 message キーできたものを返すだけの簡単なものにしてます。

from enum import Enum
from aws_lambda_powertools.utilities.typing import LambdaContext


class Status(Enum):
    OK = 200
    BAD_REQUEST = 400
    FORBIDDEN = 403
    NOT_FOUND = 404
    INTERNAL_SERVER_ERROR = 500


def handler(event: dict, _: LambdaContext) -> dict:
    msg = event.get("message")
    status = Status.OK
    if msg is None:
        msg = "Not Found"
        status = Status.BAD_REQUEST
    return {"message": msg, "status": status.value}

E2E テスト用のコードでは、推論サーバーにDocker Network 経由でリクエストを投げるようにします

import os
import json
import requests
import pytest


ORIGIN = os.getenv("ORIGIN", "http://localhost:8080")


@pytest.fixture
def sample_success_request() -> str:
    return json.dumps({"message": "Hello, World!"})


def test_app_should_success(sample_success_request: str) -> None:
    response = requests.get(
        f"{ORIGIN}/2015-03-31/functions/function/invocations",
        data=sample_success_request,
    )

    assert getattr(response, "status_code") == 200, (
        f"Requested to {ORIGIN}. Expected status code 200, but got {response.status_code} "
        + "You should check the environment variable ORIGIN in compose.yaml. "
        + "If you are on GitHub Action, please check 'http://server:8080' is allowed. "
    )

    response_json = response.json()
    assert response_json.get("message") == "Hello, World!", (
        f"Requested to {ORIGIN}. Expected message 'Hello, World!', but got {response_json.get('message')} "
        + f"RESPONSE_JSON: {response_json}"
    )

以上のコードを組み合わせて、GitHub Actions 上で E2E テストを行うことができます。

lambda_e2e_gha_log_20240804.png

コードはこのリポジトリで公開しています。

まとめ

  • AWS Lambda の Container Runtime を使った E2E テストを GitHub Actions 上で行う方法についてまとめました
  • Docker Network を使って推論サーバーを立ててからテスト用のコンテナを立てることで E2E テストを行うことができます
GitHubで編集を提案

Discussion