2日間Fabricを触った俺が

色々解説してみる
@airtoxin
Fabric?
About Fabric
Python(2.5-2.7)製のデプロイ・システ
ムタスク実行ツール。
同様のツールにRuby製のCapistranoが
ある。
Install Fabric
Pythonとpipが入っているなら
$ (sudo) pip install Fabric
#!/usr/bin/env	
  python	
  
#	
  -­‐*-­‐	
  coding:	
  utf-­‐8	
  -­‐*-­‐	
  
from	
  fabric.api	
  import	
  env,	
  local,	
  run	
  
import	
  difflib	
  
env.hosts	
  =	
  [	
  ‘mydevelopment.jp'	
  ]	
  
env.key_filename	
  =	
  [	
  '~/.ssh/mydevelopment.rsa'	
  ]	
  
env.user	
  =	
  'airtoxin'	
  
env.port	
  =	
  9724	
  
def	
  get_date():	
  
	
  	
  	
  	
  remote_date	
  =	
  run(	
  'date'	
  )	
  
	
  	
  	
  	
  local_date	
  	
  =	
  local(	
  'date',	
  capture=True	
  )	
  
	
  	
  	
  	
  for	
  line	
  in	
  difflib.ndiff(	
  [	
  remote_date	
  ],	
  [	
  local_date	
  ]	
  ):	
  
	
  	
  	
  	
  	
  	
  	
  	
  print	
  line
Example of Fabric
fabfile.py
サーバー実行コマンド
タスク名
ローカル実行コマンド
Pythonで実行結果を処理
Run Fabric
fabfile.pyのあるディレクトリで
$ fab get_date(タスク名)
タスクリスト
定義タスクの一覧確認は
$ fab --list
コマンド
• リモートでコマンドを実行 → run(‘command’)
• ローカルでコマンドを実行 → local(‘command’)
• sudoでコマンドを実行 → sudo(‘command’)
• ディレクトリの移動 → with cd(‘dirname’):
• ファイルアップロード → put(‘localpath’, ‘remotepath’)
• ファイルダウンロード → get(‘remotepath’, ‘localpath’)
コマンド
大体なんとかなる。
というか
local, run, sudoがあるので実質何でも
できる。
それって…
Shellでいいじゃん!!
正しい
Shell vs Fabric
Shell
動作環境周りで考えることがほぼ無し。
サーバー操作の共通言語として多くのエンジニア
が扱えることが期待出来る。
実際には可読性が低く、メンテナンス性が悪い。
シェル芸の乱立。
属人化したサーバー保守・デプロイのコード。い
ざ修正しようとしても手がつけられない。
Fabric
可読性の高いと言われるPythonで程よくラップしたコード
が書ける。メンテナンス性の向上。
文字列、配列操作に長けているのでコマンドの返り値を処
理するのが簡単。さらにPythonのライブラリも使える。
導入コストがほぼゼロ。
デコレーターとリスト内包表記さえ知れば問題なく使える
学習コストの低さ。
Shell vs Fabric
Shell Fabric
導入コスト
-
プリインストール
低∼中程度
Pythonのpipによる
可読性
低い
文法, ワンライナー
高い
Pythonのコード
文字列操作
難∼普通程度
perl, awk等を利用
楽
ライブラリ等を利用
学習コスト
無し∼高い
普段使い/初心者
低い∼普通
リスト内包表記, decorator
情報量
多い
言わずもがな
中
ほぼ枯れている
Shell vs Fabric
Shell Fabric
導入コスト
-
プリインストール
低∼中程度
Pythonのpipによる
可読性
低い
文法, ワンライナー
高い
Pythonのコード
文字列操作
難∼普通程度
perl, awk等を利用
楽
ライブラリ等を利用
学習コスト
無し∼高い
普段使い/初心者
低い∼普通
リスト内包表記, decorator
情報量
多い
言わずもがな
中
ほぼ枯れている
後の事を考えるならFabric
Fabricサイコー
Shellでやってるの?
それって…
Fabricでいいじゃん!!
逆にね
実践Fabric
1つのサーバーに対して特有の操作を行うことは少
ない。
• 多数のサーバーに対して同じコマンドを実行
• 段階的,多段的なデプロイ
• ユーザーを変えたサーバー接続
など
実践Fabric
まずはfabfileの分割から
Example of Fabric
fabfile.py
サーバー実行コマンド
タスク名
ローカル実行コマンド
Pythonで実行結果を処理
#!/usr/bin/env	
  python	
  
#	
  -­‐*-­‐	
  coding:	
  utf-­‐8	
  -­‐*-­‐	
  
from	
  fabric.api	
  import	
  env,	
  local,	
  run	
  
