One commit, one release. Continuously delivering a Symfony project.

80
ONE COMMIT ONE RELEASE CONTINUOUSLY DELIVERING A PROJECT

description

For the last few months we've been implementing a Continuous Delivery pipeline for the redesign of Time Out. In this talk I will demonstrate a real life example of what our pipeline looks like, the different tools we've used to get it done (phing, github, jenkins, ansible, AWS S3, ...), and peculiarities for PHP and Symfony2 projects. Most importantly, I'll be looking at things we've struggled with along the way and the lessons we've learnt.

Transcript of One commit, one release. Continuously delivering a Symfony project.

Page 1: One commit, one release. Continuously delivering a Symfony project.

ONE COMMITONE RELEASE

CONTINUOUSLY DELIVERING A PROJECT

Page 2: One commit, one release. Continuously delivering a Symfony project.

Javier López @loalf

Senior Platform Engineer @Rightster!formerly Software Architect @TimeOut

Certified Symfony Developer

Zend Certified PHP

Co-organizer @desymfony

Page 3: One commit, one release. Continuously delivering a Symfony project.

DISCLAIMER

Page 4: One commit, one release. Continuously delivering a Symfony project.

AGENDA

CONTINUOUS DELIVERY IN A NUTSHELL

BUILDING THE PIPELINE

BEFORE AND AFTER

1

2

3

Page 5: One commit, one release. Continuously delivering a Symfony project.

CONTINUOUS DELIVERY …

… IN A NUTSHELL

1

Page 6: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

When and how a change in your code is going to trigger the pipeline

Page 7: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

Checks the integerity of your code. Produces an artifact.

Page 8: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

Checks the integerity of your project. !

After this step you should be 100% confident that you could deploy your artifact.

Page 9: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

Does what it says in the tin. Triggering this step should be “a click away”.

Manual

Page 10: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

Does what it says in the tin. Triggering this step should be “a click away”.

Manual

Page 11: One commit, one release. Continuously delivering a Symfony project.

THE BOOK

Page 12: One commit, one release. Continuously delivering a Symfony project.

THE PIPELINEBUILDING

2

Page 13: One commit, one release. Continuously delivering a Symfony project.

TOOLS!OF THE TRADE

Page 14: One commit, one release. Continuously delivering a Symfony project.
Page 15: One commit, one release. Continuously delivering a Symfony project.
Page 16: One commit, one release. Continuously delivering a Symfony project.
Page 17: One commit, one release. Continuously delivering a Symfony project.
Page 18: One commit, one release. Continuously delivering a Symfony project.
Page 19: One commit, one release. Continuously delivering a Symfony project.

BUILDING!THE PIPELINE

Page 20: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

master

6ebb017

Page 21: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

master

featureA

6ebb017

Page 22: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

master

featureA

6ebb017

PULL REQUEST

Page 23: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

master

featureA

6ebb017 9046c48

Page 24: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

master

featureA

6ebb017 9046c48

BUILD TEST DEPLOY

Page 25: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

Page 26: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch

COMMIT BUILD TEST DEPLOY

Page 27: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

COMMIT BUILD TEST DEPLOY

Page 28: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies

COMMIT BUILD TEST DEPLOY

Page 29: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies

COMMIT BUILD TEST DEPLOY

—optimizer-­‐autoload  —prefer-­‐dist

Page 30: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies

COMMIT BUILD TEST DEPLOY

Page 31: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests

COMMIT BUILD TEST DEPLOY

Page 32: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests5. Fetch JS dependencies

COMMIT BUILD TEST DEPLOY

Page 33: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests5. Fetch JS dependencies6. Generate assets (JS, CSS)

COMMIT BUILD TEST DEPLOY

Page 34: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests5. Fetch JS dependencies6. Generate assets (JS, CSS)7. Generate artifact (zip file)

COMMIT BUILD TEST DEPLOY

Page 35: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests5. Fetch JS dependencies6. Generate assets (JS, CSS)7. Generate artifact (zip file)

COMMIT BUILD TEST DEPLOY

discard unneeded files

Page 36: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests5. Fetch JS dependencies6. Generate assets (JS, CSS)7. Generate artifact (zip file)

COMMIT BUILD TEST DEPLOY

Page 37: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests5. Fetch JS dependencies6. Generate assets (JS, CSS)7. Generate artifact (zip file)8. Upload artifact to S3

COMMIT BUILD TEST DEPLOY

Page 38: One commit, one release. Continuously delivering a Symfony project.

1. Checkout out master branch2. Generate parameters.ini for each environment

3. Fetch PHP dependencies4. Run PHPUnit tests5. Fetch JS dependencies6. Generate assets (JS, CSS)7. Generate artifact (zip file)8. Upload artifact to S3

COMMIT BUILD TEST DEPLOYbuild.xml

Page 39: One commit, one release. Continuously delivering a Symfony project.

