こんにちは。CTOの馬場です。
みんな大好きFabricのTipsです。
Welcome to Fabric! -- Fabric documentation
よくデプロイツールとして紹介されますが、 自動化のためのPython+SSH+コマンド実行フレームワークとして柔軟に使えて超便利です。
基本的には
- 手元でのコマンド実行
- SSHごしのリモートサーバでのコマンド実行
- SSHごしのリモートサーバでsudoしてコマンド実行
ができるツールなのですが、使い方の例を紹介します。
間違いなどあればお近くのハートビーツ社員か @netmarkjp に教えていただけると嬉しいです。
Python 2.7.10 + Fabric 1.10.2 + Paramiko 1.15.2で動作確認しました。
複数のサーバに対して同じユーザ・パスワードでログインする
ユーザ名やパスワードを一括指定できます。
鍵認証の場合は key
や key_filename
を利用しましょう。
# coding: utf-8
from fabric.api import run
from fabric.api import env
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
def go_fab():
run("hostname")
run("whoami")
複数のサーバに対してそれぞれ異なるユーザ・パスワードでログインする
env.passwords
のキーに USER@HOST:PORT
を全部書くのがミソです。
鍵認証の場合は key
や key_filename
を利用しましょう。
# coding: utf-8
from fabric.api import run
from fabric.api import env
env.hosts = ["user1@192.168.56.102", "user2@192.168.56.103"]
env.passwords = {"user1@192.168.56.102:22": "password1",
"user2@192.168.56.103:22": "password2", }
def go_fab():
run("hostname")
run("whoami")
コマンドのリターンコードが成功じゃなくても無視して進める
warn_only
を使います。
ただし特定コマンド実行のみの場合は次項の一部のみこの設定を適用する方法を参照してください。 またコマンド実行成功と判断するリターンコードを変更することもできるので次々項を参照してください。
# coding: utf-8
from fabric.api import run
from fabric.api import env
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.warn_only = True
def go_fab():
run("ls /none")
run("hostname")
run("whoami")
コマンドのリターンコードが成功じゃなくても無視して進める(特定コマンド実行のみ)
# coding: utf-8
from fabric.api import env
from fabric.api import run
from fabric.api import settings
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
def go_fab():
with settings(warn_only=True):
run("ls /none")
run("hostname")
run("whoami")
コマンド実行成功と判断するコマンドのリターンコードを指定する
rsyncとかで使いますね。
# coding: utf-8
from fabric.api import env
from fabric.api import run
from fabric.api import settings
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
def go_fab():
with settings(ok_ret_codes=[0, 24]):
run("exit 0")
run("exit 24")
run("hostname")
run("whoami")
必ず並列実行する
parallel
を使います。
なお一部タスクだけ並列実行する/しない場合は @parallel
や @serial
デコレータを使います。
# coding: utf-8
from fabric.api import run
from fabric.api import env
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.parallel = True
def go_fab():
run("hostname")
run("whoami")
sshのconfigを使う
use_ssh_config
を True
にすると使えます。
configはデフォルトで $HOME/.ssh/config
ですが、
ssh_config_path
で変更できます。
今のところ
Use
,
Port
,
HostName
,
IdentityFile
,
ForwardAgent
,
ProxyCommand
に対応しているようです。
ただし ProxyCommand
については
fabric側の gateway
( env.gateway
)を使ったほうが楽そうです(次項参照)。
# coding: utf-8
from fabric.api import run
from fabric.api import env
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.password = "password0"
env.ssh_config_path = "config.local"
env.use_ssh_config = True
def go_fab():
run("hostname")
run("whoami")
踏み台経由でSSHする
fabric側の gateway
( env.gateway
)を使います。
# coding: utf-8
from fabric.api import run
from fabric.api import env
# [gateway:192.168.56.102] -> [dest:192.168.56.102]
env.hosts = ["192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.gateway = "192.168.56.102"
def go_fab():
run("hostname")
run("whoami")
run("w")
SSHでつながらないホストは無視する
SSH接続タイムアウトも指定できます
# coding: utf-8
from fabric.api import run
from fabric.api import env
env.hosts = ["192.168.56.101", "192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.skip_bad_hosts = True
env.timeout = 3
def go_fab():
run("hostname")
run("whoami")
コマンド実行のタイムアウトを指定する
command_timeout
で指定します。
デフォルトは無限に待ちます。
長く待つ場合は次項のSSH接続キープアライブも設定しましょう。
# coding: utf-8
from fabric.api import run
from fabric.api import env
from fabric.api import settings
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.command_timeout = 1
def go_fab():
with settings(command_timeout=3):
run("sleep 2")
run("echo sleep 2")
run("sleep 4")
run("echo sleep 4")
run("hostname")
run("whoami")
SSH接続キープアライブを設定する
デフォルトは0(no keepalive)。
sshで言うところの ServerAliveInterval=60
にするなら env.keepalive = 60
です。
# coding: utf-8
from fabric.api import run
from fabric.api import env
env.hosts = ["192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.keepalive = 60
def go_fab():
run("hostname")
run("whoami")
並列実行した結果を接続先ホストごとにまとめる
ちょっとややこしいですが execute
と @runs_once
デコレータ、 @parallel
デコレータを使います。
execute()
の戻り値はdictになります。キーがホスト名、returnされたものが値です。
# coding: utf-8
from fabric.api import run
from fabric.api import env
from fabric.api import execute
from fabric.decorators import parallel
from fabric.decorators import runs_once
env.hosts = ["192.168.56.101", "192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.command_timeout = 3600
env.keepalive = 60
env.skip_bad_hosts = True
env.timeout = 3
@runs_once
def go_fab():
result = execute(_go_fab)
for k, v in sorted(result.iteritems()):
print "result:", k, v
@parallel
def _go_fab():
result = []
result.append(run("hostname"))
result.append(run("whoami"))
return result
リモートで実行した大量の結果を接続先ホストごとにまとめて手元に持ってくる
リモートサーバでfindしたりして出力された大量の結果を手元にもってくる方法です。 runやsudoの戻り値を文字列で保持するのもちょっとな...と思った時にどうぞ。
# coding: utf-8
from fabric.api import run
from fabric.api import env
from fabric.api import get
env.hosts = ["192.168.56.101", "192.168.56.102", "192.168.56.103"]
env.user = "user0"
env.password = "password0"
env.command_timeout = 3600
env.keepalive = 60
env.skip_bad_hosts = True
env.timeout = 3
env.parallel = True
def go_fab():
temp = run("mktemp")
run("for i in {1..10000}; do echo $RANDOM >>%s ; done" % (temp))
run("hostname >>%s" % (temp))
run("whoami >>%s" % (temp))
# 取得後のファイルパスを指定する場合は第二引数を指定。
# 例: get(temp, "/tmp/%s.log" % (env.host_string))
# デフォルト: env.host_string/取得元と同じファイル名 (ディレクトリは自動作成される)
get(temp)