Tag Archives: AWS PHP Development

Building PHP Lambda functions with Docker container images

Post Syndicated from Benjamin Smith original https://aws.amazon.com/blogs/compute/building-php-lambda-functions-with-docker-container-images/

At re:Invent 2020, AWS announced that you can package and deploy AWS Lambda functions as container images. Packaging AWS Lambda functions as container images brings some notable benefits for developers running custom runtimes, such as PHP. This blog post explains those benefits and shows how to use the new container image support for Lambda functions to build serverless PHP applications.

Overview

Many PHP developers are familiar with building applications as containers to create a portable artifact for easier deployment. Packaging applications as containers helps to maintain consistent PHP versions, package versions, and configurations settings across multiple environments.

The new container image support for Lambda allows you to use familiar container tooling to build your applications. It also allows you to transition your applications into a serverless event-driven model. This brings the benefits of having no infrastructure to manage, automated scalability and a pay-per-use billing.

The advantages of an event-driven model for PHP applications are explained across the blog series “The serverless LAMP stack”. It explores the concepts, methods, and reasons for creating serverless applications with PHP. The architectural patterns and service limits in this blog series apply to functions packaged using both container image and zip archive formats, with some key exceptions:

Zip archive Container image
Maximum package size 250 MB 10 GB
Lambda layers Supported Include in image
Lambda Extensions Supported Include in image

Custom runtimes with container images

For custom runtimes such as PHP, Lambda provides base images containing the required Amazon Linux or Amazon Linux 2 operating system. Extend this to include your own runtime by implementing the Lambda Runtime API in a bootstrap file.

Before container image support for Lambda, a custom runtime is packaged using the .zip format. This required the developer to:

  1. Set up an Amazon Linux environment compatible with the Lambda execution environment.
  2. Install compilation dependencies and compile a version of PHP.
  3. Save the compiled PHP binary together with a bootstrap file and package as a .zip.
  4. Publish the .zip as a runtime layer.
  5. Add the runtime layer to a Lambda function.

Any edits to the custom runtime such as new packages, PHP versions, modules, or dependences require the process to be repeated. This process can be time consuming and prone to error.

Creating a custom PHP runtime using the new container image support for Lambda can simplify changing the runtime environment. Dockerfiles allow you to have a fully scripted, faster, and portable build process without setting up an Amazon Linux environment.

This GitHub repository contains a custom PHP runtime for Lambda functions packaged as a container image. The following Dockerfile uses the base image for Amazon Linux provided by AWS. The instructions perform the following:

  • Install system-wide Linux packages (zip, curl, tar).
  • Download and compile PHP.
  • Download and install composer dependency manager and dependencies.
  • Move PHP binaries, bootstrap, and vendor dependencies into a directory that Lambda can read from.
  • Set the container entrypoint.
#Lambda base image Amazon Linux
FROM public.ecr.aws/lambda/provided as builder 
# Set desired PHP Version
ARG php_version="7.3.6"
RUN yum clean all && \
    yum install -y autoconf \
                bison \
                bzip2-devel \
                gcc \
                gcc-c++ \
                git \
                gzip \
                libcurl-devel \
                libxml2-devel \
                make \
                openssl-devel \
                tar \
                unzip \
                zip

# Download the PHP source, compile, and install both PHP and Composer
RUN curl -sL https://github.com/php/php-src/archive/php-${php_version}.tar.gz | tar -xvz && \
    cd php-src-php-${php_version} && \
    ./buildconf --force && \
    ./configure --prefix=/opt/php-7-bin/ --with-openssl --with-curl --with-zlib --without-pear --enable-bcmath --with-bz2 --enable-mbstring --with-mysqli && \
    make -j 5 && \
    make install && \
    /opt/php-7-bin/bin/php -v && \
    curl -sS https://getcomposer.org/installer | /opt/php-7-bin/bin/php -- --install-dir=/opt/php-7-bin/bin/ --filename=composer

# Prepare runtime files
# RUN mkdir -p /lambda-php-runtime/bin && \
    # cp /opt/php-7-bin/bin/php /lambda-php-runtime/bin/php
COPY runtime/bootstrap /lambda-php-runtime/
RUN chmod 0755 /lambda-php-runtime/bootstrap

# Install Guzzle, prepare vendor files
RUN mkdir /lambda-php-vendor && \
    cd /lambda-php-vendor && \
    /opt/php-7-bin/bin/php /opt/php-7-bin/bin/composer require guzzlehttp/guzzle

###### Create runtime image ######
FROM public.ecr.aws/lambda/provided as runtime
# Layer 1: PHP Binaries
COPY --from=builder /opt/php-7-bin /var/lang
# Layer 2: Runtime Interface Client
COPY --from=builder /lambda-php-runtime /var/runtime
# Layer 3: Vendor
COPY --from=builder /lambda-php-vendor/vendor /opt/vendor

COPY src/ /var/task/

CMD [ "index" ]

To deploy this Lambda function, follow the instructions in the GitHub repository.

All runtime-related instructions are saved in the Dockerfile, which makes the custom runtime simpler to manage, update, and test. You can add additional Linux packages by appending to the yum install command. To install alternative PHP versions, change the php_version argument. Import additional PHP modules by adding to the compile command.

View the complete application in the following file tree:

project/
┣ runtime/
┃ ┗ bootstrap
┣ src/
┃ ┗ index.php
┗ Dockerfile

The Lambda function code is stored in the src directory in a file named index.php. This contains the Lambda function handler “index()”.

A bootstrap file is in the ‘runtime’ directory. This uses the Lambda runtime API to communicate with the Lambda execution environment.

The shebang hash sequence at the beginning of the bootstrap script instructs Lambda to run the file with the PHP executable, set by the Dockerfile.

All environment variables used in the bootstrap are set by the Lambda execution environment when running in the AWS Cloud. When running locally, the Lambda Runtime Interface Emulator (RIE) sets these values.

#!/var/lang/bin/php

Testing locally with the Lambda RIE

Using container image support for Lambda makes it easier for PHP developers to test Lambda functions locally. The previous container image example builds from the Lambda base image provided by AWS. This base image contains the Lambda RIE.

