使用 Fabric 自动化部署

使用 Nginx 和 Gunicorn 部署 Django 博客 中,我们通过手工方式将代码部署到了服务器。整个过程涉及到十几条命令,输了 N 个字符。一旦我们本地的代码有更新,整个过程又得重复来一遍,这将变得非常繁琐。

使用 Fabric 可以在服务器中自动执行命令。因为整个代码部署过程都是相同的,只要我们用 Fabric 写好部署脚本,以后就可以通过运行脚本自动完成部署了。

安装 Fabric

Fabric 目前仅支持 Python2,如果你的系统中只有 Python3 版本,请先安装一个 Python2 的版本,推荐安装 Python 2.7。Python3 和 Python2 可以共存于一个系统,所以不用担心同时安装两个版本的 Python 会发生冲突。

接下就可以简单地通过 pip 命令安装 Fabric 了。注意要使用 Python2 环境下的 pip,这样才能把 Fabric 安装到 Python2 环境下。

pip install fabric

部署过程回顾

在写 Fabric 脚本之前,我们先来回顾一下当我们在本地开发环境下更新了代码后,在服务器上的整个部署过程。

  1. 远程连接服务器。
  2. 进入项目根目录,从远程仓库拉取最新的代码。
  3. 如果项目引入了新的依赖,需要执行 pip install -r requirement.txt 安装最新依赖。
  4. 如果修改或新增了项目静态文件,需要执行 python manage.py collectstatic 收集静态文件。
  5. 如果数据库发生了变化,需要执行 python manage.py migrate 迁移数据库。
  6. 重启 Nginx 和 Gunicorn 使改动生效。

整个过程就是这样,把每一步操作翻译成 Fabric 对应的脚本代码,这样一个自动化部署脚本就完成了。

编写 Fabric 脚本

Fabric 脚本通常位于 fabfile.py 文件里,因此先在项目根目录下建一个 fabfile.py 文件。

根据上述过程编写的脚本代码如下:

blogproject/fabfile.py

from fabric.api import env, run
from fabric.operations import sudo

GIT_REPO = "you git repository"  

env.user = 'you host username' 
env.password = 'you host password'

# 填写你自己的主机对应的域名
env.hosts = ['demo.zmrenwu.com']

# 一般情况下为 22 端口,如果非 22 端口请查看你的主机服务提供商提供的信息
env.port = '22'


def deploy():
    source_folder = '/home/yangxg/sites/zmrenwu.com/django-blog-tutorial' 

    run('cd %s && git pull' % source_folder) 
    run("""
        cd {} &&
        ../env/bin/pip install -r requirements.txt &&
        ../env/bin/python3 manage.py collectstatic --noinput &&
        ../env/bin/python3 manage.py migrate
        """.format(source_folder))  
    sudo('restart gunicorn-demo.zmrenwu.com') 
    sudo('service nginx reload')

① 你的代码托管仓库地址。

② 配置一些服务器的地址信息和账户信息,各参数的含义分别为:

  • env.user:用于登录服务器的用户名
  • env.password:用户名对应的密码
  • env.hosts:服务器的 IP 地址,也可以是解析到这个 IP 的域名
  • env.port:SSH 远程服务器的端口号

③ 需要部署的项目根目录在服务器上的位置。

④ 通过 run 方法在服务器上执行命令,传入的参数为需要执行的命令,用字符串包裹。这里执行了两条命令,不同命令间用 && 符号连接:

  1. cd 命令进入到需要部署的项目根目录
  2. git pull 拉取远程仓库的最新代码

⑤ 对应上述部署过程中 3-5 的几条命令。因为启用了虚拟环境,所以运行的是虚拟环境 ../env/bin/ 下的 pip 和 python

⑥ 重启 Gunicorn 和 Nginx,由于这两条命令要在超级权限下运行,所以使用了 sudo 方法而不是 run 方法。

注意全部的脚本代码要放在 deploy 函数里,Fabric 会自动检测 fabfile.py 脚本中的 deploy 函数并运行。

由于脚本中有登录服务器的用户名和密码等敏感信息,不要把 fabfile.py 文件也上传到公开的代码托管仓库。

