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. Deploy your application in a box
    @willemjanz
    joind.in/talk/d5be4

    View full-size slide

  2. Willem-Jan
    Zijderveld
    @willemjanz
    github.com/wjzijderveld

    View full-size slide

  3. Don't wait till the last
    slide with questions

    View full-size slide

  4. Deployment
    With the usual disclaimer

    View full-size slide

  5. It works
    So why change it?

    View full-size slide

  6. Can you deploy whenever
    you want?

    View full-size slide

  7. Are you ever hesitant to deploy?

    View full-size slide

  8. "We can't deploy, Robin
    is sick today"

    View full-size slide

  9. Do users notice when you deploy?

    View full-size slide

  10. Things break
    If you like it or not

    View full-size slide

  11. Deployments fail ●
    Network issues

    Migrations

    Broken configuration

    View full-size slide

  12. Missed revenue

    View full-size slide

  13. Angry customers

    View full-size slide

  14. Customers losing faith

    View full-size slide

  15. Scared
    to deploy

    View full-size slide

  16. Deployments get postponed

    View full-size slide

  17. Reserving dedicated time to deploy

    View full-size slide

  18. Deploying outside office hours...

    View full-size slide

  19. So what would be a more
    optimal method?

    View full-size slide

  20. Let's get started

    View full-size slide

  21. Less humans Automate your deploy process to a
    single step

    View full-size slide

  22. Possible deployment
    Migrate database
    Update code on
    server (FTP, SSH)
    Update code
    dependencies
    (composer,npm,bower)
    Clear cache Reload webserver

    View full-size slide

  23. > 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

    View full-size slide

  24. Example: after
    > ssh [email protected]
    > ./deploy.sh
    * Pulling changes
    * Updating dependencies
    * Migrating database
    * Clearing cache
    * DEPLOYED NEW VERSION

    View full-size slide

  25. No sweat
    Lose the fear of deploying

    View full-size slide

  26. Seriously, test your code

    View full-size slide

  27. New code should have tests

    View full-size slide

  28. Legacy code?
    Test the critical parts
    - Unit tests for important
    (domain) logic
    - Functional tests
    - Monitor production

    View full-size slide

  29. Continuous Integration

    View full-size slide

  30. Smaller changes

    View full-size slide

  31. 6 month projects
    Deliver something every day
    Don't wait with deploying until your
    feature is ready and approved.

    View full-size slide

  32. Short lived branches

    Allows for rapid deployment

    Prevents huge merge conflicts

    Speeds up overall development

    View full-size slide

  33. Feature toggles

    View full-size slide

  34. Example: recommendations
    // 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);
    }

    View full-size slide

  35. Example: recommendations
    // 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);
    }

    View full-size slide

  36. Give some users access

    View full-size slide

  37. Conditional toggle
    Check the context against the
    conditions of the toggle

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  42. Feature management
    GUI API Redis
    http://labs.qandidate.com/blog/2014/08/19/open-sourcing-our-feature-toggle-api-and-ui/

    View full-size slide

  43. More often - Smaller iterations
    - Don't wait for the whole feature
    to complete
    - Make use of feature toggles

    View full-size slide

  44. Prepare your deployment

    View full-size slide

  45. Deployment
    Webserver
    Application - version 1

    View full-size slide

  46. Deployment
    Webserver
    Application - version 1
    Application - version 2

    View full-size slide

  47. Deployment
    Webserver
    Application - version 1
    Application - version 2

    View full-size slide

  48. Deployment
    Webserver
    Application - version 1

    View full-size slide

  49. Deployment
    Webserver
    Application - version 1 Install code

    View full-size slide

  50. Deployment
    Webserver
    Application - version 1 Install code
    Migrations

    View full-size slide

  51. Deployment
    Webserver
    Application - version 1 Install code
    Migrations
    Warmup cache

    View full-size slide

  52. Deployment
    Webserver
    Application - version 1 Install code
    Migrations
    Warmup cache
    Application - version 2

    View full-size slide

  53. Deployment
    Webserver
    Application - version 1 Install code
    Migrations
    Warmup cache
    Application - version 2

    View full-size slide

  54. Example
    /var
    /project
    /releases
    /20160608-1.0
    /20160609-1.1
    /current -> /var/project/releases/20160608-1.0

    View full-size slide

  55. 0 downtime - Don't touch the current version
    - Abort when any of the steps fail
    - Make your migrations backwards
    compatible

    View full-size slide

  56. Put it in a box

    View full-size slide

  57. Environment
    independent

    View full-size slide

  58. The box In it's simplest form: just a zip

    View full-size slide

  59. Reusable
    On all environments

    View full-size slide

  60. Some challenges

    View full-size slide




  61. /var/www/uploads/


    View full-size slide

  62. 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

    View full-size slide




  63. id="oneup_flysystem.private_filesystem" />


    View full-size slide

  64. # config.yml
    oneup_flysystem:
    adapters:
    private_s3:
    awss3v2:
    client: aws_s3_client
    bucket: %s3_private_files_bucket%
    filesystem:
    private:
    adapter: private_s3

    View full-size slide

  65. // Uploader.php
    function uploadAvatar($filename) {
    $stream = fopen($filename, 'r+');
    $this->filesystem
    ->writeStream('avatars/' . $filename, $stream);
    if (is_resource($stream)) {
    fclose($stream);
    }
    }

    View full-size slide

  66. File is now at S3: bucket/avatars/filename.
    ext

    View full-size slide

  67. Configuration

    View full-size slide

  68. # 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

    View full-size slide

  69. What about staging and
    acceptance?

    View full-size slide

  70. Give control to the environment

    View full-size slide

  71. # 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%

    View full-size slide

  72. # 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%

    View full-size slide

  73. # 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%

    View full-size slide

  74. // 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);
    }
    }

    View full-size slide

  75. $_ENV['SYMFONY__*']
    But those values get overwritten by parameters defined in config.yml / parameters.yml

    View full-size slide

  76. PHP dot env
    https://github.com/vlucas/phpdotenv

    View full-size slide

  77. // web/index.php
    $dotenv = new Dotenv\Dotenv(__DIR__ . '/../');
    $dotenv->load();
    Load .env
    # .env
    MYAPP_S3_PRIVATE_FILES_BUCKET=yourbucket

    View full-size slide

  78. Building the box - Should be reusable
    - Eliminate hard dependencies on
    environment
    - Give control to the environment

    View full-size slide

  79. 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

    View full-size slide

  80. Other questions?
    @willemjanz
    Please leave some feedback! joind.in/talk/d5be4

    View full-size slide