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

Deploy your application in a box - 010PHP

Deploy your application in a box - 010PHP

People say that PHP applications are so easy to deploy. They make it sound like nothing ever goes wrong there. But we know better, everybody has seen broken applications because of a deployment.

In this talk I want to show you some of the issues you might face with deployment and a possible approach to get a more solid deployment strategy. We will be using deployable artifacts to achieve this.

Willem-Jan Zijderveld

June 09, 2016
Tweet

More Decks by Willem-Jan Zijderveld

Other Decks in Technology

Transcript

  1. Possible deployment Migrate database Update code on server (FTP, SSH)

    Update code dependencies (composer,npm,bower) Clear cache Reload webserver
  2. > ssh your-server.com > sudo su - > cd /var/www/

    > git pull > php composer.phar install > npm install > rm -rf app/cache/* > # Some permission commands > service php-fpm reload Example: before
  3. Example: after > ssh [email protected] > ./deploy.sh * Pulling changes

    * Updating dependencies * Migrating database * Clearing cache * DEPLOYED NEW VERSION
  4. Legacy code? Test the critical parts - Unit tests for

    important (domain) logic - Functional tests - Monitor production
  5. 6 month projects Deliver something every day Don't wait with

    deploying until your feature is ready and approved.
  6. Short lived branches • Allows for rapid deployment • Prevents

    huge merge conflicts • Speeds up overall development
  7. Example: recommendations <?php // BasketController.php function actionBasket(User $user) { $basket

    = $this->getBasketOfUser($user); $response = ['basket' => $basket->toArray()]; if ($this->featureIsActive('recommendations')) { $response['recommendations'] = $this->getRecommendations($basket); } return new JsonResponse($response); }
  8. Example: recommendations <?php // BasketController.php function actionBasket(User $user) { $basket

    = $this->getBasketOfUser($user); $response = ['basket' => $basket->toArray()]; if ($this->featureIsActive('recommendations')) { $response['recommendations'] = $this->getRecommendations($basket); } return new JsonResponse($response); }
  9. Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'

    => GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
  10. Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'

    => GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
  11. Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'

    => GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
  12. Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'

    => GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
  13. More often - Smaller iterations - Don't wait for the

    whole feature to complete - Make use of feature toggles
  14. 0 downtime - Don't touch the current version - Abort

    when any of the steps fail - Make your migrations backwards compatible
  15. Flysystem Abstract the filesystem - Makes it easier to test

    - Makes your code independent of the filesystem, f.e.: - Production uses S3 - Development uses local files
  16. <?php // Uploader.php function uploadAvatar($filename) { $stream = fopen($filename, 'r+');

    $this->filesystem ->writeStream('avatars/' . $filename, $stream); if (is_resource($stream)) { fclose($stream); } }
  17. # config_prod.yml imports: - { resource: config.yml } doctrine: dbal:

    default_connection: default connections: default: driver: pdo_mysql host: 192.168.1.10 dbname: acme_prod user: acme_prod password: SuperSecure
  18. # config.yml imports: - { resource: parameters.yml } - {

    resource: parameters.php } doctrine: dbal: default_connection: default connections: default: driver: pdo_mysql host: %db.hostname% dbname: %db.name% user: %db.user% password: %db.password%
  19. # config.yml imports: - { resource: parameters.yml } - {

    resource: parameters.php } doctrine: dbal: default_connection: default connections: default: driver: pdo_mysql host: %db.hostname% dbname: %db.name% user: %db.user% password: %db.password%
  20. # config.yml imports: - { resource: parameters.yml } - {

    resource: parameters.php } doctrine: dbal: default_connection: default connections: default: driver: pdo_mysql host: %db.hostname% dbname: %db.name% user: %db.user% password: %db.password%
  21. <?php // parameters.php $parameterMapping = [ 's3_private_files_bucket' => 'MYAPP_S3_PRIVATE_FILES_BUCKET', ];

    foreach ($parameterMapping as $parameter => $env) { if (false !== ($value = getenv($env))) { $container->setParameter($parameter, $value); } }
  22. <?php // web/index.php $dotenv = new Dotenv\Dotenv(__DIR__ . '/../'); $dotenv->load();

    Load .env # .env MYAPP_S3_PRIVATE_FILES_BUCKET=yourbucket
  23. Building the box - Should be reusable - Eliminate hard

    dependencies on environment - Give control to the environment
  24. Summary - Less manual steps, eliminate the humans - Lose

    the fear: test your stuff - Deploy more often, don't postpone - Deployment shouldn't cause downtime - A deployment should be repeatable