This is a proxy for Lambda’s Runtime and Extensions APIs. It acts as a lightweight web server that converts HTTP requests to JSON events and maintains functional parity with the Lambda Runtime API in the AWS Cloud. This allows developers to test functions locally using familiar tools such as cURL and the Docker CLI.

  1. Build the previous custom runtime image using the Docker build command:
    docker build -t phpmyfuntion .
  2. Run the function locally using the Docker run command, bound to port 9000:
    docker run -p 9000:8080 phpmyfuntion:latest
  3. This command starts up a local endpoint at:
    localhost:9000/2015-03-31/functions/function/invocations
  4. Post an event to this endpoint using a curl command. The Lambda function payload is provided by using the -d flag. This is a valid Json object required by the Runtime Interface Emulator:
    curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"queryStringParameters": {"name":"Ben"}}'
  5. A 200 status response is returned:

Building web applications with Bref container images

Bref is an open source runtime Lambda layer for PHP. Using the bref-fpm layer, you can build applications with traditional PHP frameworks such as Symfony and Laravel. Bref’s implementation of the FastCGI protocol returns an HTTP response instead of a JSON response. When using the zip archive format to package Lambda functions, Bref’s custom runtime is provided to the function as a Lambda layer. Functions packaged as container images do not support adding Lambda layers to the function configuration. In addition to runtime layers, Bref also provides a number of Docker images. These images use the Lambda runtime API to form a runtime interface client that communicates with the Lambda execution environment.

The following example shows how to compose a Dockerfile that uses the bref php-74-fpm container image:

# Uses PHP 74-fpm.0, as the base image
FROM bref/php-74-fpm
# download composer for dependency management
RUN curl -s https://getcomposer.org/installer | php
# install bref using composer
RUN php composer.phar require bref/bref
# copy the project files into a Location that the Lambda service can read from
COPY . /var/task
#set the function handler entry point
CMD _HANDLER=index.php /opt/bootstrap
  1. The first line sets the base image to use bref/php-74-fpm.
  2. Composer, a dependency manager for PHP is installed.
  3. Composer’s require command is used to add the bref package to the composer.json file.
  4. The project files are then copied into the /var/task directory, where the function code runs from.
  5. The function handler is set along with Bref’s bootstrap file.

The steps to build and deploy this image to the Amazon Elastic Container Registry are the same for any runtime, and explained in this announcement blog post.

Conclusion

The new container image support for Lambda functions allows developers to package Lambda functions of up to 10 GB in size. Using the container image format and a Dockerfile can make it easier to build and update functions with custom runtimes such as PHP.

Developers can include specific language versions, modules, and package dependencies. The Amazon Linux and Amazon Linux 2 base images give developers a starting point to customize the runtime. With the Lambda Runtime Interface Emulator, it’s simpler for developers to test Lambda functions locally. PHP developers can use existing third-party images, such as bref-fpm, to create web applications in a single Lambda function.

Visit serverlessland.com for more information on building serverless PHP applications.

The serverless LAMP stack part 6: From MVC to serverless microservices

Post Syndicated from Benjamin Smith original https://aws.amazon.com/blogs/compute/the-serverless-lamp-stack-part-6-from-mvc-to-serverless-microservices/

In this post, you learn how to build serverless PHP applications using microservices.

I show how to move from using a single Lambda function as scalable web host with an MVC framework, to a decoupled microservice model. The accompanying code examples for this blog post can be found in this GitHub repository.

The MVC architectural pattern

A traditional LAMP stack often implements the Model-View-Controller (MVC) architecture. This is a well-established way of separating application logic into three parts: the model, the view, and the controller.

  • Model: This part is responsible for managing the data of the application. Its role is to retrieve raw information from the database or receive user input from the controller.
  • View: This component focuses on the display. Data received from the model is presented to the user. Any response from the user is also recognized and sent to the controller component.
  • Controller: This part is responsible for the application logic. It responds to the user input and performs interactions on the data model objects.

The MVC principal of decoupling data, logic, and presentation layers means that changes in one layer have minimal impact on the others. This speeds the development process and makes it easier to update layouts, change business rules, and add new features. Components are more adaptable for reuse and refactoring, and allow for a degree of simultaneous development.

The serverless LAMP stack

The serverless LAMP stack

The preceding serverless LAMP stack architecture is first discussed in this post. A web application is split in to two components. A single AWS Lambda function contains the application’s MVC framework. Each response is synchronously returned via Amazon API Gateway. This architecture addresses the scalability challenge that is often seen in traditional LAMP stack applications. It scales automatically with a managed infrastructure and a pay-per-use billing model. However, the serverless paradigm makes it possible to apply the MVC principles of decoupling and reusability to an even greater degree.

The “Lambda-lith”

The preceding architecture represents a serverless monolith or “Lambda-lith”. A single Lambda function contains the entire business logic within an MVC framework. This implementation can be used to “lift and shift” from a legacy MVC to a serverless application. Simple applications often start this way too, but as the application grows more complex over time new challenges can occur.

 

day1-day100

Lambda Day 1 to day 100

A Lambda-lith is often maintained in a single repository that contains the entire application logic. This is sometimes referred to as a mono-repo.

Lamba-lith monorepo

Lamba-lith monorepo

A mono-repo makes it harder to separate responsibility of ownership between development teams. Consequently, projects in a mono-repo are prone to depend on each other, creating tight coupling. The tightly coupled code base with all of its interconnected modules be challenging to maintain a regular release cadence. Any small fix can require updates to other parts of the code base, making maintenance challenging without fracturing the whole application. Onboarding can be slow as new developers take time to learn and understand the code base and all of the interdependencies.

By applying the following principles, Lambda-lith MVC applications can be refactored into decoupled serverless microservices.

Divide into independent Lambda functions with finite business logic

The following example illustrates a Lambda-lith with all business and routing logic stored in a single Lambda function. Every request is routed to this function from API Gateway. The function code base contains a `router.php` file to direct requests to the correct model, view, or controller.

This is similar to a traditional LAMP stack implementation in which a web server such as Apache or NGINX routes all requests to a single index.php function. However, it’s often more practical to split applications into multiple functions or services.

Lambda as a web server

In the following example, this Lambda function is split into multiple functions based on each CRUD operation. The internal routing logic is now decoupled from the business logic. The API Gateway service uses rules to route requests to the correct Lambda function. This allows each function to scale independently and updates can be made to one function without impacting another.

Routing decoupled from business logic

Build micro-perimeters to enforce strict verification of every person or service.

Traditional MVC applications often use a castle-and-moat security model. This provides security by placing a perimeter around the entire application to protect it from malicious actors. This perimeter guards the application or network by verifying requests and user identities at the point of entry or exit.

This is typically achieved with firewalls, proxy servers, honeypots, and other intrusion prevention tools. It assumes that activity inside the perimeter is safe. However, a network vulnerability may provide access to everything inside.

