俺達のFabric 〜余計な仕事はFabricに任せよう〜 | サイバーエージェント 公式エンジニアブログ

どうも、ガールフレンド(仮)で窓際エンジニアをやっていたり、ウチの姫さまがいちばんカワイイで窓際エンジニアをやっていたりする Wataru です。(PCはmacです) 窓が近いとエアコン戦争が激しいわけですが、やっと秋も近づいてきて戦争も終わりが見えてきたのでしょうか?残暑お見舞い申し上げます。え?遅い?


さて、今回はFabricの紹介をさせて頂きたいと思います。



もしあなたが千手観音のようにたくさんの手を持ち、サーバのオペレーションをできるとしたら、どうでしょう? そう、そんな神様のような事をできるのがFabricというツールです。

Fabricって何?

まあまあ大げさなことを書きましたが、Fabricはコマンドラインのツールです。Pythonでできていて、SSHの作業を効率化してくれるものです。アプリケーションのデプロイや管理がすごく楽になるんです。


リモートやローカルのシェルコマンドを実行したり、ファイルのアップロードやダウンロード、それらを複数サーバに並列実行など、様々な便利な機能があります。


例えばローカルのJavascriptをGrant等で色々ゴニョゴニョして、それをリモートのサーバにアップロードして、Apache再起動なんてことを複数サーバ同時に実行などもコマンド一発で実行可能にできるのです。


まーApache再起動する必要があるかどうかはちょっと置いておいて、複数のサーバ間でのコマンドをうまいことつなぎあわせて動かしていく事ができるのです。


同様のツールでCapistranoとかtomahawk等がありますが、シンプルさやシェルスクリプトからの移行の手軽さなどからFabricが私は好きです。

インストール

さて、早速インストールしてみましょう。FabricはPythonで動くのでまずPythonをインストールして下さい。またPython2.5~2.7で動かす必要があるのでPythonはpyenvとvirtualenvで管理するのがお勧めです。

macの場合

brewでサクッと入ります。

$ brew install pyenv pyenv-virtualenv

インストールしたら下記を.zshrcか.bashrcに書きを追記して下さい。

export PYENV_ROOT="${HOME}/.pyenv"
if [ -d "${PYENV_ROOT}" ]; then
    export PATH=${PYENV_ROOT}/bin:$PATH
    eval "$(pyenv init -)"
fi

上記の設定をsourceコマンドなどで反映しておいて下さい。


ここから一気にfabricをインストールしますね。

$ pyenv install 2.7.8
$ pyenv virtualenv 2.7.8 fabric-operation
$ source ~/.zshrc
$ pip install fabric
$ source ~/.zshrc

これでインストール完了です。

CentOSの場合

pyenvとvirtualenvのインストール方法が少し異なります。


CentOSの場合はbrewが無いので、gitでcloneします。

$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv
$ git clone git://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins

あとはmacの.zshrcの書き換えの部分から同じです。

windowsの場合


ちょっとわかりません・・・

使ってみよう

まずおまじないとして適当なディレクトリを作って、そこに.python-versionというファイルを作りましょう。

fabric-operation

こんな風に書いておくと先ほどインストールしたPython2.7.8が選ばれるようになります。


Fabricは基本的にはfabfile.pyというファイルを基準に動きます。vimで言うところの.vimrcみたいなものでしょうか?(ちょっとちがくね?) とにかくまずfabfile.pyを作っていきます。簡単な例を一つ。


from fabric.api import *
 
def teach_about_woman():
    with settings(warn_only=True):
        run('man woman')

これは女性の扱い方のマニュアルを見せてくれと頼むコマンドです。


あ、そうそう、fabricもvimとかzshとかと同じように~/.fabricrcっていう設定ファイルと作るとデフォルトで呼び出してくれます。とりあえずSSHで利用するパスワードとかユーザとか書いとくといいかもしれません。

user = orenonamae
password = orenohimitu

作成したら、fabfile.pyと同じ場所で次のコマンドを実行してみてください。


$ fab -H localhost,orenoserver teach_about_woman

結果

$ fab -H orenoserver,anokonoserver teach_about_woman
[orenoserver] Executing task 'teach_about_woman'
[orenoserver] run: man woman
[orenoserver] out: No manual entry for woman
[orenoserver] out:
 
Warning: run() received nonzero return code 1 while executing 'man woman'!
 
[anokonoserver] Executing task 'teach_about_woman'
[anokonoserver] run: man woman
[anokonoserver] out: No manual entry for woman
[anokonoserver] out:
 
Warning: run() received nonzero return code 1 while executing 'man woman'!
 
Done.
Disconnecting from anokonoserver... done.
Disconnecting from orenoserver... done.

