「AWS上にマラソンマッチ用のジャッジ環境を作った」をChatGPTに投げて、Lambdaを使ったAHC用のジャッジ環境を作る。

概要

Webやサーバー関係に詳しくなくても、良い記事をChatGPTに投げればなんとかなる。

ChatGPTのいいなりになればOK

AtCoder Heuristics Contest(AHC)用にAWS Lambdaを使った自作ジャッジを作りたいなと思っていたので重い腰を上げましたがその辺の知識はありません。そこでyunixさんの素晴らしい記事を発見します。
yunix-kyopro.hatenablog.com

AWSを触ったことがない人向けには書いていないです。すいません...

と書いてありますが、気にせず上記の記事をコピペしてChatGPTに丸投げします。

以下のようにコマンドつきで基本的な部分から手取り足取り教えてくれます。

Ubuntuでも出来ますか?レベルの質問でもChatGPTは丁寧に回答してくれます。

あとは、「エラーが出たら、エラーログを添付して質問」「ハマったら原因を適当に推測してもらう」を繰り返すと、自分用のジャッジが作れます。詳しく勉強したい部分があったらそれも質問するといいです。

以下が実行結果です。できました。yunixさんありがとうございます。

はまったところ

typescriptを使った設定にする必要があった。

CDK(AWS Cloud Development Kit )はpythonやtypescriptで設定できますが、記事に載っていたのはtypescriptだったので質問しなおしました。

リージョン間違いでログが何も見れなかった…

エラーログやLambdaの各種情報は何も設定しなくても表示されます。出てなければ何かおかしいです。この手の問題があると何が起こっているかさっぱり分からないので最初に直しましょう。ChatGPTはひどい勘違い系も割とヒントをくれるのが良いです。

ログを見る場合はhttps://console.aws.amazon.comにCloudWatchを追加すると見れます。python内でprintで標準出力したものもここで見れます。特別な設定はいりませんがリージョンを間違えていると何も見れません。


ジャッジ環境のディレクトリ構成が分からなかった。



記事に載っている1つ目のtypescriptはlib以下に置きます。

記事に載っている2つ目のDockerfile, 3つ目のhandlers.pyはlambda以下に置きます。また、requirements.txt(空ファイルで良い)も置く必要があります。

なお、main.cppやAHCのテスターを置くコンテスト毎のディレクトリとは別のものです。記事に載っている4つ目のpythonスクリプトは、コンテスト毎のディレクトリからmain.cppをS3に送ったりするのに使います。

別なバケット名を設定していた。

元の記事のpython実行スクリプトが以下のようになっているので、"バケット名"を書く必要があります。

バケットは2つ出来ているかもしれませんが、marathonjudge~のほうを設定します。もう一方のcdk-~はAWS CDKが、AWS CDK自体が管理するためのバケットの名前です

各種パスの更新をわすれていた

handlers.pyを実行したら、cdk deployコマンドでデプロイしなおすのも忘れないでください。

# handlers.py
import os
import json

import boto3


def entry_handler(event, context):
    timestamp = event["timestamp"]
    contest_name = event["contest_name"]
    N = 50
    result = [
       {
            "test_path": f"{contest_name}/in/{i:04}.txt",  # 要変更。テスト用入力ファイルのパス
            "timestamp": timestamp, 
            "contest": contest_name, 
            "id": f"{i:04}"
       } 
       for i in range(0, N, 1)
    ]
    return {"tasks": result}


def exec_handler(event, context):
    print(event, os.environ["s3Bucket"])
    # 前の段からテストファイルのパス・コンテスト名を取得
    test_path = event["test_path"]
    contest_name = event["contest"]
    test_id = event["id"]
    timestamp = event["timestamp"]

    # ソースコードを取得してビルドする
    bucket = boto3.resource("s3").Bucket(os.environ["s3Bucket"])
    bucket.download_file(f"{contest_name}/main.cpp", "/tmp/main.cpp") # {contest_name}/main.cpp。ここは記事に載っている4つめのスクリプトを使えばこのままで良い。
    os.system("g++ -O2 /tmp/main.cpp -o /tmp/a.out")

    # スコア計算のスクリプトを取得
    bucket.download_file(f"{contest_name}/vis", "/tmp/vis") # 要変更。テスター。{contest_name}/visは、S3で自分で設置した場所を指定。visは"cargo run --release --bin vis"でRustコンパイルしておく。
    os.system("chmod 777 /tmp/vis")

同時実行数が最大で10になっていた。

TooManyRequestsExceptionというエラーがよくかえってきたのですが、50個のテストケースを回していたのに最大10だったからでした。デフォルトで1000らしいですがなぜか10になっていました。

最後にChatGPTによるブログ

タイトル: AWS LambdaとAmazon S3を使用した高負荷タスクの並列化

AWS LambdaとAmazon S3を組み合わせることで、高負荷タスクを並列化し、処理効率を向上させることができます。今回は、プログラミングコンテストのジャッジシステムを構築する例として、AWS CDKとAWS Step Functionsを使って、この組み合わせを実装する方法を解説します。

1. AWS CDKとLambda関数の作成

まず、AWS CDKを使用して、タスクの各ステージを処理するLambda関数を作成します。今回は、各問題を別々に評価するためのLambda関数(evaluation-lambda)、評価結果をまとめるためのLambda関数(summary-lambda)、それらを制御するエントリポイントとなるLambda関数(entry-lambda)の3つを作成します。Lambda関数は、AWS CDKのlambda.DockerImageFunctionを使用してDockerイメージから作成します。

各Lambda関数はS3バケットに対する読み書き権限を持ち、バケット名は環境変数から取得します。

2. Step Functionsの構築

次に、AWS Step Functionsを使用してこれらのLambda関数を連携させます。Step Functionsのステートマシンは、エントリポイントのLambda関数を最初に実行し、次にマップステートを使用して評価Lambda関数を並列実行し、最後に結果をまとめるLambda関数を実行します。

3. ログの確認

Lambda関数がエラーを返す場合や、デバッグが必要な場合は、AWS CloudWatch Logsを使用してログを確認できます。CDKで作成したLambda関数は、関数名が自動生成されるため、AWS Management ConsoleのLambdaのダッシュボードから関数を見つけることができます。

4. バケットとファイルパスの確認

Lambda関数がS3からファイルをダウンロードできない場合、バケット名やファイルパスが正しいか確認が必要です。S3バケットの名称はCDKスタック名とリソース名から自動生成されるため、AWS Management Consoleから確認することができます。

5. 同時実行数の調整

Lambda関数の同時実行数が多すぎる場合、TooManyRequestsExceptionが発生する可能性があります。これは、Lambda関数のデフォルトの同時実行数(1000)を超えた場合や、Lambda関数の設定で制限された場合に発生します。

解決方法としては、AWS Management ConsoleのLambdaの設定から特定のLambda関数の同時実行数の上限を増やす方法があります。さらに、Lambda関数の上限がアカウントレベルで設定された上限("予約されていない同時実行数")を超えている場合は、AWSサポートに問い合わせてアカウントレベルの同時実行数の上限を増やすことも必要です。

今回の実装を通して、AWS LambdaとAmazon S3を組み合わせて、高負荷タスクを効率的に並列化する方法を学びました。この方法は、プログラミングコンテストのジャッジシステムだけでなく、データ処理やマイクロサービスの構築など、さまざまなシナリオで応用できます。