Slide 1

Slide 1 text

ELEPHANTS IN THE CLOUDS Mastering PHP on Heroku

Slide 2

Slide 2 text

David Zuelke

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Slide 5

Slide 5 text

@dzuelke

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

5 Billion requests/day 4+ Million apps created 150+ add-on partners

Slide 8

Slide 8 text

“The Twelve-Factor App”

Slide 9

Slide 9 text

“The Twelve-Factor App”

Slide 10

Slide 10 text

“The Twelve-Factor App” is a manifesto, a methodology, a condensed collection of experiences.

Slide 11

Slide 11 text

Its goals are scalability, maintainability, portability.

Slide 12

Slide 12 text

12factor.net

Slide 13

Slide 13 text

4/12 MAIN FACTORS, CODE ACCORDINGLY I. Codebase
 From one codebase, perform many deploys to staging, prod, ... II. Dependencies
 Your application explicitly declares userland and platform deps. III. Configuration
 Read from $_ENV: API keys, database credentials, SMTP hosts, ... XI. Logging
 file_put_contents("php://stderr",  "Yay  log");

Slide 14

Slide 14 text

II. DEPENDENCIES Applications have explicitly declared dependencies.

Slide 15

Slide 15 text

II. DEPENDENCIES Applications have explicitly declared dependencies. $  cat  composer.json   {      "require":  {          "php":  ">=5.3.3",          "ext-­‐mcrypt":  "*",          "symfony/symfony":  "~2.4.6",          "twig/extensions":  "~1.0",          "symfony/monolog-­‐bundle":  "~2.4"      }   } $  cat  package.json   {      "dependencies":  {          "express":  "~4.9.x",          "cool-­‐ascii-­‐faces":  "~1.3.x"      },      "engines":  {          "node":  "0.10.x"      }   }

Slide 16

Slide 16 text

III. CONFIGURATION Store config in the environment.

Slide 17

Slide 17 text

III. CONFIGURATION Store config in the environment. Assumption: same code but different configuration per deployment target

Slide 18

Slide 18 text

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 $  heroku  config:set  EMAIL_HOST=blah.com  EMAIL_PORT=827   EMAIL_USERNAME=joecool  EMAIL_PASSWORD=itwasadarkandstormynight

Slide 19

Slide 19 text

TIME FOR A DEPLOY! It's as easy as pie.

Slide 20

Slide 20 text

$  heroku  create  a-­‐php-­‐app   Creating  a-­‐php-­‐app...  done,  stack  is  cedar-­‐14   http://a-­‐php-­‐app.herokuapp.com/  |  [email protected]:a-­‐php-­‐app.git   $  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)   -­‐-­‐-­‐-­‐-­‐>  Installing  dependencies...                Composer  version  05d991  2014-­‐10-­‐28  12:36:19                Loading  composer  repositories  with  package  information                Installing  dependencies  from  lock  file                    -­‐  Installing  monolog/monolog  (1.9.1)                        Loading  from  cache                          Generating  optimized  autoload  files   -­‐-­‐-­‐-­‐-­‐>  Discovering  process  types                Procfile  declares  types  -­‐>  web   -­‐-­‐-­‐-­‐-­‐>  Compressing...  done,  57.4MB   -­‐-­‐-­‐-­‐-­‐>  Launching...  done,  v3                http://a-­‐php-­‐app.herokuapp.com/  deployed  to  Heroku

Slide 21

Slide 21 text

what happened there?

Slide 22

Slide 22 text

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.

Slide 23

Slide 23 text

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.

Slide 24

Slide 24 text

LET'S TRY IT!

Slide 25

Slide 25 text

PREREQUISITES • Git installed on your system • A Heroku account, created via http://signup.heroku.com/ • The Heroku toolbelt installed from http://toolbelt.heroku.com/ • $  heroku  login

Slide 26

Slide 26 text

http://start.heroku.com/php & http://devcenter.heroku.com/categories/php

Slide 27

Slide 27 text