执行 Fabric 自动部署脚本

进入 fabfile.py 文件所在的目录,在 Python2 的环境下用 fab 命令运行这个脚本文件。

比如我的是 Windows 环境,Python2 安装在 C:\Python27\ 下,那么运行:

C:\Python27\Scripts\fab deploy

这时 Fabric 会自动检测到 fabfile.py 脚本中的 deploy 函数并运行,你会看到命令行输出了一系列字符串,如果在最后看到

Done. Disconnecting from zmrenwu.com... done.

说明脚本运行成功。

而如果看到

Aborting. Disconnecting from zmrenwu.com... done.

说明脚本运行中出错,检查一下命令行输入的错误信息,修复问题后重新运行脚本即可。以后当你在本地开发完相关功能后,只需要执行这一个脚本文件,就可以自动把最新代码部署到服务器了。

总结

本章节的代码位于:Step15: deploy automatically using fabric

如果遇到问题,请通过下面的方式寻求帮助。

  • 在下方评论区留言。
  • 将问题的详细描述通过邮件发送到 djangostudyteam@163.com,一般会在 24 小时内回复。
  • Pythonzhcn 社区的新手问答版块 发布帖子。

-- EOF --


63 条评论 / 36 人参与
Arrowarcher

采用pip3 install fabric3 

其他操作没什么变化:

参考

https://blog.csdn.net/qq_41854273/article/details/83344255


kevinjobs

回复上面那条,我自己搜索到怎么用密钥文件登陆了

把 env.password 替换为 env.key_filename 即可,如下所示

env.key_filename = '/path/to/key'

替换为你自己的 key 路径即可。

另外我按照博主教程写网站已经上线了 http://blog.mintin.me,自己重写了 CSS,大家互相参考下。

非常感谢博主的教程。


kevinjobs

AWS 的服务器用的是密钥文件登录,应该怎么搞哦


xusanpang

博主你好,执行fab deploy 出现这种问题是怎么回事呢?

out: django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path.


xusanpang xusanpang

知道错哪里了。。


lenkenlau

还有个细节:

还记得在 ubuntu16 下,自动启动 gunicorn 的脚本是不同于博主的,所以gunicorn的重启命令这里也要换掉:

# sudo('restart gunicorn-www.liuliqun.top')
sudo('systemctl restart lenkenlau.service')

lenkenlau

env.hosts 填域名会报错:查找该域名失败

换成IP地址就好了...不清楚为什么?


xuyy2

能不能写个shell 脚本实现上面的功能


xinyuebaiyun

自动部署拉取什么文件的代码?blog等应用里的?


玄虚

我用 supervisor 更方便。


michael198599

使用密码不安全的话,fab 脚本 可以考虑使用SSH公私钥登陆。步骤是把本机公钥放到deploy 机器的 .ssh/authorized_keys  文件中。

实测需要先把git仓库文件提交到 github 以后再fab deploy 以后本地的修改才能生效。

out:  templates/base.html.bak | 189 ------------------------------------------------------------------------------------------------------- out:  1 file changed, 189 deletions(-) 

out:  delete mode 100644 templates/base.html.bak

……………………

Done.Disconnecting from www.demonhunter07.com... done.


hualibukenni

博主,这个提示是指我的本地仓库与远程仓库没合并还是什么原因? 我记得是已经上传到Github上的了

[www.lhyaiwh.top] out: error: Your local changes to the following files would be overwritten by merge:[www.lhyaiwh.top] out: templates/base.html  

 [www.lhyaiwh.top] out: Please, commit your changes or stash them before you can merge. [www.lhyaiwh.top] out: Aborting 

 [www.lhyaiwh.top] out: Fatal error: run() received nonzero return code 1 while executing!  

Requested: cd /home/liuhanyu/sites/demo.lhyaiwh.top/blogproject && git pullExecuted: /bin/bash -l -c "cd /home/liuhanyu/sites/demo.lhyaiwh.top/blogproject && git pull" Aborting.

Disconnecting from www.lhyaiwh.top:29490... done.


追梦人物 [博主] hualibukenni

看来是你pull代码的时候出的问题咯,不好确定,确定脚本没错的话多试几次。


