12 Factor¶
Fapistrano is not a silver-bullet, you can possibly mess up deployment process on wrongly configuring you application.
Following the guideline of The Twelve-Factor App may reduce potential traps.
Codebase¶
One codebase tracked in revision control, many deploys.
Fapistrano archive that goal by configurating item stage_role_configs. Each stage can connected with several roles.
For instance, an application is deployed to production but with several instance below. Codebase is git@git.your-corp.com:owner/repo.git. Deployment instances are production + web, production + worker, production + cron. These instances are running on different path on different servers, sharing different config.:
plugins:
- fapistrano.git
- fapistrano.supervisorctl
repo: git@git.your-corp.com:owner/repo.git
stage_role_configs:
production:
web:
hosts:
- app-web01
- app-web02
linked_files:
- configs/supervisor_production_web.conf
worker:
hosts:
- app-job01
linked_files:
- configs/supervisor_production_worker.conf
cron:
hosts:
- app-job01
linked_files:
- configs/supervisor_production_cron.conf
Dependencies¶
Explicity declare and isolate dependencies.
Fapistrano archives that goal by loading property plugin.
For example, if you loading a fapistrnao.virtualenv plugin, Fapistrano will create a venv directory as python execution environment:
plugins:
- fapistrano.git
- fapistrano.virtualenv
stage_role_configs:
production:
web:
virtualenv_requirements: '%(release_path)s/production-requirements.txt
It assumes that you have a production-requirements.txt in your git repository. Once updating git repository, Fapistrano will run these commands:
$ virtualenv venv
$ venv/bin/pip install -r production-requirements.txt
WARNING: This is still not the recommend way to install dependencies for Python. Fetching dependencies and compiling binaries at build stage, bundling your codebase and wheel packages as deployment artifact may be a better practice.
Config¶
Store config in the environment.
DO NOT EVER COMMIT SECRETS INTO YOUR REPOSITORY.
It is recommended to save your secrets at your shared folder and then link them on deploying:
plugins:
- fapistrano.git
stage_role_configs:
production:
web:
linked_files:
app/settings/production.py
Load these linked files as configurations. They won’t hurt you!
Build, Release, Run¶
Strictly separate build and run stages.
It’s not recommended to write configs below:
# deploy.yml
plugins:
- fapistrano.git
- fapistrano_webpack
# fapistrano_webpack.py
def init():
signal.register('deploy.updating', compile_static_resource)
The reason is simple: build stage is totally different from release stage and run stage. It’s not worth installing entire build infrastructure on your production servers.
We prefer converting a code repo into an executable bundle first. It turned out simpler and faster to release your codebase to production.:
# deploy.yml
plugins:
- fapistrano.curl
curl_extract_tgz: true
curl_postinstall_script: "./install.sh"
In the above, all you need to do is to pass a –curl-url option into fap command. Once artifact downloaded, Fapistrano will
- Extract your final codes: python code, static resource compiled by webpack.
- Run ./install.sh which possibly create virtualenv and install python dependencies. (virtualenv and dependencies have been put into tgz)
Processes¶
Execute the app as one or more stateless processes.
Make sure your application is stateless and share-nothing.
Your application is running in a easy-to-lost directory, since release directory can only be kept to at max number of keep_releases.
If your have any persist data, commit them into database or write them into shared files:
# deploy.yml
stage_role_configs:
production:
web:
linked_files:
- log/audio-transcoding.log
- log/image-compress.log
NOTICE: do not write supervisor log in shared, since they are written by root user.
Concurrency¶
Scale out via the process model
If you want to scale out your application, you can add a new host to deploy.yml definition:
stage_role_configs:
production:
web:
hosts:
- app-web01
stage_role_configs:
production:
web:
hosts:
- app-web01
- app-web02
- app-web03
Use your load balance infrastructure to route traffic to these applciation instance:
upstream app_servers {
server app-web01:8080;
server app-web02:8080;
server app-web03:8080;
}
server {
listen 80;
server_name example.org;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://app_servers;
}
}
Disposability¶
Maximize robustness with fast startup and graceful shutdown.
Starting or stoping your application should not take a long time for waiting. Few seconds are durable.
It is recommended to rely on process manager, such as supervisor, to manage output stream, respond to crashed processes, and handle restarts and shutdowns:
plugins:
- fapistrano.supervisorctl
- fapistrano.git
supervisor_check_status: true
supervisor_output: true
supervisor_refresh: false
supervisor_conf: configs/supervisor_%(role)s.conf
Dev/Prod parity¶
Keep development, staging, and production as similar as possible.
A typically Fapistrano way of Dev/Prod parity is to deploy same code but to symlink different config files.:
stage_role_configs:
production:
web:
linked_files:
- app/settings/production.py
staging:
web:
linked_files:
- app/settings/staging.py
Admin Processes¶
Run admin/management tasks as one-off processes.
It is recommended to commit your one-off scripts into your repository and treat it as a brand new release. A one-off goal may be archived by disabling supervisor pluging and customizing running endpoint.