The Serverless PHP Application Rob Allen LaravelConf Taiwan 2020

Serverless? Rob Allen ~ @akrabat

Platform options Rob Allen ~ @akrabat

Platform options Rob Allen ~ @akrabat

Platform options Rob Allen ~ @akrabat

Platform options Rob Allen ~ @akrabat

Platform options Rob Allen ~ @akrabat

Platform options Rob Allen ~ @akrabat

Platform options Rob Allen ~ @akrabat

Serverless Serverless is all about composing software systems from a collection of cloud services. With serverless, you can lean on off-the-shelf cloud services resources for your application architecture, focus on business logic and application needs. Nate Taggart, CEO Stackery Rob Allen ~ @akrabat

FaaS Your code Rob Allen ~ @akrabat

FaaS Deployed to the cloud Rob Allen ~ @akrabat

FaaS Runs when needed Rob Allen ~ @akrabat

FaaS Scaled automatically Rob Allen ~ @akrabat

FaaS Pay only for execution Rob Allen ~ @akrabat

Where are the servers? Rob Allen ~ @akrabat

Rob Allen ~ @akrabat

Rob Allen ~ @akrabat

Use-cases Rob Allen ~ @akrabat

Use-cases Synchronous Service is invoked and provides immediate response (HTTP requests: APIs, chat bots) Rob Allen ~ @akrabat

Use-cases Synchronous Service is invoked and provides immediate response (HTTP requests: APIs, chat bots) Asynchronous Push a message which drives an action later (web hooks, timed events, database changes) Rob Allen ~ @akrabat

Benefits Rob Allen ~ @akrabat

Benefits • No need to maintain infrastructure Rob Allen ~ @akrabat

Benefits • No need to maintain infrastructure • Concentrate on application code Rob Allen ~ @akrabat

Benefits • No need to maintain infrastructure • Concentrate on application code • Pay only for what you use, when you use it Rob Allen ~ @akrabat

Benefits • No need to maintain infrastructure • Concentrate on application code • Pay only for what you use, when you use it • Language agnostic Rob Allen ~ @akrabat

Challenges Rob Allen ~ @akrabat

Challenges • Start up latency Rob Allen ~ @akrabat

Challenges • Start up latency • Time limit Rob Allen ~ @akrabat

Challenges • Start up latency • Time limit • State is external Rob Allen ~ @akrabat

Challenges • Start up latency • Time limit • State is external • Different way of thinking Rob Allen ~ @akrabat

When should you use serverless? Rob Allen ~ @akrabat

When should you use serverless? • Responding to web hooks Rob Allen ~ @akrabat

When should you use serverless? • Responding to web hooks • Additional features without extending current platform Rob Allen ~ @akrabat

When should you use serverless? • Responding to web hooks • Additional features without extending current platform • PWA/Static site contact form, et al. Rob Allen ~ @akrabat

When should you use serverless? • Responding to web hooks • Additional features without extending current platform • PWA/Static site contact form, et al. • Variable traffic levels Rob Allen ~ @akrabat

When should you use serverless? • Responding to web hooks • Additional features without extending current platform • PWA/Static site contact form, et al. • Variable traffic levels • When you want your costs to scale with traffic Rob Allen ~ @akrabat

It’s about value Rob Allen ~ @akrabat

Serverless platforms Rob Allen ~ @akrabat

Serverless languages Rob Allen ~ @akrabat

Serverless platforms with PHP support Rob Allen ~ @akrabat

Hello World AWS Lambda (Bref): <?php require DIR . ‘/vendor/autoload.php’; return function ($event) { $name = $event[‘name’] ?? ‘world’; return ‘Hello ’ . $name; }; Rob Allen ~ @akrabat

Hello World Apache OpenWhisk: <?php function main(array $args) : array { $name = $args[‘name’] ?? ‘world’; return [“greeting” => ‘Hello ’ . $name]; } Rob Allen ~ @akrabat

Hello World OpenFAAS <?php class Handler { public function handle(string $data): void { $decoded = json_decode($data, true); $name = $decoded[‘name’] ?? ‘world’; return ‘Hello ’ . $name; } } Rob Allen ~ @akrabat

Hello World Google Cloud Functions (alpha) <?php use Psr\Http\Message\ServerRequestInterface; function helloHttp(Request $request) { $name = $request->getQueryParams(‘name’) ?? ‘world’; return ‘Hello ’ . $name; } Rob Allen ~ @akrabat

Rob Allen ~ @akrabat