Microservice-based applications allow developers to apply a “zero trust” security model. This enables developers to build micro-perimeters around each resource. This is sometimes referred to as the principle of least privilege. It ensures that each request, service, or user can access only the data or resource that is necessary for its legitimate purpose. Even with a vulnerability, the blast radius is limited only to the service within that micro-perimeter.

Castle-and-moat vs zero trust security model

Use AWS Identity and Access Management (IAM) resource policies and execution roles to decouple business logic from security posture. Lambda resource policies define the events and services that are authorized to invoke the function. Lambda execution roles place constraints the resource or service the Lambda function has access to. When defining resource policies and execution roles, start with a minimum set of permissions and grant additional permissions as necessary.

Create building blocks based on common functionality

Each component is a single building block that makes up an application together with other blocks. These blocks form microservices that deliver a set of capabilities on a specific domain. This makes is easy to change, upgrade, and replace with no impact on the remaining microservice components. This creates natural ownership boundaries to help organize repositories.

Development teams can then easily be assigned ownership to individual microservice repositories. Use the AWS Serverless Application Model (AWS SAM) to organize microservices into multiple code repositories, as explained in this blog post.

Use messages to connect and communicate between microservices.

In traditional MVC applications, one part of the application uses method calls to communicate with the other parts. With serverless microservices, the code base is spread across short-lived stateless functions and services. Communication between these services is achieved using asynchronous messages or synchronous HTTP requests.

Synchronous communication

In this method, a service calls an API and waits for a response from the receiving service before proceeding. Use API Gateway to create a front door to your backend microservices. API Gateway is a fully managed service for creating and managing RESTful and WebSocket APIs.

Using API Gateway to transport data addresses common concerns such as authorization, API tokens, access control and rate limiting from your code, and helps to reduce code complexity. API Gateway can also be used for synchronous internal microservice communications where the services have clear separation, strict authentication requirements, or have been deployed across accounts.

The following architecture demonstrates an application that is deployed across two accounts. The Booking microservice, invokes a loyalty booking function via API Gateway that exists in the Loyalty points account.

Synchronous internal microservice communications

Asynchronous communication

In this pattern, a service sends a message without waiting for a response, and one or more services process the message asynchronously. Here, the services involved do not directly communicate with each other. Instead, services publish messages to a broker such as Amazon Simple Queue Service (SQS) or Amazon EventBridge. Other services can choose to subscribe to the topic in the broker that they care about. This enables further decoupling of business logic from data transportation and reduces your code complexity.

Use services instead of code, where possible

A service-first mindset is an important part of serverless application development. Each line of code you write may limit your project’s responsiveness to change and adds cognitive overhead for new developers. Using an appropriate AWS service for each domain (messaging, storage, orchestration) helps to build faster. Embracing this mind-set allows developers to focus on solving those unique challenges that add the most value to their customers.

By applying these principles to refactor an MVC Lambda-lith, I build the following CRUD API microservice. This application can be deployed from this GitHub repository. It uses an AWS Serverless Application Model (AWS SAM) template to define an HTTP API, 5 Lambda functions, an Amazon DynamoDB table and all the IAM roles required.

All routing logic and authentication is managed by Amazon API Gateway. Each Lambda function has limited scope and minimal business logic. It uses a lightweight custom-built PHP runtime, explained in this post. Each Lambda function uses the AWS PHP SDK to interact with the DynamoDB table. This architecture is suitable as a serverless microservice for a website backend.

A serverless API microservice with PHP

Conclusion

In this post, I show how to move from using a single Lambda function as a scalable web host with an MVC framework, to a decoupled microservice model. I explain the principles that can be applied to help transition an MCV application into a collection of microservices and show the benefits of doing so. I provide code examples for a serverless PHP CRUD microservice with a deployable AWS SAM template.

PHP development teams can transition from Lambda-lith MVC applications to a decoupled microservice model. This allows them to focus on shipping code to delight their customers without managing infrastructure.

Find more resources for building serverless PHP applications at ServerlessLand.com.

Introducing the CDK construct library for the serverless LAMP stack

Post Syndicated from Benjamin Smith original https://aws.amazon.com/blogs/compute/introducing-the-cdk-construct-library-for-the-serverless-lamp-stack/

In this post, you learn how the new CDK construct library for the serverless LAMP stack is helping developers build serverless PHP applications.

The AWS Cloud Development Kit (AWS CDK) is an open source software development framework for defining cloud application resources in code. It allows developers to define their infrastructure in familiar programming languages such as TypeScript, Python, C# or Java. Developers benefit from the features those languages provide such as Interfaces, Generics, Inheritance, and Method Access Modifiers. The AWS Construct Library provides a broad set of modules that expose APIs for defining AWS resources in CDK applications.

The “Serverless LAMP stack” blog series provides best practices, code examples and deep dives into many serverless concepts and demonstrates how these are applied to PHP applications. It also highlights valuable contributions from the community to help spark inspiration for PHP developers.

Each component of this serverless LAMP stack is explained in detail in the blog post series:

The CDK construct library for the serverless LAMP stack is an abstraction created by AWS Developer Advocate, Pahud Hsieh. It offers a single high-level component for defining all resources that make up the serverless LAMP stack.

CDK construct for Serverless LAMP stack

CDK construct for Serverless LAMP stack

  1. Amazon API Gateway HTTP API.
  2. AWS Lambda with Bref-FPM runtime.
  3. Amazon Aurora for MySQL database cluster with Amazon RDS Proxy enabled.

Why build PHP applications with AWS CDK constructs?

Building complex web applications from scratch is a time-consuming process. PHP frameworks such as Laravel and Symfony provide a structured and standardized way to build web applications. Using templates and generic components helps reduce overall development effort. Using a serverless approach helps to address some of the traditional LAMP stack challenges of scalability and infrastructure management. Defining these resources with the AWS CDK construct library allows developers to apply the same framework principles to infrastructure as code.

The AWS CDK enables fast and easy onboarding for new developers. In addition to improved readability through reduced codebase size, PHP developers can use their existing skills and tools to build cloud infrastructure. Familiar concepts such as objects, loops, and conditions help to reduce cognitive overhead. Defining the LAMP stack infrastructure for your PHP application within the same codebase reduces context switching and streamlines the provisioning process. Connect CDK constructs to deploy a serverless LAMP infrastructure quickly with minimal code.

Code is a liability and with the AWS CDK you are applying the serverless first mindset to infra code by allowing others to create abstractions they maintain so you don’t need to. I always love deleting code

