Slide 1

Slide 1 text

Immutable Infrastructure at Scale Vik Bhatti Senior Platform Engineer @otaku_coder

Slide 2

Slide 2 text

This Talk • Timeline • Build tools • Testing • Configuration management • Conclusions flickr.com/photos/toms/5678872701

Slide 3

Slide 3 text

“Immutable infrastructure is comprised of immutable components that are replaced for every deployment, rather than being updated in-place.” What is immutable infrastructure? Florian Motlik CTO @codeship blog.codeship.com/immutable-infrastructure

Slide 4

Slide 4 text

Immutable infrastructure is powerful

Slide 5

Slide 5 text

But difficult to run without the right tooling

Slide 6

Slide 6 text

A Brief History Zeebox • Smart TV Remote • Social Network Beamly • Content network • Ad Tech • Acquired Oct 2015

Slide 7

Slide 7 text

Live call-to-action

Slide 8

Slide 8 text

Cue frantic debugging…

Slide 9

Slide 9 text

How Do We Scale Faster? Autoscaling

Slide 10

Slide 10 text

Configure on start-up Auto Scaling group Amazon S3 bucket hashiconf.eu Elastic Load Balancer Amazon Route 53 Base OS Machine Image EC2 Instance

Slide 11

Slide 11 text

Configure on start-up EC2 Instance Auto Scaling group Amazon S3 bucket hashiconf.eu Elastic Load Balancer Amazon Route 53 Base OS Machine Image EC2 Instance

Slide 12

Slide 12 text

Configure on start-up EC2 Instance Auto Scaling group Amazon S3 bucket hashiconf.eu Elastic Load Balancer Amazon Route 53 Base OS Machine Image EC2 Instance Pull down ansible Playbook on boot (or use ansible tower)

Slide 13

Slide 13 text

Configure on start-up EC2 Instance Auto Scaling group Amazon S3 bucket hashiconf.eu Elastic Load Balancer Amazon Route 53 Base OS Machine Image EC2 Instance

Slide 14

Slide 14 text

Preconfigure on image build Auto Scaling group hashiconf.eu Elastic Load Balancer Amazon Route 53 Base OS Machine Image EC2 Instance

Slide 15

Slide 15 text

Preconfigure on image build Auto Scaling group hashiconf.eu Elastic Load Balancer Amazon Route 53 Base OS Machine Image EC2 Instance Ansible play to bundle all requirements into the image

Slide 16

Slide 16 text

Preconfigure on image build Auto Scaling group hashiconf.eu Elastic Load Balancer Amazon Route 53 Base OS Machine Image EC2 Instance EC2 Instance EC2 Instance

Slide 17

Slide 17 text

Build Pipeline Compile Artifact Provision Image Install Artifact Snapshot Machine Image Test Machine Image Deploy New Image SCM

Slide 18

Slide 18 text

Build Pipeline

Slide 19

Slide 19 text

Build Tools (gears) AMI bake • Lookup component metadata • Generate packer template • Execute packer build AMI deploy • Trigger rolling push via asgard • or run ansible deploy playbook

Slide 20

Slide 20 text

Build Tools (gears) AMI test • Spin up test instance • Run smoke tests via Smolder • github.com/sky-shiny/smolder

Slide 21

Slide 21 text

Automated Testing Smolder

Slide 22

Slide 22 text

Standard Service Endpoints • Every service MUST implement standard endpoints • Each endpoint has consistent response type across languages • Easy to reason about and test services

Slide 23

Slide 23 text

Introducting SE4 - github.com/beamly/se4

Slide 24

Slide 24 text

Smolder – github.com/sky-shiny/smolder

Slide 25

Slide 25 text

Smolder tests are defined in JSON or YAML --- tests: name: "foobar-service" uri: "/service/status" port: 8080 method: "GET" inputs: headers: Host: "beamly.com" content-type: "application/json” outcomes: response_body_contains: "foobar-stage" expect_status_code: 200

Slide 26

Slide 26 text

Smoke testing with smolder ------------------------------------------------------------------------------------- foobar-status http://10.0.3.50:8080/service/status curl -vsH "content-type: Application/json" -X GET "http://10.0.3.50:8080/service/status" ------------------------------------------------------------------------------------- expect_status_code: Status code == 200.........................................[PASS] response_body_contains: Body contains "foobar-stage"...........................[PASS] ALL TESTS PASSED!

