Deploying PHP applications

with zero downtime





ConFoo, Montreal - February 23rd, 2023

© David Buchmann







David Buchmann - david@liip.ch

PHP Engineer, Liip AG, Switzerland

If you really have to, use FTP with TLS: SFTP

Upload your application


git pull / scp / rsync



Replacing files in place on the running system

Replace files in place

Goals for a deployment


Automate everything!

Deploy from CI

Have your CI run the deployments

Atomic deployment

To Cloud or not to Cloud?

(Virtual) servers Cloud
Keep running Create new instances
Server configuration changes? New server
Replace the codebase New instance created with new codebase
Clear local caches too Only clear persistent caches

Deploying to a server

Symbolic Link (symlink)


Symbolic Link (symlink)


Consequences

PHP Opcache

Reset PHP Opcache

Resolve symlinks

Make webserver use resolved path when talking to php-fpm

Zero Downtime?

Rolling Updates

Rolling Updates: Gotchas

Beware of caches!

Client side cache

Migration tasks

Database changes

Non-breaking add column

Non-breaking remove table

Non-breaking remove column

Non-breaking rename column

Security Considerations

Rollback

When the shit hits the fan,
you need a fast way back

Rollback



Into the Clouds

Cloud


Tools for deploying

Deploying

Tools for deploying

Currently, the tool of choice seems to be deployer, especially for PHP applications

Many other tools:
Fabric, Capistrano, Jenkins, Apache Ant, ...

Deployer

Deployer

host('example.org')
    ->set('remote_user', 'deployer')
    ->set('deploy_path', '~/example');
set('shared_dirs', [
    'web/sites/default/files',
]);
set('shared_files', [
    '.env',
    'web/sites/default/settings.php',
]);

Then use dep provision for interactive configuration

Deployer server folders

~/example                      # deploy_path
 |- current -> releases/1      # Symlink current release
 |- releases                   # Dir for all releases
    |- 1                       # Files of release 1
       |- ...
       |- .env -> shared/.env  # Symlink to shared .env file
 |- shared                     # Dir for files to preserve
    |- ...
    |- .env                    # Example: shared .env file
 |- .dep                       # Deployer config files
            

Tasks

task('apt:update', function () {
    run('apt-get update');
})->oncePerNode();
            

Tasks can access per-host configuration

Some Primitives

Chaining tasks

$deploy_pre = [             $deploy_post = [
    'deploy:release',           'deploy:shared',
    'build:prepare',            'deploy:symlink',
    'vendors:install',          'build:cleanup',
    'package:build',            'cleanup',
    'package:copy',             'success',
    'package:extract',      ];
];

task('deploy', array_merge(
    $deploy_pre,
    $deploy_post
))->desc('Deploy with tar packaging');
            

Hints



Thank you!


@dbu

David Buchmann, Liip SA