Says Matt Coulter, creator of CDK patterns – An open source resource for CDK based architecture patterns.

Building a serverless Laravel application with the ServerlessLaravel construct

The cdk-serverless-lamp construct library is built with aws/jsii and published as npm and Python modules. The stack is deployed in either TypeScript or Python and includes the ServerlessLaravel construct. This makes it easier for PHP developers to deploy a serverless Laravel application.

First, follow the “Working with the AWS CDK with in TypeScript“ steps to prepare the AWS CDK environment for TypeScript.

Deploy the serverless LAMP stack with the following steps:

  1. Confirm the CDK CLI instillation:
    $ cdk –version
  2. Create a new Laravel project with AWS CDK:
    $ mkdir serverless-lamp && cd serverless-lamp
  3. Create directories for AWS CDK and Laravel project:
    $ mkdir cdk codebase
  4. Create the new Laravel project with docker
    $ docker run --rm -ti \
    --volume $PWD:/app \
    composer create-project --prefer-dist laravel/laravel ./codebase

The cdk-serverless-lamp construct library uses the bref-FPM custom runtime to run PHP code in a Lambda function. The bref runtime performs similar functionality to Apache or NGINX by forwarding HTTP requests through the FastCGI protocol. This process is explained in detail in “The Serverless LAMP stack part 3: Replacing the web server”. In addition to this, a bref package named larval-bridge automatically configures Laravel to work on Lambda. This saves the developer from having to manually implement some of the configurations detailed in “The serverless LAMP stack part 4: Building a serverless Laravel application

  1. Install bref/bref and bref/laravel-bridge packages in the vendor directories:
    $ cd codebase
    $ docker run --rm -ti \
    --volume $PWD:/app \
    composer require bref/bref bref/laravel-bridge
  2. Initialize the AWS CDK project with typescript.
    $ cd ../cdk
    $ cdk init -l typescript
  3. Install the cdk-severless-lamp npm module
    $ yarn add cdk-serverless-lamp

This creates the following directory structure:

.
├── cdk
└── codebase

The cdk directory contains the AWS CDK resource definitions. The codebase directory contains the Laravel project.

Building a Laravel Project with the AWS CDK

Replace the contents of ./lib/cdk-stack.ts with:

import * as cdk from '@aws-cdk/core';
import * as path from 'path';
import { ServerlessLaravel } from 'cdk-serverless-lamp';

export class CdkStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new ServerlessLaravel(this, 'ServerlessLaravel', {
      brefLayerVersion: 'arn:aws:lambda:us-east-1:209497400698:layer:php-74-fpm:12',
      laravelPath: path.join(__dirname, '../../codebase'),
    });
  }
}

The brefLayerVersion argument refers to the AWS Lambda layer version ARN of the Bref PHP runtime. Select the correct ARN and corresponding Region from the bref website. This example deploys the stack into the us-east-1 Region with the corresponding Lambda layer version ARN for the Region.

  1. Deploy the stack:
    cdk deploy

Once the deployment is complete, an Amazon API Gateway HTTP API endpoint is returned in the CDK output. This URL serves the Laravel application.

CDK construct output for Serverless LAMP stack

The application is running PHP on Lambda using bref’s FPM custom runtime. This entire stack is deployed by a single instantiation of the ServerlessLaravel construct class with required properties.

Adding an Amazon Aurora database

The ServerlessLaravel stack is extended with the DatabaseCluster construct class to provision an Amazon Aurora database. Pass a Amazon RDS Proxy instance for this cluster to the ServerlessLaravel construct:

  1. Edit the ./lib/cdk-stack.ts :
 import * as cdk from '@aws-cdk/core';
 import { InstanceType, Vpc } from '@aws-cdk/aws-ec2';
 import * as path from 'path';
 import { ServerlessLaravel, DatabaseCluster } from 'cdk-serverless-lamp';

 export class CdkStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
 const vpc = new Vpc(this, 'Vpc',{ maxAzs: 3, natGateways: 1 } )
    // the DatabaseCluster sharing the same vpc with the ServerlessLaravel
    const db = new DatabaseCluster(this, 'DatabaseCluster', { vpc, instanceType: new InstanceType('t3.small'), rdsProxy: true, })
    // the ServerlessLaravel
    new ServerlessLaravel(this, 'ServerlessLaravel', {
      brefLayerVersion: 'arn:aws:lambda:us-east-1:209497400698:layer:php-74-fpm:12',
      laravelPath: path.join(__dirname, '../composer/laravel-bref'),
      vpc, 
      databaseConfig: { writerEndpoint: db.rdsProxy!.endpoint, },
    });
  }
 }
  1. Run cdk diff to check the difference :
    $ cdk diff

The output shows that a shared VPC is created for the ServerlessLaravel stack and the DatabaseCluster stack. An Amazon Aurora DB cluster with a single DB instance and a default secret from AWS Secrets Manager is also created. The cdk-serverless-lamp construct library configures Amazon RDS proxy automatically with the required AWS IAM policies and connection rules.

  1. Deploy the stack.
    $ cdk deploy

The ServerlessLaravel stack is running with DatabaseCluster in a single VPC. A single Lambda function is automatically configured with the RDS Proxy DB_WRITER and DB_READER stored as Lambda environment variables.

Database authentication

The Lambda function authenticates to RDS Proxy with the execution IAM role. RDS Proxy authenticates to the Aurora DB cluster using the credentials stored in the AWS Secrets Manager. This is a more secure alternative to embedding database credentials in the application code base. Read “Introducing the serverless LAMP stack – part 2 relational databases” for more information on connecting to an Aurora DB cluster with Lambda using RDS Proxy.

Clean up

To remove the stack, run:
$ cdk destroy

The video below demonstrates a deployment with the CDK construct for the serverless LAMP stack.

Conclusion

This post introduces the new CDK construct library for the serverless LAMP stack. It explains how to use it to deploy a serverless Laravel application. Combining this with other CDK constructs such as DatabaseCluster gives PHP developers the building blocks to create scalable, repeatable patterns at speed with minimal coding.

With the CDK construct library for the serverless LAMP stack, PHP development teams can focus on shipping code without changing the way they build.

Start building serverless applications with PHP.

The serverless LAMP stack part 4: Building a serverless Laravel application

Post Syndicated from Benjamin Smith original https://aws.amazon.com/blogs/compute/the-serverless-lamp-stack-part-4-building-a-serverless-laravel-application/

In this post, you learn how to deploy a Laravel application with a serverless approach.

This is the fourth post in the “Serverless LAMP stack” series, previous posts covered:

