mirror of
https://github.com/kennethreitz/12factor.git
synced 2026-06-05 23:10:17 +00:00
processes and port binding
This commit is contained in:
@@ -7,6 +7,6 @@ A twelve-factor app is completely self-contained and does not rely on runtime in
|
||||
|
||||
In a local development environment, the developer visits a service URL like `http://localhost:5000/` to access the service exported by their app. In deployment, a routing layer handles routing requests from a public-facing hostname to the port-bound web processes.
|
||||
|
||||
This is typically implemented by using [dependency declaration](#) to add a webserver library to the app, such as [Tornado](http://www.tornadoweb.org/) for Python, [Thin](http://code.macournoyer.com/thin/) for Ruby, or [Jetty](http://jetty.codehaus.org/jetty/) for Java and other JVM-based languages. But this happens completely inside the app's code space, and the app's developers could chose to accept raw TCP requests and parse the HTTP themselves. Either one looks the same to the execution environment: the contract is binding to a port to serve requests, leaving the implementation details of what webserver is being used up to the web framework and/or the app's developers.
|
||||
This is typically implemented by using [dependency declaration](/dependencies) to add a webserver library to the app, such as [Tornado](http://www.tornadoweb.org/) for Python, [Thin](http://code.macournoyer.com/thin/) for Ruby, or [Jetty](http://jetty.codehaus.org/jetty/) for Java and other JVM-based languages. But this happens completely inside the app (known in this context as [*user space*](http://en.wikipedia.org/wiki/User_space)), and if the app developers chose to, they could write app code to accept raw TCP requests and parse the HTTP without any supporting libraries. Either way looks the same to the execution environment: the contract with the execution environment is binding to a port to serve requests, leaving the implementation details of what webserver is being used up to the web framework and/or the app's developers.
|
||||
|
||||
HTTP is not the only service that can be exported by port binding. Nearly any kind of server software, from ejabberd (speaking XMPP) to Redis (speaking the Redis protocol), can be run in the process-binds-to-a-port-to-receive-requests model.
|
||||
HTTP is not the only service that can be exported by port binding. Nearly any kind of server software can be run via process-binds-to-a-port-to-receive-requests model. To examples include ejabberd (speaking XMPP), and Redis (speaking the Redis protocol).
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
## VI. Processes
|
||||
### Stateless, disposable processes handle application logic
|
||||
|
||||
The business logic of an app happens in the app code. This code gets executed in the execution environment as one or more *processes*. the simplest case, the code is a stand-alone script, the execution environment is a developer's local laptop and an installed language runtime, and the process is launched via a simple command line: for example, `python my_script.py`. On the other end of the spectrum, a production deploy of a sophisticated app may use many [process types, instantiated into zero or more running processes](#).
|
||||
The business logic of an app happens in the app code. This code gets executed in the execution environment as one or more *processes*.
|
||||
|
||||
Processes are stateless and share-nothing. The filesystem sitting beneath the process is either read-only (attempting a write will produce an error), or completely ephemeral (when the process terminates, all state written to disk will be discarded). Anything that needs to persist must be stored in a stateful [backing service](#), typically a [database](#).
|
||||
In the simplest case, the code is a stand-alone script, the execution environment is a developer's local laptop with an installed language runtime, and the process is launched via the command line (for example, `python my_script.py`). On the other end of the spectrum, a production deploy of a sophisticated app may use many [process types, instantiated into zero or more running processes](/concurrency).
|
||||
|
||||
The process memory space and filesystem can potentially be used as a brief, single-transacton cache. For example, downloading a large file, operating on it, and storing the results of the operation in the database. The twelve-factor app never assumes that anything cached in memory or on disk will be available on a future request or job - with many processes of each type running, chances are high that that a future request will be served by a different process. Even with a single process, a restart (triggered by code deploy, config change, or the executing environment deciding to relocate the process to a different physical location) will wipe all state.
|
||||
Twelve-factor processes are stateless and [share-nothing](http://en.wikipedia.org/wiki/Shared_nothing_architecture). The filesystem sitting beneath the process is either read-only (attempting a write will produce an error), or completely ephemeral (when the process terminates, all state written to disk will be discarded). Anything that needs to persist must be stored in a stateful [backing service](/backing-services), typically a database.
|
||||
|
||||
Asset packagers (such as [Jammit](http://documentcloud.github.com/jammit/)) or [django-assetpackager](http://code.google.com/p/django-assetpackager/)) use the filesystem as a cache for compiled assets. A twelve-factor app is best served by doing this compiling during the [build stage](#) rather than at runtime.
|
||||
Process memory space and ephemeral filesystem can potentially be used as a brief, single-transaction cache. For example, downloading a large file, operating on it, and storing the results of the operation in the database. The twelve-factor app never assumes that anything cached in memory or on disk will be available on a future request or job - with many processes of each type running, chances are high that that a future request will be served by a different process. Even when running only one process, a restart (triggered by code deploy, config change, or the execution environment relocating the process to a different physical location) will wipe all state.
|
||||
|
||||
Asset packagers (such as [Jammit](http://documentcloud.github.com/jammit/) or [django-assetpackager](http://code.google.com/p/django-assetpackager/)) use the filesystem as a cache for compiled assets. A twelve-factor app is best served by doing this compiling during the [build stage](/build-release-run) rather than at runtime.
|
||||
|
||||
Some web systems rely on "sticky sessions" - that is, caching user session data in memory of the app's process and expecting future requests from the same visitor to be routed to the same process. Sticky sessions are a violation of twelve-factor and should never be used or relied upon. Session state is a good candidate for a datastore that offers time-expiration, such as Memcached or Redis.
|
||||
|
||||
### Processes are disposable
|
||||
#### Processes are disposable
|
||||
|
||||
Processes should be architected to be stopped and started at a moment's notice, faciliting fast elastic scaling and rapid deployment of code or config changes. This is a key element in ensuring robustness of a production deployment.
|
||||
Processes should be architected to be stopped and started at a moment's notice, facilitating fast elastic scaling and rapid deployment of code or config changes. This is a key element in ensuring robustness of a production deployment.
|
||||
|
||||
Processes should be prepared to shut down gracefully at any time on receiving a [SIGTERM](http://en.wikipedia.org/wiki/SIGTERM) signal from the process manager. The process will be granted a brief window (perhaps ten seconds) to shut down, after which it will be force-killed.
|
||||
|
||||
@@ -23,5 +25,5 @@ For a worker process, graceful shutdown is achieved by returning the current job
|
||||
|
||||
Developers should attempt to keep process startup time low. Ideally, it should only take a few seconds from the time the launch command is executed until the process is up and ready to receive requests or jobs. But even a large app should try to keep its startup to to no more than 30 seconds. Briefer startup time provides more agility for deploys, code changes, scaling up; moreover, it helps robustness, because the process manager can more easily move processes to new physical machines when warranted.
|
||||
|
||||
Processes should also be robust against sudden death, in the case of a failure in the underlying hardware. While this is a much less common occurence than a graceful shutdown with `SIGTERM`, it can still happen. Use of a robust queueing backend (such as Beanstalkd) that returns jobs to the queue when clients disconnect or time out, can make all the difference here. Either way, a twelve-factor app is architected to handle unexpected, non-graceful terminations. Taking this idea to its logical conclusion may even result in a [crash-only](http://lwn.net/Articles/191059/) [design](http://couchdb.apache.org/docs/overview.html).
|
||||
Processes should also be robust against sudden death, in the case of a failure in the underlying hardware. While this is a much less common occurrence than a graceful shutdown with `SIGTERM`, it can still happen. Use of a robust queueing backend (such as Beanstalkd) that returns jobs to the queue when clients disconnect or time out, can make all the difference here. Either way, a twelve-factor app is architected to handle unexpected, non-graceful terminations. Taking this idea to its logical conclusion may even result in a [crash-only](http://lwn.net/Articles/191059/) [design](http://couchdb.apache.org/docs/overview.html).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user