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 Slide

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

    View Slide

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

    View Slide

  4. Deployment
    With the usual disclaimer

    View Slide

  5. It works
    So why change it?

    View Slide

  6. Can you deploy whenever
    you want?

    View Slide

  7. Are you ever hesitant to deploy?

    View Slide

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

    View Slide

  9. Do users notice when you deploy?

    View Slide

  10. Things break
    If you like it or not

    View Slide

  11. Deployments fail ●
    Network issues

    Migrations

    Broken configuration

    View Slide

  12. Downtime

    View Slide

  13. Missed revenue

    View Slide

  14. Angry customers

    View Slide

  15. Customers losing faith

    View Slide

  16. Scared
    to deploy

    View Slide

  17. Deployments get postponed

    View Slide

  18. Reserving dedicated time to deploy

    View Slide

  19. Deploying outside office hours...

    View Slide

  20. So what would be a more
    optimal method?

    View Slide

  21. Less humans

    View Slide

  22. No sweat

    View Slide

  23. More often

    View Slide

  24. 0 downtime

    View Slide

  25. Repeatable

    View Slide

  26. Let's get started

    View Slide

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

    View Slide

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

    View Slide

  29. > 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 Slide

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

    View Slide

  31. No sweat
    Lose the fear of deploying

    View Slide

  32. Testing

    View Slide

  33. Seriously, test your code

    View Slide

  34. New code should have tests

    View Slide

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

    View Slide

  36. Continuous Integration

    View Slide

  37. More often

    View Slide

  38. Smaller changes

    View Slide

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

    View Slide

  40. Short lived branches

    Allows for rapid deployment

    Prevents huge merge conflicts

    Speeds up overall development

    View Slide

  41. Feature toggles

    View Slide

  42. 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 Slide

  43. 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 Slide

  44. Give some users access

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. 0 downtime

    View Slide

  53. Prepare your deployment

    View Slide

  54. Deployment
    Webserver
    Application - version 1

    View Slide

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

    View Slide

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

    View Slide

  57. Deployment
    Webserver
    Application - version 1

    View Slide

  58. Deployment
    Webserver
    Application - version 1 Install code

    View Slide

  59. Deployment
    Webserver
    Application - version 1 Install code
    Migrations

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  65. Put it in a box

    View Slide

  66. Repeatable

    View Slide

  67. Environment
    independent

    View Slide

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

    View Slide

  69. Independent

    View Slide

  70. Reusable
    On all environments

    View Slide

  71. Some challenges

    View Slide

  72. Filesystem

    View Slide




  73. /var/www/uploads/


    View Slide

  74. 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 Slide




  75. id="oneup_flysystem.private_filesystem" />


    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  79. Configuration

    View Slide

  80. # 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 Slide

  81. What about staging and
    acceptance?

    View Slide

  82. Give control to the environment

    View Slide

  83. # 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 Slide

  84. # 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 Slide

  85. # 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 Slide

  86. // 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  91. 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 Slide

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

    View Slide