Laravel is an open source web application framework for PHP. Using a framework helps developers to build faster by reusing generic components and modules. It also helps long-term maintenance by complying with development standards. However, there are still challenges when scaling PHP frameworks with a traditional LAMP stack. Deploying a framework using a serverless approach can help solve these challenges.

There are a number of solutions that simplify the deployment of a Laravel application onto a serverless infrastructure. The following solution uses an AWS Serverless Application Model (AWS SAM) template. This deploys a Laravel application into a single Lambda function. The function uses the Bref FPM custom runtime layer to run PHP. The AWS SAM template deploys the following architecture, explained in detail in “The Serverless LAMP stack Part 3: Replacing the web server”:

The serverless LAMP stack

Deploying Laravel and Bref with AWS SAM

Composer is a dependency management tool for PHP. It allows you to declare and manage your project libraries and dependencies such as Laravel and Bref.

Deploy Laraval and Bref with AWS SAM using the following steps:

  1. Download the Laravel installer using Composer:
    composer global require Laravel/installer
  2. Install Laravel:
    composer create-project --prefer-dist laravel/laravel blog
  3. In the Laravel project, install Bref using Composer:
    composer require bref/laravel-bridge
  4. Clone the AWS SAM template in your application’s root directory:
    git clone https://github.com/aws-samples/php-examples-for-aws-lambda/
  5. Change directory into “0.4-Building-A-Serverless-Laravel-App-With-AWS-SAM”:
    cd 0.4-Building-A-Serverless-Laravel-App-With-AWS-SAM
  6. Deploy the application using the AWS SAM CLI guided deploy:
    sam deploy -g

Once AWS SAM deploys the application, it returns the Amazon CloudFront distribution’s domain name. This distribution serves the serverless Laravel application.

CloudFront domain name

CloudFront domain name from AWS SAM template

Configuring Laravel for Lambda

There are some configuration changes required for Laravel to run in a Lambda function.

Session data store

While Lambda includes a 512 MB temporary file system, this is an ephemeral resource not intended for durable storage. This is because there is no guarantee of reusing the same Lambda function environment for each invocation.

For this reason, if you need Laravel session data, it must be stored outside of the Lambda function. There are a range of different options available for managing state with serverless applications. In this instance, it is recommended to store session data either in a database or using browser cookies.

Update the Laravel .env file to set the session_driver to cookie.

SESSION_DRIVER=cookie

Logging

Laravel implements a PHP logging library called Monolog as a common interface to write logs to a number of destinations. Laravel Monolog uses log channels to specify these destinations. Each channel is defined within the /config/logging.php file as an associative array.

Since the Lambda filesystem is not shared between multiple Lambda function invocations, application logs must be written to an external central location such as Amazon CloudWatch Logs. All errors, warnings, and notices emitted by PHP are forwarded onto CloudWatch Logs. This makes it easy to view, search, filter, or archive logs for future analysis from a single location. To configure this, add the following to the Laravel .env file:

LOG_CHANNEL=stderr

This ensures that the stderr channel is used to write all application logs which are automatically forwarded to CloudWatch Logs. This channel is defined in /config/logging.php:

'stderr' => 
    [ 
    'driver' => 'monolog', 
    'handler' => StreamHandler::class, 
    'formatter' => env('LOG_STDERR_FORMATTER'), 
    'with' => [ 
        'stream' => 'php://stderr', 
    ], 
],
CloudWatch Logs for a single Lambda invocation

CloudWatch Logs for a single Lambda invocation

Compiled views

Views contain the HTML served by an application, separating application logic from presentation logic. By default, views are compiled on demand inside the application’s storage directory.

As Lambda does not have write access to the storage directory, Laravel must be configured to write views to the function’s /tmp directory. This is a temporary file system for ephemeral data that’s only needed for the duration of each HTTP request.

In the .env file, add the following line to configure Laravel to use a new directory path for compiled views:

VIEW_COMPILED_PATH=/tmp/storage/framework/views

Laravel uses service providers to register or “bootstrap” components to your application. The AppServiceProvider.php file provides a central location to share data with all views. Add the following code to the Providers/AppServiceProvider.phpfile.

public function boot() { 
    // Make sure the directory for compiled views exist 
    if (! is_dir(config('view.compiled'))) { 
        mkdir(config('view.compiled'), 0755, true); 
    } 
}

This ensures that the view directory is automatically created for each Lambda function invocation, if it does not already exist.

File system abstraction with Amazon S3

Laravel uses a filesystem abstraction package called Flysystem. This provides a simple driver mechanism to configure the filesystem location. As Lambda’s /tmp directory is ephemeral, the filesystem location must be outside of the Lambda function. Configure Laravel to use the Amazon S3 filesystem driver by adding the following line to the .env file:

FILESYSTEM_DRIVER=s3

The AWS SAM template deploys an S3 bucket to store these objects:

Storage:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: php-example-laravel-FileSystemBucket

The bucket name is provided to the Lambda function as an environment variable from within the AWS SAM template:

    Environment:
      Variables:
        AWS_BUCKET: !Ref Storage

The Lambda function is granted permission to read/write to the S3 bucket, using an IAM policy definition:

Policies:
        - S3FullAccessPolicy:
            BucketName: !Ref Storage

Laravel’s filesystem configuration is found at config/filesystems.php. This is where the S3 filesystem disk is defined using the AWS SAM environment variable.

's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'token' => env('AWS_SESSION_TOKEN'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
        ],

The AWS account information and bucket ARN are provided by the Lambda environment that is running PHP, using Laravel’s env() function.

Public asset files

Laravel has a public disk driver for storing publicly accessible files such as images
and CSS files. By default, the public disk driver stores these files in storage/app/public/. These files must rather be stored in S3. Change the configuration in config/filesystems.php to the following:

+ 'public' => env('FILESYSTEM_DRIVER_PUBLIC', 'public_local'),
    
    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

- 'public => [
+ 'public_local' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'token' => env('AWS_SESSION_TOKEN'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
        ],

+ 's3_public' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'token' => env('AWS_SESSION_TOKEN'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_PUBLIC_BUCKET'), + 'url' => env('AWS_URL'), + ],

    ],

This adds a new filesystem disk named s3_public, which uses an S3 driver. Laravel’s env() function retrieves the environment variable env(‘AWS_PUBLIC_BUCKET’) to set/configure the bucket location. The bucket name is passed to the Lambda function as an environment variable.

Add the following line to the .env file to configure the public disk to use S3:

