入门¶
欢迎!本教程重点介绍Fabric的核心功能;有关更多详细信息,请参阅中的链接或文档索引,其中包含指向概念和API文档部分的链接。
关于 imports 的说明¶
Fabric组合了几个其他库,并在顶部提供自己的层;用户代码通常从 fabric
包,但有时直接从 invoke
或 paramiko
也是:
Invoke 实现CLI解析、任务组织和shell命令执行(通用框架加上本地命令的特定实现)。
- 任何不特定于远程系统的东西都倾向于活在invoke中,并且它经常被不需要任何远程功能的程序员单独使用。
- Fabric用户经常导入Invoke对象,在这种情况下,Fabric本身不需要子类化或修改Invoke提供的内容。
Paramiko 实现低/中级别的ssh功能-ssh和sftp会话、密钥管理等。
- Fabric 主要在引擎盖下使用;用户很少直接从帕拉米科进口。
Fabric 将其他库粘合在一起,并提供自己的高级对象,例如:
- 对invoke的上下文和命令运行程序类进行子类化,将它们包装在paramiko级别的原语中;
- 使用paramiko扩展invoke的配置系统
ssh_config
解析机制; - 实现新的高级原语,如端口转发上下文管理器。(这些可能会及时向下迁移到帕拉米科。)
通过连接和 run
¶
Fabric最基本的用途是通过ssh在远程系统上执行shell命令,然后(可选)询问结果。默认情况下,远程程序的输出直接打印到终端, and 捕获。一个基本示例:
>>> from fabric import Connection
>>> c = Connection('web1')
>>> result = c.run('uname -s')
Linux
>>> result.stdout.strip() == 'Linux'
True
>>> result.exited
0
>>> result.ok
True
>>> result.command
'uname -s'
>>> result.connection
<Connection host=web1>
>>> result.connection.host
'web1'
相遇 Connection
它表示一个ssh连接并提供fabric的api核心,例如 run
. Connection
对象至少需要一个主机名才能成功创建,并且可以通过用户名和/或端口号进一步参数化。您可以通过args/kwargs明确地给出这些:
Connection(host='web1', user='deploy', port=2202)
或通过填充 [user@]host[:port]
字符串输入 host
论点(尽管这纯粹是为了方便;每当出现歧义时总是使用Kwarg!)::
Connection('deploy@web1:2202')
Connection
对象的方法(如 run
)通常返回 invoke.runners.Result
(或其子类)公开上面看到的各种细节:请求的内容、远程操作发生时发生的内容以及最终结果。
注解
通过使用 connect_kwargs argument .
通过自动响应的超级用户权限¶
需要作为远程系统的超级用户运行吗?你可以调用 sudo
程序通过 run
,并且(如果远程系统没有配置无密码sudo)手动响应密码提示,如下所示。(注意我们需要如何请求远程伪终端;大多数 sudo
否则,在密码提示时,实现会变得很坏。)
>>> from fabric import Connection
>>> c = Connection('db1')
>>> c.run('sudo useradd mydbuser', pty=True)
[sudo] password:
<Result cmd='sudo useradd mydbuser' exited=0>
>>> c.run('id -u mydbuser')
1001
<Result cmd='id -u mydbuser' exited=0>
每次使用密码都会变老;谢天谢地,invoke强大的命令执行功能包括 auto-respond 用预先定义的输入对输出进行编程。我们可以用这个 sudo
:
>>> 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>
很难在代码片段中显示,但当执行上述操作时,用户不需要键入任何内容; mypassword
已自动发送到远程程序。容易多了!
这个 sudo
帮手¶
使用观察者/响应者在这里工作得很好,但是每次都要设置很多样板文件-特别是在现实世界中,需要更多的工作来检测失败/不正确的密码。
为了帮助实现这一点,invoke提供了 Context.sudo
为您处理大多数样板文件的方法(如 Connection
子类 Context
,免费获取此方法。) sudo
不会做任何用户自己不能做的事情——但和往常一样,常见问题最好通过共享解决方案来解决。
用户需要做的就是确保 sudo.password
configuration value 填写(通过配置文件、环境变量或 --prompt-for-sudo-password
) Connection.sudo
处理其余部分。为了清晰起见,下面是一个示例,库/shell用户执行自己的 getpass
-基于密码提示:
>>> 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>
在本例中,我们在运行时预先填写了sudo密码;在实际情况下,您也可以通过配置系统(可能使用环境变量,以避免污染配置文件)提供该密码,或者理想情况下使用机密管理系统。
传输文件¶
除了shell命令执行之外,ssh连接的另一个常见用法是文件传输; Connection.put
和 Connection.get
存在以满足这一需求。例如,假设您有一个要上载的存档文件:
>>> 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/
这些方法通常遵循 cp
and scp
/sftp
在参数评估方面——例如,在上面的代码片段中,我们省略了远程路径参数的文件名部分。
多个动作¶
一行程序是很好的例子,但并不总是实际的用例——通常需要多个步骤来完成任何有趣的事情。在最基本的层面上,您可以通过调用 Connection
方法多次:
from fabric import Connection
c = Connection('web1')
c.put('myfiles.tgz', '/opt/mydata')
c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')
您可以(但不必)将这些代码块转换为函数,并用 Connection
来自调用方的对象,以鼓励重用:
def upload_and_unpack(c):
c.put('myfiles.tgz', '/opt/mydata')
c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')
正如您将在下面看到的,这些函数可以被传递给其他API方法,以支持更复杂的用例。
多个服务器¶
大多数实际用例涉及在多个服务器上执行操作。简单的方法可以是遍历 Connection
争论(或) Connection
对象本身,也许通过 map
):
>>> 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
这种方法是可行的,但是随着用例变得越来越复杂,将主机集合视为单个对象可能很有用。进入 Group
一个类包装一个或多个 Connection
对象并提供类似的API;具体来说,您需要使用其具体的子类之一,如 SerialGroup
或 ThreadingGroup
.
上一个示例,使用 Group
(SerialGroup
具体来说),如下所示:
>>> from fabric import SerialGroup as Group
>>> results = Group('web1', 'web2', 'mac1').run('uname -s')
>>> print(results)
<GroupResult: {
<Connection 'web1'>: <CommandResult 'uname -s'>,
<Connection 'web2'>: <CommandResult 'uname -s'>,
<Connection 'mac1'>: <CommandResult 'uname -s'>,
}>
>>> for connection, result in results.items():
... print("{0.host}: {1.stdout}".format(connection, result))
...
...
web1: Linux
web2: Linux
mac1: Darwin
Connection
方法返回单个 Result
对象(例如) fabric.runners.Result
) Group
方法返回 GroupResult
- dict
-类似于提供对每个连接结果以及整个运行的元数据的访问的对象。
当 Group
遇到错误, GroupResult
轻轻地包裹在 GroupException
,它被提升。因此,聚合行为类似于个体的聚合行为。 Connection
方法,返回成功时的值或在失败时引发异常。
把所有的东西放在一起¶
最后,我们得出了最现实的用例:您有大量的命令和/或文件传输,您希望将其应用到多个服务器上。你 能够 使用倍数 Group
要执行此操作的方法调用::
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')
一旦逻辑变得必要,这种方法就不适用了——例如,如果您只想在以下情况下执行上面的复制和取消标记 /opt/mydata
是空的。执行这种检查需要对每台服务器执行。
你可以通过使用 Connection
对象(尽管这放弃了使用 Groups
):
from fabric import Connection
for host in ('web1', 'web2', 'web3'):
c = Connection(host)
if c.run('test -f /opt/mydata/myfile', warn=True).failed:
c.put('myfiles.tgz', '/opt/mydata')
c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')
或者,还记得我们如何在前面的例子中使用函数吗?你可以改走那条路:
from fabric import SerialGroup as Group
def upload_and_unpack(c):
if c.run('test -f /opt/mydata/myfile', warn=True).failed:
c.put('myfiles.tgz', '/opt/mydata')
c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')
for connection in Group('web1', 'web2', 'web3'):
upload_and_unpack(connection)
最后一种方法所缺少的唯一便利就是 Group.run
-如果你想追踪所有 upload_and_unpack
作为一个集合,你必须自己去做。希望将来的功能版本能在这个空间中获得更多的信息!
附录: fab
命令行工具¶
从shell运行结构代码通常很有用,例如部署应用程序或在任意服务器上运行sysadmin作业。你可以用普通的 Invoke tasks 其中包含结构库代码,但另一种选择是结构自己的“面向网络”工具, fab
.
fab
使用主机选择等功能包装Invoke的CLI机制,使您可以在各种服务器上快速运行任务,而无需定义 host
对你所有的任务或类似的事情都不感兴趣。
注解
这种模式是Fabric1.x的主要API;从2.0开始,它只是一种方便。每当您的用例不在这些快捷方式的范围内时,应该可以很容易地直接恢复到库API(无论是否包含了Invoke不那么固执己见的CLI任务)。
对于最后一个代码示例,让我们将前面的示例调整为 fab
任务模块已调用 fabfile.py
::
from invoke import task
@task
def upload_and_unpack(c):
if c.run('test -f /opt/mydata/myfile', warn=True).failed:
c.put('myfiles.tgz', '/opt/mydata')
c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz')
不难——我们所做的只是将临时任务功能复制到一个文件中,然后在文件上贴上一个装饰。 task
通知CLI机器在命令行上公开任务::
$ fab --list
Available tasks:
upload_and_unpack
然后,什么时候 fab
实际上,它调用一个任务,它知道如何将控制目标服务器的参数缝合在一起,并为每台服务器运行一次该任务。要在单个服务器上运行该任务,请执行以下操作:
$ fab -H web1 upload_and_unpack
当这种情况发生时, c
任务内部被有效地设置为 Connection("web1")
-和前面的例子一样。类似地,可以给多个主机,这些主机多次运行任务,每次使用不同的主机 Connection
上缴实例:
$ fab -H web1,web2,web3 upload_and_unpack