The Road to Continuous Deployment (Codemotion Amsterdam 2017)

The Road to Continuous Deployment (Codemotion Amsterdam 2017)

It's a situation many of us are familiar with: a large legacy application, limited or no tests, slow & manual release process, low velocity, no confidence.... Oh, and management wants new features, fast. Using examples and lessons learned from a real-world case, I'll show you how to strangle a legacy application with a modern service architecture and build a continuous integration and deployment pipeline to deliver value from the first sprint. On the way, we’ll take a look at the process, automated testing, monitoring, master/trunk based development and various tips and best practices.

2f4800411154a8c66dde489448a044d2?s=128

Michiel Rook

May 16, 2017
Tweet

Transcript

  1. 3.
  2. 9.

    FRONTEND MYSQL DB BACKEND LOAD BALANCERS / VARNISH ITBANEN INTERMEDIAIR

    NATIONALEVACATUREBANK FRONTEND FRONTEND FRONTEND BACKEND BACKEND BACKEND MEMCACHE FTP EXT. SERVICES SOLR @michieltcs
  3. 21.

    APPROACH ▸ API first ▸ Services per domain object (job,

    jobseeker, ...) ▸ Migrate individual pages @michieltcs
  4. 29.
  5. 31.

    CD?

  6. 43.
  7. 44.
  8. 50.
  9. 52.
  10. 53.
  11. 59.
  12. 67.

    DEFENSE IN DEPTH UNIT TESTS INTEGRATION
 TESTS ACCEPTANCE UI TESTS

    @Test
 public void jobCannotBeFound() {
 when(jobRepository.getById(EXPECTED_JOB_ID))
 .thenReturn(null);
 
 JobService jobService = new JobService(jobRepository);
 
 assertNull(jobService.getById(EXPECTED_JOB_ID));
 verify(jobRepository).getById(EXPECTED_JOB_ID);
 } @michieltcs
  13. 68.

    DEFENSE IN DEPTH UNIT TESTS INTEGRATION
 TESTS ACCEPTANCE
 TESTS UI

    TESTS @Test
 public void shouldFindJob() {
 expectedJob = loadFixture('active_job.yml');
 actualJob = repository.getById(expectedJob.getId());
 
 assertThat(actualJob, isA(Job.class));
 assertEquals(expectedJob.getId(), actualJob.getId());
 } @michieltcs
  14. 69.

    DEFENSE IN DEPTH UNIT TESTS INTEGRATION
 TESTS ACCEPTANCE
 TESTS UI

    TESTS Scenario: Link to related job
 Given a job exists
 And there are related jobs available
 When that job is viewed
 Then a list of related jobs is shown
 And each related job links to the detail page of the related job @michieltcs
  15. 72.

    UNIT TESTS INTEGRATION TESTS ACCEPTANCE TESTS UI TESTS SMOKE
 TESTS

    Cost Speed Exploratory
 testing Monitoring @michieltcs
  16. 73.

    PIPELINE AS CODE node {
 stage('Run tests') {
 sh "phpunit"


    sh "behat"
 }
 
 stage('Build docker image') {
 sh "docker build -t jobservice:${env.BUILD_NUMBER} ."
 sh "docker push jobservice:${env.BUILD_NUMBER}"
 }
 
 stage('Deploy staging') {
 sh "ansible-playbook -e BUILD=${env.BUILD_NUMBER}
 -i staging deploy.yml"
 }
 
 stage('Deploy production') {
 sh "ansible-playbook -e BUILD=${env.BUILD_NUMBER}
 -i prod deploy.yml"
 }
 } @michieltcs
  17. 74.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER @michieltcs
  18. 75.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER docker pull @michieltcs
  19. 76.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER docker run @michieltcs
  20. 77.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER wait_for: port=8080 delay=5 timeout=15 @michieltcs
  21. 78.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER uri:
 url: http://localhost:8080/health
 status_code: 200
 timeout: 30 @michieltcs
  22. 79.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER template: src=haproxy.cfg.j2
 dest=/etc/haproxy/haproxy.cfg service: name=haproxy state=reloaded @michieltcs
  23. 80.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER template: src=haproxy.cfg.j2
 dest=/etc/haproxy/haproxy.cfg service: name=haproxy state=reloaded @michieltcs
  24. 81.

    PULL IMAGE START NEW CONTAINER WAIT FOR PORT SMOKE TESTS

    / HEALTH CHECKS ADD NEW CONTAINER TO LB REMOVE OLD CONTAINER FROM LB STOP OLD CONTAINER docker stop docker rm @michieltcs
  25. 83.
  26. 84.
  27. 87.

    Build per service
 < 10 min. 1 50+ deploys per

    day Reduced number of issues 2 3 @michieltcs
  28. 88.

    Build per service
 < 10 min. 1 50+ deploys per

    day Reduced number of issues Improved page load times 2 3 4 @michieltcs
  29. 91.