<?xml  version="1.0"  encoding="UTF-­‐8"  ?>  <project  name="Time  Out  Miyagi"  description="Time  Out  Website  V4"  default="build"    !    <!-­‐-­‐  Install  PHP  dependencies  (composer)  -­‐-­‐>      <target  name="build:php-­‐dependencies"  depends="build:params">          <echo  msg="Installing  PHP  dependencies  (composer  install)"  />          <exec  command="/opt/composer/composer.phar  install  -­‐-­‐optimize-­‐autoloader  -­‐-­‐prefer-­‐dist"                        logoutput="true"                        checkreturn=“true"          />      </target>  !    <!-­‐-­‐  Test  JS  &  Generate  JS/CSS  assets  (grunt)  -­‐-­‐>      <target  name="build:frontend-­‐dependencies">          <echo  msg="Test  JS  and  Generate  JS/CSS  assets  (grunt)"  />          <exec  command="npm  install"                        logoutput="true"  checkreturn="true"  dir="./web-­‐src"  />          <exec  command="xvfb-­‐run  grunt  -­‐-­‐env=dist"                        logoutput="true"  checkreturn="true"  dir="./web-­‐src"  />          </target>      </project>

build.xml

Page 40: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

Split into two jobs in Jenkins 1. Test deployment script 2. Run automated tests

Page 41: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

TEST DEPLOYMENT SCRIPT

Page 42: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

TEST DEPLOYMENT SCRIPT

1. SSH to QA server

Page 43: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

TEST DEPLOYMENT SCRIPT

1. SSH to QA server2. Fetch artifact from S3

Page 44: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

TEST DEPLOYMENT SCRIPT

1. SSH to QA server2. Fetch artifact from S33. Unzip artifact

Page 45: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

TEST DEPLOYMENT SCRIPT

1. SSH to QA server2. Fetch artifact from S33. Unzip artifact4. Set right permissions

Page 46: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

TEST DEPLOYMENT SCRIPT

1. SSH to QA server2. Fetch artifact from S33. Unzip artifact4. Set right permissions5. Update virtual host

Page 47: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

TEST DEPLOYMENT SCRIPT

1. SSH to QA server2. Fetch artifact from S33. Unzip artifact4. Set right permissions5. Update virtual host6. Reload PHP-FPM and nginx

Page 48: One commit, one release. Continuously delivering a Symfony project.

-­‐-­‐-­‐  -­‐  hosts:  qa:beta:prod      sudo:  true      vars:          amazon_bucket  :  "releases-­‐miyagi"          base_dir            :  "/var/www/v4.timeout.com"          project_dir      :  "{{  base_dir  }}/{{  git_hash  }}"          web_dir              :  "{{  project_dir  }}/web"      tasks:          -­‐  name:  Fetch  artefact  from  S3              s3:  bucket={{  amazon_bucket  }}  aws_access_key={{  amazon_key  }}  aws_secret_key={{  amazon_secret  }}  object={{  git_hash  }}.gzip  dest="{{  project_dir  }}/{{artefact_zip}}"  mode="get"  !        -­‐  name:  Uncompress  zip  file              command:  chdir="{{  project_dir  }}"  tar  -­‐xf  {{  artefact_zip  }}  !        -­‐  name:  Apply  right  permissions  to  project  root              file:  dest="{{  project_dir  }}"  state=directory  mode=0755  group=nginx  owner=nginx  recurse=true                    -­‐  name:  Restart  PHP-­‐FPM                service:  name=php-­‐fpm  state=reloaded

deploy.yml

Page 49: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

RUN AUTOMATED TESTS

Page 50: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

RUN AUTOMATED TESTS

1. Checkout 9046c48

Page 51: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

RUN AUTOMATED TESTS

1. Checkout 9046c48

2. Fetch PHP dependencies

Page 52: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

RUN AUTOMATED TESTS

1. Checkout 9046c48

2. Fetch PHP dependencies3. Run behat against QA box

Page 53: One commit, one release. Continuously delivering a Symfony project.

COMMIT BUILD TEST DEPLOY

Split into two jobs in Jenkins 1. Run deployment script (production) 2. Run automated tests agains production

Page 54: One commit, one release. Continuously delivering a Symfony project.

BUILD DEPLOY AUTOMATED!TESTS

6ebb017

6ebb017

QA

6ebb017

QA

DEPLOY AUTOMATED!TESTS

6ebb017

PROD

6ebb017

PROD

6ebb017

6ebb017.zip

Page 55: One commit, one release. Continuously delivering a Symfony project.

BUILD DEPLOY AUTOMATED!TESTS

6ebb017

6ebb017

QA

6ebb017

QA

DEPLOY AUTOMATED!TESTS

6ebb017

STAGING

6ebb017

STAGING

DEPLOY AUTOMATED!TESTS

6ebb017

PROD

6ebb017

