Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A practical guide to PHP serverless applications - LeedsPHP

Thibault
November 18, 2021

A practical guide to PHP serverless applications - LeedsPHP

Slides in web format: https://leedsphp-serverless.netlify.app
Replay of the live talk: https://www.youtube.com/watch?v=2BiidUPHXCM
Associated github repository: https://github.com/t-richard/leeds-php-demo

Talk abstract

This is a step-by-step guide to hosting your PHP applications in AWS Lambda, using an approach that you can apply to almost any API or Website.

Symfony Demo uses Twig, Webpack Encore, a database, sessions, console commands, ... Sounds familar? It is the perfect fit to test this out!

And if you're using another framework like Laravel: fear not, this will work for you too!

We'll see that we can host a fully-featured application without prior knowledge about AWS or Serverless technologies and instantly benefit from infinite scaling and on-demand pricing.

Then we will expand on more advanced use cases to get a broader picture of what a serverless PHP application can look like.

You'll get a clear view of what it takes, what the benefits are, and some of the constraints and limitations.

Thibault

November 18, 2021
Tweet

More Decks by Thibault

Other Decks in Programming

Transcript

  1. A practical guide to PHP serverless applications @t__richard - 17/11/2021

    - Leeds PHP - 03 / 76 Using symfony/demo as an example
  2. Let’s go 🚀 @t__richard - 17/11/2021 - Leeds PHP -

    17 / 76 $ symfony new --demo leeds-talk
  3. Why the Symfony Demo? Complete sample project Symfony Twig Database

    Users & sessions Webpack Encore Console Commands … Self-contained by default Pre-built assets Seeded SQLite database @t__richard - 17/11/2021 - Leeds PHP - 18 / 76
  4. Install Bref @t__richard - 17/11/2021 - Leeds PHP - 19

    / 76 $ composer require bref/bref bref/symfony-bridge
  5. What is Bref? PHP runtimes for AWS Lambda Helpers to

    integrate with AWS services Deployment utilities Framework bridges Documentation @t__richard - 17/11/2021 - Leeds PHP - 20 / 76 Bref provides
  6. A few config tweaks 🔧 @t__richard - 17/11/2021 - Leeds

    PHP - 21 / 76 // src/Kernel.php namespace App; + use Bref\SymfonyBridge\BrefKernel; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; - use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\RouteCollectionBuilder; - class Kernel extends BaseKernel + class Kernel extends BrefKernel { // ...
  7. A few config tweaks 🔧 @t__richard - 17/11/2021 - Leeds

    PHP - 22 / 76 # config/packages/framework.yaml framework: trusted_proxies: '127.0.0.1' trusted_headers: [ 'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-host' ]
  8. A few config tweaks 🔧 @t__richard - 17/11/2021 - Leeds

    PHP - 23 / 76 # config/packages/prod/monolog.yaml monolog: handlers: # ... nested: type: stream path: php://stderr
  9. serverless.yml @t__richard - 17/11/2021 - Leeds PHP - 25 /

    76 service: symfony provider: name: aws region: us-east-1 stage: dev runtime: provided.al2 environment: APP_ENV: prod plugins: - ./vendor/bref/bref functions: web: events: - httpApi: '*' handler: public/index.php layers: - ${bref:layer.php-80-fpm} timeout: 28
  10. serverless.yml @t__richard - 17/11/2021 - Leeds PHP - 26 /

    76 service: leeds-talk-blog provider: name: aws region: us-east-1 stage: dev runtime: provided.al2 environment: APP_ENV: prod plugins: - ./vendor/bref/bref functions: web: events: - httpApi: '*' handler: public/index.php layers: - ${bref:layer.php-80-fpm} timeout: 28
  11. serverless.yml @t__richard - 17/11/2021 - Leeds PHP - 27 /

    76 service: leeds-talk-blog provider: name: aws region: eu-west-2 # London stage: dev runtime: provided.al2 environment: APP_ENV: prod plugins: - ./vendor/bref/bref functions: web: events: - httpApi: '*' handler: public/index.php layers: - ${bref:layer.php-80-fpm} timeout: 28
  12. Maybe it should be a bit more like… @t__richard -

    17/11/2021 - Leeds PHP - 29 / 76 $ composer install --no-dev --optimize-autoloader $ bin/console cache:clear --env=prod $ serverless deploy
  13. We need an efficient and reliable way of delivering assets

    @t__richard - 17/11/2021 - Leeds PHP - 33 / 76
  14. Install Lift @t__richard - 17/11/2021 - Leeds PHP - 34

    / 76 $ serverless plugin install -n serverless-lift
  15. What is Lift? Serverless Framework plugin Abstraction layer for AWS

    infrastructure Focused on use-cases, not infrastructure @t__richard - 17/11/2021 - Leeds PHP - 35 / 76
  16. Server-side website @t__richard - 17/11/2021 - Leeds PHP - 36

    / 76 # serverless.yml service: leeds-talk-blog provider: ... plugins: - ./vendor/bref/bref - serverless-lift functions: ... constructs: website: type: server-side-website assets: '/build/*': public/build '/favicon.ico': public/favicon.ico '/apple-touch-icon.png': public/apple-touch-icon.png '/robots.txt': public/robots.txt
  17. Exclude assets from Lambda @t__richard - 17/11/2021 - Leeds PHP

    - 37 / 76 # serverless.yml service: leeds-talk-blog ... package: patterns: - '!assets/**' - '!node_modules/**' - '!public/build/**' - '!tests/**' - '!var/**' - 'var/cache/prod/**' - 'public/build/entrypoints.json' - 'public/build/manifest.json'
  18. SQL Database construct 🗄 @t__richard - 17/11/2021 - Leeds PHP

    - 43 / 76 # serverless.yml service: leeds-talk-blog provider: ... environment: APP_ENV: prod # replaced by postgres://user:pass@host/dbName DATABASE_URL: ${construct:database.databaseUrl} plugins: ... functions: ... constructs: website: ... database: type: database/sql engine: postgres password: ${env:DB_PASSWORD}
  19. Enable pdo_pgsql PHP extension ⚙ @t__richard - 17/11/2021 - Leeds

    PHP - 44 / 76 ` ` # php/conf.d/php.ini extension=pdo_pgsql # you can configure more INI stuff here
  20. Deploying is so simple it becomes boring 😴 😪 @t__richard

    - 17/11/2021 - Leeds PHP - 45 / 76 $ serverless deploy
  21. See the logs 📜 @t__richard - 17/11/2021 - Leeds PHP

    - 48 / 76 $ serverless logs -f web
  22. See the logs 📜 SQLSTATE[42P01]: Undefined table: 7 ERROR: relation

    "symfony_demo_post" does not exist @t__richard - 17/11/2021 - Leeds PHP - 49 / 76
  23. The console function 🎮 @t__richard - 17/11/2021 - Leeds PHP

    - 51 / 76 # serverless.yml ... functions: web: events: - httpApi: '*' handler: public/index.php layers: - ${bref:layer.php-80-fpm} timeout: 28 console: handler: bin/console layers: - ${bref:layer.php-80} - ${bref:layer.console} timeout: 120
  24. Back to our deployment script @t__richard - 17/11/2021 - Leeds

    PHP - 52 / 76 $ serverless deploy $ vendor/bin/bref cli leeds-talk-blog-dev-console -- doctrine:migration:migrate --no-interaction
  25. We’re done! 🎉 Deployed our code Served assets from a

    CDN Created a Database Executed migrations @t__richard - 17/11/2021 - Leeds PHP - 54 / 76
  26. An application doesn’t need more, right!? Wait, I use sessions!

    What about queues? And HTTP cache? Where is my beloved crontab ? I need dev, staging & prod environments! I can’t keep those ugly domains! @t__richard - 17/11/2021 - Leeds PHP - 55 / 76 ` `
  27. Wait, I use sessions! @t__richard - 17/11/2021 - Leeds PHP

    - 57 / 76 Store them in a shared store like the Database or DynamoDB # config/services.yaml services: # ... Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: arguments: - '%env(DATABASE_URL)%'
  28. Wait, I use sessions! @t__richard - 17/11/2021 - Leeds PHP

    - 58 / 76 Store them in a shared store like the Database or DynamoDB # config/packages/framework.yaml framework: session: # ... handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
  29. What about queues? @t__richard - 17/11/2021 - Leeds PHP -

    59 / 76 I want to use Symfony Messenger! $ composer require bref/symfony-messenger
  30. What about queues? @t__richard - 17/11/2021 - Leeds PHP -

    60 / 76 I want to use Symfony Messenger! # serverless.yml service: leeds-talk-blog provider: name: aws environment: ... MESSENGER_TRANSPORT_DSN: ${construct:jobs.queueUrl} constructs: ... jobs: type: queue alarm: [email protected] worker: handler: bin/consumer.php timeout: 20 layers: - ${bref:layer.php-80}
  31. What about queues? @t__richard - 17/11/2021 - Leeds PHP -

    61 / 76 I want to use Symfony Messenger!
  32. What about queues? @t__richard - 17/11/2021 - Leeds PHP -

    62 / 76 I want to use Symfony Messenger! # bin/consumer.php <?php declare(strict_types=1); use Bref\Symfony\Messenger\Service\Sqs\SqsConsumer; use Symfony\Component\Dotenv\Dotenv; require dirname(__DIR__).'/vendor/autoload.php'; (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); $kernel = new \App\Kernel($_SERVER['APP_ENV'], (bool)$_SERVER['APP_DEBUG']); $kernel->boot(); // Return the Bref consumer service return $kernel->getContainer()->get(SqsConsumer::class);
  33. What about queues? @t__richard - 17/11/2021 - Leeds PHP -

    63 / 76 I want to use Symfony Messenger! # config/packages/messenger.yaml framework: messenger: transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' options: auto_setup: false routing: 'Symfony\Component\Mailer\Messenger\SendEmailMessage': async
  34. And HTTP cache? It already works thanks to the Server-side

    construct! @t__richard - 17/11/2021 - Leeds PHP - 64 / 76 class BlogController extends AbstractController { /** * @Route("/", defaults={"page": "1", "_format"="html"}, methods="GET", name="blog_index") * @Route("/rss.xml", defaults={"page": "1", "_format"="xml"}, methods="GET", name="blog_rss") * @Route("/page/{page<[1-9]\d*>}", defaults={"_format"="html"}, methods="GET", name="blog_index_paginated") * @Cache(smaxage="10") */ public function index(Request $request, int $page, string $_format, PostRepository $posts, TagRepository $tags): Response { ... } }
  35. Where is my beloved crontab ? @t__richard - 17/11/2021 -

    Leeds PHP - 65 / 76 ` ` # serverless.yml ... functions: ... console: handler: bin/console layers: - ${bref:layer.php-80} - ${bref:layer.console} timeout: 120 events: - schedule: # cron(minutes hours day month weekday year) rate: cron(0 12 ? * MON-FRI *) # Monday to Friday at noon input: '"app:list-users [email protected]"'
  36. I need dev, staging & prod environments! @t__richard - 17/11/2021

    - Leeds PHP - 66 / 76 service: leeds-talk-blog provider: name: aws region: eu-west-2 # London stage: dev runtime: provided.al2 environment: ...
  37. I need dev, staging & prod environments! @t__richard - 17/11/2021

    - Leeds PHP - 67 / 76 $ serverless deploy --stage=dev $ serverless deploy --stage=staging $ serverless deploy --stage=production $ serverless deploy --stage=marketing-demo $ serverless deploy --stage=acme-corp-tenant
  38. I can’t keep those ugly domains! @t__richard - 17/11/2021 -

    Leeds PHP - 68 / 76 # serverless.yml ... constructs: website: # ... domain: blog.leedsphp.org certificate: arn:aws:acm:us-east-1:123456615250:certificate/0a28e63d-d3a9-4578-9f8b-14347bfe8123
  39. I can’t keep those ugly domains! @t__richard - 17/11/2021 -

    Leeds PHP - 69 / 76 # serverless.yml ... constructs: website: # ... domain: - www.leedsphp.org - leedsphp.org redirectToMainDomain: true certificate: arn:aws:acm:us-east-1:123456615250:certificate/0a28e63d-d3a9-4578-9f8b-14347bfe8123
  40. I can’t keep those ugly domains! @t__richard - 17/11/2021 -

    Leeds PHP - 70 / 76 # serverless.yml ... constructs: website: # ... domain: - www.${env:DOMAIN_NAME} - ${env:DOMAIN_NAME} redirectToMainDomain: true certificate: ${env:CERTIFICATE_ARN}
  41. We’re done! 🎉 Deployed our code Served assets from a

    CDN Created a Database Executed migrations Stored sessions in the database Added Symfony Messenger Scheduled CRON jobs Enabled HTTP caching Deployed to multiple environments Configured domains @t__richard - 17/11/2021 - Leeds PHP - 71 / 76
  42. ✅ Benefits Autoscaling built-in Paying for usage, not uptime High

    security and availability Less infrastructure to manage yourself Easy deployment and replication Quick ramp-up thanks to Bref, Serverless framework and Lift Fast major/minor/patch PHP updates @t__richard - 17/11/2021 - Leeds PHP - 72 / 76
  43. @t__richard - 17/11/2021 - Leeds PHP - 73 / 76

    Thibault Richard @t__richard Nodejs 16 was released in April and still no support on Lambda. Python 3.9 release was in Oct 2020 and support was announced 15 days ago. Meanwhile, the @brefphp community is actively working on supporting @official_php 8.1 even before RC1 is available. PHP is dead they say 4:24 PM · Aug 30, 2021 43 1 Copy li… Tweet your reply
  44. ❌ Constraints Platform constraints 28 seconds HTTP timeout 15 minutes

    event timeout Readonly & ephemeral filesystem Cold-starts Database is still a fixed resource Paradigm shift Best suited for greenfield projects @t__richard - 17/11/2021 - Leeds PHP - 74 / 76
  45. Thank you so much for coming tonight 🤩 @t__richard Symfony

    & Bref Slack 💌 [email protected] ⭐ github.com/brefphp/bref ⭐ github.com/getlift/lift @t__richard - 17/11/2021 - Leeds PHP - 76 / 76 It’s question time!