When using Intervention\Image in laravel on lambda
The following error has occurred.
By the way, it works in the local environment.
I have to add gd.
[2021-08-17 10:37:18] DEV.ERROR: GD Library extension not available with this PHP installation.
{"exception":"[object] (Intervention\Image\Exception\NotSupportedException(code: 0):
GD Library extension not available with this PHP installation.
at /var/task/vendor/intervention/image/src/Intervention/Image/Gd/Driver.php:19)
What I looked up
https://bref.sh/docs/environment/php.html#extensions
https://github.com/brefphp/extra-php-extensions
Deployment method
We are deploying to lambda using the sls command.
sls deploy --stage dev
Based on the investigation, the following is implemented
composer require bref/extra-php-extensions
Added below
serverless.yml
plugins:
- ./vendor/bref/bref
- ./vendor/bref/extra-php-extensions #add
functions:
# This function runs the Laravel website/API
web:
image:
name: laravel
events:
- httpApi: '*'
# This function lets us run artisan commands in Lambda
artisan:
handler: artisan
timeout: 120 # in seconds
layers:
- ${bref:layer.php-80}
- ${bref:layer.console}
- ${bref-extra:gd-php-80} #add
Even if the above settings are added and deployed, they are not updated. .. why?
enviroment
Laravel Framework 8.33.1
PHP 7.4.3
bref
serverless
I'm sorry if English is strange.
Put the layers into web "tag".
plugins:
- ./vendor/bref/bref
- ./vendor/bref/extra-php-extensions #add
functions:
# This function runs the Laravel website/API
web:
image:
name: laravel
layers:
- ${bref-extra:gd-php-80} #add
events:
- httpApi: '*'
# This function lets us run artisan commands in Lambda
artisan:
handler: artisan
timeout: 120 # in seconds
layers:
- ${bref:layer.php-80}
- ${bref:layer.console}
Then add the folder php/conf.d inside put a file with extension .ini. For example php.ini. In it just put:
extension=gd
Related
I'm using Cloud Build to deploy my app on Cloud Run. I'd like to set php artisan commands in my cloudbuild.yaml to run migrations, init passport library... But I got this error on my Laravel init step:
Starting Step #3 - "Laravel Init"
Step #3 - "Laravel Init": Already have image (with digest): gcr.io/cloud-builders/gcloud
Step #3 - "Laravel Init": bash: php: command not found
Step #3 - "Laravel Init": bash: line 1: php: command not found
Step #3 - "Laravel Init": bash: line 2: php: command not found
Step #3 - "Laravel Init": bash: line 3: php: command not found
Step #3 - "Laravel Init": bash: line 4: php: command not found
Finished Step #3 - "Laravel Init"
ERROR
ERROR: build step 3 "gcr.io/cloud-builders/gcloud" failed: step exited with non-zero status: 127
And here is my cloudbuild.yaml
steps:
# Build the container image
- name: 'gcr.io/cloud-builders/docker'
args:
...
id: Build
# Push the container image to Artifacts Registry
- name: 'gcr.io/cloud-builders/docker'
...
id: Push
# Deploy container image to Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
id: Deploy
entrypoint: gcloud
...
# Laravel Init
- name: 'gcr.io/cloud-builders/gcloud'
id: Laravel Init
entrypoint: "bash"
args:
- "-c"
- |
php artisan migrate --force
php artisan db:seed --force
php artisan db:seed --class=Database\\Seeders\\UsersTableSeeder --force
php artisan passport:install
images:
- 'europe-west3-docker.pkg.dev/$PROJECT_ID/.....'
tags:
- latest
How can I do to execute my php artisan commands ?
I found a solution. I used the helper exec-wrapper. With this helper I can use my laravel container env and connect to Cloud SQL with the embedded cloud sql proxy. So I just have to pass my latest current image previously built in the first step in cloudbuild.yaml. I set the database socket connection and then I pass the migration.sh file where I can run all my php artisan commands.
I'm using mysql so you have to adjust port and connection name if you are using another Database.
cloudbuild.yaml:
steps:
# Build the container image
- name: 'gcr.io/cloud-builders/docker'
args:
...
id: Build
# Push the container image to Artifacts Registry
- name: 'gcr.io/cloud-builders/docker'
...
id: Push
# Deploy container image to Cloud Run
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
id: Deploy
entrypoint: gcloud
...
# Laravel Init
- name: 'gcr.io/google-appengine/exec-wrapper'
id: Laravel Init
args: [
'-i', '<YOUR_IMAGE_URL>',
'-e', 'DB_CONNECTION=mysql',
'-e', 'DB_SOCKET=/cloudsql/<YOUR_CLOUD_SQL_INSTANCE>',
'-e', 'DB_PORT=3306',
'-e', 'DB_DATABASE=<YOUR_DATABASE_NAME>',
'-e', 'DB_USERNAME=<YOUR_DB_USER>',
'-e', 'DB_PASSWORD=<YOUR_DB_PASS>',
'-s', '<YOUR_CLOUD_SQL_INSTANCE>',
'--', '/app/scripts/migration.sh'
]
images:
- 'europe-west3-docker.pkg.dev/$PROJECT_ID/.....'
Care about /app folder in /app/scripts/migration.sh. /app is my WORKDIR that I set in my Dockerfile
migration.sh look like this:
#!/bin/bash
php artisan migrate --force
php artisan db:seed --force
#... add more commands
Don't forget to Add the permission Client Cloud SQL to the Cloud Build service in the IAM section else Cloud Build cannot connect to your Cloud SQL instance.
And care about if your image has an entrypoint file. You have to use exec $# to execute the -- command from app engine exec wrapper. If you don't use it the commands will be ignored.
Based off this output php: command not found, it looks like you don't have PHP installed in your cloud environment
I'm using laravel 8 and bref to deploy it on lambda. After making a cron job function to send email. When I deploy it, there's a problem with the facade
{
"errorType": "RuntimeException",
"errorMessage": "A facade root has not been set.",
"stackTrace": [
"#0 /var/task/app/functions/sendTestMail.php(11): Illuminate\\Support\\Facades\\Facade::__callStatic()",
"#1 /var/task/vendor/bref/bref/src/Runtime/Invoker.php(34): Bref\\Runtime\\FileHandlerLocator->App\\Functions\\{closure}()",
"#2 /var/task/vendor/bref/bref/src/Runtime/LambdaRuntime.php(102): Bref\\Runtime\\Invoker->invoke()",
"#3 /opt/bref/bootstrap.php(43): Bref\\Runtime\\LambdaRuntime->processNextEvent()",
"#4 {main}"
]
}
here is my directory structure and the function:
sendTestMail.php
serverless.yml:
service: test
provider:
name: aws
# The AWS region in which to deploy (us-east-1 is the default)
region: ap-southeast-1
# The stage of the application, e.g. dev, production, staging… ('dev' is the default)
stage: dev
runtime: provided.al2
package:
# Directories to exclude from deployment
exclude:
- node_modules/**
- public/storage
- resources/assets/**
- storage/**
- tests/**
functions:
# This function runs the Laravel website/API
web:
handler: public/index.php
timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
layers:
- ${bref:layer.php-80-fpm}
events:
- httpApi: "*"
# This function lets us run artisan commands in Lambda
artisan:
handler: artisan
timeout: 120 # in seconds
layers:
- ${bref:layer.php-80} # PHP
- ${bref:layer.console} # The "console" layer
cron:
handler: app/functions/sendTestMail.php
layers:
- ${bref:layer.php-80}
events:
- schedule: rate(5 minutes)
plugins:
# We need to include the Bref plugin
- ./vendor/bref/bref
Anyone know how to resolve this issue? And btw, how can I test a handler function on my local machine before deploying? Thank you
I believe this is due to the issue that in your web function the handler is public/index.php. This properly initializes the Laravel application. Your cron functions handler is app/functions/sendTestMail.php so index.php never gets called and the Laravel kernel never handles the request.
I don't have a great solution at the moment because I feel like it is breaking a lot of best practices and rules in Laravel and want to experiment more with it. But I was able to take the entire index.php content and pasted it above my return function in the file that Lambda was calling.
So in other words I think if you paste this
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
use App\Models\Task;
define('LARAVEL_START', microtime(true));
if (file_exists(__DIR__.'/storage/framework/maintenance.php')) {
require __DIR__.'/storage/framework/maintenance.php';
}
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = tap($kernel->handle(
$request = Request::capture()
))->send();
$kernel->terminate($request, $response);
As the first thing in your app/functions/sendTestMail.php file, it will likely work. Depending on what you have coded in your Middleware as that will run first.
This worked for me in my application.
You can try this for the error
php artisan config:cache
php artisan config:clear
php artisan cache:clear
I created aws lambda function based on laravel framework using bref. Link: https://bref.sh
this is my code to download using media library spartie
$mediaItem = Media::where('model_id',$id)->where('model_type','App\MODEL')->first();
return $mediaItem;
My configuration is
AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXX+XXXXXXXX
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=my-bucket
When I download file locally all work perfectly, but when I try to download on s3 in lambda function I got error:
{ "message" : "Internal server error" }
I use this package aws:
league/flysystem-aws-s3-v3 ~1.0
my serverless.yaml
service: serverless-api
provider:
name: aws
region: eu-central-1
runtime: provided
environment:
APP_ENV: production
iamRoleStatements:
- Effect: Allow
Action:
- s3:*
Resource: 'arn:aws:s3:::bucket/*'
plugins:
- ./vendor/bref/bref
package:
exclude:
- node_modules/**
- public/storage
- storage/**
- tests/**
- .env
functions:
website:
handler: public/index.php
timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
layers:
- arn:aws:lambda:eu-central-1:209497400698:layer:php-72-fpm:23
events:
- http: 'ANY /'
- http: 'ANY /{proxy+}'
artisan:
handler: artisan
timeout: 120 # in seconds
layers:
- arn:aws:lambda:eu-central-1:209497400698:layer:php-72:22
- arn:aws:lambda:eu-central-1:209497400698:layer:console:22
also i assign role for s3 , But I have no idea how it's the problem .
thank you .
I am investigating on how I can create an AWS lambda in php using the bref library
Therefore, according to documentation I set up the environment with the following command cocktail:
sudo -H npm install -g serverless
composer require bref/bref
Then using the following command created my first php lambda:
vendor/bin/bref init
And I selected the first option PHP Function provided by default. Creating the following creating an index.php file:
declare(strict_types=1);
require __DIR__.'/vendor/autoload.php';
lambda(function ($event) {
return 'Hello ' . ($event['name'] ?? 'world');
});
Then I changed my serverless.yml into that:
service: app
provider:
name: aws
region: eu-central-1
runtime: provided
stage: ${opt:stage,'local'}
package:
exclude:
- '.gitignore'
plugins:
- ./vendor/bref/bref
functions:
dummy:
handler: index.php
name: Dummy-${self:provider.stage}
description: 'Dummy Lambda'
layers:
- ${bref:layer.php-73}
And I try to launch it via the following command:
sls invoke local --stage=local --docker --function dummy
But I get the following error:
{"errorType":"exitError","errorMessage":"RequestId: 6403ebee-13b6-179f-78cb-41cb2f517460 Error: Couldn't find valid bootstrap(s): [/var/task/bootstrap /opt/bootstrap]"}
Therefore, I want to ask why I am unable to run my lambda localy?
Since this question is getting a lot of views, I recommend to have a look at the Bref documentation:
Local development for PHP functions
That involves using the bref local CLI command instead of serverless invoke local:
$ vendor/bin/bref local hello
Hello world
# With JSON event data
$ vendor/bin/bref local hello '{"name": "Jane"}'
Hello Jane
# With JSON in a file
$ vendor/bin/bref local hello --file=event.json
Hello Jane
On my local, clearing cache before invoking lambda worked fine, I'm using linux / ubuntu
docker system prune --all
sudo apt-get autoremove
sudo apt-get clean
sudo apt-get autoclean
sudo rm -rf ~/.cache/
sudo rm -rf /var/cache/
It is a known bug for bref. It can be solved via providing the layer manually on your function in serverless.yml. So the functions section at serverless.yml should change from:
functions:
dummy:
handler: index.php
name: Dummy-${self:provider.stage}
description: 'Dummy Lambda'
layers:
- ${bref:layer.php-73}
Into:
functions:
dummy:
handler: index.php
name: Dummy-${self:provider.stage}
description: 'Dummy Lambda'
layers:
- 'arn:aws:lambda:eu-central-1:209497400698:layer:php-73:15'
The reason why is that ${bref:layer.php-73} cannot be resolved into a php layer. Therefore, you need to provide manually the arn for the lambda layer.
Keep in mind that the arn comes into various "versions" that is being inidcated from the last number in the arn seperated with :. So in the arn
arn:aws:lambda:eu-central-1:209497400698:layer:php-73:15
Indicated that the layer is in version "15" whitch is thje latest at the moment of the answer. The next one logically should be the:
arn:aws:lambda:eu-central-1:209497400698:layer:php-73:16
I would like deploy my Symfony 4 website thanks to AWS Lambda and Symfony Bref, I followed this docs :
https://bref.sh/docs/frameworks/symfony.html
https://www.vertcitron.fr/node-lambda-serverless/
but I have this error :
My serverless.yaml file :
service: bref-symfony
package:
exclude:
- node_modules/**
- venv/**
provider:
name: aws
region: eu-west-3
runtime: provided
environment:
# Symfony environment variables
APP_ENV: prod
plugins:
- ./vendor/bref/bref
functions:
website:
handler: public/index.php
timeout: 30 # in seconds (API Gateway has a timeout of 30 seconds)
layers:
- ${bref:layer.php-73-fpm}
events:
- http: 'ANY /'
- http: 'ANY /{proxy+}'
console:
handler: bin/console
timeout: 120 # in seconds
layers:
- ${bref:layer.php-73} # PHP
- ${bref:layer.console} # The "console" layer
What is the problem please ?
It looks like you need to cut back on what is included in your app image. Make sure you are running prod-only dependencies on composer:
composer inst --no-dev -o
You also can add something like this to your serverless.yaml to only include the files you need to run your Symfony app:
package:
exclude:
- '*'
- '**'
include:
- 'bin/**'
- 'config/**'
- 'public/index.php'
- 'src/**'
- 'var/cache/prod/**'
- 'translations/**'
- 'vendor/**'
- '.env'