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.
Most of these serverless websites rely on static web technologies like JavaScript. This permits the simplest serverless website solution as pictured below.
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.
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.
-
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 -
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/ -
Build Your Docker Image
docker build -t ${AWS_ECR_REPOSITORY} .
-
Tag Your Docker Image
docker tag "${AWS_ECR_REPOSITORY}:latest" "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWS_ECR_REPOSITORY}:latest"
-
Push Your Docker Image
docker push "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AWS_ECR_REPOSITORY}:latest"
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.
After that is done we need to use API Gateway to provide an endpoint for our function.
Then we can reference that endpoint as a CloudFront origin to serve our website.
Finally, we can optimize this architecture by uploading our static content to S3 and serving that content with a separate origin.
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}
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.
Top comments (3)
Awesome article! It loaded for me on 2 sec. Lighthouse gives it a good score too.
Thanks for your analysis. I'm not familiar with Lighthouse, but will definitely look into them now!
It's look good :).. load time was 10 sec.. I had amaizing time waiting this to be loaded