FILESYSTEM_DRIVER_PUBLIC=s3

Referencing static assets in view templates

Laravel’s asset() helper function generates a URL for an asset using the current scheme of the request (HTTP or HTTPS):

$url = asset('img/photo.jpg');

These assets must be stored on S3 and served via CloudFront’s global CDN. Configure the URL host by setting the ASSET_URL variable in your .env file:

ASSET_URL=https://{YourCloudFrontDomain}.cloudfront.net

This allows the application to correctly reference assets from S3, via the CloudFront domain. Laravel’s native asset() helper function is used from within the view templates with the following format:

<img src="{{ asset('assets/icons.png') }}">
Serverless Laravel App with Lambda

Serverless Laravel App with Lambda

Alternative deployments methods for a serverless Laravel application

1. Bref, an open source custom runtime for PHP, recently merged a new pull request to automatically configure Laravel for Lambda. This new package also provides a way to integrate Amazon SQS with the Laravel Queues Jobs system.

2. Laravel Vapour is a serverless deployment platform for Laravel. This is a paid service, built by the Laravel team on the AWS Cloud.

Conclusion

This post explains how to deploy a PHP Laravel application using a serverless approach with AWS SAM. It explains the initial Laravel configuration steps required to implement a session store and centralised logging with an external filesystem and static assets in S3.

PHP development teams can focus on shipping code without changing the way they build. Start building serverless applications with PHP.

Visit this GitHub repository for accompanying code and instructions.

The Serverless LAMP stack part 3: Replacing the web server

Post Syndicated from Benjamin Smith original https://aws.amazon.com/blogs/compute/the-serverless-lamp-stack-part-3-replacing-the-web-server/

In this post, you learn how to build serverless PHP applications without needing a web server.

Later in this post, Matthieu Napoli the creator of Bref and Serverless Visually Explained, tells how the implementation of FastCGI Process Manager inside of Lambda helps makes this possible. Bref is an open source runtime Lambda layer for PHP.

I show how to configure Amazon CloudFront to securely serve and cache static assets from a private Amazon S3 bucket. Dynamic requests are routed downstream to Amazon API Gateway and onto a single AWS Lambda function.

These services combine to replace the traditional web server for PHP applications.

Visit this GitHub repository for the sample code.

This serverless LAMP stack architecture is first discussed in this post. A web application is split in to two components (static assets and and the backend application that generates dynamic content). The Lambda function contains the application’s business logic and interactions with the MySQL database. Each response is synchronously returned via API Gateway.

Routing with API Gateway

The serverless LAMP stack does not use an http server. Instead, API Gateway replaces the routing mechanism of Apache or NGINX. The AWS Serverless Application Model (AWS SAM) is used to configure API Gateway routing rules.

      Events:
        DynamicRequestsRoot:
          Type: HttpApi
          Properties:
            Path: /
            Method: ANY
        DynamicRequestsProxy:
          Type: HttpApi
          Properties:
            Path: /{proxy+}
            Method: ANY

AWS SAM template to route all inbound requests from HTTP API to a single Lambda function.

The preceding template creates an HTTP API with a “catch-all” rule for inbound requests. The request context is sent downstream to a single Lambda function. This is similar behavior to that of a PHP MVC framework that forwards requests to an index.php file. The following shows how this is achieved in a traditional LAMP stack, using a combination of web server and .htaccess configurations.

Alias /yourdir /var/www/html/yourdir/public/ 
<Directory “/var/www/html/yourdir/public”> 
AllowOverride All 
Order allow,deny 
Allow from all 
</Directory>

apache2.conf file configuration

<IfModule mod_rewrite.c> 
RewriteEngine On 
RewriteBase / 
RewriteRule ^index\.php$ - [L] 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule . /index.php [L] 
</IfModule>

Public/.htaccess configuration

Using Bref to host traditional PHP frameworks

Bref is an open source PHP runtime layer for Lambda. Using the bref-fpm layer, it’s possible to build applications with traditional PHP frameworks such as Symfony and Laravel. The framework sits within a single Lambda function and is invoked using the service architecture and routing rules illustrated previously. This is made possible due to Bref’s implementation of FastCGI Process Manager. Matthieu Napoli, creator of Bref, explains how.

Bref’s “FPM runtime” runs the php-fpm binary. PHP-FPM is a server implementing the FastCGI protocol, developed by the PHP core team. It is traditionally used with HTTP servers like Apache or NGINX.

Bref’s implementation of PHP-FPM allows PHP applications to run in a familiar environment by:

  • Running each HTTP request in a new process, which is the foundation of PHP’s “shared-nothing” execution model.
  • Populating the global variables ($_GET, $_POST…) used to access HTTP request data.
  • Providing a mechanism for PHP scripts to return HTTP responses (the header() function, stdout…).
  • Providing performance optimizations, such as OPcache (opcode cache), APCu (shared memory cache), or database persistent connections.

Most PHP frameworks are built around these PHP-FPM features, making this runtime an excellent transition from “server hosting” to serverless.

Here is an overview of how the runtime works:

Bref-fpm cycle

Startup

On initial invoke of a new Lambda environment, Bref’s bootstrap is executed and starts the php-fpm process in the background. This PHP-FPM server now waits for new connections on the FastCGI protocol.

The request/response cycle

Whenever a new HTTP request is sent to the application, the following happens:

  1. API Gateway receives the HTTP request and invokes AWS Lambda.
  2. The Lambda function environment executes the bootstrap for the Bref based runtime.
  3. Bref converts the HTTP request from the API Gateway format to the FastCGI format.
  4. Bref calls PHP-FPM through the FastCGI protocol.
  5. PHP-FPM runs the PHP handler and returns its response.
  6. Bref converts the FastCGI response to the API Gateway format.
  7. Bref returns the response to API Gateway, which returns the HTTP response to the client.

While there are multiple processes, this happens quickly.

AWS X-Ray trace view shows that the Lambda function finishes executing in 9 ms.

AWS X-Ray trace view shows that the Lambda function finishes executing in 9ms.

The Bref runtime performs a job similar to Apache or NGINX (forwarding an HTTP request through the FastCGI protocol), and PHP-FPM has been optimized for decades. Between requests, PHP-FPM does not kill and create new PHP processes. It keeps the same process and reset its memory (preserving in-memory caches like OPcache and APCu).

Configuring PHP for Lambda

