見出し画像

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のネットワークの確認が必要でした。



この記事が気に入ったらサポートをしてみませんか?