12 Factor of Fapistrano Deployments
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.