import	
  difflib	
  
env.hosts	
  =	
  [	
  ‘mydevelopment.jp'	
  ]	
  
env.key_filename	
  =	
  [	
  '~/.ssh/mydevelopment.rsa'	
  ]	
  
env.user	
  =	
  'airtoxin'	
  
env.port	
  =	
  9724	
  
def	
  get_date():	
  
	
  	
  	
  	
  remote_date	
  =	
  run(	
  'date'	
  )	
  
	
  	
  	
  	
  local_date	
  	
  =	
  local(	
  'date',	
  capture=True	
  )	
  
	
  	
  	
  	
  for	
  line	
  in	
  difflib.ndiff(	
  [	
  remote_date	
  ],	
  [	
  local_date	
  ]	
  ):	
  
	
  	
  	
  	
  	
  	
  	
  	
  print	
  line
fabfileの分割
fabfileをディレクトリ化しPythonのラ
イブラリとして定義することが可能。
操作のまとまり毎にファイル分割して
見通しをよくする。
fabfileディレクトリ内のファイ
ルにタスクを定義。
__init__.pyで各ファイルをイン
ポート
操作毎にファイルを分割。必要
ならばサーバーへの接続情報の
みを定義するタスクも書く。
fabfile構成
.
├── .gitignore
└── fabfile
├── __init__.py
├── deploy.py
├── hosts.py
├── monitor.py
└── test.py
ホストの設定のみ
hosts.py
.
├── .gitignore
└── fabfile
├── __init__.py
├── deploy.py
├── hosts.py
├── monitor.py
└── test.py
@task
def staging():
env.hosts = [
'mystaging.jp'
]
@task
def production():
env.hosts = [
'production.com',
'myhost.jp',
'myapp.jp'
]
実行タスク
.
├── .gitignore
└── fabfile
├── __init__.py
├── deploy.py
├── hosts.py
├── monitor.py
└── test.py
deploy.py
@task( default=True )
def deploy():
with cd( '/path/to/myapp' ):
run( 'git checkout .' )
run( 'git pull' )
run( 'npm install' )
run( 'npm run compile' )
run( 'forever restart app.js' )
@task
def tagging():
hostname = local( 'hostname' )
run( 'git tag {host}-
{datetime}'.format( host=hostname,
datetime=now ) )
run( 'git push --tags' )
.
├── .gitignore
└── fabfile
├── __init__.py
├── deploy.py
├── hosts.py
├── monitor.py
└── test.py
__init__.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hosts
import deploy
import monitor
import test
Task List
fu:fabric_test$ fab -l
Available commands:
deploy
deploy.deploy
deploy.tagging
hosts.production
hosts.staging
monitor
monitor.all
monitor.disc_capacity
monitor.load_average
@task( default=True )
def deploy():
実行コマンド
$ fab [task1] [task2] [task3]…
$ fab hosts.staging deploy

stagingサーバー群に対してdeployコマンド
が実行される
hostを直接指定するなら

$ fab -H myhost [tasks…]
Fabricの実行順
Fabricはデフォルトでtaskを直列実行する。
host1,host2 と taskA,taskB がある時

host1のtaskA

host2のtaskA

host1のtaskB

host2のtaskB と順次実行される
直列タスク
from fabric.decorators
import task, parallel
@task
def deploy():
print env.host
fu:fabric_test$ fab hosts.production deploy
[production.com] Executing task 'deploy'
production.com
[myhost.jp] Executing task 'deploy'
myhost.jp
[myapp.jp] Executing task 'deploy'
myapp.jp
Done.
host毎に直列実行

されている
並列タスク
from fabric.decorators
import task, parallel
@task
@parallel
def deploy():
print env.host
fu:fabric_test$ fab hosts.production deploy
[production.com] Executing task 'deploy'
[myhost.jp] Executing task 'deploy'
[myapp.jp] Executing task 'deploy'
myapp.jp
production.com
myhost.jp
Done.
これだけ
並列実行されている
Fabricサイコー
fabfileの構成まとめ
対象ホストが多数ある場合はホスト設
定のみを行うタスクを作るのが便利。
[対象ホスト]と[実行コマンド]が分離で
きるので、[どこに] [何を]するのかが
明確に区別できるようになる。
fabfileの構成まとめ
@parallelした時に並列化されるのはタ
スク単位なので、実行のまとまりとし
てタスクを定義する。
同時に実行されたらやばそうな所をま
とめる感じでタスク定義を行えば良い。
Fabricまとめ
保守とか考えない・書捨てのワンライ
ナーとかならShellでいい。
保守性を考えるならFabricを使おう。
おわり

2日間Fabricを触った俺が
 色々解説してみる