Custom Runtimes
on AWS Lambda
My experience doing Serverless PHP
bene@theodo.co.uk
Ben Ellerby



@EllerbyBen

@EllerbyBen


serverless-transformation


@EllerbyBen






@EllerbyBen
Serverless


What is this Serverless thing?
-
Architectural movement
- Developers send application code which is run by the cloud provider in isolated containers abstracted from the developer.
- Use 3rd party services used to manage backend logic and state (e.g. Firebase, Cognito)
- A framework with the same name

@EllerbyBen

Why Serverless?
💰 Cost reduction
👷♂️ #NoOps
💻 Developers focus on delivering business value
📈 More scalable
🌳 Greener

@EllerbyBen

Not just Lambda (FaaS)



Lambda
S3
Dynamo

API Gateway
Compute
Storage
Data
API Proxy

Cognito
Auth

SQS
Queue

EventBridge
Event Bus

Power and Flexibility








@EllerbyBen

Runtimes

@EllerbyBen


Runtimes

@EllerbyBen








@EllerbyBen



@EllerbyBen

<?php
echo "Not my choice!";
?/>

@EllerbyBen

404
RUNTIME NOT FOUND



@EllerbyBen

200
Custom Runtimes

@EllerbyBen

Runtime Interface
HTTP API for custom runtimes to receive invocation events from Lambda and send response data back within the Lambda execution environment.

@EllerbyBen

Runtime Interface
curl
"http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next"
-
Next Invocation
-
Invocation Response
-
Invocation Error
-
Initialization Error

@EllerbyBen

/runtime/invocation/next
-
Body: JSON Payload
-
Headers: Meta Data
Retrieves an invocation event.
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
GET

@EllerbyBen

.../invocation/AwsRequestId
/response
-
Body: Response
Sends an invocation response to Lambda.
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
POST

@EllerbyBen

.../invocation/AwsRequestId
/error
-
Body: Error Object
Sends an error response
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
POST

@EllerbyBen

Custom Runtime
A runtime is a program that runs a Lambda function's handler method when the function is invoked.
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html

@EllerbyBen

Custom Runtime

bootstrap
-
Shell Script
-
Script in supported language
-
Binary file compiled on Amazon Linux

@EllerbyBen


bootstrap
-
Setup
-
Reading Handler Name (env)
-
Polling for invocations from API
-
Calling Handler with body
-
Posting response

@EllerbyBen

#!/bin/sh
cd $LAMBDA_TASK_ROOT
./node-v11.1.0-linux-x64/bin/node
runtime.js
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html

@EllerbyBen

#!/bin/sh
set -euo pipefail
# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
# Processing
while true
do
HEADERS="$(mktemp)"
# Get an event
EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# Execute the handler function from the script
RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
# Send the response
curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html

@EllerbyBen

runtime-tutorial
├ bootstrap
└ function.sh
https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html

@EllerbyBen


bootstrap

handler

@EllerbyBen


bootstrap

handler
34M

@EllerbyBen













Layers

@EllerbyBen


Layers

@EllerbyBen

A layer is a ZIP archive that contains libraries, a custom runtime, or other dependencies. With layers, you can use libraries in your function without needing to include them in your deployment package.
https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html

@EllerbyBen


bootstrap



handler
handler
handler

@EllerbyBen

-
Reduce deployment package size.
-
Large dependency deployed less often.
-
Easier to manage when change rarely
-
Reduced code storage (75GB)

@EllerbyBen


https://aws.amazon.com/blogs/aws/new-for-aws-lambda-use-any-programming-language-and-share-common-components/

@EllerbyBen

layers:
php7:
path: layers/runtime
functions:
setup:
handler: hello
layers:
- { Ref: php7 }


@EllerbyBen

- Find the AMI of the correct AWS Linux: amzn-ami-hvm-.*-gp2
- Spin up a Large EC2 and ssh in.
- Follow the download and build steps for your language of choice.
- scp a zip down to local
- touch and chmod +x 'bootstrap'
- zip a runtime directory with the binary and bootstrap

@EllerbyBen

#!/opt/bin/php
<?php
// amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2
function getNextRequest()
{
$client = new \GuzzleHttp\Client();
$response = $client->get('http://' . $_ENV['AWS_LAMBDA_RUNTIME_API'] . '/2018-06-01/runtime/invocation/next');
return [
'invocationId' => $response->getHeader('Lambda-Runtime-Aws-Request-Id')[0],
'payload' => json_decode((string) $response->getBody(), true)
];
}
function sendResponse($invocationId, $response)
{
$client = new \GuzzleHttp\Client();
$client->post(
'http://' . $_ENV['AWS_LAMBDA_RUNTIME_API'] . '/2018-06-01/runtime/invocation/' . $invocationId . '/response',
['body' => $response]
);
}
// This is the request processing loop. Barring unrecoverable failure, this loop runs until the environment shuts down.
do {
// Ask the runtime API for a request to handle.
$request = getNextRequest();
// Obtain the function name from the _HANDLER environment variable and ensure the function's code is available.
$handlerFunction = array_slice(explode('.', $_ENV['_HANDLER']), -1)[0];
require_once $_ENV['LAMBDA_TASK_ROOT'] . '/src/' . $handlerFunction . '.php';
// Execute the desired function and obtain the response.
$response = $handlerFunction($request['payload']);
// Submit the response back to the runtime API.
sendResponse($request['invocationId'], $response);
} while (true);
?>

@EllerbyBen


@EllerbyBen

Local Dev

bootstrap

handler

local bootstrap

web
server

@EllerbyBen

Works on my cloud


@EllerbyBen

Would Bref not be Brief?


@EllerbyBen

Would Bref not be Brief
-
Documentation
-
PHP runtimes for AWS Lambda
-
Deployment tooling
-
PHP frameworks integration
https://bref.sh/docs/

@EllerbyBen



@EllerbyBen

Any Runtime
But at what cost?
- More complex local dev env
- Managing lambda layer versions
- Updating the underlying language
- OS concerns (e.g. OpenSSL)
Conclusion
-
The custom runtime API let's you run any binary you want.
-
Lambda layers are a great way to share large dependencies, like your runtime and bootstrap.
-
Lambda layers reduce your deployment size.

@EllerbyBen
-
AWS Lambda is a very flexible tool, use it wisely.

serverless-transformation


@EllerbyBen







@EllerbyBen


Custom Runtimes on AWS Lambda: My experience doing Serverless PHP - AWS User Group Meetup 2019
By Ben Ellerby
Custom Runtimes on AWS Lambda: My experience doing Serverless PHP - AWS User Group Meetup 2019
Talk given at the React-Native-London meetup.
- 1,285