Dimitris Blog logo
Dimitris Blog
Nov 6, 2020

Truly zero downtime deployments with Laravel Envoyer

Deployments PHP

Laravel Envoyer is a great tool that reduces the pain from automating the deployments of your projects. According their marketing page they offer "Zero Downtime PHP Deployment".

How is zero downtime achieved?

Similar to the open source Deployer, Envoyer is stores new builds inside new and separate release folders.

After running all the commands needed for creating the new build (like composer install, artisan migrate etc), the new build is ready to release. Envoyer then creates a symlink linking the nginx public root to the new release directory.

In this sense, it's a zero downtime deployment because the creation of a symlink is almost instant. However there is a catch.

The problem

I noticed that during the deployment process some http requests failed.

Occasionally, the server responded with an unfriendly error page, because nginx was not able to handle the request.

I also confirmed the errors by looking into the nginx error log. This was the error in /var/log/nginx/site-error.log.

2020/08/18 09:02:57 [error] 6383 #6383: *10527974 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 2.84.198.47, server: metabook.gr, request: "GET / HTTP/2.0", upstream: "fastcgi://unix:/var/run/php/php7.2-fpm.sock:", host: "metabook.gr"

The cause of these errors was that after every deployment, Envoyer was configured to reload php-fpm in order to reset the php cache (opcache).

Although the downtime was small, it was still a pain in the ass because we wanted truly zero downtime deployments and offer an excellent user experience to our users.

Because of this small downtime, we were conservative with the number of deploys per day. For example we avoided deployments during high server load for example and this was creating bottlenecks in our product development process.

The solution

Luckily, reloading php-fpm is not the only way to reset the opcache. There is a php function opcache_reset() that resets the cache but there is a catch: This function has to be executed from the php-fpm process (nginx) and not the command line.

This means that if you just run the function inside artisan, it won't work.

However thanks to the open source Cachetool command, we are able to reset the cache from the command line! I presume this tool makes an http call behind the scenes that resets the opcache.

How to install CacheTool

We can create the following script and execute it once on our web server:

# download cachetool.phar curl -sLO https://github.com/gordalina/cachetool/releases/latest/download/cachetool.phar # make it executable chmod +x cachetool.phar # make it globally available mv cachetool.phar /usr/local/bin/ # To execute for php-fpm, run # cachetool.phar opcache:reset

After installing, we execute as final step the command below to reset the opcache inside our deployment process.

cachetool.phar opcache:reset

Also, do not forget to stop the execution of php-fpm reload causing the downtime!

That's it!

That's how we converted an "Almost zero downtime deployment" to a Truly zero downtime deployment with PHP!

The database can be avoided
Simplicity Coding Open Source
11 months ago