Build Modern, Scalable Web Apps
Methodology to build web apps that:
Scalable, cloud based web applications!
... As long as you're building a web app
The Twelve-Factor App is language, platform, and toolset agnostic!
| I. Codebase | VII. Port Binding |
| II. Dependencies | VIII. Concurrency |
| III. Config | IX. Disposability |
| IV. Backing Services | X. Dev/Prod Parity |
| V. Build, Release, Run | XI. Logs |
| VI. Processes | XII. Admin Processes |
One codebase tracked in revision control, many deploys
One app per code base
Multiple code bases == Distributed System
Each app in a distributed system is a tewlve-factor app
Code base MUST be tracked in revision control
Allows for collaborative development
Recommend git but any revision control will do
Each running instance is a deploy
Might have dev, test, and production deploys
Each running instance on a developer's machine is a deploy
Code base for each deploy is the same, but probably different versions and configuration
Helps new developers become familiar by having single source
Eliminates compliations with dependencies on other source code
Deploys are easy because you just need one source repo
All the benefits of a revision control system
Explicitly declare and isolate dependencies
Most programming languages have some sort of package manager
CPAN for perl, PyPI for python, npm for node.js, etc.
A twelve-factor app has dependencies (including version) declared in a dependency manifest
A twelve-factor app never relies on implicit existence of system-wide packages
Use a dependency isolation tool to ensure no leak
Vendor or bundle dependencies locally with the app
This includes "system" tools, don't expect curl to be installed everywhere!
Helps new developers because all they need is:
Everything is set up with a declarative build command
Store Config In The Environment
An app’s config is everything that is likely to vary between deploys
For example:
A twelve-factor App never stores any config in the code base
Protip: If you can open source the codebase at any time and not compromise yourself, you're probably good!
Unversioned config file
Store config in environment variables
But how do you manage the config?
The Twelve-Factor App doesn't talk much about this point
If you can't store secrets in version control, where do you store them?
Seperate secure/encrypted "vault" like 1password or truecrypt?
Tool to securely manage keys and config like ansible-vault or Hashicorp Vault?
Security
Scalability
Separation of deploy targets config
Treat backing services as attached resources
A backing service is any service the app consumes over a network
A twelve-factor app makes no distinction of local, remote, or third-party services
It treats all the same and accesses them using a URI specified in config
Should be able to swap out services without any code changes
Makes managing multiple deploy targets or environment easier
Hot-swapping of services without code change
Strictly separate build and run stages
Non-production deploy is created in three stages
Transforms code base into an executable
Compilation of binaries and assets
Vendoring of dependencies
Bundles together into a build
Combines build with config for the deploy target into a release
Resulting release is ready for execution on target
Runs the app in the execution environment on the target
Launches the release as a set of processes
The twelve-factor app uses strict separation between the build, release, and run stages
Can't change code at runtime or release time
An aside to The Twelve-Factor App
Some build tools manage releases
Releases are tagged with incrementing version number or timestamp
Current release is symlinked, rollback is easy, just change symlink
Builds are done by DevOps performing the deploy
Runtime execution can happen automatically after a reboot or when a process manager restarts after a crash
Runtime should therefore be as simple as possible, while builds can be more complex
Improved uptime by:
Execute the app as one or more stateless processes
Runs in environment as one or more managed processes
Managed processes: Start at bootup, restart automatically when needed
Can use things like upstart, init, foreman, etc.
App processes are stateless and share nothing
Persistent data should be stored in a backing service
Memory space and filesystem can act as single transaction cache
Some apps use sticky sessions
Sticky sessions break twelve-factor and should not be used
Any session data should be in backing service with data expiry
Allows scalability and composability since processes are stateless
Export services via port binding
A twelve-factor app is not run inside a web server container
A twelve-factor app exports HTTP as a service by binding to a port
Use a small, lightweight http server library dependency
By binding to ports, the app can be a backing service for another app
Just specify url and port in configuration to chain together
Any protocol can be exported via port binding, not just HTTP
Composability
Each app is a separate self containted entity
We can compose a larger app by using other apps as backing services
Scale out via the process model
Thanks to factors VI and VII scaling should be easy
Processes are independent and composable
Architect for diverse workloads using process types
Shouldn't self daemonize, use OS process manager
Add processes to increase concurrency
Add process types to increase process diversity
Add machines to scale horizontally
Maximize robustness with fast startup and graceful shutdown
Should minimize startup time
Can be started, stopped or restarted at a moments notice
Helps with scaling, config changes, and fast deploys
Process manager handles all of this for you
Processes should gracefully shutdown on SIGTERM from process manager
Graceful shutdown means:
Graceful shutdown means:
Getting SIGTERM is optimal, but process may die in case of hardware failure
In this case application should handle non-graceful terminations
Can use robust job queue that automatically returns jobs after disconnect or timeout
Servers in the cloud are super disposable
Likelihood of failure is high, risk of failure should be low
Auto scaling up and down, and reconfiguration is easy when processes are disposable
Keep development, staging, and production as similar as possible
Historical development gapped between dev and prod
This prevented Continuous Deployment
Keep the gaps small!
Adapters for backing services can be useful
Adapters help with porting to different services easier
Don't rely on adapters for dev/prod parity
Keeping the gap between dev and prod small enables continuous deployment
Features are released, bugs are found, and fixes are made faster
Treat logs as event streams
A twelve-factor app should write directly to stdout
Event stream is one event per line in text format
App isn't concerned with routing or storage
Execution environment handles the event stream from the process
In development, dev watches stdout in terminal
In production, logs are captured by environment and written to file
Logs can be piped to log indexing and analysis system
Splunk and SumoLogic are examples
Powerful analysis tools can show usage trends, common exceptions, and alert in special circumstances
Good separation of concerns
App isn't bogged down with managing log streams
Enables use of powerful logging tools
Increases portability of app because environment handles the logs
Run admin/management tasks as one-off processes
Normal app business happens in the process formation set up by process manager
DevOps might want to run admin or maintenance like:
Admin processes should be run like other processes:
Twelve-Factor favours languages with a REPL which make admin easier
Separate admin work from app increases reliability of app processes
Bundling with release and isolating dependencies help ensure admin processes work everywhere the same
| I. Codebase | VII. Port Binding |
| II. Dependencies | VIII. Concurrency |
| III. Config | IX. Disposability |
| IV. Backing Services | X. Dev/Prod Parity |
| V. Build, Release, Run | XI. Logs |
| VI. Processes | XII. Admin Processes |
This talk is available at noseworthy.github.io