Slide 27

Slide 27 text

Packer Building Machine Images

Slide 28

Slide 28 text

Builders create machines and generate images { ‘builders’: [ ‘name’: ‘build-aws-hvm’, ‘type’: ‘amazon-chroot’, ‘source_ami’: ‘ami-1234abcd’, ‘ami_name’: ‘hashiconf_eu-2016-v1’, ‘ami_virtualization_type’: ‘hvm’, ‘tags’: { ‘component’: ‘hashiconf_eu’, ‘version’: ‘1.0.3’ } ] }

Slide 29

Slide 29 text

Provisioners define how to install dependencies { ‘provisioners’: [ ‘type’: ‘ansible-local’, ‘playbook_file’: ‘/tmp/myplay.yml’, ‘extra_arguments’: [‘--extra-vars version=1.0.3’] ] }

Slide 30

Slide 30 text

Packer takes a template file and executes the build $ packer build –machine-readable /tmp/mytemplate.json

Slide 31

Slide 31 text

Packer build – Runs builders in parallel 12:41:13.168 1453293673,,ui,say,==> hvm: Prevalidating AMI Name... 12:41:13.871 1453293673,,ui,say,==> hvm: Gathering information about this EC2 instance... 12:41:13.922 1453293673,,ui,say,==> paravirtual: Gathering information about this EC2 instance... 12:41:13.983 1453293673,,ui,say,==> hvm: Inspecting the source AMI... 12:41:14.000 1453293673,,ui,say,==> paravirtual: Inspecting the source AMI... 12:41:14.052 1453293674,,ui,say,==> paravirtual: Checking the root device on source AMI... 12:41:14.053 1453293674,,ui,say,==> paravirtual: Creating the root volume... 12:41:14.054 1453293674,,ui,say,==> hvm: Checking the root device on source AMI... 12:41:18.399 1453293678,,ui,say,==> paravirtual: Attaching the root volume to /dev/sdf 12:41:21.182 1453293681,,ui,say,==> hvm: Creating the root volume... 12:41:21.183 1453293681,,ui,say,==> paravirtual: Mounting the root device... 12:41:21.296 1453293681,,ui,say,==> paravirtual: Mounting additional paths within the chroot... 12:41:21.497 1453293681,,ui,message, paravirtual: Mounting: /proc 12:41:21.512 1453293681,,ui,message, paravirtual: Mounting: /sys 12:41:21.570 1453293681,,ui,message, paravirtual: Mounting: /dev 12:41:21.577 1453293681,,ui,message, paravirtual: Mounting: /dev/pts 12:41:21.586 1453293681,,ui,message, paravirtual: Mounting: /proc/sys/fs/binfmt_misc 12:41:21.594 1453293681,,ui,say,==> paravirtual: Copying files from host to chroot... 12:41:21.594 1453293681,,ui,message, paravirtual: /etc/resolv.conf 12:41:21.733 1453293681,,ui,say,==> paravirtual: Provisioning with shell script: /tmp/packer-shell284329197 12:41:25.585 1453293685,,ui,say,==> hvm: Attaching the root volume to /dev/sdh 12:41:30.412 1453293690,,ui,say,==> hvm: Mounting the root device... 12:41:30.615 1453293690,,ui,say,==> hvm: Mounting additional paths within the chroot...

Slide 32

Slide 32 text

Gears ‘injects’ vars into the chroot via provisioner { "type": "ansible-local", "playbook_file": "/tmp/go-agent/pipelines/cpt-1.x-bake/ansible/site.yml”, "extra_arguments": [ "--extra-vars ‘COMPONENT_NAME=cpt’", "--extra-vars ‘COMPONENT_VERSION=1.0.81’" ], "role_paths": [ "/tmp/go-agent/pipelines/cpt-1.x-bake/ansible/roles/jdauphant/ansible-role-nginx", "/tmp/go-agent/pipelines/cpt-1.x-bake/ansible/roles/beamly.cpt” ] }

Slide 33

