DEPLOYMENTWITHOUT THE HASSLE Arne Blankerts Sebastian Heuer Principal Consultant & Co-Founder Developer Advocate thePHP.cc die kartenmacherei OUR FEATURE IS DONE, LET'S DEPLOY!

$ ssh -A [email protected] [dave@ip-172-31-4-54 ~]$ cd /srv/application/ [dave@ip-172-31-4-54 application]$ git pull From bitbucket.org:example.com/application 3c068b7..1560b43 develop -> origin/develop Updating 3c068b7..1560b43 composer. | 12 + composer.lock | 22 ++ src/Application. | 6 + [dave@ip-172-31-4-54 application]$ composer install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Your requirements could not be resolved to an installable set of packages. COMPOSER INSTALL FAILS BOB STARTS SHOUTING AT DAVE BECAUSE THE WEBSITE IS BROKEN LET'S DEPLOY INTO A SEPARATE DIRECTORY AND USE A SYMLINK! $ ssh -A [email protected] [dave@ip-172-31-4-54 ~]$ git clone [email protected]:/example.com/application /srv/application-1.23.0/ Cloning into 'application—1.23.0' remote: Counting objects: 644, done. remote: Compressing objects: 100% (34/34), done. remote: Total 644 (delta 9), reused 0 (delta 0), pack-reused 610 Receiving objects: 100% (644/644), 85.50 KiB | 0 bytes/s, done. Resolving deltas: 100% (369/369), done. Checking connectivity... done. [dave@ip-172-31-4-54 ~]$ cd /srv/application-1.23.0/ [dave@ip-172-31-4-54 ~]$ composer install Loading composer repositories with package information Updating dependencies (including require-dev) Nothing to install or update Writing lock file Generating autoload files DEPLOYMENT IS FINISHED, SYMLINK IS UPDATED

[dave@ip-172-31-4-54 ~]$ ln -sfn /srv/application-1.23.0 /srv/application [dave@ip-172-31-4-54 ~]$ ls -l /srv/ -rw-r—-r—- 1 dave dave 4096 Oct 05 12:37 application-1.22.3 -rw-r—-r—- 1 dave dave 4096 Oct 21 16:49 application-1.23.0 -rw-r—-r—- 1 dave dave 4096 Oct 21 16:49 application -> /srv/application-1.23.0 WEBSITE BEHAVES WEIRD, NOT REPRODUCIBLE ON A DEV MACHINE, MAGICALLY FIXES ITSELF AFTER A COUPLE OF MINUTES REASON: REALPATH CACHE “If you are flipping a symlink on a live server which can happen in the middle of a request then your deploy mechanism is broken.”

–Rasmus Lerdorf http://www.serverphorums.com/read.php?7,1233612 SOMEBODY ELSE SHOULD DECIDE WHICH VERSION TO USE

(WITHOUT THE NEED OF RESTARTING SERVICES OR INVALIDATING CACHES) HTTPD

PHP active version?

APP V1 v1 APP V2 APP V2 APP

active version? v2 PHP HTTPD APP V1 APP INSTALLATION AND ACTIVATION ARE DECOUPLED HTTPD

PHP active version?

APP V1 v2 APP V2

$redis = new Redis; $redis->connect('127.0.0.1', 6379); $redis->select(0);

$runWithVersion = $redis->get('version'); require '/var/www/php/application/' . $runWithVersion . '/bootstrap.php'; ACTIVATION ONLY AFTER INSTALLATION SUCCEEDED ON ALL SERVERS NEXT DEPLOYMENT WEB SERVER IS POINTED TO NEW VERSION EXISTING SESSIONS BREAK EXISTING SESSIONS SHOULD STICK TO THEIR INITIAL VERSION HTTPD

PHP existing session?

APP V1 v1 APP V2 HTTPD

PHP active version?

APP V1 v2 APP V2

$keys[] = 'default_version';

$redis = new Redis; $redis->connect('127.0.0.1', 6379); $redis->select(0);

$runWithVersion = array_filter($redis->mGet($keys)); require '/var/www/php/application/' . $runWithVersion[0] . '/bootstrap.php'; NEXT DEPLOYMENT WEB SERVER IS POINTED TO NEW VERSION WEBSITE BEHAVES WEIRD, NOT REPRODUCIBLE ON A DEV MACHINE REASON: COMPOSER HAS DEV-MASTER DEPENDENCIES, INSTALLED DIFFERENT VERSION WE SHOULD NOT RUN BUILD PROCESSES ON THE TARGET SERVERS WE NEED A BUILD SERVER OUR CI SERVER CAN DO THE BUILDS! HOW DO WE PUT THE BUILD RESULT ON OUR SERVERS? WE USE RSYNC THE NEW VERSION NEEDS A PHP EXTENSION WE DON'T HAVE ON OUR SERVERS YET

