💭

AWS LambdaでWeb操作してみた

に公開

やったこと

API GateWayにPOST そしてLambdaコンテナでSeleniumでタイトルを取得した。

対象読者

  • Lambdaでweb操作したい方
  • API Gatewayを使いたい方

検証環境

  • AWS Lambda Python
  • Selenium
  • chrome
  • umihico/docker-lambda-selenium
  • AWS CLI
  • Docker
  • Ubuntu 24.04LTS (AWS CLIを実行した環境)

手順

ステップ1: ファイル構成

最終的なファイル構成は以下のようになります:

docker-selenium-lambda/
├── Dockerfile
├── main.py

作業ディレクトリを作成

mkdir docker-selenium-lambda
cd docker-selenium-lambda

ステップ2: Dockerfileを作成

Lambdaの環境にはブラウザがインストールされてないため、コンテナを利用します。

以下のDockerイメージを活用させていただきます。ありがとうございます!
https://github.com/umihico/docker-selenium-lambda/

以下のようにDockerfileとして保存する

# Dockerfile
FROM umihico/aws-lambda-selenium-python:latest

# Lambda関数コードをコピー
COPY main.py ./

# Lambda関数のハンドラを指定
CMD ["main.handler"]

ステップ3: main.pyを作成

# main.py
from selenium import webdriver
from tempfile import mkdtemp


def handler(event, context):
    # Chrome オプションの設定
    options = webdriver.ChromeOptions()
    options.binary_location = '/opt/chrome/chrome'
    options.add_argument("--headless=new")
    options.add_argument('--no-sandbox')
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1280,1696")
    options.add_argument("--single-process")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--disable-dev-tools")
    options.add_argument("--no-zygote")
    options.add_argument(f"--user-data-dir={mkdtemp()}")
    options.add_argument(f"--data-path={mkdtemp()}")
    options.add_argument(f"--disk-cache-dir={mkdtemp()}")
    options.add_argument("--remote-debugging-port=9222")
    
    # ChromeDriverサービスの設定
    service = webdriver.ChromeService("/opt/chromedriver")
    
    # WebDriverを起動 with文でエラーが起きても確実にdriver.quitする
    with webdriver.Chrome(service=service, options=options) as driver:
        driver.get("https://www.google.com/")
        title = driver.title
        print(f"ページタイトル: {title}")
        
        return {'statusCode': 200, 'body': title}

ステップ4: デプロイ

今回使う変数

AWS_ACCOUNT_ID=あなたのアカウントID 後で変数に格納する。
AWS_REGION=あなたの使うリージョン
FUNCTION_NAME=ラムダ関数の名前
REPOSITORY_NAME=リポジトリで使う名前
ROLE_NAME=IAMロールの名前

AWS_ACCOUNT_ID="000011112222" 
AWS_REGION="ap-northeast-1"
FUNCTION_NAME="selenium"
REPOSITORY_NAME="selenium-lambda"
ROLE_NAME="lambda-selenium-execution-role"

AWSアカウントIDを取得

AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

ECRリポジトリを作成

aws ecr create-repository --repository-name $REPOSITORY_NAME --region $AWS_REGION

実行結果

test@test:~$ aws ecr create-repository --repository-name $REPOSITORY_NAME --region $AWS_REGION
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:************:repository/selenium-lambda",
        "registryId": "************",
        "repositoryName": "selenium-lambda",
        "repositoryUri": "************.dkr.ecr.ap-northeast-1.amazonaws.com/selenium-lambda",
        "createdAt": "2025-11-08T02:02:58.122000+09:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

Dockerイメージをビルド

docker build -t $REPOSITORY_NAME .

ECRにログイン

aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

実行結果

test@test:~$ docker build -t $REPOSITORY_NAME .
[+] Building 53.5s (12/12) FINISHED                                                                                                                         docker:default
 => [internal] load build definition from Dockerfile                                                                                                                  0.0s
 => => transferring dockerfile: 426B                                                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/ubuntu:22.04                                                                                                       2.5s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                                                                         0.0s
 => [internal] load .dockerignore                                                                                                                                     0.0s
 => => transferring context: 2B                                                                                                                                       0.0s
 => [1/7] FROM docker.io/library/ubuntu:22.04@sha256:09506232a8004baa32c47d68f1e5c307d648fdd59f5e7eaa42aaf87914100db3                                                 2.4s
 => => resolve docker.io/library/ubuntu:22.04@sha256:09506232a8004baa32c47d68f1e5c307d648fdd59f5e7eaa42aaf87914100db3                                                 0.0s
 => => sha256:392fa14dddd09da29a5c3d26948ff81c494424035b755d01b84ab12d92127433 2.30kB / 2.30kB                                                                        0.0s
 => => sha256:af6eca94c8104c8e90d3f9efe59c2b3a02b20aad3d985e31c7cd009ea104c447 29.54MB / 29.54MB                                                                      1.3s
 => => sha256:09506232a8004baa32c47d68f1e5c307d648fdd59f5e7eaa42aaf87914100db3 6.69kB / 6.69kB                                                                        0.0s
 => => sha256:4cb780d50443fc4463f1f9360c03ca46512e4fdd8fd97c5ce7e69c8758924575 424B / 424B                                                                            0.0s
 => => extracting sha256:af6eca94c8104c8e90d3f9efe59c2b3a02b20aad3d985e31c7cd009ea104c447                                                                             0.9s
 => [2/7] RUN apt-get update && apt-get install -y openssh-server sudo                                                                                               45.2s
 => [3/7] RUN mkdir /var/run/sshd                                                                                                                                     0.5s
 => [4/7] RUN mkdir -p /root/.ssh                                                                                                                                     0.5s
 => [5/7] RUN chmod 700 /root/.ssh                                                                                                                                    0.4s
 => [6/7] RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config                                                   0.5s
 => [7/7] RUN sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config                                                                     0.4s
 => exporting to image                                                                                                                                                0.8s
 => => exporting layers                                                                                                                                               0.8s
 => => writing image sha256:9c491dda071ec10dba8d4342dac54d6dbf564c0d2343c953b847dc41e54addbd                                                                          0.0s
 => => naming to docker.io/library/selenium-lambda  

