CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: Fabric をスクリプトに組み込んで使う

Fabric は Python で書かれたデプロイやオペレーションを自動化するためのツール。 Fabric では、タスクと呼ばれるオペレーション内容も Python で書く。

今回は、普段ならコマンドラインツールから使うことが多い Fabric を Python のスクリプトに組み込んで使う方法について書く。 尚、Fabric はリモートのホストに接続して使うことが多いため、そのホストとして Vagrant を使って Ubuntu 16.04 LTS のマシンを用意した。

使った環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1004
$ python --version
Python 2.7.10

インストール

まずは pip を使って Fabric をインストールしておく。

$ pip install fabric

Vagrant で仮想マシンを用意する

ここでは Vagrant がすでにインストール済みであることを想定する。 もし、入っていないときは公式サイトのバイナリか Homebrew Cask などを使ってインストールしてほしい。

まずは Vagrant の設定ファイル (Vagrantfile) を用意する。 イメージは Chef 社の提供する bento リポジトリにあるものを使った。

$ cat << 'EOF' > Vagrantfile
# -*- mode: ruby -*-
Vagrant.configure("2") do |config|
  config.vm.box = "bento/ubuntu-16.04"
  config.vm.provider "virtualbox" do |vb|
    vb.cpus = "2"
    vb.memory = "1024"
  end
end
EOF

仮想マシンを起動する。

$ vagrant up

起動できたら vagrant ssh-config コマンドを実行しよう。 ここで Port の項目を確認しておく。 おそらく通常は 2222 が割り振られているはず。 ただ、異なる場合には後述する手順で指定するポート番号を変更する必要がある。

$ vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/amedama/Documents/vagrant/ubuntu1604/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

これは OpenSSH の設定ファイルと同じフォーマットになっている。 上記の設定から、先ほど Vagrant で作った仮想マシンには 127.0.0.1:2222 に SSH することでログインできることがわかる。

さて、これで Fabric を使う対象となる仮想マシンの準備ができた。

Fabric の設定ファイルを用意する

Fabric はデフォルトで fabfile という名前のモジュール (またはパッケージ) を、タスクが記述された処理対象として扱う。 なので fabfile.py という名前でファイルを用意しよう。 Python ではスクリプトファイル (*.py) とモジュールが 1:1 で対応している。 例えば fabfile.py であれば Python インタプリタはそれを fabfile というモジュールとして扱うということ。

それでは、今回の主役となる fabfile.py を用意する。 これには task() 関数として Fabric のタスクが定義されている。 この task() 関数はリモートのホスト上で、通常ユーザ権限を使って uptime コマンドを実行することを示している。 また、main() 関数では task 関数を引数にして execute() 関数を実行することもわかる。

$ cat << 'EOF' > fabfile.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from fabric.api import run
from fabric.api import execute

def task():
    run('uptime')


def main():
    execute(task, hosts=['vagrant@127.0.0.1:2222'])


if __name__ == '__main__':
    main()
EOF

普段の使い方

Fabric がインストールされていると fab コマンドが使えるようになる。 この fab コマンド経由でタスクを実行するのが Fabric の普段の使い方だ。

先ほど用意した fabfile.py も fab コマンド経由で実行してみよう。 -H オプションでホスト名、--port オプションでポート名、-u オプションでユーザ名を指定する。 最後の引数は実行するタスク名だ。 仮想マシンのログインパスワードを聞かれるので「vagrant」答えよう。

$ fab -H localhost --port 2222 -u vagrant task
[localhost] Executing task 'task'
[localhost] run: uptime
[localhost] Login password for 'vagrant':
[localhost] out:  10:46:20 up 6 min,  1 user,  load average: 0.00, 0.05, 0.02
[localhost] out:


Done.
Disconnecting from localhost:2222... done.

上手く実行できた。

Python スクリプトとして実行する

先ほどは fab コマンドからタスクを実行した。 今度は Python スクリプトからタスクを実行してみよう。

これは、ただ単に python コマンドで fabfile.py ファイルを実行するだけだ。 先ほどと同じようにログインパスワードを聞かれるので「vagrant」と答える。

$ python fabfile.py
[vagrant@127.0.0.1:2222] Executing task 'task'
[vagrant@127.0.0.1:2222] run: uptime
[vagrant@127.0.0.1:2222] Login password for 'vagrant':
[vagrant@127.0.0.1:2222] out:  10:48:08 up 8 min,  1 user,  load average: 0.00, 0.03, 0.01
[vagrant@127.0.0.1:2222] out:

ちゃんと実行できたことがわかる。

実行した fabfile.py の内容をもう一度掲載しておく。 Python スクリプトとして実行したときは、この中の main() 関数が実行される。 そして、その中では execute() 関数にタスクとログイン名、ホスト名、ポート番号を指定していた。 つまり、組み込みで実行したいときはこの execute() 関数を使えば良いということが分かる。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from fabric.api import run
from fabric.api import execute

def task():
    run('uptime')


def main():
    execute(task, hosts=['vagrant@127.0.0.1:2222'])


if __name__ == '__main__':
    main()

環境を設定する

先ほどの例ではパスワードを手動で入力しなければいけなかった。 今度は、そこも自動化してみよう。

次の fabfile.py ではパスワードの手動入力が不要になっている。 ホスト名やユーザ名、パスワードといった Fabric の環境設定は fabric.api.env を指定すれば良い。

$ cat << 'EOF' > fabfile.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from fabric.api import run
from fabric.api import execute
from fabric.api import env

def task():
    run('uptime')


def main():
    env.hosts = ['127.0.0.1:2222']
    env.user = 'vagrant'
    env.password = 'vagrant'
    execute(task)


if __name__ == '__main__':
    main()
EOF

実行してみよう。

$ python fabfile.py
[127.0.0.1:2222] Executing task 'task'
[127.0.0.1:2222] run: uptime
[127.0.0.1:2222] out:  10:48:44 up 8 min,  1 user,  load average: 0.00, 0.03, 0.00
[127.0.0.1:2222] out:

ちゃんと実行できて、今度はパスワードを聞かれることもない。

まとめ

Fabric を Python スクリプトに組み込んで使うときは fabric.api.execute() 関数を使おう。

追記

このやり方にはちょっとした注意点があるため、次の記事で補足している。

blog.amedama.jp