どうでしょう?それぞれのホストからそんなもんねーよって返ってきましたか? ばばばっと指定したホストでコマンドが実行されたのがわかったかと思います。 このように複数のホストに対して指定したコマンドを実行できるのです。


ここまでで作ったファイルを一応まとめておきます。

~/
├── .fabricrc
└── fabric
    ├── .python-version
    └── fabfile.py

もうすこし実践的に

なんだよ、それだけかよと思ったあなた。 いやいやいや、こんなもんじゃありませんよ!!もう少し実践的なやつをやってみましょう。


次の例はWebサーバのhostsファイルを書き換えて、Apacheを再起動するものです。


from fabric.api import *
 
@parallel(pool_size=2)
def edit_hosts():
    sudo('echo "# Do not remove the following line, or various programs" > /etc/hosts')
    sudo('echo "# that require network functionality will fail." >> /etc/hosts')
    sudo('echo "127.0.0.1   localhost.localdomain localhost" >> /etc/hosts')
    sudo('echo "::1 localhost6.localdomain6 localhost6" >> /etc/hosts')
    sudo('echo "" >> /etc/hosts')
    sudo('echo "{host}  `hostname`" >> /etc/hosts'.format(**env))
    sudo('echo "" >> /etc/hosts')
    sudo('echo "# DB" >> /etc/hosts')
    sudo('echo "192.0.2.0   anokonopc" >> /etc/hosts')
 
    sudo('/usr/local/apache/bin/apachectl -k graceful')

echoでゴニョゴニョ書いててちょっとダサい感じもしますが、'echo "{host} hostname" >> /etc/hosts'.format(**env)こんなふうに自分のIPに対して自分のHost名を記述するなんてことも指定できます。 この辺をうまく使うと、偶数のサーバは特定のIPに向ける、等といったことも簡単にできます。


こんな感じで実行してみましょう。


$ fab -H orenoserver,anokonoserver edit_hosts
 
[orenoserver] Executing task 'edit_hosts'
[anokonoserver] Executing task 'edit_hosts'
・
・
・
[orenoserver] out:
[anokonoserver] out:
 
Done.

バババババっと実行結果が表示され、複数のサーバが並列で処理した事が確認できたと思います。 @parallelを使うと指定したHostへの処理が並列に実行されます。pool_sizeを指定することで最大同時実行数を制限することもできるので、例えばサーバが10台あるのを一気に実行するのはちょっとという場合は、2台づつ実行等と指定することもできるわけです。

応用?

Fabricは単体で使っても十分に便利な事がおわかり頂けたかと思います。 このFabricをJenkinsと組み合わせることで複数サーバへのアプリのデプロイやファイルの配布等といった使い方もできます。


実際、弊社のガールフレンド(仮)というゲームでは、サーバに対する運用オペレーションは開発環境・本番環境含め、ほとんどのオペレーションをJenkinsとfabricを組み合わせた形で行えるようになっています。 このようにすることで運用面で開発、ステージング、本番環境をできるだけ一致させた状態を保つ事ができ、Twelve-Factor Appの思想に近づけると考えています。


Fabricを使っていく規模を広げていくと、fabfile.pyだけではファイルが肥大化していってしまいます。ガールフレンド(仮)ではfabfile.pyから他のpythonファイルをimportすることで視認性をあげています。参考までにファイル構成をご紹介します。


.
├── README.md
├── a10_change_vip
│   ├── vipa_change_prd.list
│   ├── vipb_change_prd.list
│   └── change_mainte_a10.sh
├── fabfile.py
├── git.py
├── id_rsa-cy_tomcat
├── img.py
├── jq
├── requirements.txt
├── sensu.py
├── server.json
├── server_list.py
├── test.py
└── voice.py

git.pyやvoice.py等と用途ごとにpythonのファイルを分けて管理しています。またjqコマンドや組み合わせて使うshellスクリプトなども同じプロジェクトとして管理しています。 server.jsonというファイルで一部のサーバのIPを管理していたりもしていますが、全体のサーバのIPは別のサーバ管理用のWebベースのツール(独自作成 by 佐野さん)のものを使って、そこからAPI経由でfabricが取得するような仕組みをとっています。サーバの追加や除外などの管理が一元化されているのです。下記がその画面です。


またHubotと連携さて、障害時にスマホから緊急対応できるような仕組みを最近では導入したみたいですね^^ まだまだ使い方は無限大なのでしょうか。


Fabricはけして新しいツールではありませんが、古いツールにはフォースが宿っています。 そう、マスター・ヨーダも「フォースはお前と共にいるのだ、いかなるときも」と言っておりました。


まだ使ったことの無い方は是非一度Fabricを使ってみてはいかがでしょうか?