Fabric学习

[toc]

总结

fabric3 移除了很多实用的功能和装饰器,变得更精简了,另外fab这个cli入口提供的参数能力也变弱了,目前个人实用的方式是typer+fabric3+makefile:

  • typer 作为参数解析入口
  • makefile 固定一些操作逻辑,可以使用它的并发操作,而不是ThreadingGroup

What is Fabric

python的一个high level的库,通过ssh执行shell命令。

基于以下两个包构建的:

  • Invoke,subprocess command execution and command-line features
  • Paramiko,SSH protocol implementation

私钥配置

  • 配置ssh的authorized_keys,将本机的id_rsa.pub信息复制到远程的authorized_keys文件中

简要用法

  • pty(prompt by hand),如果sudo有密码,那么用pty来手动输入
>>> from fabric import Connection
>>> c = Connection('192.168.9.139')
>>> c.run('sudo useradd mydbuser', pty=True)
[sudo] password:
<Result cmd='sudo useradd mydbuser' exited=0>
>>> c.run('id -u mydbuser')
  • 利用Invoke的自动回应来输入sudo密码,注意pattern的正确匹配,以及repsonese的结尾符号
>>> from invoke import Responder
>>> from fabric import Connection
>>> c = Connection('host')
>>> sudopass = Responder(
...     pattern=r'\[sudo\] password:',
...     response='mypassword\n',
... )
>>> c.run('sudo whoami', pty=True, watchers=[sudopass])
[sudo] password:
root
<Result cmd='sudo whoami' exited=0>
  • watchers虽然提供了自动填充密码的功能,但是代码中多次引用这种配置比较麻烦,所以考虑将sudopass的内容放入配置里面,让Connection.sudo来处理剩下的工作。
>>> import getpass
>>> from fabric import Connection, Config
>>> sudo_pass = getpass.getpass("What's your sudo password?")
What's your sudo password?
>>> config = Config(overrides={'sudo': {'password': sudo_pass}})
>>> c = Connection('db1', config=config)
>>> c.sudo('whoami', hide='stderr')
root
<Result cmd="...whoami" exited=0>
>>> c.sudo('useradd mydbuser')
<Result cmd="...useradd mydbuser" exited=0>
>>> c.run('id -u mydbuser')
1001
<Result cmd='id -u mydbuser' exited=0>
  • 文件传输,transfer files,利用Connection.put和Connection.get。在使用时发现如果像下面这样定义remote的话,会报错OSError,要带上远程的文件重命名就不会报错。
>>> from fabric import Connection
>>> result = Connection('web1').put('myfiles.tgz', remote='/opt/mydata/')
>>> print("Uploaded {0.local} to {0.remote}".format(result))
Uploaded /local/myfiles.tgz to /opt/mydata/
  • 之前都是单行的操作,实际多半不是这样,多个同时执行,如上传、解压,路径、文件名可以作为参数传入,以便复用函数
from fabric import Connection
c = Connection('web1')
c.put('myfiles.tgz', '/opt/mydata')
c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')

def upload_and_unpack(c):
    c.put('myfiles.tgz', '/opt/mydata')
    c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')

多服务器的情况,这个单列出来

  • 最直观的方法是遍历一个connection列表
>>> from fabric import Connection
>>> for host in ('web1', 'web2', 'mac1'):
>>>     result = Connection(host).run('uname -s')
...     print("{}: {}".format(host, result.stdout.strip()))
...
...
web1: Linux
web2: Linux
mac1: Darwin
  • fabric提供了一个更好的解决办法,把所有的对象放到一个Group里面。
    • Group和SerialGroup(Subclass of Group which executes in simple, serial fashion. New in version 2.0.)
    • 返回的结果看,Connection返回单个的Result对象,Group返回GroupResult对象,dict-like
In [18]: results = Group('peter@192.168.9.218', 'peter@192.168.9.208').run('uname -s')
Linux
Linux

In [19]: for conn, result in results.items():
    ...:     print("{0.host}: {1.stdout}".format(conn, result))
    ...:
192.168.9.218: Linux
192.168.9.208: Linux
  • Group对象来执行的操作会对所有服务器生效,但是这里官方教程的pool.put会得到一个AttributeError: 'SerialGroup' object has no attribute 'put'错误,已在github提交issue
from fabric import SerialGroup as Group
pool = Group('web1', 'web2', 'web3')
pool.put('myfiles.tgz', '/opt/mydata')
pool.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')

fab cli tool

封装了Invoke的CLI功能,能够很快的在多个机器上运行tasks。

  • 将之前的任务编程一个fab task来运行,要定义一个fabfile.py

实际部署

这里参考的一篇博客,使用的是python2版本的fabric,仅供学习思路。

流程

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

fabfile.py

位于项目(真正的代码项目,而非包含各种配置的部署项目)的根目录,

python2 fabric mac路径问题

I got same problem. If I run brew link --overwrite python2. There was still zsh: /usr/local/bin//fab: bad interpreter: /usr/local/opt/python/bin/python2.7: no such file or directory.

方案1

  • 如果python本来指向了Celler的python3.6,那么先移除这个链接
  • 然后在用过ln -s python2 python软连接
  • 如果没有python2可以用brew reinstall python2,之后就能查看到

方案2

cd /usr/local/opt/
mv python2 python

Solved it! Now we can use python2 version fabric.