DEV Community

Cover image for Creating Serverless Websites with AWS, Bref, and PHP

Creating Serverless Websites with AWS, Bref, and PHP

Prerequisites

I would like to start by explaining that I am going to assume you are familiar with AWS and the following topics. If that's not the case then I would recommend you at least try to understand the basics of these topics before attempting to follow along.


Introduction

Serverless websites on AWS are a common theme. This is expected since hosting a serverless website can have many benefits over traditional web hosting solutions that involve virtual servers. Benefits like: better scalability, lower costs, and reduced latency.

https://dashbird.io/blog/business-benefits-of-serverless/

Most of these serverless websites rely on static web technologies like JavaScript. This permits the simplest serverless website solution as pictured below.

Static Website Architecture

The problem we're aiming to solve is that this simplistic architecture does not support dynamic web technologies like ASP.NET, JSP, or PHP that are still used to build incredible websites today. Websites like Etsy, Facebook, and Slack.

https://trio.dev/blog/companies-using-php

Dynamic websites can leverage AWS to achieve serverless hosting. It just takes a couple of extra steps and a couple of extra services. Let's walk through a working solution.


Bref

In the scenario of PHP, there is an amazing project called Bref, which we can use to simplify our configuration. With Bref we can run our PHP websites on Lambda instead of relying on services like EC2.

To quote their documentation,

Bref is an open-source project that brings full support for PHP and its frameworks to AWS Lambda.

Usage

The simplest implementation is to package our website as a container image and eventually deploy it to Lambda. In order to reference an image for our Lambda function, we need to upload our image to ECR first. Luckily AWS makes interacting with ECR extremely easy. Let's walk through the steps.

  1. Create Dockerfile

    # reference bref as the base image
    FROM bref/php-74-fpm
    
    # install composer
    RUN curl -s https://getcomposer.org/installer | php
    
    # require bref
    RUN php composer.phar require bref/bref
    
    # copy contents into expected directory
    COPY . /var/task
    
    # set handler to our index
    CMD _HANDLER=index.php /opt/bootstrap
    

    https://docs.docker.com/engine/reference/builder/
    https://github.com/wheelers-websites/CloudGuruChallenge_20.10/blob/master/php-api/Dockerfile

  2. Authenticate to ECR

    aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
    

    https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html
    https://docs.docker.com/engine/reference/commandline/login/

  3. Build Your Docker Image

    docker build -t ${AWS_ECR_REPOSITORY} .
    

    https://docs.docker.com/engine/reference/commandline/build/

  4. Tag Your Docker Image

    docker tag "${AWS_ECR_REPOSITORY}:latest" "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWS_ECR_REPOSITORY}:latest"
    

    https://docs.docker.com/engine/reference/commandline/tag/

  5. Push Your Docker Image

    docker push "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWS_ECR_REPOSITORY}:latest"
    

    https://docs.docker.com/engine/reference/commandline/push/

ECR Commands

Find these push commands customized to your account in the ECR console


Architecture

Now that our container image is within ECR we are able to create a Lambda function that references it.

ECR ConsoleLambda Console

After that is done we need to use API Gateway to provide an endpoint for our function.

API Gateway Console

Then we can reference that endpoint as a CloudFront origin to serve our website.

CloudFront OriginsCloudFront Behaviors

Finally, we can optimize this architecture by uploading our static content to S3 and serving that content with a separate origin.

PHP Diagram


CI/CD

As we all know that websites change regularly, I wanted to mention Continuous Integration and Continuous Deployment briefly. I would implement a process to build and update the container image in ECR and on Lambda when code changes are pushed to a Git repository. This is crucial to keep your environment current with your Git repository while avoiding manual deployments. Manual deployments are error-prone and completely avoidable. The image creation and ECR updates can easily be done by repeating the steps outlined above when the source changes in a script. Following those steps with another like below we can update the Lambda function automatically.

aws lambda update-function-code \
--function-name ${AWS_LAMBDA_FUNCTION} \
--image-uri "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWS_ECR_REPOSITORY}:latest" \
--region ${AWS_REGION}
Enter fullscreen mode Exit fullscreen mode

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html

If you're new to CI/CD then I would recommend these great resources to get started.


Conclusion

We started by discussing the problem of hosting dynamic websites using serverless technology. Then we discussed Bref and Docker, which both help in hosting a serverless version of PHP. Then we walked through the architecture required. We pushed our container image to ECR and deployed our PHP code as a Lambda function. Then we created an API Gateway endpoint to invoke our Lambda function over HTTP. Finally, we uploaded our static content to S3 and placed both origins behind a CloudFront distribution. To conclude I would like to reiterate that dynamic websites can enjoy the benefits of serverless and I wholeheartedly believe it's worth the effort.

Please find this live serverless PHP website that I developed as a living example.

If you liked this content maybe you would like to Buy Me a Coffee or connect with me on LinkedIn.

Discussion (1)

Collapse
arminkardovic profile image
Armin

It's look good :).. load time was 10 sec.. I had amaizing time waiting this to be loaded