├── .gitignore ├── README.md ├── config ├── flask_project ├── flask_project.conf └── post-receive ├── fabfile.py ├── flask_project ├── app.py └── static │ └── index.html ├── images ├── githooks.jpg ├── githooks2.jpg ├── localhost.jpg ├── localhost2.jpg └── wsgi.jpg └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.DS_Store 3 | env 4 | readme.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | flask-deploy 2 | ============ 3 | 4 | Check out the blog post: https://realpython.com/blog/python/kickstarting-flask-on-ubuntu-setup-and-deployment/ 5 | -------------------------------------------------------------------------------- /config/flask_project: -------------------------------------------------------------------------------- 1 | server { 2 | location / { 3 | proxy_pass http://localhost:8000; 4 | proxy_set_header Host $host; 5 | proxy_set_header X-Real-IP $remote_addr; 6 | } 7 | location /static { 8 | alias /home/www/flask_project/static/; 9 | } 10 | } -------------------------------------------------------------------------------- /config/flask_project.conf: -------------------------------------------------------------------------------- 1 | [program:flask_project] 2 | command = gunicorn app:app -b localhost:8000 3 | directory = /home/www/flask_project 4 | user = newuser -------------------------------------------------------------------------------- /config/post-receive: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | GIT_WORK_TREE=/home/www/flask_project git checkout -f -------------------------------------------------------------------------------- /fabfile.py: -------------------------------------------------------------------------------- 1 | ############### 2 | ### imports ### 3 | ############### 4 | 5 | from fabric.api import cd, env, lcd, put, prompt, local, sudo 6 | from fabric.contrib.files import exists 7 | 8 | 9 | ############## 10 | ### config ### 11 | ############## 12 | 13 | local_app_dir = './flask_project' 14 | local_config_dir = './config' 15 | 16 | remote_app_dir = '/home/www' 17 | remote_git_dir = '/home/git' 18 | remote_flask_dir = remote_app_dir + '/flask_project' 19 | remote_nginx_dir = '/etc/nginx/sites-enabled' 20 | remote_supervisor_dir = '/etc/supervisor/conf.d' 21 | 22 | env.hosts = ['add_ip_or_domain'] # replace with IP address or hostname 23 | env.user = 'newuser' 24 | # env.password = 'blah!' 25 | 26 | 27 | ############# 28 | ### tasks ### 29 | ############# 30 | 31 | def install_requirements(): 32 | """ Install required packages. """ 33 | sudo('apt-get update') 34 | sudo('apt-get install -y python') 35 | sudo('apt-get install -y python-pip') 36 | sudo('apt-get install -y python-virtualenv') 37 | sudo('apt-get install -y nginx') 38 | sudo('apt-get install -y gunicorn') 39 | sudo('apt-get install -y supervisor') 40 | sudo('apt-get install -y git') 41 | 42 | 43 | def install_flask(): 44 | """ 45 | 1. Create project directories 46 | 2. Create and activate a virtualenv 47 | 3. Copy Flask files to remote host 48 | """ 49 | if exists(remote_app_dir) is False: 50 | sudo('mkdir ' + remote_app_dir) 51 | if exists(remote_flask_dir) is False: 52 | sudo('mkdir ' + remote_flask_dir) 53 | with lcd(local_app_dir): 54 | with cd(remote_app_dir): 55 | sudo('virtualenv env') 56 | sudo('source env/bin/activate') 57 | sudo('pip install Flask==0.10.1') 58 | with cd(remote_flask_dir): 59 | put('*', './', use_sudo=True) 60 | 61 | 62 | def configure_nginx(): 63 | """ 64 | 1. Remove default nginx config file 65 | 2. Create new config file 66 | 3. Setup new symbolic link 67 | 4. Copy local config to remote config 68 | 5. Restart nginx 69 | """ 70 | sudo('/etc/init.d/nginx start') 71 | if exists('/etc/nginx/sites-enabled/default'): 72 | sudo('rm /etc/nginx/sites-enabled/default') 73 | if exists('/etc/nginx/sites-enabled/flask_project') is False: 74 | sudo('touch /etc/nginx/sites-available/flask_project') 75 | sudo('ln -s /etc/nginx/sites-available/flask_project' + 76 | ' /etc/nginx/sites-enabled/flask_project') 77 | with lcd(local_config_dir): 78 | with cd(remote_nginx_dir): 79 | put('./flask_project', './', use_sudo=True) 80 | sudo('/etc/init.d/nginx restart') 81 | 82 | 83 | def configure_supervisor(): 84 | """ 85 | 1. Create new supervisor config file 86 | 2. Copy local config to remote config 87 | 3. Register new command 88 | """ 89 | if exists('/etc/supervisor/conf.d/flask_project.conf') is False: 90 | with lcd(local_config_dir): 91 | with cd(remote_supervisor_dir): 92 | put('./flask_project.conf', './', use_sudo=True) 93 | sudo('supervisorctl reread') 94 | sudo('supervisorctl update') 95 | 96 | 97 | def configure_git(): 98 | """ 99 | 1. Setup bare Git repo 100 | 2. Create post-receive hook 101 | """ 102 | if exists(remote_git_dir) is False: 103 | sudo('mkdir ' + remote_git_dir) 104 | with cd(remote_git_dir): 105 | sudo('mkdir flask_project.git') 106 | with cd('flask_project.git'): 107 | sudo('git init --bare') 108 | with lcd(local_config_dir): 109 | with cd('hooks'): 110 | put('./post-receive', './', use_sudo=True) 111 | sudo('chmod +x post-receive') 112 | 113 | 114 | def run_app(): 115 | """ Run the app! """ 116 | with cd(remote_flask_dir): 117 | sudo('supervisorctl start flask_project') 118 | 119 | 120 | def deploy(): 121 | """ 122 | 1. Copy new Flask files 123 | 2. Restart gunicorn via supervisor 124 | """ 125 | with lcd(local_app_dir): 126 | local('git add -A') 127 | commit_message = prompt("Commit message?") 128 | local('git commit -am "{0}"'.format(commit_message)) 129 | local('git push production master') 130 | sudo('supervisorctl restart flask_project') 131 | 132 | 133 | def rollback(): 134 | """ 135 | 1. Quick rollback in case of error 136 | 2. Restart gunicorn via supervisor 137 | """ 138 | with lcd(local_app_dir): 139 | local('git revert master --no-edit') 140 | local('git push production master') 141 | sudo('supervisorctl restart flask_project') 142 | 143 | 144 | def status(): 145 | """ Is our app live? """ 146 | sudo('supervisorctl status') 147 | 148 | 149 | def create(): 150 | install_requirements() 151 | install_flask() 152 | configure_nginx() 153 | configure_supervisor() 154 | configure_git() 155 | -------------------------------------------------------------------------------- /flask_project/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | 3 | app = Flask(__name__) 4 | 5 | 6 | @app.route('/') 7 | def index(): 8 | return 'Flask is running!' 9 | 10 | 11 | @app.route('/data') 12 | def names(): 13 | data = { 14 | "first_names": ["John", "Jacob", "Julie", "Jenny"] 15 | } 16 | return jsonify(data) 17 | 18 | 19 | if __name__ == '__main__': 20 | app.run() 21 | -------------------------------------------------------------------------------- /flask_project/static/index.html: -------------------------------------------------------------------------------- 1 |

Test!

-------------------------------------------------------------------------------- /images/githooks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/flask-deploy/1dcb21507ead03c4594095160004555b592acdc6/images/githooks.jpg -------------------------------------------------------------------------------- /images/githooks2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/flask-deploy/1dcb21507ead03c4594095160004555b592acdc6/images/githooks2.jpg -------------------------------------------------------------------------------- /images/localhost.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/flask-deploy/1dcb21507ead03c4594095160004555b592acdc6/images/localhost.jpg -------------------------------------------------------------------------------- /images/localhost2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/flask-deploy/1dcb21507ead03c4594095160004555b592acdc6/images/localhost2.jpg -------------------------------------------------------------------------------- /images/wsgi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realpython/flask-deploy/1dcb21507ead03c4594095160004555b592acdc6/images/wsgi.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | Jinja2==2.7.3 3 | MarkupSafe==0.23 4 | Werkzeug==0.9.6 5 | itsdangerous==0.24 6 | wsgiref==0.1.2 7 | --------------------------------------------------------------------------------