Bref optimizes the configuration of PHP-FPM for AWS Lambda:

  • PHP-FPM runs a single “worker” because a Lambda instance handles one HTTP request at a time.
  • The standard error output of PHP-FPM is forwarded to CloudWatch. This makes logging from PHP as easy as writing to “stderr”.
  • All PHP errors, warnings, and notices are logged outside of the HTTP response and forwarded to CloudWatch by default.
  • PHP’s OPcache is optimized to avoid reading from disk because the PHP code base is mounted as read-only in Lambda.

Additionally, Bref adds behaviors that provide an easy migration from Apache/NGINX to API Gateway and Lambda:

  • Uploaded files are bridged with PHP-FPM’s uploaded file mechanism.
  • HTTP requests with binary content are automatically decoded from API Gateway’s base64 format.
  • Binary HTTP responses can also be automatically encoded to base64 by Bref.
  • Cookies are adapted to work with PHP-FPM’s mechanisms.

Bref also supports both v1 and v2 payload formats from API Gateway requests.

Static content routing and caching with Amazon CloudFront

The Lambda pricing model charges per request and per duration at GB of RAM allocated. This makes it ideal for handling requests for dynamic compute.

Amazon CloudFront handles requests for static content more efficiently than a server. This is a large scale, global, content delivery network (CDN) that provides secure, scalable delivery of content. It does this by caching data across a points of presence distributed all over the globe. This reduces the load on an application origin and improves the experience of the requestor by delivering a local copy of the content.

A CloudFront web distribution can serve different types of data from multiple origins. This template configures CloudFront to route requests for static assets directly to an S3 bucket. It routes all other requests directly to API Gateway.

Origins:
   -   Id: Website  
   DomainName: !Join ['.', [!Ref ServerlessHttpApi, 'execute-api', !Ref AWS::Region, 'amazonaws.com']]
   # This is the stage
   OriginPath: "/dev"
   CustomOriginConfig:
   	   OriginProtocolPolicy: 'https-only' # API Gateway only supports HTTPS
   # The assets (S3)
   -   Id: Assets
   DomainName: !GetAtt Assets.RegionalDomainName
   S3OriginConfig: {}

API Gateway routing is configured with HTTP APIs to route all inbound requests downstream to a single Lambda function, as previously shown.

Restricting access to Amazon S3 assets by using an origin access identity (OAI)

It is best practice to implement least privilege access permissions for each resource. This reduces security risk and the impact that could result from errors or malicious intent. Following this best practice, a security restriction is applied to the S3 bucket. The bucket is made private, with the objects inside only made available via the CloudFront distribution.

This is achieved by using an origin access identity (OAI). The OAI is defined within the CloudFormation template:

  S3OriginIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: Cloudfront AOI

It is then set as a principal within the S3 bucket’s policy.

AssetsBucketPolicy: 
    Type: AWS::S3::BucketPolicy
    Properties: 
      Bucket:
        Ref: Assets # References the bucket we defined above
      PolicyDocument: 
        Statement:
          Effect: Allow  
          Action: s3:GetObject # to read
          Principal: 
            CanonicalUser: 
              Fn::GetAtt: S3OriginIdentity.S3CanonicalUserId
          Resource: # things in the bucket 'arn:aws:s3:::<bucket-name>/*'
            Fn::Join: 
                - ""
                - 
                  - "arn:aws:s3:::"
                  - 
                    Ref: Assets
                  - "/*"

Deploying the infrastructure

This GitHub repository contains an AWS SAM template with instructions to deploy this infrastructure. It has a single Lambda function (index.php) which uses Bref’s php-73-fpm:25 runtime layer:

Layers:
        - 'arn:aws:lambda:us-east-1:209497400698:layer:php-73-fpm:25'

A /vendors directory, holding the Bref runtime dependencies is also included. The handler inside Index.php returns HTML content to API Gateway’s requests. Within the Lambda function handler, there is a reference to a static image and static css file:

<link href="/assets/style.css" rel="stylesheet">
…
<img src="/assets/serverless-lamp-stack.png">

These files are referenced relatively (and not absolutely), because they are served under the same CloudFront domain as the dynamic portion of the website. Navigating to the generated CloudFront domain shows the dynamic webpage, along with the referenced static image. The Lambda function uses the global $_GET variable, made available to it by FastCGI process manager.

Servelress PHP website exampleBy building or replacing the index.php with your own framework, it’s possible to deploy feature-rich serverless web applications with PHP. Refer to Bref’s documentation for more information on building with popular PHP frameworks using the bref-fpm custom runtime.

Conclusion

This post explains how to build PHP applications with Lambda and API Gateway in place of an HTTP server like Apache or NGINX. It describes how to separate your application into static and dynamic requests. All dynamic HTTP requests are routed to a single Lambda function using Bref’s FPM custom runtime layer. The custom runtime’s implementation of FastCGI Process Manager makes it possible to build PHP applications with traditional frameworks.

Replacing the HTTP server frees developers from the responsibilities of web server maintenance, configuration, synchronization and scaling. PHP development teams can focus on shipping code without changing the way they build.

Start building serverless applications with PHP.

Introducing the new Serverless LAMP stack

Post Syndicated from Benjamin Smith original https://aws.amazon.com/blogs/compute/introducing-the-new-serverless-lamp-stack/

This is the first in a series of posts for PHP developers. The series will explain how to use serverless technologies with PHP. It covers the available tools, frameworks and strategies to build serverless applications, and why now is the right time to start.

In future posts, I demonstrate how to use AWS Lambda for web applications built with PHP frameworks such as Laravel and Symphony. I show how to move from using Lambda as a replacement for web hosting functionality to a decoupled, event-driven approach. I cover how to combine multiple Lambda functions of minimal scope with other serverless services to create performant scalable microservices.

In this post, you learn how to use PHP with Lambda via the custom runtime API. Visit this GitHub repository for the sample code.

The Serverless LAMP stack

The Serverless LAMP stack

The challenges with traditional PHP applications

Scalability is an inherent challenge with the traditional LAMP stack. A scalable application is one that can handle highly variable levels of traffic. PHP applications are often scaled horizontally, by adding more web servers as needed. This is managed via a load balancer, which directs requests to various web servers. Each additional server brings additional overhead with networking, administration, storage capacity, backup and restore systems, and an update to asset management inventories. Additionally, each horizontally scaled server runs independently. This can result in configuration synchronization challenges.

Horizontal scaling with traditional LAMP stack applications.

Horizontal scaling with traditional LAMP stack applications.

New storage challenges arise as each server has its own disks and filesystem, often requiring developers to add a mechanism to handle user sessions. Using serverless technologies, scalability is managed for the developer.

