Daemons, Deployment and Datacentres Andrew Godwin @andrewgodwin - - PowerPoint PPT Presentation

daemons deployment and datacentres
SMART_READER_LITE
LIVE PREVIEW

Daemons, Deployment and Datacentres Andrew Godwin @andrewgodwin - - PowerPoint PPT Presentation

Daemons, Deployment and Datacentres Andrew Godwin @andrewgodwin Who am I? Django core developer South author Cofounder of ep.io What's ep.io? Hosts Python sites/daemons Technically language-independent Supports multiple


slide-1
SLIDE 1

Daemons, Deployment and Datacentres

Andrew Godwin @andrewgodwin

slide-2
SLIDE 2

Who am I?

 Django core developer  South author  Cofounder of ep.io

slide-3
SLIDE 3

What's ep.io?

 Hosts Python sites/daemons  Technically language-independent  Supports multiple kinds of database  Mainly hosted in the UK on our own hardware

slide-4
SLIDE 4

What I'll Cover

 Our architecture

 ZeroMQ and redundancy  Eventlet everywhere  The upload process  The joy of networks

 General Challenges

 ”The Stack”  Backups and replication  Sensible architecture

slide-5
SLIDE 5

ZeroMQ & Redundancy

slide-6
SLIDE 6

ZeroMQ

 Most importantly, not a message queue  Advanced sockets, with multiple endpoints  Has both deliver-to-single-consumer, and

deliver-to-all-consumers.

 Uses TCP (or other things) as a transport.

slide-7
SLIDE 7

Socket Types

REQ / REP PUB / SUB PUSH / PULL

slide-8
SLIDE 8

Redundancy

 Our internal rule is that there must be at least two

  • f everything inside ep.io.

 Not quite true yet, but getting very close.  Even our ”find the servers running X” service is

doubly redundant.

slide-9
SLIDE 9

Example

# Make and connect the socket sock = ctx.socket(zmq.REQ) for endpoint in self.config.query_addresses(): sock.connect(endpoint) # Construct the message payload = json.dumps({"type": type, "extra": extra}) # Send the message with Timeout(30): sock.send(self.sign_message(payload)) # Recieve the answer return self.decode_message(sock.recv())

slide-10
SLIDE 10

Redundancy's Not Easy

 Several things can only run once (cronjobs)  We currently have a best-effort distributed locking

daemon to help with this

slide-11
SLIDE 11

Eventlet Everywhere

slide-12
SLIDE 12

What is Eventlet?

 Coroutine-based asynchronous concurrency  Basically, lightweight threads with explicit context

switching

 Reads quite like procedural code

slide-13
SLIDE 13

Highly Contrived Example

import eventlet from eventlet.green import urllib2 urls = ['http://ep.io', 'http://t.co'] results = [] def fetch(url): results.append(urllib2.urlopen(url).read()) for url in urls: eventlet.spawn(fetch, url)

slide-14
SLIDE 14

Integration

 Most of our codebase uses Eventlet (~20,000 lines)  Used for concurrency in daemons, workers, and

batch processing

 ZeroMQ and Eventlet work together nicely

slide-15
SLIDE 15

Why?

 Far less race conditions than threading  Multiprocessing can't handle ~2000 threads  More readable code than callback-based systems

slide-16
SLIDE 16

The Upload Process

slide-17
SLIDE 17

Background

 Every time an app is uploaded to ep.io it gets a

fresh app image to deploy into

 Each app image has its own virtualenv  The typical ep.io app has around 3 or 4

dependencies

 Some have more than 40

slide-18
SLIDE 18

Parellised pip

 Installing 40 packages in serial takes quite a while  Our custom pip version installs them in parallel,

with caching

 Not 100% compatable with complex dependency

sets yet

slide-19
SLIDE 19

Some Rough Numbers

 15 requirements, some git, some pypi:

 Traditional: ~300 seconds  Parellised, no cache: 30 seconds  Parellised, cached: 2 seconds

slide-20
SLIDE 20

Compiled Modules

 ep.io app bundles are technically architecture-

independent

 All compiled dependencies currently installed as

system packages with dual 2.6/2.7 versions

 Will probably move to just bundling .so files too

slide-21
SLIDE 21

It's not just uploads

 Upload servers are general SSH endpoint  Also do rsync, scp, command running  Commands have semi-custom terminal emulation

transported over ZeroMQ

 Hope you never have to use pty, ioctl or fcntl

slide-22
SLIDE 22

A Little Snippet

  • ld = termios.tcgetattr(fd)

new = old[:] new[0] &= ~(termios.ISTRIP|termios.INLCR| termios.IGNCR|termios.ICRNL|termios.IXON| termios.IXANY|termios.IXOFF) new[2] &= ~(termios.OPOST) new[3] &= ~(termios.ECHO|termios.ISIG|termios.ICANON| termios.ECHOE|termios.ECHOK|termios.ECHONL| termios.IEXTEN) tcsetattr_flags = termios.TCSANOW if hasattr(termios, 'TCSASOFT'): tcsetattr_flags |= termios.TCSASOFT

