Lambdaで動くLaravelからSQSにJobを投げた
現在、LaravelのプログラムをLambdaで動かして、APIサーバとして稼働させています。API Gateway経由なので、30秒の制限があり、30秒以上掛かりそうな処理に関しては、Jobにして、SQS経由で別のLambda関数で処理しています。今回、LaravelからSQSへ投入ができなかったので詰まったポイントを解説します。
環境
現在の簡単な環境の解説です。
プログラム
PHP 8.2
Laravel 10
bref/bref 2.1
bref/laravel-bridge 2.2
aws/aws-sdk-php-laravel 3.8
サーバ(AWS)
Lambda(APPサーバ)
API Gateway(WEBサーバ)
Aurora Serverless V2(DBサーバ)
CodePipeline(CI/CD)
VPC
プライベートサブネット 2つ
パブリックサブネット 2つ
NATゲートウェイ 1つ
環境構築手順
まずはVPC環境を構築します。現在のGUI操作でのVPC環境構築が大分楽になったので、その手順どおりに、一般的なVPC環境を構築します。
次にCodePipelineの環境を構築します。ソースコードはCodeCommitに置き、CodeBuildでbuildspec.ymlの手順どおりにビルドします。buildspec.ymlの例です。
version: 0.2
env:
variables:
VAR: "{コミットしても問題無い値}"
parameter-store:
ENV: "{ソースコードに含めない値}"
phases:
install:
runtime-versions:
php: 8.2
nodejs: 18
commands:
- apt-get update -y
- apt-get install -y mysql-client-core-8.0 # CodeBuildからAurora Serverless接続用
- npm install -g serverless # デプロイ用のserverless frameworkをインストール
# DBの作成
- mysql -u xxxxxx -pxxxxxx -h xxxxxx -e "CREATE DATABASE IF NOT EXISTS ${database_name};"
- composer install # アプリケーションのライブラリをインストール
- serverless plugin install -n serverless-lift
pre_build:
commands:
# ENVファイルをParameter Storeからコピーして、.envにする
- aws ssm get-parameter --name xxxxx --region=${AWS_DEFAULT_REGION} --with-decryption --output text --query 'Parameter.Value' > .env
build:
commands:
# テストを実行する
- XDEBUG_MODE=coverage ./vendor/bin/phpunit ./tests --coverage-cobertura coverage-report.xml --log-junit unittest-report.xml
post_build:
commands:
# デプロイ
- php artisan migrate --force
- php artisan config:clear
- php artisan cache:clear
- php artisan optimize:clear
- composer dump-autoload -o
- sls deploy
reports:
coverage-report:
files:
- coverage-report.xml
file-format: CoberturaXml
unittest-report:
files:
- unittest-report.xml
file-format: JunitXml
artifaces:
files: '**/*'
cache:
paths:
- 'node_modules/**/*'
- 'vendor/**/*'
という感じでCodeBuildを走らせています。デプロイまで全てCodeBuildで終わらせるので、CodePipelineである必要性は微妙です。
sls deployコマンドで実行されるserverless.ymlの例です。
service: xxxxx
provider:
name: aws
region: xx-xxxxxx-1
stage: prod
runtime: provided
deploymentBucket:
name: xxxxxx
vpc:
securityGroupIds:
- sg-xxxxxx # VPCでLambdaが利用するSecurityGroup
subnetIds: # VPCのプライベートサブネットを指定する
- subnet-xxxxxxx
- subnet-xxxxxxx
environment:
QUEUE_CONNECTION: sqs
SQS_QUEUE: ${construct:jobs.queueUrl}
plugins:
- ./vendor/bref/bref
- serverless-lift
package:
exclude:
- node_modules/** # APIサーバのため不要
- public/storage # ファイルアップロード機能は無い
- resources/assets/** # Viewが無い
- storage/**
- tests/**
functions:
api: # APIサーバの設定
handler: public/index.php
timeout: 28
layers:
- ${bref:layer.php-82-fpm}
events:
- http:
path: /
method: any
- http:
path: /{proxy+}
method: any
constructs:
jobs: # SQSの設定
type: queue
worker:
handler: Bref\LaravelBridge\Queue\QueueHandler
runtime: php-82
timeout: 900
SQSの設定をconstructsで行い、${construct:jobs.queueUrl}をLambdaの実行環境の変数に入れているので、Laravelの.envに設定する必要は無いです。
LambdaをPrivate Subnetに設置する理由は、API Gateway経由でのみのアクセスのため、Publicに置く必要が無いからです。
最初LambdaをPublic Subnetに置き、SQS Queueを投げようとしたのですが、SQSまでメッセージが飛んでいませんでした。VPC Lambdaでは、パブリックIPアドレスが提供されていないためでした。
ネットワークの設定を見直し、RDS、Lambda、CodeBuildを全てPrivate Subnetに置き、Public Subnetにはインターネットゲートウェイを置く措置に変更しました。今回のLaravelのAPIでは、外部アクセスが必須だったため、ネットワークインターフェース(ENI)にElasticIPを付与するか、インターネットゲートウェイを設置するか迷いましたが、インターネットゲートウェイにしました。
CodeBuildで利用していたので、すでに設置済みだったこと。ENIにEIPを設定する方法もあったが、いろいろな制約があること。2つのパターンからインターネットゲートウェイの設置にしました。
単にSQSへのデータ送信のみであれば、インターネットゲートウェイよりもSQSエンドポイントを設定する方が良いと思います。
Laravel
設定に必要な.envやconfig/queue.phpを見直し、Jobを作成しました。
php artisan make:job SampleJob
このコマンドでJobが作成できるので、あとは必要な場面で、
SampleJob::dispatch()
でSQSへのトリガーが無事に発行されました。dispatch()前後でログを見て、Brefがtimeoutになっている。SQSにイベントのメッセージが来ていないなどの場合は、VPCのネットワークの確認が必要でした。
この記事が気に入ったらサポートをしてみませんか?