[建站记录]3.自动部署

git hook 和fabric的使用,实现自动化部署.

Git hook实现项目部署自动化

https://www.digitalocean.com/community/tutorials/how-to-set-up-automatic-deployment-with-git-with-a-vps

接下来设置git hook,实现我们本地push到repo后git自动部署到VPS.
其实就是自己搭建一个私人的github服务~
安装git,建立bare仓库,bare意思是文件夹里没有源代码,只是用于版本控制:

$ sudo apt-get install -y git
$ sudo mkdir /home/git && cd /home/git
$ sudo mkdir flask_project.git && cd flask_project.git
$ sudo git init --bare

小贴士:在shell prompt显示你当前位于哪个分支:
加入如下到/etc/profile(所有用户) ~/.bashrc(当前用户) :

parse_git_branch() {
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\u@\h \W\[\033[32m\]\$(parse_git_branch)\[\033[00m\] $ "

在/home/git/flask_project.git下:

$ sudo vim hooks/post-receive

写入:

#!/bin/sh
GIT_WORK_TREE=/home/www/flask_project git checkout -f

sudo supervisorctl restart flask_project

如果想要推送后自动更新supervisor就把supervisor的控制命令也加入钩子,但是需要用root身份ssh登录到git,这个在本地项目根目录下.git/config设置。

现在每次push都会自动执行一次上面的脚本.推送到vps

$ sudo chmod +x hooks/post-receive

现在在本地开发机的flask应用根目录,vps相当于github,设置这个ssh路径是容易出错的地方:

$ git init
$ git remote add production root@<your_ip_or_domain>:/home/git/flask_project.git

或者对于修改过vps ssh端口号的情况:
如在第一篇中设置过ssh别名为vps($vi ~/.ssh/config, 登录vps的用户名为user)

$ git remote add bandwagon user@vps:home/leroy/git/flask_project.git

如果想修改这个地址直接如下,改里面的url:

$ vim .git/config

修改,删除再添加,如果只是改名就rename:

$ git remote -v
$ git remote rm production

这时候,config 里面关键的一行是这样的:

url = ssh://root@bw:/home/leroy/git/flask_project.git

这就设置完了。
这里可以同时加入github的ssh连接,一次push到两个地方。

以后对代码库进行修改后,

$ git add -A
$ git commit -am "initial"
$ git push production master
或者
$ git push bandwagon master

就部署完毕了,这时候重启一下应用就上线

$ sudo supervisorctl restart flask_project

补充: 1.如果没有在virtualenv里安装需要的包,推送后部署会错误,需要自己手动去安装一下。
2.这样的设置无法pull只能push。

Fabric自动化部署脚本

把这三篇文章的命令放在一个脚本里面,以后就只用改改脚本即可自动化部署.
使用方法:
视频演示
把包含所有这三篇文章修改过的文件的整个文件夹,放到全新装完系统的vps上,一键部署:

$ fab creatve

如果想通过git推送更改:

$ fab deploy

如果有问题需要回滚:

$ fab rollback

下面是fab文件:

###############
### imports ###
###############

from fabric.api import cd, env, lcd, put, prompt, local, sudo
from fabric.contrib.files import exists


##############
### config ###
##############

local_app_dir = './flask_project'
local_config_dir = './config'

remote_app_dir = '/home/www'
remote_git_dir = '/home/git'
remote_flask_dir = remote_app_dir + '/flask_project'
remote_nginx_dir = '/etc/nginx/sites-enabled'
remote_supervisor_dir = '/etc/supervisor/conf.d'

env.hosts = ['add_ip_or_domain']  # replace with IP address or hostname
env.user = 'newuser'
# env.password = 'blah!'


#############
### tasks ###
#############

def install_requirements():
""" Install required packages. """
sudo('apt-get update')
sudo('apt-get install -y python')
sudo('apt-get install -y python-pip')
sudo('apt-get install -y python-virtualenv')
sudo('apt-get install -y nginx')
sudo('apt-get install -y gunicorn')
sudo('apt-get install -y supervisor')
sudo('apt-get install -y git')


def install_flask():
"""
1. Create project directories
2. Create and activate a virtualenv
3. Copy Flask files to remote host
"""
if exists(remote_app_dir) is False:
    sudo('mkdir ' + remote_app_dir)
if exists(remote_flask_dir) is False:
    sudo('mkdir ' + remote_flask_dir)
with lcd(local_app_dir):
    with cd(remote_app_dir):
        sudo('virtualenv env')
        sudo('source env/bin/activate')
        sudo('pip install Flask==0.10.1')
    with cd(remote_flask_dir):
        put('*', './', use_sudo=True)


def configure_nginx():
"""
1. Remove default nginx config file
2. Create new config file
3. Setup new symbolic link
4. Copy local config to remote config
5. Restart nginx
"""
sudo('/etc/init.d/nginx start')
if exists('/etc/nginx/sites-enabled/default'):
    sudo('rm /etc/nginx/sites-enabled/default')
if exists('/etc/nginx/sites-enabled/flask_project') is False:
    sudo('touch /etc/nginx/sites-available/flask_project')
    sudo('ln -s /etc/nginx/sites-available/flask_project' +
         ' /etc/nginx/sites-enabled/flask_project')
with lcd(local_config_dir):
    with cd(remote_nginx_dir):
        put('./flask_project', './', use_sudo=True)
sudo('/etc/init.d/nginx restart')


def configure_supervisor():
"""
1. Create new supervisor config file
2. Copy local config to remote config
3. Register new command
"""
if exists('/etc/supervisor/conf.d/flask_project.conf') is False:
    with lcd(local_config_dir):
        with cd(remote_supervisor_dir):
            put('./flask_project.conf', './', use_sudo=True)
            sudo('supervisorctl reread')
            sudo('supervisorctl update')


def configure_git():
"""
1. Setup bare Git repo
2. Create post-receive hook
"""
if exists(remote_git_dir) is False:
    sudo('mkdir ' + remote_git_dir)
    with cd(remote_git_dir):
        sudo('mkdir flask_project.git')
        with cd('flask_project.git'):
            sudo('git init --bare')
            with lcd(local_config_dir):
                with cd('hooks'):
                    put('./post-receive', './', use_sudo=True)
                    sudo('chmod +x post-receive')


def run_app():
""" Run the app! """
with cd(remote_flask_dir):
    sudo('supervisorctl start flask_project')


def deploy():
"""
1. Copy new Flask files
2. Restart gunicorn via supervisor
"""
with lcd(local_app_dir):
    local('git add -A')
    commit_message = prompt("Commit message?")
    local('git commit -am "{0}"'.format(commit_message))
    local('git push production master')
    sudo('supervisorctl restart flask_project')


def rollback():
"""
1. Quick rollback in case of error
2. Restart gunicorn via supervisor
"""
with lcd(local_app_dir):
    local('git revert master  --no-edit')
    local('git push production master')
    sudo('supervisorctl restart flask_project')


def status():
""" Is our app live? """
sudo('supervisorctl status')


def create():
install_requirements()
install_flask()
configure_nginx()
configure_supervisor()
configure_git()

What’s next?

  • 一个staging server作为上线前的测试平台.
  • 自动单元测试.

日新又新.