slide-23
SLIDE 23

The Joy of Networks

slide-24
SLIDE 24

It's not just the slow ones

 Any network has a significant slowdown

compared to local access

 Locking and concurrent access also an issue  You can't run everything on one machine forever

slide-25
SLIDE 25

It's also the slow ones

 Transatlantic latency is around 100ms  Internal latency on EC2 can peak higher than 10s  Routing blips can cause very short outages

slide-26
SLIDE 26

Heuristics and Optimism

 Sites and servers get a short grace period if they

vanish in which to reappear

 Another site instance gets booted if needed – if

the old one reappears, it gets killed

 Everything is designed to be run at least twice, so

launching more things is not an issue

slide-27
SLIDE 27

Security

 We treat our internal network as public  All messages signed/encrypted  Firewalling of unnecessary ports  Separate machines for higher-risk processes

slide-28
SLIDE 28

General Challenges

The Stack

slide-29
SLIDE 29

Three years ago

 Apache and mod_wsgi  PostgreSQL 8.x  Memcached

slide-30
SLIDE 30

Today

 Nginx (static files/gzipping)  Gunicorn (dynamic pages, unix socket best)  PostgreSQL 9  Redis  virtualenv

slide-31
SLIDE 31

Higher loads?

 Varnish for site caching  HAProxy or Nginx for loadbalancing  Give PostgreSQL more resources

slide-32
SLIDE 32

Development and Staging

 No need to run gunicorn/nginx locally  PostgreSQL 9 still slightly annoying to install  Redis is very easy to set up  Staging should be EXACTLY the same as live

slide-33
SLIDE 33

Backups and Redundancy

slide-34
SLIDE 34

Archives != High Availability

 Your PostgreSQL slave is not a backup  We back up using multiple formats to diverse

locations

slide-35
SLIDE 35

It's not just disasters

 Many other things other than theft and failure can

lose data

 Don't back up to the same provider, they can

cancel your account...

slide-36
SLIDE 36

Keep History

 You may not realise you need backups until the

next month

 Take backups before any major change in

database or code

slide-37
SLIDE 37

Check your backups restore

 Just seeing if they're there isn't good enough  Try restoring your entire site onto a fresh box

slide-38
SLIDE 38

Replication is hard

 PostgreSQL and Redis replication both require

your code to be modified a bit

 Django offers some help with database routers  It's also not always necessary, and can cause

bugs for your users.

slide-39
SLIDE 39

An Easy Start

 Dump your database nightly to a SQL file  Use rdiff-backup (or similar) to sync that,

codebase and uploads to a backup directory

 Also sync offsite – get a VPS with a different

provider than your main one

 Make your backup server pull the backups, don't

push them to it

slide-40
SLIDE 40

Sensible Architecture

slide-41
SLIDE 41

Ship long-running tasks off

 Use celery, or your own worker solution  Even more critical if you have synchronous

worker threads in your web app

 Email sending can be very slow

slide-42
SLIDE 42

Plan for multiple machines

 That means no SQLite  Make good use of database transactions  How are you going to store uploaded files?

slide-43
SLIDE 43

Loose Coupling

 Simple, loosely-connected components  Easier to test and easier to debug  Enforces some rough interface definitions

slide-44
SLIDE 44

Automation

 Use Puppet or Chef along with Fabric  If you do something more than three times,

automate it

 Every time you manually SSH in, a kitten gets

extremely worried

slide-45
SLIDE 45

War Stories

slide-46
SLIDE 46

What happens with a full disk?

 Redis and MongoDB have historically both hated

this situation, and lost data

 We had this with Redis – there was more than 10%

disk free, but that wasn't enough to dump everything into.

slide-47
SLIDE 47

Stretching tools

 Our load balancer was initally HAProxy  It really doesn't like having 3000 backends

reloaded every 10 seconds

 Custom eventlet-based loadbalancer was simpler

and slightly faster

slide-48
SLIDE 48

When Usernames Aren't There

 NFSv4 really, really hates UIDs with no

corresponding username

 In fact, git does as well  Variety of workarounds for different tools

slide-49
SLIDE 49

Even stable libraries have bugs

 Incompatability between psycopg2 and greenlets

caused interpreter lockups

 Fixed in 2.4.2  Almost impossible to debug

slide-50
SLIDE 50

Awkward Penultimate Slide

 You don't have to be mad to write a distributed

process management system, but it helps

 ZeroMQ is really, really nice. Really.  Eventlet is a very useful concurrency tool  Every developer should know a little ops  Automation, consistency and preparation are key

slide-51
SLIDE 51

Thank you.

Questions, comments or heckles?

Andrew Godwin

andrew@ep.io / @andrewgodwin