イメージにタグを付ける

docker tag $REPOSITORY_NAME:latest $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPOSITORY_NAME:latest

ECRにプッシュ

docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPOSITORY_NAME:latest

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPOSITORY_NAME:latest
The push refers to repository [************.dkr.ecr.ap-northeast-1.amazonaws.com/selenium-lambda]
3904acea8cae: Pushed
42ad71c1513d: Pushed
7423929354e4: Pushed
770d3ade5423: Pushed
dfa564011b45: Pushed
d04b1852737c: Pushed
d2bc5d348ed7: Pushed
7c5a65f222db: Pushed
98f316bb40c4: Pushed
fc1541de968a: Pushed
231ae437bba4: Pushed
latest: digest: sha256:6823d0ed47085d7fa27c86dd9b176f4d7a5a3f1013df96ba8526219ea402705f size: 2629

ロールを作成

aws iam create-role --role-name $ROLE_NAME --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {"Service": "lambda.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }]
  }'

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ aws iam create-role --role-name $ROLE_NAM
E --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {"Service": "lambda.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }]
  }'
{
    "Role": {
        "Path": "/",
        "RoleName": "lambda-selenium-execution-role",
        "RoleId": "************",
        "Arn": "arn:aws:iam::************:role/lambda-selenium-execution-role",
        "CreateDate": "2025-11-07T17:16:50+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}

ロールにポリシーを付与

aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

lambda関数を作成

ロールが付与されるのを待ってから作成します。なので30秒ほど待ちます。

ROLE_ARN="arn:aws:iam::$AWS_ACCOUNT_ID:role/lambda-selenium-execution-role"

aws lambda create-function --function-name $FUNCTION_NAME --package-type Image --code ImageUri=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPOSITORY_NAME:latest --role $ROLE_ARN --timeout 300 --memory-size 2048 --ephemeral-storage Size=2048 --region $AWS_REGION

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ ROLE_ARN="arn:aws:iam::$AWS_ACCOUNT_ID:ro
le/lambda-selenium-execution-role"

aws lambda create-function --function-name $FUNCTION_NAME --package-type Image --code ImageUri
=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPOSITORY_NAME:latest --role $ROLE_ARN --
timeout 300 --memory-size 2048 --ephemeral-storage Size=2048 --region $AWS_REGION
{
    "FunctionName": "selenium",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:************:function:selenium",
    "Role": "arn:aws:iam::************:role/lambda-selenium-execution-role",
    "CodeSize": 0,
    "Description": "",
    "Timeout": 300,
    "MemorySize": 2048,
    "LastModified": "2025-11-07T17:21:05.761+0000",
    "CodeSha256": "6823d0ed47085d7fa27c86dd9b176f4d7a5a3f1013df96ba8526219ea402705f",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "0cdee188-39f0-45e9-9cf7-5776bcc34c73",
    "State": "Pending",
    "StateReason": "The function is being created.",
    "StateReasonCode": "Creating",
    "PackageType": "Image",
    "Architectures": [
        "x86_64"
    ],
    "EphemeralStorage": {
        "Size": 2048
    },
    "SnapStart": {
        "ApplyOn": "None",
        "OptimizationStatus": "Off"
    },
    "LoggingConfig": {
        "LogFormat": "Text",
        "LogGroup": "/aws/lambda/selenium"
    }
}

ステップ6: テスト実行

# Lambda関数をテスト
aws lambda invoke --function-name $FUNCTION_NAME --region $AWS_REGION --log-type Tail --query 'LogResult' --output text response.json | base64 -d

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ # Lambda関数をテスト
aws lambda invoke --function-name $FUNCTION_NAME --region $AWS_REGION --log-type Tail --query
'LogResult' --output text response.json | base64 -d
START RequestId: 0d243959-e165-49ed-98b6-37612856f102 Version: $LATEST
ページタイトル: Google
END RequestId: 0d243959-e165-49ed-98b6-37612856f102
REPORT RequestId: 0d243959-e165-49ed-98b6-37612856f102  Duration: 5713.08 ms    Billed Duratio
n: 6183 ms      Memory Size: 2048 MB    Max Memory Used: 535 MB Init Duration: 469.11 ms
# レスポンスを確認
cat response.json

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ cat response.json
{"statusCode": 200, "body": "Google"}

ステップ7: ログの確認

# CloudWatch Logsでリアルタイムにログを確認
aws logs tail /aws/lambda/$FUNCTION_NAME --follow --region $AWS_REGION

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ # CloudWatch Logsでリアルタイムにログを確認
aws logs tail /aws/lambda/$FUNCTION_NAME --follow --region $AWS_REGION
2025-11-07T17:25:06.647000+00:00 2025/11/07/[$LATEST]9c42a19112ab43eca03f3247eefb5e10 START RequestId: 0d243959-e165-49ed-98b6-37612856f102 Version: $LATEST
2025-11-07T17:25:12.074000+00:00 2025/11/07/[$LATEST]9c42a19112ab43eca03f3247eefb5e10 ページタイトル: Google
2025-11-07T17:25:12.361000+00:00 2025/11/07/[$LATEST]9c42a19112ab43eca03f3247eefb5e10 END RequestId: 0d243959-e165-49ed-98b6-37612856f102
2025-11-07T17:25:12.361000+00:00 2025/11/07/[$LATEST]9c42a19112ab43eca03f3247eefb5e10 REPORT RequestId: 0d243959-e165-49ed-98b6-37612856f102  Duration: 5713.08 ms    Billed Duration: 6183ms      Memory Size: 2048 MB    Max Memory Used: 535 MB Init Duration: 469.11 ms

API Gatewayを設定する

HTTP APIを作成

今回は低コストで運用できるためHTTP APIを使用
また、次のインテグレーションでIDを使うため変数に格納します。

API_ID=$(aws apigatewayv2 create-api --name "selenium-test-api" --protocol-type HTTP --region $AWS_REGION --query 'ApiId' --output text)

test用のインテグレーションを作成

また、インテグレーションで使うIDを変数に格納します。

TEST_INTEGRATION_ID=$(aws apigatewayv2 create-integration --api-id $API_ID --integration-type AWS_PROXY --integration-uri arn:aws:lambda:$AWS_REGION:$AWS_ACCOUNT_ID:function:$FUNCTION_NAME --payload-format-version 2.0 --region $AWS_REGION --query 'IntegrationId' --output text)

ルートを作成

aws apigatewayv2 create-route --api-id $API_ID --route-key "POST /test" --target integrations/$TEST_INTEGRATION_ID --region $AWS_REGION

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ aws apigatewayv2 create-route --api-id $API_ID --route-key "POST /test" --target integrations/$TEST_INTEGRATION_ID --region $AWS_REGION{
    "ApiKeyRequired": false,
    "AuthorizationType": "NONE",
    "RouteId": "************",
    "RouteKey": "POST /test",
    "Target": "integrations/e4o2enn"
}

ステージを作成しデプロイする

aws apigatewayv2 create-stage --api-id $API_ID --stage-name prod --auto-deploy --region $AWS_REGION

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ aws apigatewayv2 create-stage --api-id$API_ID --stage-name prod --auto-deploy --region $AWS_REGION
{
    "AutoDeploy": true,
    "CreatedDate": "2025-11-07T17:29:47+00:00",
    "DefaultRouteSettings": {
        "DetailedMetricsEnabled": false
    },
    "LastUpdatedDate": "2025-11-07T17:29:47+00:00",
    "RouteSettings": {},
    "StageName": "prod",
    "StageVariables": {},
    "Tags": {}
}

Lambda関数に API Gateway からの呼び出し権限を付与

aws lambda add-permission --function-name $FUNCTION_NAME --statement-id apigateway-test --action lambda:InvokeFunction --principal apigateway.amazonaws.com --source-arn "arn:aws:execute-api:$AWS_REGION:$AWS_ACCOUNT_ID:$API_ID/*/*/test" --region $AWS_REGION

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ aws lambda add-permission --function-name
 $FUNCTION_NAME --statement-id apigateway-test --action lambda:InvokeFunction --principal apig
ateway.amazonaws.com --source-arn "arn:aws:execute-api:$AWS_REGION:$AWS_ACCOUNT_ID:$API_ID/*/*
/test" --region $AWS_REGION
{
    "Statement": "{\"Sid\":\"apigateway-test\",\"Effect\":\"Allow\",\"Principal\":{\"Service\"
:\"apigateway.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lam
bda:ap-northeast-1:************:function:selenium\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":
\"arn:aws:execute-api:ap-northeast-1:************:urmkpnelmf/*/*/test\"}}}"
}

API エンドポイント

ENDPOINT=https://$API_ID.execute-api.$AWS_REGION.amazonaws.com/prod/test

API Gatewayのテスト

curl -X POST $ENDPOINT

実行結果

test@test:~/lambda/test/docker-selenium-lambda$ curl -X POST $ENDPOINT
Google

以上!

Discussion