Get Laravel Production-Ready in the Google App Engine

Viet Yen Nguyen
Hypefactors
Published in
4 min readMay 24, 2018

At Hypefactors, we rely heavily on the power and benefits of cloud computing. One of those great benefits is reduced DevOps. Managed services may cost more, yet they save time. With that in mind, and also the growth we’ve been having, the time was ripe to migrate one of our core systems, our API backend server, written in Laravel from its original incepted AWS EC2 instance to a fault-tolerant, load-balanced and auto-scaled service using Google App Engine (GAE) Flexible.

GAE Flexible is the GAE that allows you to run a Docker image. It has loads of other benefits that come with GAE, like HTTP/2, SSL termination, auto-renewal of your Let’s Encrypt SSL certs, versioning and such.

There are a few pre-existing handful articles out there that will help you get started. Check out for example a community tutorial and the blog post on Updivision. Both articles will help you to get a first version up and running, but don’t go into the details and the tuning of an actual production-ready application. In this article, I’ll share with you the four most important tunings to get your Laravel application production-ready in the Google App Engine.

Our Setup

  • Google App Engine Flexible, using our customized Docker image
  • A pretty substantial Laravel 5.6 app, about 50K LOC
  • CircleCI for Jest and PHPUnit unit tests, end-to-end tests with Cypress and deployments
  • Google Cloud SQL database
  • Google Memorystore Redis (for managing queued jobs with Horizon)

Tuning 1: Running Scheduled Artisan Commands

The Artisan scheduler needs to be run every minute to ensure your artisan commands gets executed. Even though there’s a cron scheduler with Google App Engine, it can only call an URL endpoint. In theory this could be used to trigger the Artisan scheduler, but I decided for another way to avoid potential HTTP timeouts. Every GAE Flexible runs supervisord, which can be extended with additional tasks. Simply add the following to your additional-supervisord.conf

cron-artisan.sh is a shellscript that uses the Gist here to ensure that the Artisan scheduler is run every minute.

If you’re running multiple instances for fault-tolerance and load balancing, like we do, then you’ll start seeing Artisan commands on all instances. This is probably not what you want for a most Artisan commands. Fortunately, you can fix that by using the recently introduced onOneServer directive. You need a shared cache driver, like Redis, to ensure it works correctly.

Tuning 2: Configure Trusted Proxies

It’s a good practice to rate limit requests. When you move from a relatively simple and direct EC2 instance to a load-balanced scenario, your users probably will see more frequent rate limits popping up. The problem: you need to tell Laravel that there’s a proxy in between that you trust. The solution is to configure the TrustProxies middleware. The GAE load balancer doesn’t seem to have a fixed internal IP, so we’ve set the $proxies to * . See the code snippet below

Tuning 3: Enable Persistent MySQL

If you’re like us, and also like the first two articles linked that get you started with Laravel on GAE, you’re using the cloud_sql_instances option in the beta_settings to enable Cloud SQL Proxy such that you can connect to your MySQL server using a socket connection. That’s all good of course. You’ll however see lots of logging starting to pop up, something like:

2018/05/15 17:20:52 Client closed local connection on /cloudsql/XXX:europe-west1:YYY

2018/05/15 17:20:52 New connection for …

And that every second. What’s up with that? It’s actually alright, and that means a new MySQL connection is opened on every request. With the availability of persistent connections, that’s not very efficient. And since the MySQL PHP driver by default resets the state on every persisted connection, you should be good to use it in nearly all production work loads.

Update May 2018: Note that in between we learned that only the mysqli driver resets the state on the persisted connections. The PDO driver doesn’t. So be aware of this, and check if this works for your use case!

To enable persistent MySQL connections, edit your config/database.php and add the following to options to make it look like this

Bonus: you’ll shave off at least a dozen milliseconds on every request, even if the database is in the same zone as your GAE instance. Most users won’t notice that though.

En voila. You’re all set for serving API requests at a high-performance under high-load using the Google App Engine. Enjoy!

p.s. if you thoroughly enjoy stuff like this why not check our job openings at Hypefactors?

--

--