The anatomy of an action function main(array $args) : array { // Marshall inputs from event parameters $name = $args[‘name’] ?? ‘world’; // Do the work $message = ‘Hello ’ . $name // Return result return [“body” => $message]; } Rob Allen ~ @akrabat

Hello World function main(array $args) : array { // Marshall inputs from event parameters $name = $args[‘name’] ?? ‘world’; // Do the work $message = ‘Hello ’ . $name // Return result return [“body” => $message]; } Rob Allen ~ @akrabat

Hello World function main(array $args) : array { // Marshall inputs from event parameters $name = $args[‘name’] ?? ‘world’; // Do the work $message = ‘Hello ’ . $name // Return result return [“body” => $message]; } Rob Allen ~ @akrabat

Hello World function main(array $args) : array { // Marshall inputs from event parameters $name = $args[‘name’] ?? ‘world’; // Do the work $message = ‘Hello ’ . $name // Return result return [“body” => $message]; } Rob Allen ~ @akrabat

Hello World function main(array $args) : array { // Marshall inputs from event parameters $name = $args[‘name’] ?? ‘world’; // Do the work $message = ‘Hello ’ . $name // Return result return [“body” => $message]; } Rob Allen ~ @akrabat

Deploy to OpenWhisk $ zip -q hello.zip hello.php Rob Allen ~ @akrabat

Deploy to OpenWhisk $ zip -q hello.zip hello.php $ wsk action update —kind php:7.3 hello hello.zip ok: updated action hello Rob Allen ~ @akrabat

Run it $ wsk action invoke hello —result —param name Rob Rob Allen ~ @akrabat

Run it $ wsk action invoke hello —result —param name Rob { “body”: “Hello Rob!” } Rob Allen ~ @akrabat

Under the hood Rob Allen ~ @akrabat

OpenWhisk’s architecture Rob Allen ~ @akrabat

Create an action Rob Allen ~ @akrabat

Invoke an action Rob Allen ~ @akrabat

Action container lifecycle • Hosts the user-written code • Controlled via two end points: /init & /run Rob Allen ~ @akrabat

Action container lifecycle • Hosts the user-written code • Controlled via two end points: /init & /run Rob Allen ~ @akrabat

Architecture Rob Allen ~ @akrabat

Monolith architecture Rob Allen ~ @akrabat

Serverless architecture Rob Allen ~ @akrabat

Serverless architecture pattern Rob Allen ~ @akrabat

Functions are key Rob Allen ~ @akrabat

Functions are the Unit of Deployment Rob Allen ~ @akrabat

Functions are the Unit of Scale Rob Allen ~ @akrabat

Functions are Stateless Rob Allen ~ @akrabat

Functions have Structure Rob Allen ~ @akrabat

Structure If it’s non-trivial, software engineering principles apply! • Use multiple methods Rob Allen ~ @akrabat

Structure If it’s non-trivial, software engineering principles apply! • Use multiple methods • Use multiple files Rob Allen ~ @akrabat

Structure If it’s non-trivial, software engineering principles apply! • Use multiple methods • Use multiple files • Integrate reusable dependencies Rob Allen ~ @akrabat

Serverless state machines Rob Allen ~ @akrabat

Serverless state machines Rob Allen ~ @akrabat

Rob Allen ~ @akrabat

Rob Allen ~ @akrabat

Case study Project 365 photo website Rob Allen ~ @akrabat

Project 365 Static website to display my photo-a-day picture for each day of the year. • Hosted on S3 • CloudFront CDN • Lambda/PHP function Rob Allen ~ @akrabat

Lambda/PHP function Rob Allen ~ @akrabat

Infrastructure as code functions: update: handler: index.php events: - schedule: name: project365-build rate: cron(0 */2 * * ? *) Rob Allen ~ @akrabat

Infrastructure as code functions: update: handler: index.php events: - schedule: name: project365-build rate: cron(0 */2 * * ? *) Rob Allen ~ @akrabat

Infrastructure as code functions: update: handler: index.php events: - schedule: name: project365-build rate: cron(0 */2 * * ? *) Rob Allen ~ @akrabat

Process 1. 2. 3. 4. 5. Gather credentials from environment Download photos from Flickr API Create HTML page Upload to S3 Invalidate CloudFront cache Rob Allen ~ @akrabat

main() function main(array $eventData) : array { $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator(); $html = $pageCreator->update($year); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat

main() function main(array $eventData) : array { $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator(); $html = $pageCreator->update($year); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat

main() function main(array $eventData) : array { $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator(); $html = $pageCreator->update($year); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat

main() function main(array $eventData) : array { $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator(); $html = $pageCreator->update($year); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat

main() function main(array $eventData) : array { $year = $eventData[‘year’] ?? date(‘Y’); $pageCreator = new PhotoPageCreator(); $html = $pageCreator->update($year); $uploader = new Uploader($cloudFrontId); $uploader->uploadOne($year, $html, $s3Bucket); $uploader->invalidateCache([‘/’.$year]); } Rob Allen ~ @akrabat

The finished website Rob Allen ~ @akrabat

Thank you! Rob Allen ~ @akrabat

Photo credits - Assembly line: https://www.flickr.com/photos/adiram/3886212918 - Under the hood: https://www.flickr.com/photos/atomichotlinks/7736849388 - Pantheon: https://www.flickr.com/photos/shawnstilwell/4335732627 - Watch mechanism: https://www.flickr.com/photos/shinythings/2168994732 - Holiday snaps: https://www.flickr.com/photos/kjgarbutt/5358075923 - Computer code: https://www.flickr.com/photos/n3wjack/3856456237 - Stars: https://www.flickr.com/photos/gsfc/19125041621 Rob Allen ~ @akrabat