Categories
AWS English WordPress

WordPress on AWS Lambda (EFS Edition)

I previously wrote a post about running WordPress on AWS Lambda, but it was before EFS support was announced (EFS is a managed network file system AWS provides). Being able to use EFS completely changes the way WordPress works in Lambda (for the better!), so I felt it warranted a new blog post.

In addition, this time I’m using Terraform instead of SAM. This matches the existing infrastructure-as-code setup I use when I deploy infrastructure for clients. Here’s the Terraform module (source code).

Summary

It works. It’s OK. Check it out, it’s running here. It’s not the best, but it isn’t bad, either. The biggest performance bottleneck is the EFS filesystem, and there’s no getting around that. PHP is serving static assets bundled with WordPress as well, which adds to some latency (in this configuration, CloudFront is caching most of these files, however). Tuning opcache to cache files in memory longer helped a lot.

Because EFS is synchronized across all the instances of Lambda, online updates, installs, and uploads work as expected.

What You’ll Need

In this setup, Lambda is only used for running PHP — installing the initial WordPress files is done on an EC2 instance that has the EFS volume mounted. This is a list of what you’ll need.

  1. An AWS account.
  2. A VPC with Internet access through a NAT gateway or instance (comparison). This is important because EFS connectivity requires Lambda to be set up in a VPC, but it won’t have Internet access by default.
  3. Terraform (the module uses v0.12 syntax, so you’ll need to use v0.12.)
  4. A MySQL database (I’m using MySQL on RDS using the smallest instance available)
  5. An EC2 instance to perform the initial setup and install of WordPress.

For a list of the resources that Terraform will provision, take a look at the Resources page here.

Steps

These steps assume you’re running this Terraform module standalone — if you want to run it in the context of an existing Terraform setup, prepare to adjust accordingly.

If you’re following this step-by-step, be sure to choose the us-west-2 region. Lambda Layer that I’m using for this is only published in the us-west-2 region. I’m working on getting the layer published in other regions, but in the meantime, use my fork of the php-lambda-layer to create your own in the region of your choosing.

1. Start the EC2 instance.

(If it isn’t already running)

I’m using a t3a.nano instance. Install the amazon-efs-utils package to get ready for mounting the EFS volume.

Also, while you’re in the console, note down the ID of a Security Group that allows access to RDS and the IDs of the private subnets to launch Lambda in.

2. Get Terraform up and running.
$ git clone https://github.com/KotobaMedia/terraform-aws-wordpress-on-lambda-efs
$ cd ./terraform-aws-wordpress-on-lambda-efs

Create a file called local.auto.tfvars, and put the following contents in to it:

# An array of the Security Group IDs you listed in step 1.
security_group_ids = ["sg-XXX"]

# An array of the Subnet IDs you listed in step 1.
subnet_ids = ["subnet-XXX", "subnet-XXX", "subnet-XXX"]

If you want to use a custom domain name (instead of the default randomly-generated CloudFront domain name), set the acm_certificate_arn and domain_name variables as well.

Now, you’re ready to create the resources.

$ terraform apply

If you’re asked for your AWS credentials, Ctrl-C and try setting the authentication information via environment variables. I manage a lot of AWS accounts, so I use the AWS_PROFILE environment variable.

Terraform will ask you if you want to go ahead with the apply or not — look over the changes (the initial apply should not have any modifications or deletions), then respond yes.

When the apply has finished, you should see some outputs. If you don’t (or you already closed the window), you can always run terraform output. Keep this window open, you’ll need it in the next step.

3. Mount EFS on the EC2 instance.

First, we need to give the EC2 instance access to the EFS filesystem. Terraform created a security group for us (it’s in the efs_security_group_id output), so attach that to your EC2 instance.

Log in to your EC2 server, then mount the EFS filesystem (replace fs-XXXXX with the value of the efs_file_system_id output):

$ sudo -s
# mkdir /mnt/efs
# mount -t efs fs-XXXXX:/ /mnt/efs

If you’re having trouble mounting the filesystem, double check the security groups and take a look at the User Guide.

4. Install WordPress.