PROD

6ebb017

6ebb017.zip

SAFETY NET

Page 56: One commit, one release. Continuously delivering a Symfony project.

PARAMETERS.INI!DILEMMA

Page 57: One commit, one release. Continuously delivering a Symfony project.

1. One parameters.ini per environment

2. Inject environment specific parameters on virtual host http://symfony.com/doc/current/cookbook/configuration/external_parameters.html

Page 58: One commit, one release. Continuously delivering a Symfony project.

TIME

Page 59: One commit, one release. Continuously delivering a Symfony project.

BUILD DEPLOY AUTOMATED!TESTS

~3 mins ~30 secs ~5 mins

Page 60: One commit, one release. Continuously delivering a Symfony project.

VISIBILITYEVERYBODY IN THE TEAM SHOULD BE

AWARE OF THE STATUS OF THE PIPELINE

Page 61: One commit, one release. Continuously delivering a Symfony project.

BUILD DEPLOY AUTOMATED!TESTS

9046c48

9046c48

QA

9046c48

QA9046c48

BUILD DEPLOY AUTOMATED!TESTS

6ebb017

6ebb017

QA

6ebb017

QA6ebb017

BUILD DEPLOY AUTOMATED!TESTS

b0b325

b0b325

QA

b0b325

QAb0b325

BUILD DEPLOY AUTOMATED!TESTS

99e6d6

99e6d6

QA

99e6d6

QA99e6d6

Page 62: One commit, one release. Continuously delivering a Symfony project.
Page 63: One commit, one release. Continuously delivering a Symfony project.

TRACEABILITYEVERYBODY SHOULD KNOW WHAT VERSION

IS DEPLOYED IN WHICH ENVIRONMENT

Page 64: One commit, one release. Continuously delivering a Symfony project.

curl  -­‐I  http://www.timeout.com/las-­‐vegas  !HTTP/1.1  200  OK  Server:  nginx/1.4.7  Vary:  Accept-­‐Encoding  Cache-­‐Control:  no-­‐cache  Content-­‐Type:  text/html;  charset=UTF-­‐8  Date:  Fri,  19  Sep  2014  06:07:29  GMT  Transfer-­‐Encoding:  chunked  Access-­‐Control-­‐Allow-­‐Origin:  http://media.timeout.com  Connection:  Keep-­‐Alive  X-­‐TIMEOUT-­‐V:  d645127afb423e543d90ab5a7b8eae94f248b137  X-­‐Powered-­‐By:  PHP/5.5.14

VERSION NUMBER

Page 65: One commit, one release. Continuously delivering a Symfony project.

https://github.com/symfony/symfony/commits/a469c56

Page 66: One commit, one release. Continuously delivering a Symfony project.

ROLLING!BACK

Page 67: One commit, one release. Continuously delivering a Symfony project.

YOU’LL NEED!ALLIES

Page 68: One commit, one release. Continuously delivering a Symfony project.

YOUR ALLIES

Page 69: One commit, one release. Continuously delivering a Symfony project.

DEV TEAM

YOUR ALLIES

Page 70: One commit, one release. Continuously delivering a Symfony project.

DEV TEAMQA TEAM

YOUR ALLIES

Page 71: One commit, one release. Continuously delivering a Symfony project.

DEV TEAMQA TEAMDEVOPS

YOUR ALLIES

Page 72: One commit, one release. Continuously delivering a Symfony project.

DEV TEAMQA TEAMDEVOPSPRODUCT

YOUR ALLIES

Page 73: One commit, one release. Continuously delivering a Symfony project.

DEV TEAMQA TEAMDEVOPSPRODUCTTHE BRASS

YOUR ALLIES

Page 74: One commit, one release. Continuously delivering a Symfony project.

BEFORE AFTER &

3

Page 75: One commit, one release. Continuously delivering a Symfony project.

ONE QA BOX PER FEATURE

SAME QA BOX FOR EVERYONE

BEFORE

AFTER

Page 76: One commit, one release. Continuously delivering a Symfony project.

~ !WEEKS FROM DEVELOPMENT TO RELEASE

~ !DAYS FROM DEVELOPMENT TO RELEASE

BEFORE

AFTER

Page 77: One commit, one release. Continuously delivering a Symfony project.

5 PEOPLE TO RELEASE TO PRODUCTION

1 PERSON TO RELEASE TO PRODUCTION

BEFORE

AFTER

Page 78: One commit, one release. Continuously delivering a Symfony project.

~30 MINUTES TO RUN DEPLOYMENT SCRIPT

~30 SECONDS TO RUN DEPLOYMENT SCRIPT

BEFORE

AFTER

Page 79: One commit, one release. Continuously delivering a Symfony project.

RELEASING WAS AN EVENT

RELEASING WAS A NO EVENT

BEFORE

AFTER

Page 80: One commit, one release. Continuously delivering a Symfony project.

@loalf