Slide 33 text

Gears looks up and generates tags for builder { "source_ami": "ami-df0607b5", "name": "hvm", "ami_regions": [ "us-east-1”, ”eu-west-1” ], "tags": { "GIT_SHA": "b675d08113ba0129fec3b6291ec7ba25e8843ad1", "AMI_VERSION": "1.0.81", "COMPONENT_REPO": "[email protected]:beamly/cpt.git", "COMPONENT_VERSION": "1.0.81", "CREATED_AT": "201606101438", "COMPONENT_NAME": ”cpt" }, "ami_name": ”cpt-1.0.81-hvm-201606101438", }

Slide 34

Slide 34 text

Gotcha – AWS chroot Builder Provisioning tasks must not leave any processes running, or packer can’t unmount the volume. Remove handlers and notify calls from ansible roles/tasks

Slide 35

Slide 35 text

Tracking changes Metadata

Slide 36

Slide 36 text

“A manifest or customs manifest or "cargo document" is a document listing the cargo, passengers, and crew of a ship, aircraft, or vehicle, for the use of customs and other officials” What is a shipping manifest? Wikipedia https://en.wikipedia.org/wiki/Manifest_(transportation)

Slide 37

Slide 37 text

AWS Tags Image tags • Component Name • Component Version • AMI Version • Build Time Instance Tags (via ASG config) • Component Name • Component Version • AMI Version • Environment • Country

Slide 38

Slide 38 text

How do we update service config? Configuration

Slide 39

Slide 39 text

v1 - Set config file to use on boot 1. Write multiple configuration files • For each environment/region 2. Inspect metadata on boot and use the matching config file

Slide 40

Slide 40 text

Environment-specific config ... vars: us: stage: db_url: ‘stage-us-db.rds.aws.amazon.com’ cache_key: ‘stage-us-foobar’ live: db_url: ‘live-us-db.rds.aws.amazon.com’ uk: stage: db_url: ‘stage-uk-rds.aws.amazon.com’ cache_key: ‘stage-uk-foobar’ ...

Slide 41

Slide 41 text

Use with_items to write multiple config files - name: write config files template: src: myconfig.j2 dest: “/etc/{{ item.country }}-{{ item.environment }}-conf.json” owner: root mode: 0644 with_items: - { country: ‘uk’, environment: ‘stage’ } - { country: ‘uk’, environment: ‘live’ } - { country: ‘us’, environment: ‘stage’ } - { country: ‘us’, environment: ‘live’ }

Slide 42

Slide 42 text

Consul Distributed Configuration Management

Slide 43

Slide 43 text

Config v2 – Use Service Discovery/Config KV 1. Local consul-template agent watches for changes in KV values 2. Writes new config to disk on change 3. Restarts corresponding service Note: config value changes != template changes

Slide 44

Slide 44 text

Machine images should be the same regardless of where they are deployed

Slide 45

Slide 45 text

Would a config change trigger a service outage? …yes... use consul distributed locks!

Slide 46

Slide 46 text

How do you keep environmental config in sync? Marco Polo

Slide 47

Slide 47 text

Structure your KV data

Slide 48

Slide 48 text

Structured KV • Standard structure for KV data – Namespaced by AWS region and env level • /component/version/region/env • Easy to reason about config • Minimise cross-team config mixups

Slide 49

Slide 49 text

Structured KV Component keyspace – {{ component }} / {{ version }} / {{ country }} / {{ environment }} / {{ key }} Service ports – {{ component }} / static / global / common / port Shared config (e.g. API keys) – common / {{ country }} / {{ component }} / key

Slide 50

Slide 50 text

Config should be versioned just like code

Slide 51

Slide 51 text

Introducing ’Marco Polo’ • Written in Scala • Hooks into CD pipelines • Synchronises config trees between environments (stage -> live)

Slide 52

Slide 52 text

Marco Polo – CD Pipelines Build Agent Marcopolo Consul AMI Bake Job

Slide 53

Slide 53 text

Marco Polo – CD Pipelines Build Agent Marcopolo Consul AMI Bake v23 POST components/foobar/ 23/sync

Slide 54

Slide 54 text

Marco Polo – New Component Version Build Build Agent Marcopolo Consul AMI Bake v23 Read LIVE v22 config tree

Slide 55

Slide 55 text

Marco Polo – New Component Version Build Build Agent Marcopolo Consul AMI Bake v23 Create STAGE v23 tree

Slide 56

Slide 56 text

Marco Polo – New Component Version Build Build Agent Marcopolo Consul AMI Bake v23 200 OK

Slide 57

Slide 57 text

Marco Polo – Component Version Deploy Live Build Agent Marcopolo Consul AMI Deploy v23

Slide 58

Slide 58 text

Marco Polo – Component Version Deploy Live Build Agent Marcopolo Consul AMI Deploy v23 POST components/foobar/ 23/sync

Slide 59

Slide 59 text

Marco Polo – Component Version Deploy Live Build Agent Marcopolo Consul AMI Deploy v23 Copy stagetree for v23 to live

Slide 60

Slide 60 text

Marco Polo – Component Version Deploy Live Build Agent Marcopolo Consul AMI Deploy v23 200 OK

Slide 61

Slide 61 text

How do components know what config to use? Consul Template

Slide 62

Slide 62 text

Dirty Hack Alert

Slide 63

Slide 63 text

Client Side Config Lookup – Consul Template AMI + Autoscaling group tagged: – Component, version, environment, country (ec2 region) Script on boot: 1. Look up tags 2. Pre-render .ctmpl file, filling in the KV lookup tree

Slide 64

Slide 64 text

Config template file fb.oauth.redirect=” {{key ”foobar/1.0.23/<>/<>/facebook/redirect_url" }}” ga.tracking.code=” {{key ”foobar/1.0.23/<>/<>/ga_tracking_code" }}”

Slide 65

Slide 65 text

Pre-rendered consul template file fb.oauth.redirect="{{ key ”foobar/1.0.23/us/stage/facebook/redirect_url" }}” ga.tracking.code="{{ key ”foobar/1.0.23/us/stage/ga_tracking_code" }}”

