Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Twelve-Factor App (and PHP on Heroku)

The Twelve-Factor App (and PHP on Heroku)

Best practices for modern PHP apps. Presented at International PHP Conference Spring Edition 2014 in Berlin, Germany.

D6ccd6409910643d05ddaea3b2cd6f13?s=128

David Zuelke

June 03, 2014
Tweet

Transcript

  1. THE TWELVE-FACTOR APP Best Practices for Modern PHP Applications

  2. David Zuelke

  3. David Zülke

  4. None
  5. dz@heroku.com

  6. @dzuelke

  7. “The Twelve-Factor App” is a manifesto, a methodology, a condensed

    collection of experiences.
  8. Its goals are scalability, maintainability, portability.

  9. I. CODEBASE One codebase, many deploys.

  10. I. CODEBASE One codebase, many deploys. Git, Mercurial, SVN, even

    CVS are okay. A samba share is never okay. Neither are floppy disks.
  11. II. DEPENDENCIES Applications have explicitly declared dependencies.

  12. 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"        } }
  13. III. CONFIGURATION Store config in the environment.

  14. III. CONFIGURATION Store config in the environment. Assumption: same code

    but different configuration per deployment target
  15. 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
  16. 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.
  17. 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.
  18. X. DEV/PROD PARITY Keep dev, stage and prod envs as

    similar as possible.
  19. X. DEV/PROD PARITY Keep dev, stage and prod envs as

    similar as possible. SQLite ≠ MySQL Apache ≠ Nginx File based sessions ≠ Redis based sessions
  20. 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: Vagrant is always your friend!
  21. XI. LOGGING Treat your logs as a stream of events.

  22. 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.
  23. XII. ADMIN PROCESSES Management tasks like DB migrations are one-off

    processes.
  24. XII. ADMIN PROCESSES Management tasks like DB migrations are one-off

    processes. The same release, the same config, the same code!
  25. PHP ON HEROKU Putting it all together!

  26. $  heroku  create $  git  push  heroku  master -­‐-­‐-­‐-­‐-­‐>  PHP

     app  detected -­‐-­‐-­‐-­‐-­‐>  Setting  up  runtime  environment...              -­‐  PHP  5.5.12              -­‐  Apache  2.4.9              -­‐  Nginx  1.4.6 -­‐-­‐-­‐-­‐-­‐>  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
  27. DEMO TIME!

  28. DEV/PROD PARITY

  29. heroku-­‐python-­‐app  $  cat  Procfile web:  gunicorn  hello:app

  30. heroku-­‐ruby-­‐app  $  cat  Procfile web:  bundle  exec  unicorn  -­‐p  $PORT

     -­‐c  ./config/unicorn.rb
  31. heroku-­‐java-­‐app  $  cat  Procfile web:  java  -­‐jar  target/dependency/jetty-­‐runner.jar  -­‐-­‐port  $PORT

      target/*.war
  32. heroku-­‐php-­‐app  $  cat  Procfile web:  php  -­‐S  0.0.0.0:$PORT

  33. PHP needs a dedicated web server

  34. heroku-­‐php-­‐app  $  cat  Procfile web:  vendor/bin/heroku-­‐php-­‐nginx heroku-­‐php-­‐app  $  composer  require

     -­‐-­‐dev  heroku/heroku-­‐ buildpack-­‐php ./composer.json  has  been  updated Loading  composer  repositories  with  package  information Updating  dependencies  (including  require-­‐dev)    -­‐  Installing  heroku/heroku-­‐buildpack-­‐php  (v15)        Loading  from  cache Writing  lock  file Generating  autoload  files
  35. (only needed if you want to run things locally)

  36. heroku-­‐php-­‐app  $  foreman  start 17:47:26  web.1    |  started  with

     pid  70338 17: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.conf 17: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  root 17:47:27  web.1    |  [29-­‐Apr-­‐2014  17:47:27]  NOTICE:  [pool  www]   'user'  directive  is  ignored  when  FPM  is  not  running  as  root 17:47:27  web.1    |  [29-­‐Apr-­‐2014  17:47:27]  NOTICE:  fpm  is   running,  pid  70379 17:47:27  web.1    |  [29-­‐Apr-­‐2014  17:47:27]  NOTICE:  ready  to   handle  connections
  37. ONE MORE THING...

  38. heroku-­‐php-­‐app  $  git  rm  Procfile heroku-­‐php-­‐app  $  hhvm  `which  composer`

     require  hhvm  3.0.1 ./composer.json  has  been  updated Loading  composer  repositories  with  package  information Updating  dependencies  (including  require-­‐dev) Nothing  to  install  or  update Generating  autoload  files heroku-­‐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.0.1  in  composer.json. -­‐-­‐-­‐-­‐-­‐>  Setting  up  runtime  environment...              -­‐  HHVM  3.0.1              -­‐  Apache  2.4.9              -­‐  Nginx  1.4.6 -­‐-­‐-­‐-­‐-­‐>  Building  runtime  environment...              NOTICE:  No  Procfile,  defaulting  to  'web:  vendor/bin/ heroku-­‐hhvm-­‐apache2'
  39. !e End

  40. THE TWELVE-FACTOR APP Further reading: http://12factor.net/ http://devcenter.heroku.com/categories/php I'm @dzuelke, thank

    you for listening :)