$  mkdir  php-­‐heroku   $  cd  php-­‐heroku   $  git  init   Initialized  empty  Git  repository  in  php-­‐heroku/.git/   $  curl  -­‐sS  https://getcomposer.org/installer  |  php   $  php  composer.phar  require  silex/silex  monolog/monolog   ...   ./composer.json  has  been  created   Loading  composer  repositories  with  package  information   Updating  dependencies  (including  require-­‐dev)   ...   $  echo  "/vendor/"  >  .gitignore   $  echo  "hi"  >  index.php   $  git  add  .gitignore  composer.*  index.php   $  git  commit  -­‐m  "first  rough  version"   [master  (root-­‐commit)  82b60dd]  first  rough  version    5  files  changed,  498  insertions(+)   ...   $  heroku  create  sunshinephp-­‐$(whoami)   Creating  sunshinephp-­‐dzuelke...  done,  stack  is  cedar-­‐14   https://sunshinephp-­‐dzuelke.herokuapp.com/  |   [email protected]:sunshinephp-­‐dzuelke.git   Git  remote  heroku  added   $  git  push  heroku  master

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

X. DEV/PROD PARITY Keep dev, stage and prod envs as similar as possible. SQLite ≠ MySQL Apache ≠ Nginx File based sessions ≠ Redis based sessions

Slide 30

Slide 30 text

X. DEV/PROD PARITY Keep dev, stage and prod envs as similar as possible. SQLite ≠ MySQL Apache ≠ Nginx File based sessions ≠ Redis based sessions

Slide 31

Slide 31 text

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!

Slide 32

Slide 32 text

VI. PROCESSES

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

heroku-­‐node-­‐app  $  cat  Procfile   worker:  node  worker.js   web:  node  index.js

Slide 35

Slide 35 text

heroku-­‐ruby-­‐app  $  cat  Procfile   worker:  env  TERM_CHILD=1  bundle  exec  rake  resque:work   web:  bundle  exec  unicorn  -­‐p  $PORT  -­‐c  ./config/unicorn.rb

Slide 36

Slide 36 text