DEPLOYMENT IS BLOCKED, DEV WAITS FOR OPS DEV: WE WANT TO BE ABLE TO MAKE INFRASTRUCTURE CHANGES OURSELVES OPS GOES ON STRIKE

WHO IS RESPONSIBLE FOR WHAT? OPS: MACHINES OS BASE CONFIGURATION DEV: APPLICATION APPLICATION-SPECIFIC CONFIGURATION OPS PROVIDES OS PACKAGES

(BY ALLOWING US TO USE THE DISTRIBUTION'S REPOSITORY OR PROVIDING THEIR OWN) ALL ENVIRONMENTS CAN USE THE SAME PACKAGE SOURCE DEV IS ENABLED TO DEFINE REQUIRED TRUSTED PACKAGES SO THEY CAN BE INSTALLED DURING DEPLOYMENT $ composer install

Loading composer repositories with package information Updating dependencies (including require-dev) Your requirements could not be resolved to an installable set of packages.

Problem 1 - The requested PHP extension ext-imagick * is missing from your system. DON'T INVENT CUSTOM STUFF, THERE IS SOMETHING YOU CAN USE OUT OF THE BOX OS PACKAGES ALLOW DEFINING DEPENDENT PACKAGES ALREADY WE SHOULD PACKAGE OUR APPLICATION HOW TO BUILD OS PACKAGES .RPM Summary: example.com's e-commerce store frontend example-package-1.23.0/ Name: example-package build/ Version: 1.23.0 application/ Release: vendor.1 src/ domain/ Provides: example-package-%{version}-%{release} http/ Requires: php-fpm >=7.0.11, example- = 1.23.0 Application.php vendor/ BuildRoot: %{_tmppath}/%{name}-%{version}-\ autoload.php %{release}-root-%(%{__id_u} -n) web/ BuildArch: noarch index.php application.spec %description example.com's e-commerce store frontend PHP layer %install [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT example-package-1.23.0/ build/ install -m 755 -d $RPM_BUILD_ROOT/srv/application application/ src/ cp -R build/application/* \ domain/ $RPM_BUILD_ROOT/srv/application http/ Application.php %files vendor/ %defattr(-,root,root) autoload.php %dir /srv/application web/ /srv/application/* index.php application.spec %post

%preun $ cd ~/projects/ $ rpmbuild -bb example-package-1.23.0/application.spec Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.AZ7B61 […] Wrote: /home/dave/rpmbuild/RPMS/noarch/example-\ package-1.23.0-vendor.1.noarch.rpm + exit 0 . example-package-1.23.0/ DEBIAN/ control postinst Package: example-application-1.23.0 preremove Version: 1 srv/ Maintainer: Dave application/ Description: example.com's e-commerce store frontend src/ Section: all domain/ Priority: optional http/ Depends: php7-fpm (>=7.0.11), example-library-1.23.0 Application.php Architecture: all vendor/ autoload.php web/ index.php $ cd ~/projects/ $ fakeroot -deb —-build example-package-1.23.0/ dpkg-deb: building package 'example-application-1.23.0' in 'example-package-1.23.0.deb'. $ ls -l -rw-r—-r—- 1 dave dave 4096 Oct 21 16:49 example-package-1.23.0 -rw-r—-r—- 1 dave dave 910 Oct 23 10:32 example-package-1.23.0.deb YOU CAN BUILD VARIOUS PACKAGES OUT OF ONE SOURCE

(LIKE STATIC CONTENT AND PHP APPLICATION) BUT WAIT, THERE'S MORE! TARGET SERVERS DON'T NEED BUILD TOOLS LIKE COMPOSER OR INSTALLATION HAPPENS IN A TRANSACTION HOOKS

(PREINSTALL, POSTINSTALL, PREREMOVE, …) CONFIGURATION MANAGEMENT app v1.rpm app-nginx-config-dev.rpm

requires

provides app-nginx-config.rpm provides

requires app v2.rpm app-nginx-config-prod.rpm MULTIPLE PACKAGES CAN NOT OWN THE SAME FILE USE PUPPET & CO WHAT ABOUT DATABASE MIGRATIONS? YOU DON'T "MIGRATE" THE DATABASE NO ALTER TABLE APP V1 APP V2

TABLE V1 TABLE V2 AUTOMATE EVERYTHING, MANUAL TRIGGERS SHOULD BE A BUSINESS DECISION IMMUTABLE SERVER Q&A

Arne Blankerts Sebastian Heuer @thephpcc @techdotkam @arneblankerts @belanur