Treehl

请教杨大!!运行fabfile报错,我在本地添加了服务器上的仓库地址,仍然显示git仓库权限有问题。服务器上的git密钥之前在部署环节就生成好了,请问是怎么回事呢!!

fatal: Could not read from remote repository. 

 out: Please make sure you have the correct access rights 

out: and the repository exists.

out:Fatal error: run() received nonzero return code 1 while executing!


追梦人物 [博主] Treehl

显然是远程仓库出了问题,有可能是权限问题,也可能是网络问题,不好说。


MoNaiZi

博主我报错如下 我是python2的环境下

C:\Users\zmy\blogo\blogproject>fab deploy 

[zhongminyong.tech] Executing task 'deploy'

[zhongminyong.tech] run: cd /home/zmy/sites/zhongminyong.tech/gitskills && git pull

Fatal error: No existing session

Underlying exception:

    No existing sessionAborting.


追梦人物 [博主] MoNaiZi

根据异常信息谷歌一下,看不出问题。


LYCai

一直遇到权限问题,不得不把`run('cd %s && git pull' % source_folder)`改成`sudo('cd %s && git pull' % source_folder)`,我想问下po,怎么在Fabric3切换用户。

我执行`run('su - user')`老是要手动输入密码,然后就不能继续执行了


追梦人物 [博主] LYCai

查一下fabric的文档的,我对这个也不是很清楚。


Fossen Wang

我将部署过程写进python脚本,然后在服务器上用cron定时任务定期执行该脚本,实现了自动部署,部署结果通过邮件发送到指定邮箱。

有兴趣的可以看看:cron + python 实现自动部署


追梦人物 [博主] Fossen Wang

Greate job!学习了。


Annihilater Fossen Wang

进去看了下,python3可以使用fabric3

直接pip install fabric3就可以了


Fossen Wang Annihilater

原来如此,知道的晚了


Arrowarcher Fossen Wang

请问层主,去看了你的博客,可以冒昧问一下,前端是用什么工具写的吗,更想做这个风格简约的。


追梦人物 [博主] Arrowarcher

你说的是这个博客吗?github 搜索 mobi.css 即可,一个微软大佬写的微型css库


量飞

fabric3,坐等大佬更新博客。


四有_青年

博主你好,我的网站部署之后nginx经常挂掉,可能是什么原因?


追梦人物 [博主] 四有_青年

服务器不稳定吧。


526468056

cd  { } 是什么意思


追梦人物 [博主] 526468056

进入 cd 后的目录。


lenkenlau 526468056

format函数了解一下,这个 {} 与上面的 % 作用基本一样


zhangyu1402

Traceback (most recent call last):  File "/Library/Frameworks/Python.framework/Versions/3.6/bin/fab", line 11, in <module>    load_entry_point('Fabric==1.14.0', 'console_scripts', 'fab')()  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 565, in load_entry_point    return get_distribution(dist).load_entry_point(group, name)  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2631, in load_entry_point    return ep.load()  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2291, in load    return self.resolve()  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2297, in resolve    module = __import__(self.module_name, fromlist=['__name__'], level=0)  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/fabric/main.py", line 13, in <module>    from operator import isMappingTypeImportError: cannot import name 'isMappingType'

这个是怎么回事啊


Simoral

# 安装fabric

$ sudo apt-get install fabric


renzhe00

怎么执行那个脚本,没有看懂


高珲

现在可以用pip3 install fabric3安装依赖在python3环境下运行,我的py2文件夹找不到fab,用这个方法测试脚本成功。


EruDev 高珲

有点没看明白,请问具体是怎么运行的`fab deploy`


zifeng9502

博主问个问题,我每次自动化部署之后,数据库是不是就清空了啊?


追梦人物 [博主] zifeng9502

不会的。


zifeng9502 追梦人物 [博主]

好的,谢谢,


ShiLong

博主你好;

我打算在生产环境下用Mysql数据库;那么每次部署都要修改settings.py文件的数据库配置和DEBUG模式。

是不是太麻烦了,有更好的办法吗?


追梦人物 [博主] ShiLong