heroku-­‐java-­‐app  $  cat  Procfile   worker:  sh  worker/target/bin/worker   web:  java  -­‐jar  target/dependency/jetty-­‐runner.jar  -­‐-­‐port  $PORT   target/*.war

Slide 37

Slide 37 text

heroku-­‐php-­‐app  $  cat  Procfile   worker:  php  background.php   web:  php  -­‐S  0.0.0.0:$PORT

Slide 38

Slide 38 text

PHP needs a dedicated web server

Slide 39

Slide 39 text

heroku-­‐php-­‐app  $  cat  Procfile   worker:  php  background.php   web:  vendor/bin/heroku-­‐php-­‐apache2  #  or  heroku-­‐php-­‐nginx automatically injected into the app during build

Slide 40

Slide 40 text

RUNNING PHP APPS LOCALLY heroku-­‐php  $  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  (v43)          Loading  from  cache   Writing  lock  file   Generating  autoload  files   heroku-­‐php  $  foreman  start   14:30:45  web.1  |  started  with  pid  11175   14:30:46  web.1  |  Booting  on  port  5000...   14:30:46  web.1  |  Starting  php-­‐fpm...   14:30:46  web.1  |  Starting  nginx...

Slide 41

Slide 41 text

(or you use your good old VirtualHost setup)

Slide 42

Slide 42 text

LET'S IMPROVE OUR APP

Slide 43

Slide 43 text

http://silex.sensiolabs.org/doc/intro.html

Slide 44

Slide 44 text

$  mkdir  web   $  git  mv  index.php  web/   $  echo  "web:  vendor/bin/heroku-­‐php-­‐nginx  web/"  >  Procfile   $  git  add  Procfile   $  git  commit  -­‐m  "use  a  custom  document  root"   [master  e87d03e]  use  a  custom  document  root    2  files  changed,  1  insertion(+)    create  mode  100644  Procfile    rename  index.php  =>  web/index.php  (100%)   $  echo  'get("/",  function  ()  use  ($app)  {          return  "Hello  World!";   });   $app-­‐>run();'  >  web/index.php   $  git  add  web/index.php   $  git  commit  -­‐m  "use  Silex"   [master  fe86acb]  use  Silex    1  file  changed,  10  insertions(+),  1  deletion(-­‐)   $  git  push  heroku  master

Slide 45

Slide 45 text

LOGGING

Slide 46

Slide 46 text

http://silex.sensiolabs.org/doc/providers/monolog.html

Slide 47

Slide 47 text

$  vi  web/index.php  #  or  whatever  your  editor  is   $  git  diff  #  edit  the  file  like  in  this  diff:   diff  -­‐-­‐git  a/web/index.php  b/web/index.php   index  4272703..02b453d  100644   -­‐-­‐-­‐  a/web/index.php   +++  b/web/index.php   @@  -­‐3,7  +3,12  @@  require_once  __DIR__."/../vendor/ autoload.php";        $app  =  new  Silex\Application();       +$app-­‐>register(new  Silex\Provider\MonologServiceProvider(),   array(   +        "monolog.logfile"  =>  "php://stderr",   +));   +    $app-­‐>get("/",  function  ()  use  ($app)  {   +        $app["monolog"]-­‐>addNotice("The  world  is  not  enough!");            return  "Hello  World!";    });   $  git  add  web/index.php   $  git  commit  -­‐m  "log  something"   [master  72c4ea3]  log  something    1  file  changed,  5  insertions(+)   $  git  push  heroku  master

Slide 48

Slide 48 text

$  heroku  logs  -­‐-­‐tail   2014-­‐11-­‐06T06:35:07.223192+00:00  app[web.1]:          [2014-­‐11-­‐06  06:35:07]  myapp.INFO:              Matched  route  "GET_"              (parameters:  "_controller":  "{}",  "_route":  "GET_")  []  []   2014-­‐11-­‐06T06:35:07.223499+00:00  app[web.1]:          [2014-­‐11-­‐06  06:35:07]  myapp.INFO:  >  GET  /  []  []   2014-­‐11-­‐06T06:35:07.224280+00:00  app[web.1]:          [2014-­‐11-­‐06  06:35:07]  myapp.NOTICE:              The  world  is  not  enough!  []  []   2014-­‐11-­‐06T06:35:07.225503+00:00  app[web.1]:          [2014-­‐11-­‐06  06:35:07]  myapp.INFO:  <  200  []  []   2014-­‐11-­‐06T06:35:07.226102+00:00  app[web.1]:          10.12.50.108  -­‐  -­‐  [06/Nov/2014:06:35:07  +0000]          "GET  /  HTTP/1.1"  200  12  "-­‐"          "Mozilla/5.0  (Macintosh;  Intel  Mac  OS  X  10_9_5)  AppleWebKit          /600.1.17  (KHTML,  like  Gecko)  Version/7.1  Safari/537.85.10"   2014-­‐11-­‐06T06:35:07.230159+00:00  heroku[router]:          at=info  method=GET  path="/"          host=blooming-­‐spire-­‐8023.herokuapp.com          request_id=57502c87-­‐4b60-­‐472e-­‐8aa2-­‐3e1977f8a086          fwd="199.36.244.11"          dyno=web.1  connect=1ms  service=18ms  status=200  bytes=245

Slide 49

Slide 49 text

ADD-ONS

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

$  heroku  addons:add  heroku-­‐postgresql

Slide 52

Slide 52 text

$  heroku  addons:add  redistogo

Slide 53

Slide 53 text

$  heroku  addons:add  mongolabs

Slide 54

Slide 54 text

$  heroku  addons:add  memcachier

Slide 55

Slide 55 text

$  heroku  addons:add  foundelasticsearch

Slide 56

Slide 56 text

$  heroku  addons:add  cloudamqp

Slide 57

Slide 57 text

$  heroku  addons:add  papertrail

Slide 58

Slide 58 text

$  heroku  addons:add  newrelic

Slide 59

Slide 59 text

http://addons.heroku.com/

Slide 60

Slide 60 text

$  heroku  addons:add  heroku-­‐postgresql   Adding  heroku-­‐postgresql  on  tnphptest...  done,  v6  (free)   Attached  as  HEROKU_POSTGRESQL_AQUA_URL   Use  `heroku  addons:docs  heroku-­‐postgresql`  to  view  documentati…   $  heroku  addons:add  heroku-­‐postgresql   Adding  heroku-­‐postgresql  on  tnphptest...  done,  v7  (free)   Attached  as  HEROKU_POSTGRESQL_AMBER_URL   Use  `heroku  addons:docs  heroku-­‐postgresql`  to  view  documentati…   $  heroku  addons:add  newrelic   Adding  newrelic  on  tnphptest...  done,  v8  (free)   Use  `heroku  addons:docs  newrelic`  to  view  documentation.   $  heroku  addons:add  papertrail   Adding  papertrail  on  tnphptest...  done,  v9  (free)   Use  `heroku  addons:docs  papertrail`  to  view  documentation.   $  heroku  config   ===  tnphptest  Config  Vars   DATABASE_URL:                                postgres://u:p@host12:5432/db18172   HEROKU_POSTGRESQL_AMBER_URL:  postgres://u:p@host42:5198/db24438   HEROKU_POSTGRESQL_AQUA_URL:    postgres://u:p@host12:5432/db18172   NEW_RELIC_LICENSE_KEY:              c9eecdcf9523862f981e   PAPERTRAIL_API_TOKEN:                13Ii1sjmchTboDS24I

Slide 61

Slide 61 text

IV. BACKING SERVICES Treat backing services as attached resources.

Slide 62

Slide 62 text

SCALING

Slide 63

Slide 63 text

SCALING $  heroku  ps:scale  web=10   Scaling  dynos...  done,  now  running  web  at  10:1X.   $  heroku  ps   ===  web  (1X):  `vendor/bin/heroku-­‐php-­‐apache2  web/`   web.1:  starting  2014/11/05  20:36:39  (~  4s  ago)   web.2:  starting  2014/11/05  20:36:39  (~  4s  ago)   web.3:  starting  2014/11/05  20:36:39  (~  4s  ago)   web.4:  starting  2014/11/05  20:36:38  (~  4s  ago)   web.5:  starting  2014/11/05  20:36:38  (~  4s  ago)   web.6:  starting  2014/11/05  20:36:39  (~  4s  ago)   web.7:  starting  2014/11/05  20:36:38  (~  4s  ago)   web.8:  starting  2014/11/05  20:36:39  (~  4s  ago)   web.9:  starting  2014/11/05  20:36:38  (~  4s  ago)   web.10:  starting  2014/11/05  20:36:39  (~  4s  ago)   $  heroku  ps:scale  web=1   Scaling  dynos...  done,  now  running  web  at  1:1X.

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

VIII. CONCURRENCY Scale out via the process model.

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

XII. ADMIN PROCESSES Management tasks like DB migrations are one-off processes. They run in isolation, just like all other "dynos". With the same release: same code, same config!

Slide 72

Slide 72 text

XII. ADMIN PROCESSES Management tasks like DB migrations are one-off processes. $  heroku  run  "php  app/console  doctrine:migrations:migrate"   Running  `php  app/console…`  attached  to  terminal...  up,  run.4062   Migrating  up  to  20100416130452  from  0      >>  migrating  20100416130452            -­‐>  CREATE  TABLE  users  (username  VARCHAR(255)  NOT  NULL,                  password  VARCHAR(255)  NOT  NULL)  ENGINE  =  InnoDB      >>  migrated They run in isolation, just like all other "dynos". With the same release: same code, same config!

Slide 73

Slide 73 text

PIPELINES

Slide 74

Slide 74 text

PIPELINES $  heroku  labs:enable  pipelines   $  heroku  plugins:install  git://github.com/heroku/heroku-­‐pipeline   $  heroku  pipeline:add  your-­‐app-­‐production  -­‐-­‐app  your-­‐app-­‐staging   Pipeline:  your-­‐app-­‐staging  -­‐-­‐-­‐>  your-­‐app-­‐production   $  heroku  pipeline:diff   Comparing  your-­‐app-­‐staging  to  your-­‐app-­‐production...done,    your-­‐app-­‐staging  ahead  by  1  commit:   73ab415    2014-­‐11-­‐05    Fix  checkout  for  IE  users    (Joe)   $  heroku  pipeline:promote   Promoting  myapp-­‐staging  to  myapp-­‐production...done,  v817

Slide 75

Slide 75 text

HEROKU POSTGRES

Slide 76

Slide 76 text

HEROKU POSTGRES We run the world's largest fleet of PostgreSQL databases!

Slide 77

Slide 77 text

FORK Clone a database using a single command. $  heroku  addons:add  heroku-­‐postgresql:standard-­‐0  \      -­‐-­‐fork  HEROKU_POSTGRESQL_CHARCOAL_URL   Adding  heroku-­‐postgresql:standard-­‐0  on  app...  done,  v7  ($50/mo)   Attached  as  HEROKU_POSTGRESQL_SILVER_URL   Database  will  become  available  after  it  completes  forking   Use  `heroku  pg:wait`  to  track  status   $  heroku  pg:wait   Waiting  for  database  HEROKU_POSTGRESQL_SILVER_URL...  available

Slide 78

Slide 78 text

FOLLOW Horizontal scalability using read-only replicas. $  heroku  addons:add  heroku-­‐postgresql:standard-­‐0  \      -­‐-­‐follow  HEROKU_POSTGRESQL_CHARCOAL_URL   Adding  heroku-­‐postgresql:standard-­‐0  to  foo...  done,  v8  ($50/mo)   Attached  as  HEROKU_POSTGRESQL_RED   $  heroku  pg:unfollow  HEROKU_POSTGRESQL_WHITE_URL   !        HEROKU_POSTGRESQL_WHITE  will  become  writable  and  no  longer   !        follow  HEROKU_POSTGRESQL_CHARCOAL.  This  cannot  be  undone.   !        WARNING:  Potentially  Destructive  Action   !        This  command  will  affect  the  app:  foo   !        To  proceed,  type  "foo"  or  re-­‐run  this  command  with  -­‐-­‐ confirm  foo   >  foo   Unfollowing...  done

Slide 79

Slide 79 text

ROLLBACK Jump back in time like that litte disaster never happened. $  heroku  addons:add  heroku-­‐postgresql:standard-­‐0  \      -­‐-­‐rollback  HEROKU_POSTGRESQL_RED_URL  -­‐-­‐by  '1  hour'   Adding  heroku-­‐postgresql:standard-­‐0  on  foo...  done,  v9  ($50/mo)   Attached  as  HEROKU_POSTGRESQL_YELLOW_URL   Database  will  become  available  after  it  completes  rolling  back   to  2015-­‐01-­‐16  09:02:14  +0000  (01:00:00  ago)   Use  `heroku  pg:wait`  to  track  status.   Use  `heroku  addons:docs  heroku-­‐postgresql`  to  view   documentation.

Slide 80

Slide 80 text

now you know it all

Slide 81

Slide 81 text

go have fun!

Slide 82

Slide 82 text

ONE MORE THING...

Slide 83

Slide 83 text

heroku-­‐php  $  git  rm  Procfile   heroku-­‐php  $  composer  require  -­‐-­‐ignore-­‐platform-­‐reqs  hhvm  ~3.2   ./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  $  git  add  composer.*   heroku-­‐php  $  git  commit  -­‐m  'use  HHVM'   heroku-­‐php  $  git  push  heroku  master   -­‐-­‐-­‐-­‐-­‐>  PHP  app  detected   -­‐-­‐-­‐-­‐-­‐>  Detected  request  for  HHVM  3.4.2  in  composer.lock.   -­‐-­‐-­‐-­‐-­‐>  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'   -­‐-­‐-­‐-­‐-­‐>  Compressing...  done,  77.4MB   -­‐-­‐-­‐-­‐-­‐>  Launching...  done,  v3                http://your-­‐hhvm-­‐app.herokuapp.com/  deployed  to  Heroku

Slide 84

Slide 84 text

The End

Slide 85

Slide 85 text

ELEPHANTS IN THE CLOUDS Thanks for listening! Contact: @dzuelke & [email protected]. rate my talk on joind.in!