Now that the filesystem is mounted, we can finally proceed to install WordPress. Terraform automatically created a directory in the EFS filesystem (/mnt/efs/roots/wp-lambda-$RANDOM_STRING), so cd to there first. Download the latest version of WordPress, then extract the files there.

Now, you can go ahead with the famous five-minute install like you would with any other WordPress site! If you didn’t set a custom domain name, your site should be accessible at the domain name outputted at cloudfront_distribution_domain_name. If you did set a custom domain, then set a CNAME or alias to the CloudFront distribution domain name, then you should be able to access the site there.

Where to go from here

Here are some ideas for performance improvements that I haven’t tried, but should have some potential.

  • Upload files to S3 instead of WordPress. I use this plugin by Human Made: humanmade/S3-Uploads.
  • Experiment with adjusting the opcache settings in src/php.ini.
  • Use a lightweight nginx server to serve static assets from EFS to CloudFront.
  • Experiment with setting Cache-Control headers in handler.php for static files.

Limitations

There are a couple hard limits imposed by AWS due to the technical limitations of the infrastructure.

Here are some other limitations that you’ll have to keep in mind.

  • No FTP / SSH access — you’ll need to manage an EC2 instance if you need command line or direct file access.
  • All the considerations of accessing a connection-oriented database from Lambda. You can try using Aurora Serverless if you run in to connection problems. RDS Proxy may also be able to provide you with a solution.

Thanks!

Thanks for reading! If you have any questions or comments, please don’t hesitate to leave a comment or send me a tweet.

7 replies on “WordPress on AWS Lambda (EFS Edition)”

Hi Keita,
Thanks for providing the information, I want to setup WordPress on Lambda using Aurora Serverless. Blog is providing the concept but if possible please provide step by step guide so it will help me as well others.
Thanks in Advance.

Setting up Aurora Serverless is a little out of scope for this post, but I’ll let you know when I write a step-by-step guide for setting Aurora Serverless up for WordPress!

Hi Ketia,
I have searched and found that currently RDS proxy is not supporting Arora serverless and unable to figure out how I connect directly Arora serverless from Lambda.
I have followed the blog and run terrafom apply command and it has created lambda php runction along with other resources. I have created RDS Instance and Proxy and added the proxy in Lambda function. But while accessing the cloudfront domain getting error as “Proxy Authentication Failed”
Could you give me clue, Where I am wrong?
Thanks in advance,
Regards,
Tarun

Interesting post. Can you talk more about the issues you faced with connecting from Lambda -> MySQL? Were you not able to reuse a MySQL connection pool?

The problem with connecting directly to MySQL from Lambda is that Lambda has unlimited concurrency, while MySQL has a limit to concurrent connections. PHP connection pooling doesn’t work in this case because PHP can’t control or know about how many different instances of PHP are running. RDS Proxy could be used to create a connection pool outside of Lambda.

Try to use the serverless Aurora Mysql instead of mysql RDS with the smallest instance type:

https://aws.amazon.com/about-aws/whats-new/2020/06/announcing-aurora-serverless-with-mysql-5-7-compatibility/

resource "aws_rds_cluster" "default" {
  cluster_identifier = "myserverless-aurora"
  engine = "aurora"
  engine_mode = "serverless"
  availability_zones = ["us-east-1a", "us-east-1b"]
  master_username = "admin"
  master_password = "password1234"
  backup_retention_period = "1"
  preferred_backup_window = "07:00-09:00"
  db_subnet_group_name = "mysubnetgroups"
  skip_final_snapshot = "true"
  vpc_security_group_ids = ["sg-xxxxxxxxxxxxxx"]
}

The reason why I opted to use a cheap RDS instance over Aurora Serverless is because Aurora Serverless can get pretty expensive if it’s running for a while. In us-west-2, where I did this test, it’s $0.06 per ACU-Hour. I’m using a t3.micro RDS instance, which is $12.41 monthly. That means I get about 200 hours with Serverless when it’s cheaper than RDS — that’s only about 30% of the month.

Everyone’s needs vary, though, and Aurora Serverless is probably a good match for this setup in a scenario resembling production more than this demo case — not due to the cost savings but due to the convenience and flexibility of autoscaling.

Thanks for reading and the comment! I’ll update the post with some notes on Aurora Serverless in the future.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.