If traffic surges, the services scale to meet the demand without having to deploy additional servers. This allows applications to quickly transition from prototype to production.

The serverless LAMP architecture

A traditional web application can be split in to two components:

  • The static assets (media files, css, js)
  • The dynamic application (PHP, MySQL)

A serverless approach to serving these two components is illustrated below:

The serverless LAMP stack

The serverless LAMP stack

All requests for dynamic content (anything excluding /assets/*) are forwarded to Amazon API Gateway. This is a fully managed service for creating, publishing, and securing APIs at any scale. It acts as the “front door” to the PHP application, routing requests downstream to Lambda functions. The Lambda functions contain the business logic and interaction with the MySQL database. You can pass the input to the Lambda function as any combination of request headers, path variables, query string parameters, and body.

Notable AWS features for PHP developers

Amazon Aurora Serverless

During re:Invent 2017, AWS announced Aurora Serverless, an on-demand serverless relational database with a pay-per-use cost model. This manages the responsibility of relational database provisioning and scaling for the developer.

Lambda Layers and custom runtime API.

At re:Invent 2018, AWS announced two new Lambda features. These enable developers to build custom runtimes, and share and manage common code between functions.

Improved VPC networking for Lambda functions.

In September 2019, AWS announced significant improvements in cold starts for Lambda functions inside a VPC. This results in faster function startup performance and more efficient usage of elastic network interfaces, reducing VPC cold starts.

Amazon RDS Proxy

At re:Invent 2019, AWS announced the launch of a new service called Amazon RDS Proxy. A fully managed database proxy that sits between your application and your relational database. It efficiently pools and shares database connections to improve the scalability of your application.

 

Significant moments in the serverless LAMP stack timeline

Significant moments in the serverless LAMP stack timeline

Combining these services, it is now it is possible to build secure and performant scalable serverless applications with PHP and relational databases.

Custom runtime API

The custom runtime API is a simple interface to enable Lambda function execution in any programming language or a specific language version. The custom runtime API requires an executable text file called a bootstrap. The bootstrap file is responsible for the communication between your code and the Lambda environment.

To create a custom runtime, you must first compile the required version of PHP in an Amazon Linux environment compatible with the Lambda execution environment .To do this, follow these step-by-step instructions.

The bootstrap file

The file below is an example of a basic PHP bootstrap file. This example is for explanation purposes as there is no error handling or abstractions taking place. To ensure that you handle exceptions appropriately, consult the runtime API documentation as you build production custom runtimes.

#!/opt/bin/php
<?PHP

// This invokes Composer's autoloader so that we'll be able to use Guzzle and any other 3rd party libraries we need.
require __DIR__ . '/vendor/autoload.php;

// 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);

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]
    );
}

The #!/opt/bin/php declaration instructs the program loader to use the PHP binary compiled for Amazon Linux.

The bootstrap file performs the following tasks, in an operational loop:

  1. Obtains the next request.
  2. Executes the code to handle the request.
  3. Returns a response.

Follow these steps to package the bootstrap and compiled PHP binary together into a `runtime.zip`.

Libraries and dependencies

The runtime bootstrap uses an HTTP-based local interface. This retrieves the event payload for each Lambda function invocation and returns back the response from the function. This bootstrap file uses Guzzle, a popular PHP HTTP client, to make requests to the custom runtime API. The Guzzle package is installed using Composer package manager. Installing packages in this way creates a mechanism for incorporating additional libraries and dependencies as the application evolves.

Follow these steps to create and package the runtime dependencies into a `vendors.zip` binary.

Lambda Layers provides a mechanism to centrally manage code and data that is shared across multiple functions. When a Lambda function is configured with a layer, the layer’s contents are put into the /opt directory of the execution environment. You can include a custom runtime in your function’s deployment package, or as a layer. Lambda executes the bootstrap file in your deployment package, if available. If not, Lambda looks for a runtime in the function’s layers. There are several open source PHP runtime layers available today, most notably:

The following steps show how to publish the `runtime.zip` and `vendor.zip` binaries created earlier into Lambda layers and use them to build a Lambda function with a PHP runtime:

  1.  Use the AWS Command Line Interface (CLI) to publish layers from the binaries created earlier
    aws lambda publish-layer-version \
        --layer-name PHP-example-runtime \
        --zip-file fileb://runtime.zip \
        --region eu-west-1

    aws lambda publish-layer-version \
        --layer-name PHP-example-vendor \
        --zip-file fileb://vendors.zip \
        --region eu-west-1

  2. Make note of each command’s LayerVersionArn output value (for example arn:aws:lambda:eu-west-1:XXXXXXXXXXXX:layer:PHP-example-runtime:1), which you’ll need for the next steps.

Creating a PHP Lambda function

You can create a Lambda function via the AWS CLI, the AWS Serverless Application Model (SAM), or directly in the AWS Management Console. To do this using the console:

  1. Navigate to the Lambda section  of the AWS Management Console and choose Create function.
  2. Enter “PHPHello” into the Function name field, and choose Provide your own bootstrap in the Runtime field. Then choose Create function.
  3. Right click on bootstrap.sample and choose Delete.
  4. Choose the layers icon and choose Add a layer.
  5. Choose Provide a layer version ARN, then copy and paste the ARN of the custom runtime layer from in step 1 into the Layer version ARN field.
  6. Repeat steps 6 and 7 for the vendor ARN.
  7. In the Function Code section, create a new folder called src and inside it create a new file called index.php.
  8. Paste the following code into index.php:
    //index function
    function index($data)
    {
     return "Hello, ". $data['name'];
    }
    
  9. Insert “index” into the Handler input field. This instructs Lambda to run the index function when invoked.
  10. Choose Save at the top right of the page.
  11. Choose Test at the top right of the page, and  enter “PHPTest” into the Event name field. Enter the following into the event payload field and then choose Create:{ "name": "world"}
  12. Choose Test and Select the dropdown next to the execution result heading.

You can see that the event payload “name” value is used to return “hello world”. This is taken from the $data['name'] parameter provided to the Lambda function. The log output provides details about the actual duration, billed duration, and amount of memory used to execute the code.

Conclusion

This post explains how to create a Lambda function with a PHP runtime using Lambda Layers and the custom runtime API. It introduces the architecture for a serverless LAMP stack that scales with application traffic.

Lambda allows for functions with mixed runtimes to interact with each other. Now, PHP developers can join other serverless development teams focusing on shipping code. With serverless technologies, you no longer have to think about restarting webhosts, scaling or hosting.

Start building your own custom runtime for Lambda.