Slide 66

Slide 66 text

What about system state? Sidenote

Slide 67

Slide 67 text

State is hard …but can be managed and encapsulated

Slide 68

Slide 68 text

Stateful ‘things’ • Logs • Metrics • Data Storage/Databases • Caches • Filesystem • DNS

Slide 69

Slide 69 text

Routing • Every service runs on a unique port • Port/Component mappings stored in KV • HAProxy tier • Generate backends from instance list (based on component tag)

Slide 70

Slide 70 text

Routing HAProxy Instance Zone a Auto Scaling group EC2 Instance EC2 Instance haproxy.beamly.com Component Ports KV HAProxy Instance Zone b Auto Scaling group

Slide 71

Slide 71 text

Routing HAProxy Instance Zone a Auto Scaling group EC2 Instance EC2 Instance haproxy.beamly.com Component Ports KV HAProxy Instance Zone b Auto Scaling group EC2 Instance

Slide 72

Slide 72 text

Routing HAProxy Instance Zone a Auto Scaling group EC2 Instance EC2 Instance haproxy.beamly.com Component Ports KV HAProxy Instance Zone b Auto Scaling group EC2 Instance Script – finds all instances and generates new config

Slide 73

Slide 73 text

Routing HAProxy Instance Zone a Auto Scaling group EC2 Instance EC2 Instance haproxy.beamly.com Component Ports KV HAProxy Instance Zone b Auto Scaling group EC2 Instance

Slide 74

Slide 74 text

Conclusions A few things to take away

Slide 75

Slide 75 text

Immutable infrastructure is complex at first

Slide 76

Slide 76 text

But wicked fast to scale up and down when all the tools are in place

Slide 77

Slide 77 text

Be pragmatic. Use bootstrapping to bring instances into service

Slide 78

Slide 78 text

Immutable infrastructure simplifies change management

Slide 79

Slide 79 text

Trust your continuous delivery tooling

Slide 80

Slide 80 text

Thanks for listening Questions?

Slide 81

Slide 81 text

LONDON Drury House 34-43 Russell Street London WC2B 5HA NEW YORK CITY 350 Fifth Avenue Suite 1700 New York NY 10018 © Beamly 2016. All Rights Reserved.