当然有,在百度或者 google 上搜索会有答案。


ShiLong 追梦人物 [博主]

以前我学ruby on rails的配置文件就可以分别配置开发环境,测试环境,生产环境的设置。我也百度过,diango的设置貌似有点麻烦。


追梦人物 [博主] ShiLong

基本思路也是一样的,把配置文件拆分成生产环境、开发环境两个配置文件,在不同环境下使用不同的配置文件。


ShiLong 追梦人物 [博主]

好的,谢谢。


Fossen Wang ShiLong

我是不把setting.py加入git,即在.gitignore里加入setting.py,手动在服务器上vim setting.py。

这样每次上传代码就不会更新setting.py,本地和服务器就可以同时使用不同setting。

不过如果setting有需要改动的话还得手动登录服务器去修改。


王思茂

用shell自动化部署也可以把?


zgGitHub

用sudo pip install fabric按照fabric 的时候报如下错误:

Command python setup.py egg_info failed with error code 1 in /tmp/pip_build_root/pynaclStoring debug log for failure in /root/.pip/pip.log


oliverfree

博主写得还是很清楚的,自动化部署成功了。


xueshichaoya
这个脚本不能重复使用吧,不然git pull 那里会报错啊

追梦人物 [博主] xueshichaoya
把 git 的仓库地址改成自己仓库的应该就可以。

sakura1357
../env/bin/pip install -r requirements.txt &&
../env/bin/python3 manage.py collectstatic --noinput &&
../env/bin/python3 manage.py migrate
建议使用绝对路径来写,/home/user/sites/env/bin/***,我用相对路径..执行的时候会报错,换成绝对路径执行就没问题了。

楔子_菜菜
这时 Fabric 会自动检测到 fabfile.py 脚本中的 deploy 函数并运行。貌似不对吧,应该得到"fabfile.py"所在目录执行:fab deploy,是不是?

追梦人物 [博主] 楔子_菜菜
是的,我的表述可能不够准确,不过我相信你能理解我的意思。

志子杰-勇士哥 楔子_菜菜

我服务器上是ubuntu系统,这个操作实在服务器上完成还是本地完成?应该在什么环境下输入fab deploy呢?求赐教,这部分有点看不明白~


志子杰-勇士哥 楔子_菜菜

我自己搞明白了,谢谢哈,本地就可以运行这个fabfile.py~


tianjigor
这条 sudo('restart gunicorn-tianjigo.com') 就出错了

Requested: restart gunicorn-tianjigo.com
Executed: sudo -S -p 'sudo password:' /bin/bash -l -c "restart gunicorn-tianjigo.com"

是不是使用系统第一次sudo,还要把密码输进去呢?

追梦人物 [博主] tianjigor
是用的 ubuntu 么?

tianjigor 追梦人物 [博主]
服务器系统是Ubuntu,本地是win7

追梦人物 [博主] tianjigor
有没有在脚本指定密码?看着像是你的 bash 有一些问题,具体问题我也不太清楚,我对 linux 了解也不多。

我是单永旭 tianjigor
老铁,你把sudo换成run试一下,如果出现了restart:unknown instance的话那就把restart换成start.

陈栋 我是单永旭
run可以不用验证密码了。
报错
Unable to connect to system bus: Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory
用sudo apt-get install dbus解决了
但是又出现了 Rejected send message, 1 matched rules; type="method_call", sender=":1.11" (uid=1000 pid=2280 comm="restart gunicorn-www.ftc300.pub ") interface="com.ubuntu.Upstart0_6.Job" m
ember="Restart" error name="(unset)" requested_reply="0" destination="com.ubuntu.Upstart" (uid=0 pid=1 comm="init")的错误了
so sad~

我是单永旭 陈栋
我只能在心里默默的祝福你了....手动笔芯~~~

lanlingsheng tianjigor

是你的Ubuntu 服务器没有启动 gunicorn-tianjigo.com, 所以不能restart。

需要先在Ubuntu服务器上执行 sudo start gunicorn-tianjigo.com,

然后运行本地 fab deploy

其实你的报错信息应该是 Fatal error: sudo() received nonzero return code 1 while executing!