PHP ON HEROKUDavid Zuelke
Heroku
@dzuelke
Dreamforce 2014
David Zuelke
David Zülke
“The Twelve-Factor App”is
a manifesto,a methodology,
a condensed collection of experiences.
Its goals arescalability,
maintainability,portability.
I. CODEBASE
One codebase, many deploys.
I. CODEBASE
One codebase, many deploys.
Git, Mercurial, SVN, even CVS are okay.A samba share is never okay.
Neither are floppy disks.
II. DEPENDENCIES
Applications have explicitly declared dependencies.
II. DEPENDENCIES
Applications have explicitly declared dependencies.
$ cat composer.json{ "require": { "php": ">=5.3.3", "ext-mcrypt": "*", "symfony/symfony": "~2.4.6", "doctrine/orm": "~2.2,>=2.2.3", "doctrine/doctrine-bundle": "~1.2", "twig/extensions": "~1.0", "symfony/monolog-bundle": "~2.4" }}
III. CONFIGURATION
Store config in the environment.
III. CONFIGURATION
Store config in the environment.
Assumption:same code but different configuration per deployment target
III. CONFIGURATION
Store config in the environment.
$transport = Swift_SmtpTransport::newInstance( getenv('EMAIL_HOST'), getenv('EMAIL_PORT')?:25) ->setUsername(getenv('EMAIL_USERNAME')) ->setPassword(getenv('EMAIL_PASSWORD'));
Assumption:same code but different configuration per deployment target
V. BUILD, RELEASE, RUN
A build step vendors dependencies, prepares assets, etc.A release step creates a package from build and config.
A runtime step executes, without special knowledge.
V. BUILD, RELEASE, RUN
A build step vendors dependencies, prepares assets, etc.A release step creates a package from build and config.
A runtime step executes, without special knowledge.
X. DEV/PROD PARITY
Keep dev, stage and prod envs as similar as possible.
X. DEV/PROD PARITY
Keep dev, stage and prod envs as similar as possible.
SQLite ≠ MySQLApache ≠ Nginx
File based sessions ≠ Redis based sessions
X. DEV/PROD PARITY
Keep dev, stage and prod envs as similar as possible.
SQLite ≠ MySQLApache ≠ Nginx
File based sessions ≠ Redis based sessions
X. DEV/PROD PARITY
Keep dev, stage and prod envs as similar as possible. SQLite ≠ MySQL
Apache ≠ Nginx File based sessions ≠ Redis based sessions
If apt-get or brew don't get the job done on your box:Vagrant is always your friend!
XI. LOGGING
Treat your logs as a stream of events.
XI. LOGGING
Treat your logs as a stream of events.Stop rotating logs and so forth in your app.
Let the runtime worry about it.Log to STDOUT/STDERR.
Centrally archive it.
XII. ADMIN PROCESSES
Management tasks like DB migrations are one-off processes.
XII. ADMIN PROCESSES
Management tasks like DB migrations are one-off processes.
The same release,the same config,the same code!
PHP ON HEROKU
• Putting it all together!
$ heroku create$ git push heroku master-----> PHP app detected-----> Setting up runtime environment... - PHP 5.5.16 - Apache 2.4.10 - Nginx 1.6.0-----> Installing PHP extensions: - opcache (automatic; bundled) - memcached (composer.json; downloaded) - intl (composer.json; bundled) - newrelic (add-on detected; downloaded)-----> Installing dependencies... Composer version 05d991 2014-04-29 12:36:19 Loading composer repositories with package information Installing dependencies from lock file - Installing psr/log (1.0.0) Loading from cache - Installing monolog/monolog (1.9.1) Loading from cache Generating optimized autoload files
DEMO TIME!
DEV/PROD PARITY
heroku-python-app $ cat Procfileweb: gunicorn hello:app
heroku-ruby-app $ cat Procfileweb: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
heroku-java-app $ cat Procfileweb: java -jar target/dependency/jetty-runner.jar --port $PORT target/*.war
heroku-php-app $ cat Procfileweb: php -S 0.0.0.0:$PORT
PHP needs a dedicated web server
heroku-php-app $ cat Procfileweb: vendor/bin/heroku-php-nginxheroku-php-app $ composer require --dev heroku/heroku-buildpack-php./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev) - Installing heroku/heroku-buildpack-php (v43) Loading from cache
Writing lock fileGenerating autoload files
(only needed if you want to run things locally)
heroku-php-app $ foreman start17:47:26 web.1 | started with pid 7033817:47:26 web.1 | Booting on port 5000...17:47:26 web.1 | Using PHP-FPM configuration file 'vendor/heroku/heroku-buildpack-php/conf/php/php-fpm.conf'17:47:26 web.1 | Using PHP configuration (php.ini) file 'vendor/heroku/heroku-buildpack-php/conf/php/php.ini'17:47:26 web.1 | Using Nginx server-level configuration include 'vendor/heroku/heroku-buildpack-php/conf/nginx/default_include.conf'17:47:27 web.1 | Using Nginx configuration file 'vendor/heroku/heroku-buildpack-php/conf/nginx/heroku.conf.php'17:47:27 web.1 | Interpreting vendor/heroku/heroku-buildpack-php/conf/nginx/heroku.conf.php to heroku.conf17:47:27 web.1 | Starting log redirection...17:47:27 web.1 | Starting php-fpm...17:47:27 web.1 | Starting nginx...17:47:27 web.1 | [29-Apr-2014 17:47:27] NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root17:47:27 web.1 | [29-Apr-2014 17:47:27] NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root17:47:27 web.1 | [29-Apr-2014 17:47:27] NOTICE: fpm is running, pid 7037917:47:27 web.1 | [29-Apr-2014 17:47:27] NOTICE: ready to handle connections
ONE MORE THING...
heroku-php-app $ git rm Procfileheroku-php-app $ hhvm `which composer` require hhvm ~3.2./composer.json has been updatedLoading composer repositories with package informationUpdating dependencies (including require-dev)Nothing to install or updateGenerating autoload filesheroku-php-app $ git add composer.*heroku-php-app $ git ci -m 'use HHVM'heroku-php-app $ git push heroku master
-----> PHP app detected-----> Detected request for HHVM 3.2.0 in composer.json.-----> Setting up runtime environment... - HHVM 3.2.0 - Apache 2.4.10 - Nginx 1.6.0-----> Building runtime environment... NOTICE: No Procfile, defaulting to 'web: vendor/bin/heroku-hhvm-apache2'
The End
PHP ON HEROKUFurther reading:
http://12factor.net/http://devcenter.heroku.com/categories/php
I'm @dzuelke, thank you for listening :)