The Twelve-Factor App
SaaS delivery that doesn't hurt (as much)
This presentation
- Plant a seed
- Get you excited
- (hopefully)
Three years ago
- one VPS (512M, combined app/db)
- FTP-based deploy
- coding in production
-
coding in production
-
Today
- upwards 30 VMs, load balancers and DB servers
- several geographically distributed data centers
- CDN delivery (Oct 2015: avg. 293 GB data over 31K requests per day)
- SCM-based deploy and rollback
The future
- One platform to rule them all
- Automatic, data-driven scaling
- Continuous delivery
- Containerization
- Resource management
- Micro services
We're building complex systems!
(and that's great, but...)
The Twelve-Factor App
One app <=> one codebase
Explicit dependencies
Config by environment
Backing services
Build, release, run
Stateless processes
Self-contained services
Scale via the process model
Disposability
Dev-prod parity
Logs as streams
Admin processes
One app <=> one codebase
- Codebase = repository
- Use version control (duh!)
- Shared code as dependencies
- Same codebase across deploys
Why?
- Clean mapping between revision and deploy
- Promotes explicit dependencies
- Great for CI
Explicit dependencies
- Dependency declaration (e.g. pom.xml, project.json)
- Dependency resolution (e.g. Maven, KPM)
- Dependency isolation (e.g. bundle exec)
- No deps in version control, but commit "lock file"
Why?
- Facilitates dev-prod parity
- Easier dev workflow
Config by environment
- No config in version control
- Use environment variable substitution
- No "grouping"
Why?
- Agnostic
- Simple to distribute
- Secure
Build, release, run
- Strict separation of stages
- Build produces binaries
- Release combines binaries and configuration
- Run executes a given release
- Releases are versioned
- Configuration change = new version
Why?
- Run stage is kept as dumb as possible
- Easier automation
- Behaviour is versioned
The shared state problem (2)
The shared state problem (3)
The shared state problem (4)
Stateless processes (cont.)
Why?
- Resilience
- Scaling
- Automation
Self-contained services
- Bundle web server
- Export port bindings
web: bundle exec rails server -p $PORT
Why?
- Web server becomes just another dependency
- The Docker Way ™
Scale via the process model
- Process formation
- Scale individually
- Managed externally
web: bundle exec rails server -p $PORT
worker: bundle exec rake jobs:work
Disposability
- Assume processes may be started/stopped at any time
- Minimize startup time
- Shut down gracefully
- POSIX signals (SIGTERM/SIGKILL)
Why?
- Elastic scaling
- Rapid deployment
Dev-prod parity
- Deploy often
- Deploy yourself
- Don't rely on abstractions
Why?
- Ensure well-behavior
- Easier rollback
- Devs know what they're doing
Logs as streams
- Log files, no more!
- Just print to STDOUT/STDERR
Logs as streams (2)
- Log files, no more!
- Just print to STDOUT/STDERR
Why?
- Less management
- Indexing and analysis
- Storage allocation
Admin processes
- Run admin/management tasks as one-off processes
- ...inside the environment of the app
- Database migrations
- REPL
- Admin code should ship with application code
Why?
- Ease of use
- Reproducible environment
The Twelve-Factor App
SaaS delivery that doesn't hurt (as much)