Symfony on a lambda: first deployment
Matsounga Jules
Posted on November 8, 2019
Symfony on a lambda: first deployment
English isn't my first language, help me improve my posts by pointing my mistakes
We can run serveless on many platforms, and same goes for cloud functions. As Bref only support AWS, we will focus on this platform.
As mentioned, we will use Bref. It help run PHP on a AWS Lambda.
We will use it with Symfony, a popular PHP framework.
If you are lost, you can find the project on Github: link
Each branch will match a chapter.
Requirements
Have the requirements to run Symfony on your computer (PHP and some extensions)
Have an AWS account(you can create one here: registration). We will stay below free tier limit, so no worries for your wallet.
-
Create an access key:
- Create a new user here
- Add a user name
- Enable
Programmatic access
- Click Attach existing policies directly, search for **AdministratorAccess **and select it.
Warning: it is recommended to only select right that you really need. But too keep this presentation simple, we will use a full access key.
- Finish creating the user
- Take note of keys generated, we will need them after
Install serverless:
npm install -g serverless
Create the configuration of serverless:
serverless config credentials --provider aws --key <key> --secret <secret>
where key and secret are from the keys generated earlier
Symfony
We now have everything we need to start our development. Let's go !
Installation of Symfony
In your terminal, type the following command at the root of your projects composer create-project symfony/website-skeleton [my_project_name]
(remember to replace [my_project_name] with yours)
Once the installation done, you can go in the folder where is your Symfony application.
We can now install Bref, which is required to deploy PHP on a lambda. To do so, once in the project, type composer require bref/bref
Creation of the serverless.yml
This file will define the architecture that we will deploy on AWS, that will be called CloudFormation. It's in this file that we will define services and resources and their configurations that we want on AWS.
To create this file, Bref give us a command that will help init a project. Type vendor/bin/bref init
et select HTTP application
.
The command create our serverless.yml file and an index.php file at the root of our project. You can delete index.php we wont use it.
The serverless.yml should look like this:
service: app #Name of your application
provider:
name: aws #Provider used by Serverless
region: us-east-1 #Region where you will deploy your CloudFormation
runtime: provided
plugins:
- ./vendor/bref/bref
functions:
api: #Name of the function
handler: index.php
description: ''
timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
layers:
- ${bref:layer.php-73-fpm}
events:
- http: 'ANY /'
- http: 'ANY /{proxy+}'
The most interesting part is functions where we will be able to define our functions that we need (it can be commands, apis, crons, etc...).
- handler: the php file used by the lambda
- description: well, a description ?
- timeout: maximum execution time of the function
- layers: Layers are environment used by the lambda to run, you can have multiple layers to add other extensions, dependencies etc. Here, we use the layer of Bref, that run PHP with the layer of Node (more on layers)
- events: events that will trigger the function. On this function, the HTTP event will trigger the lambda, but there is a lot of other events triggered by AWS that we can listen. We will see an other one later
Now that we have a better understanding of our file, lets changed it to run Symfony.
service: cloud-project
provider:
name: aws
region: eu-west-2
runtime: provided
stage: dev
environment:
APP_ENV: prod
plugins:
- ./vendor/bref/bref
functions:
website:
handler: public/index.php
timeout: 28
layers:
- ${bref:layer.php-73-fpm}
events:
- http: 'ANY /'
- http: 'ANY /{proxy+}'
console:
handler: bin/console
timeout: 120
layers:
- ${bref:layer.php-73}
- ${bref:layer.console}
Not too many change. I changed the name of the application in cloud-project
(you can put whatever you want). I also add a stage that define the environment publish.
In environment we have the environment variables used by our functions.
I also create 2 functions:
- website that is the same than the api function we had in the file. We only change the handler that now target the index.php of Symfony.
- console used to run Symfony command
Symfony configuration
We will have to change some files in Symfony to make it work on a lambda.
System file is readonly except /tmp, we have to change where are stored cache and logs.
In src/Kernel.php, we need to add 2 methods:
// src/Kernel.php
...
public function getLogDir(): string
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/log/';
}
return parent::getLogDir();
}
public function getCacheDir(): string
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/cache/'.$this->environment;
}
return parent::getCacheDir();
}
We also need to change index.php.
Once deployed, the lambda that use API Gateway have a domain that is created and that end with th stage deployed (ex: https://lamnda/dev). It can create some issue with PHP framework. You can easily solve this by creating a custom domain that get ride of this suffix (more information](https://bref.sh/docs/environment/custom-domains.html)). But to keep this presentation accessible, we won't do it.
We have to change some servers variables so the Symfony routing wont break.
// public/index.php
$_SERVER['SCRIPT_NAME'] = '/dev/index.php';
if (strpos($_SERVER['REQUEST_URI'], '/dev') === false) {
$_SERVER['REQUEST_URI'] = '/dev'.$_SERVER['REQUEST_URI'];
}
You need to put this line before new Kernel(...)
. /dev
is the stage defined in serverless.yml. If you create your own application, you should really create your own domain. It can be a bit long (propagation of DNS) but otherwise it's quite simple.
We don't need anything else. Let's code a bit.
Building a homepage
We will create a simple landing page si we have something to deploy. But we will keep it simple:
Create a file HomeController in src/Controller
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/", name="home")
*/
public function homeAction(): Response
{
return $this->render('home/index.html.twig');
}
}
Create a file index.html.twig in templates/home
{% extends 'base.html.twig' %}
{% block body %}
<h1>Symfony and lambdas</h1>
{% endblock %}
Yes, it's really simple but we don't really need anything else.
To see if it's working, type: php bin/console server:run
and go to the url displayed. You should see your page.
Now, let's deploy it !
Deploy in the cloud
It might disappoint you, but you just need to run serverless deploy
. The command will package your project, create the required resources on AWS. After few minutes, the endpoints of your function will be displayed. Use the first one, and you should see your site deployed !
You can see your CloudFormation on AWS here. You can also see the resources created by selecting Model then Display in Designer.
We now have a Symfony application running on AWS. Of course we are only at the beginning, but we will discover more in the next chapters.
In the next chapter we will talk about assets. Because without assets, there is no CSS or JS !
Posted on November 8, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.