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

WordPress as a 12 Factor App - Midwest PHP 2017

WordPress as a 12 Factor App - Midwest PHP 2017

Love it or hate it, WordPress is here to stay. Powering over 25% of the web, it's one of the most commonly used projects out there.

WordPress strive to make life easy for their end users first and foremost, but what if I told you that with less than an hour's work you could make it a joy to work with for developers too?

By following the Twelve Factor App method (http://12factor.net/) we can deploy WordPress in a way that makes it easy to develop for, easy to update and easy to maintain. Come and watch as we take a standard WordPress install and bring it into the future.

Bbf9decfbfc2ab5b450ec503749ded28?s=128

Michael Heap

March 18, 2017
Tweet

More Decks by Michael Heap

Other Decks in Technology

Transcript

  1. @mheap at #mwphp17 WordPress as a 12 Factor App

  2. @mheap @mheap

  3. @mheap Chris AKA @tweetingsherry

  4. @mheap This talk is not a step by step how-to

    guide
  5. @mheap It’s designed to show you what’s possible

  6. @mheap What is a 12 Factor Application?

  7. @mheap WordPress as a 12 Factor Application

  8. @mheap Ready?

  9. @mheap Go!

  10. @mheap What is a 12 Factor Application?

  11. @mheap The Twelve-Factor App https://12factor.net/

  12. @mheap Codebase One codebase tracked in revision control, many deploys

  13. @mheap Dependencies Explicitly declare and isolate dependencies

  14. @mheap Config Store config in the environment

  15. @mheap Backing Services Treat backing services as attached resources

  16. @mheap Build, Release, Run Strictly separate build and run stages

  17. @mheap Processes Execute the app as one or more stateless

    processes
  18. @mheap Port Binding Export services via port binding

  19. @mheap Concurrency Scale out via the process model

  20. @mheap Disposability Maximize robustness with fast startup and graceful shutdown

  21. @mheap Dev/Prod Parity Keep development, staging, and production as similar

    as possible
  22. @mheap Logs Treat logs as event streams

  23. @mheap Admin Processes Run admin/management tasks as one-off processes

  24. @mheap 1. Codebase 2. Dependencies 3. Config 4. Backing Services

    5. Build, Release, Run 6. Processes 7. Port Binding 8. Concurrency 9. Disposability 10. Dev/Prod Parity 11. Logs 12. Admin Processes The Twelve-Factor App
  25. @mheap Why is the Codebase important? 1 /12

  26. @mheap Add to version control $ git init $ git

    add . $ git commit -m "Initial Commit"
  27. @mheap One codebase Development Staging Production

  28. @mheap No web based updates Update locally Commit to version

    control Build and deploy
  29. @mheap Disable Automatic Updates <?php define('AUTOMATIC_UPDATER_DISABLED', true); wp-config.php

  30. @mheap Disable Online Editor <?php define('DISALLOW_FILE_EDIT', true); wp-config.php

  31. @mheap Change file ownership $ chown <username>:<username> -R * $

    chown www-data:www-data wp-content/uploads
  32. @mheap </Codebase>

  33. @mheap Why are Dependencies important? 2 /12

  34. @mheap commit 599d45019f92f6cc191e39014674caeeedcd87b0 Author: Scott Taylor <scott.c.taylor@mac.com> Date: Sat Aug

    27 03:49:28 2016 +0000 Bootstrap: add `composer.lock` and `src/wp-vendor` files. Nothing is using this code yet, just going through the motions. Ignore the files in `src/wp-vendor/composer` that will explode in PHP 5.2. See #36335. Built from https://develop.svn.wordpress.org/trunk@38394 git-svn-id: http://core.svn.wordpress.org/trunk@38335 1a063a9b-81f0-0310-95a4-ce76da25c4cd WordPress’ composer.json :)
  35. @mheap commit f1631f4e03303945c5c47682a36c184542cf094b Author: Scott Taylor <scott.c.taylor@mac.com> Date: Wed Aug

    31 20:36:30 2016 +0000 Bootstrap: goodnight sweet prince. See #36335. Built from https://develop.svn.wordpress.org/trunk@38480 git-svn-id: http://core.svn.wordpress.org/trunk@38421 1a063a9b-81f0-0310-95a4-ce76da25c4cd WordPress’ composer.json :(
  36. @mheap @mheap

  37. @mheap Do it yourself!

  38. @mheap { "packages": { "mheap/wordpress": { "v4.7.3": { "name": "mheap/wordpress",

    "dist" : { "url" : "http://wordpress.org/wordpress-4.7.3.zip", "type": "zip" }, "source" : { "url" : "https://github.com/WordPress/WordPress", "type" : "git", "reference": "4.7.3" }, "type": "package", "version": "4.7.3" } } } }
  39. @mheap { "config": { "secure-http": false }, "repositories" : [

    { "type" : "composer", "url" : "http://example.com" } ], "require" : { "mheap/wordpress" : ">=4.7.3" } }
  40. @mheap . ├── composer.json ├── composer.lock └── vendor ├── autoload.php

    ├── composer │ ├── ClassLoader.php │ ├── LICENSE │ ├── autoload_classmap.php │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_real.php │ ├── autoload_static.php │ └── installed.json └── mheap └── wordpress ├── <other files> └── wp-config.php
  41. @mheap Do it yourself! (Take Two)

  42. @mheap https://getcomposer.org/doc/articles/custom-installers.md

  43. @mheap { "packages": { "mheap/wordpress": { "v4.7.3": { "name": "mheap/wordpress",

    "dist" : { "url" : "http://wordpress.org/wordpress-4.7.3.zip", "type": "zip" }, "source" : { "url" : "https://github.com/WordPress/WordPress", "type" : "git", "reference": "4.7.3" }, "type": "wordpress-core", "version": "4.7.3", "require": { "johnpbloch/wordpress-core-installer": "~0.1" } } } } }
  44. @mheap . ├── composer.json ├── composer.lock ├── vendor │ ├──

    autoload.php │ ├── composer │ └── johnpbloch └── wordpress ├── <other files> └── wp-config.php
  45. @mheap Do it yourself! (Take Three, the real way)

  46. @mheap { "require": { "johnpbloch/wordpress": ">=4.7.3" } }

  47. @mheap . ├── composer.json ├── composer.lock ├── vendor │ ├──

    autoload.php │ ├── composer │ └── johnpbloch └── wordpress ├── <other files> └── wp-config.php
  48. @mheap http://example.com/wordpress @mheap

  49. @mheap Remove /wordpress/ mkdir web mv wordpress/index.php web mv wordpress/wp-config.php

    .
  50. @mheap . ├── composer.json ├── composer.lock ├── vendor ├── web

    │ └── index.php ├── wordpress └── wp-config.php
  51. @mheap Edit wp-config.php /* That's all, stop editing! Happy blogging.

    */ /** Absolute path to the WordPress directory. */ if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/'); /** Sets up WordPress vars and included files. */ require_once(ABSPATH . 'wp-settings.php');
  52. @mheap Edit wp-config.php /* That's all, stop editing! Happy blogging.

    */ /** Absolute path to the WordPress directory. */ if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/../wordpress'); /** Sets up WordPress vars and included files. */ require_once(ABSPATH . 'wp-settings.php');
  53. @mheap Edit index.php define('WP_USE_THEMES', true); /** Loads the WordPress Environment

    and Template */ require( dirname( __FILE__ ) . '/wp-blog-header.php' );
  54. @mheap Edit index.php define('WP_USE_THEMES', true); /** Loads the WordPress Environment

    and Template */ require( dirname( __FILE__ ) . '/../wordpress/wp-blog- header.php' );
  55. @mheap Control wp-content mv wordpress/wp-content web

  56. @mheap Edit wp-config.php /** Absolute path to the WordPress directory.

    */ if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/wordpress'); define('WP_HOME', 'http://example.com'); define('CONTENT_DIR', '/wp-content'); define('WP_CONTENT_DIR', __DIR__ . '/web' . CONTENT_DIR); define('WP_CONTENT_URL', WP_HOME . CONTENT_DIR); /** Sets up WordPress vars and included files. */ require_once(ABSPATH . 'wp-settings.php');
  57. @mheap Create .gitignore vendor wordpress

  58. @mheap . ├── composer.json ├── composer.lock ├── vendor ├── web

    │ ├── index.php │ └── wp-content │ ├── plugins │ ├── themes │ └── uploads ├── wordpress └── wp-config.php
  59. @mheap . ├── composer.json ├── composer.lock ├── web │ ├──

    index.php │ └── wp-content │ ├── plugins │ ├── themes │ └── uploads └── wp-config.php
  60. @mheap . ├── composer.json ├── composer.lock ├── web │ ├──

    index.php │ └── wp-content │ ├── plugins │ ├── themes │ └── uploads └── wp-config.php
  61. @mheap @mheap

  62. @mheap { "repositories":[ { "type":"composer", "url":"https://wpackagist.org" } ], "require": {

    "johnpbloch/wordpress": ">=4.7.3" } }
  63. @mheap { "repositories":[ { "type":"composer", "url":"https://wpackagist.org" } ], "require": {

    "johnpbloch/wordpress": ">=4.7.3", "wpackagist-plugin/captcha":">=3.9", "wpackagist-theme/hueman":">=3.3.7" } }
  64. @mheap Edit .gitignore vendor wordpress wp-content/* !wp-content/uploads/

  65. @mheap │ ├── customize.php │ ├── edit-comments.php │ ├── edit-form-advanced.php

    │ ├── edit-form-comment.php │ ├── edit-link-form.php │ ├── edit-tag-form.php │ ├── edit-tags.php │ ├── edit.php │ ├── export.php │ ├── freedoms.php │ ├── images │ │ ├── align-center-2x.png │ │ ├── align-center.png │ │ ├── align-left-2x.png │ │ ├── align-left.png │ │ ├── align-none-2x.png │ │ ├── align-none.png │ │ ├── align-right-2x.png │ │ ├── align-right.png │ │ ├── arrows-2x.png │ │ ├── arrows.png │ │ ├── browser-rtl.png │ │ ├── browser.png │ │ ├── bubble_bg-2x.gif │ │ ├── bubble_bg.gif │ │ ├── comment-grey-bubble-2x.png │ │ ├── comment-grey-bubble.png │ │ ├── date-button-2x.gif │ │ ├── date-button.gif │ │ ├── generic.png │ │ ├── icons32-2x.png │ │ ├── icons32-vs-2x.png │ │ ├── icons32-vs.png │ │ ├── icons32.png │ │ ├── imgedit-icons-2x.png │ │ ├── imgedit-icons.png │ │ ├── list-2x.png │ │ ├── list.png │ │ ├── loading.gif │ │ ├── marker.png │ │ ├── mask.png │ │ ├── media-button-2x.png │ │ ├── media-button-image.gif │ │ ├── media-button-music.gif │ │ ├── media-button-other.gif │ │ ├── media-button-video.gif │ │ ├── media-button.png │ │ ├── menu-2x.png │ │ ├── menu-vs-2x.png │ │ ├── menu-vs.png │ │ ├── menu.png │ │ ├── no.png │ │ ├── post-formats-vs.png │ │ ├── post-formats.png │ │ ├── post-formats32-vs.png │ │ ├── post-formats32.png │ │ ├── resize-2x.gif │ │ ├── resize-rtl-2x.gif │ │ ├── resize-rtl.gif │ │ ├── resize.gif │ │ ├── se.png │ │ ├── sort-2x.gif │ │ ├── sort.gif │ │ ├── spinner-2x.gif │ │ ├── spinner.gif │ │ ├── stars-2x.png │ │ ├── stars.png │ │ ├── w-logo-blue.png │ │ ├── w-logo-white.png │ │ ├── wheel.png │ │ ├── wordpress-logo-white.svg │ │ ├── wordpress-logo.png │ │ ├── wordpress-logo.svg │ │ ├── wpspin_light-2x.gif │ │ ├── wpspin_light.gif │ │ ├── xit-2x.gif │ │ ├── xit.gif │ │ └── yes.png │ ├── import.php │ ├── includes │ │ ├── admin-filters.php │ │ ├── admin.php │ │ ├── ajax-actions.php │ │ ├── bookmark.php │ │ ├── class-automatic-upgrader-skin.php │ │ ├── class-bulk-plugin-upgrader-skin.php │ │ ├── class-bulk-theme-upgrader-skin.php │ │ ├── class-bulk-upgrader-skin.php │ │ ├── class-core-upgrader.php │ │ ├── class-file-upload-upgrader.php │ │ ├── class-ftp-pure.php │ │ ├── class-ftp-sockets.php │ │ ├── class-ftp.php │ │ ├── class-language-pack-upgrader-skin.php │ │ ├── class-language-pack-upgrader.php │ │ ├── class-pclzip.php │ │ ├── class-plugin-installer-skin.php │ │ ├── class-plugin-upgrader-skin.php │ │ ├── class-plugin-upgrader.php │ │ ├── class-theme-installer-skin.php │ │ ├── class-theme-upgrader-skin.php │ │ ├── class-theme-upgrader.php │ │ ├── class-walker-category-checklist.php │ │ ├── class-walker-nav-menu-checklist.php │ │ ├── class-walker-nav-menu-edit.php │ │ ├── class-wp-ajax-upgrader-skin.php │ │ ├── class-wp-automatic-updater.php │ │ ├── class-wp-comments-list-table.php │ │ ├── class-wp-filesystem-base.php │ │ ├── class-wp-filesystem-direct.php │ │ ├── class-wp-filesystem-ftpext.php │ │ ├── class-wp-filesystem-ftpsockets.php │ │ ├── class-wp-filesystem-ssh2.php │ │ ├── class-wp-importer.php │ │ ├── class-wp-internal-pointers.php │ │ ├── class-wp-links-list-table.php │ │ ├── class-wp-list-table-compat.php │ │ ├── class-wp-list-table.php │ │ ├── class-wp-media-list-table.php │ │ ├── class-wp-ms-sites-list-table.php │ │ ├── class-wp-ms-themes-list-table.php │ │ ├── class-wp-ms-users-list-table.php │ │ ├── class-wp-plugin-install-list-table.php │ │ ├── class-wp-plugins-list-table.php │ │ ├── class-wp-post-comments-list-table.php │ │ ├── class-wp-posts-list-table.php │ │ ├── class-wp-press-this.php │ │ ├── class-wp-screen.php │ │ ├── class-wp-site-icon.php │ │ ├── class-wp-terms-list-table.php │ │ ├── class-wp-theme-install-list-table.php │ │ ├── class-wp-themes-list-table.php │ │ ├── class-wp-upgrader-skin.php │ │ ├── class-wp-upgrader.php │ │ ├── class-wp-users-list-table.php │ │ ├── comment.php │ │ ├── continents-cities.php │ │ ├── credits.php │ │ ├── dashboard.php │ │ ├── deprecated.php │ │ ├── edit-tag-messages.php │ │ ├── export.php │ │ ├── file.php │ │ ├── image-edit.php │ │ ├── image.php │ │ ├── import.php │ │ ├── list-table.php │ │ ├── media.php │ │ ├── menu.php │ │ ├── meta-boxes.php │ │ ├── misc.php │ │ ├── ms-admin-filters.php │ │ ├── ms-deprecated.php │ │ ├── ms.php . ├── composer.json ├── composer.lock ├── web │ ├── index.php │ └── wp-content └── wp-config.php (1762 files)
  66. @mheap Bedrock https://roots.io/bedrock/

  67. @mheap </Dependencies>

  68. @mheap Why is Config important? 3 /12

  69. @mheap wp-config.php Committed to git Hard to change between environments

  70. @mheap if ($_SERVER['REMOTE_ADDR'] == '82.123.234.12') { define('DB_USER', 'prod_wp'); } else

    if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') { define('DB_USER', 'root'); }
  71. @mheap if ($_SERVER['REMOTE_ADDR'] == '82.123.234.12') { include 'wp-config-staging.php'; } else

    if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') { include 'wp-config-dev.php'; }
  72. @mheap composer.json { "repositories":[ { "type":"composer", "url":"https://wpackagist.org" } ], "require":

    { "johnpbloch/wordpress": ">=4.7.3", "wpackagist-plugin/captcha":">=3.9", "wpackagist-theme/hueman":">=3.3.7", "vlucas/phpdotenv": "^2.4" } }
  73. @mheap wp-config.php require_once __DIR__.'/vendor/autoload.php'; $dotenv = new Dotenv\Dotenv(__DIR__); $dotenv->load(); /**

    * The base configuration for WordPress *
  74. @mheap .env DB_NAME=wptest DB_USER=user DB_PASSWORD=secret DB_HOST=localhost

  75. @mheap wp-config.php /** The name of the database for WordPress

    */ define('DB_NAME', getenv('DB_NAME')); /** MySQL database username */ define('DB_USER', getenv('DB_USER')); /** MySQL database password */ define('DB_PASSWORD', getenv('DB_PASSWORD')); /** MySQL hostname */ define('DB_HOST', getenv('DB_HOST'));
  76. @mheap </Config>

  77. @mheap Why are Backing Services important? 4 /12

  78. @mheap Attached Resources Local or remote Your application doesn’t care

  79. @mheap Config Driven Zero code changes Scale up/down at will

  80. @mheap Misbehaving Hardware Attach / detach at will No need

    to redeploy, config only
  81. @mheap We do this by default In PHP, everything is

    a detached service
  82. @mheap wp-config.php /** The name of the database for WordPress

    */ define('DB_NAME', getenv('DB_NAME')); /** MySQL database username */ define('DB_USER', getenv('DB_USER')); /** MySQL database password */ define('DB_PASSWORD', getenv('DB_PASSWORD')); /** MySQL hostname */ define('DB_HOST', getenv('DB_HOST'));
  83. @mheap @mheap

  84. @mheap </Backing Services>

  85. @mheap Why is Build, Release, Run important? 5 /12

  86. @mheap Build $ composer install $ zip -r app.zip *

  87. @mheap Release $ scp app.zip remote-host:~ $ ssh remote-host $

    unzip app.zip $ cd /var/www $ mv ~/app releases/20161201 $ ln -s releases/20161201 live_new $ mv -Tf live_new live
  88. @mheap Build $ composer install $ fpm -s dir -t

    rpm -n ${name} -v '$ {version}' .=/var/www/${name} —after- install after-install.sh --after-remove after-remove.sh
  89. @mheap Release $ sudo yum upgrade my-app

  90. @mheap Run PHP does this for free (Maybe restart php-fpm)

  91. @mheap </Build, Release, Run>

  92. @mheap Why are Processes important? 6 /12

  93. @mheap Shared Resources Memory Disk

  94. @mheap @mheap

  95. @mheap Caveats It only works going forwards

  96. @mheap </Processes>

  97. @mheap Why is Port Binding important? 7 /12

  98. @mheap PHP-FPM Binds it’s own port Doesn’t talk HTTP

  99. @mheap $ php -S localhost:8000 Built in server

  100. @mheap ReactPHP Async PHP event loop

  101. @mheap </Port Binding>

  102. @mheap Why is Concurrency important? 8 /12

  103. @mheap Scale out via processes Web process Worker process Admin

    process
  104. @mheap PHP-FPM Daemonises 12 Factor app should not write PID

    file Instead, rely on process manager
  105. @mheap </Concurrency>

  106. @mheap Why is Disposability important? 9 /12

  107. @mheap Elastic Scaling

  108. @mheap Rapid Deployment

  109. @mheap Config Changes

  110. @mheap Fast Startup Time

  111. @mheap </Disposability>

  112. @mheap Why is Dev / Prod parity important? 10/12

  113. @mheap Sync your code Follow factors one (codebase) and two

    (dependencies)
  114. @mheap Sync your database wp-sync-db (Free) wp-migrate-db-pro (Paid)

  115. @mheap Reducing the gap The time gap The personnel gap

    The tools gap
  116. @mheap Reducing the gap Make the time gap small Make

    the personnel gap small Make the tools gap small
  117. @mheap Releases Time: Hours People: Code Authors Tools: Identical

  118. @mheap Jenkins https://jenkins.io/

  119. @mheap Trellis https://roots.io/trellis/

  120. @mheap Shameless Plug

  121. @mheap </Dev / Prod Parity>

  122. @mheap Why are Logs important? 11/12

  123. @mheap Logs are streams Write to STDOUT Managed by process

    manager
  124. @mheap WordPress can do this define( 'WP_DEBUG_LOG', true );

  125. @mheap Spotting patterns Finding specific events in the past. Large-scale

    graphing of trends Triggering alerts
  126. @mheap </Logs>

  127. @mheap Why are Admin processes important? 12/12

  128. @mheap update_links.php Updates all posts to switch links from http://example.com

    to https:// example.com
  129. @mheap Ship with app code Deployed in the same package

    Code reviews and tests
  130. @mheap </Admin Processes>

  131. @mheap The Twelve-Factor App https://12factor.net/

  132. @mheap Bonus!

  133. @mheap wp-cli Comments Menus Posts Users

  134. @mheap composer.json Explorer http://composer.json.jolicode.com/

  135. https://joind.in/ 20313 @mheap at #mwphp17 Michael Heap @mheap m@michaelheap.com