2012年10月31日

とりあえず10月も毎日更新してみた

ほぼ前日の予約投稿だけれども、10月も毎日更新してみた。
やはり多少の時間がかかるのと、記事の内容が薄くなったりしてしまう。
11月は、更新毎日はやめる事にして、気が向いた時に更新してみる。
更新に利用していた時間を開発に回す予定。

2012年10月30日

Twitter 関連のライブラリを API 1.1 に対応させるのは結構大変

Python から Twitter API を扱うライブラリに「tweepy」があるが、これは API 1.1 に対応していない。
そこで、API 1.1 の調査を兼ねていろいろ変更してみている「sakito / tweepy — Bitbucket」。

単純に endpoint を 1.1 にするだけでも大丈夫そうな感じがするが、細かい所まで対応しようとするとかなり大変。
使う機能以外を気にすると時間がかかりすぎる気がするので主要な機能だけ、徐々に対応してみようと思う。

2012年10月29日

Python の in 演算子

概要

Python の in 演算子に関して詳細に記述してある「python `in` operator use cases」という記事を見つけた。
日本語を使うとどうなるかとりあえず試してみた。

ソース

いつも通り unittest で記述した。

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


class TestIn(unittest.TestCase):

    def test_list(self):
        """
        list
        """
        lst = ['あ', 'い', 'う', 'え', 'お']
        self.assertTrue('あ' in lst)
        self.assertFalse('か' in lst)

    def test_nested_list(self):
        """
        nested list
        """
        lst = [
            ['あ', 'い', 'う', 'え', 'お'],
            ['さ', 'し', 'す', 'せ', 'そ'],
            [],
        ]
        self.assertFalse('あ' in lst)
        self.assertTrue(['あ', 'い', 'う', 'え', 'お'] in lst)
        self.assertTrue([] in lst)

    def test_dict(self):
        """
        dict
        """
        dct = {'名前': 'サンプル', '国': '日本', 'OS': 'Mac',
               '言語': {'web': 'python'}}
        self.assertTrue('名前' in dct)
        self.assertFalse('web' in dct)

    def test_dict_class(self):
        """
        dict class
        """
        class Person(dict):
            pass

        p = Person()
        self.assertEqual({}, p)

        p['name'] = '名前'
        self.assertTrue('name' in p)

    def test_set(self):
        """
        set
        """
        stlst = {'foo', 'bar', 'foo'}
        self.assertEqual(set(['foo', 'bar']), stlst)
        self.assertTrue('foo' in stlst)

    def test_generator(self):
        """
        generator
        """
        self.assertTrue(2, xrange(4))

    def test_string(self):
        """
        string
        """
        msg = 'Python はシンプルで強力な言語です'
        self.assertTrue('Python' in msg)
        self.assertTrue('強力' in msg)
        self.assertEqual(0, msg.find('Python'))
        self.assertEqual(25, msg.find('強力'))

if __name__ == '__main__':
    unittest.main()

まとめ

当然のように日本語でも特に問題はない。in はそれほど複雑ではないが、いろいろと応用が効いて便利。

2012年10月28日

多言語対応テンプレート言語 mustache を試してみた

概要

{{ mustache }}」は多言語対応のテンプレート言語。各種エディタの plugin も提供されていて便利そうなので、試してみた。

ドキュメント

文法は「mustache」を参照。

Python で試してみる

とりあえずPython で試してみる。「Pystache」を使う。

pip install Pystache

「pystache」コマンドがインストールされるので、簡単なサンプルを試してみる。

pystache 'Hello {{name}}' '{"name": "World"}'

普通はテンプレートファイルを作成する。以下のような内容で「sample.txt」として作成。

<html>
<head>
<title>こんにちは {{name}}</title>
</head>
<body>
サンプル
{{#lists}}
  <b>{{name}}</b>
{{/lists}}
</body>
</html>

これを利用するソースは以下。

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

from pystache.loader import Loader
from pystache import Renderer


class TestPystache(unittest.TestCase):

    def test_file(self):
        DATA_DIR = './data'
        loader = Loader(search_dirs=[DATA_DIR, ],
                        file_encoding='utf-8',
                        extension='txt')
        template = loader.load_name('sample')

        context = {'name': 'World',
                   'lists': [
                       {'name': 'a'},
                       {'name': 'b'},
                       {'name': 'c'},
                   ],
                   }

        renderer = Renderer()
        actual = renderer.render(template, context)

        self.assertEqual('<html>\n'
                         '<head>\n'
                         '<title>こんにちは World</title>\n'
                         '</head>\n'
                         '<body>\n'
                         'サンプル\n'
                         '  <b>a</b>\n'
                         '  <b>b</b>\n'
                         '  <b>c</b>\n'
                         '</body>\n'
                         '</html>\n'.decode('utf-8'),
                         actual)

if __name__ == '__main__':
    unittest.main()

まとめ

多言語対応なのは利点。
Python だと Jinja2 という強力なテンプレートエンジンがあるので、それほど利点がないかもしれない。
テンプレートエンジンの選択肢が少ないような言語だとかなり便利かもしれない。

2012年10月27日

Python 標準ロギングモジュール logging の使い方メモ

概要

logging を利用してみる。

ドキュメント

モジュールの使い方は、公式ドキュメント「logging — Logging facility for Python」に概要が書いてある。 また「Logging HOWTO」が具体的な使い方に関して書いてある。

使い方

以下が一番簡単な例。

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


def main():
    logging.warning('わーにんぐ')
    logging.info('いんふぉ')

if __name__ == '__main__':
    main()

実行すると以下のようになる。これはデフォルトレベルが「WARNING」以上だから。

WARNING:root:わーにんぐ

ログのレベルは以下のようになる。

レベル 関数 数値 概要
CRITICAL logging.critical() 50 停止してしまうような致命的な問題用
ERROR logging.error() 40 重大な問題用
WARNING logging.warning() 30 実行機能で問題が発生した場合用
INFO logging.info() 20 動作情報表示用
DEBUG logging.debug() 10 詳細な情報表示用
NOTSET 0 全てを出力。基本的に設定用の値

ファイルへの出力

エラーをファイルに出したい場合があるが以下のようにする。

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


def main():
    logging.basicConfig(filename='example.log', level=logging.DEBUG)
    logging.debug('でばっぐ')
    logging.info('いんふぉ')
    logging.warning('わーにんぐ')

if __name__ == '__main__':
    main()

「sample.log」ファイルに以下のように出力される。

DEBUG:root:でばっぐ
INFO:root:いんふぉ
WARNING:root:わーにんぐ

ログ出力形式の指定

ログの出力形式はいろいろ変更できる。

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


def main():
    logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s',
                        level=logging.DEBUG)
    logging.debug('でばっぐ')
    logging.info('いんふぉ')
    logging.warning('わーにんぐ')

if __name__ == '__main__':
    main()

以下のように出力される。

2012-10-01 11:21:46,999:DEBUG:でばっぐ
2012-10-01 11:21:46,999:INFO:いんふぉ
2012-10-01 11:21:47,000:WARNING:わーにんぐ

使えるフォーマットは「LogRecord attributes」に一覧がある。主な物は以下。

フォーマット 概要
%(asctime)s 実行時刻
%(filename)s ファイル名
%(funcName)s 関数名
%(levelname)s DEBUG、INFO等のレベル名
%(lineno)d 行番号
%(name)s 呼びだしたログの定義名
%(module)s モジュール名
%(message)s ログメッセージ
%(process)d プロセスID
%(thread)d スレッドID

設定ファイルの利用

フォーマットやログレベルの設定は、通常は設定ファイルを書いて利用する。設定ファイルのファイル名な何でも良い。ここでは以下の内容で「logging.conf」として作成する。
以下の例だと、コンソールとファイルに「DEBUG」レベルでログを出力する。

[loggers]
keys=root, logExample

[handlers]
keys=consoleHandler, fileHandler

[formatters]
keys=logFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_logExample]
level=DEBUG
handlers=consoleHandler, fileHandler
qualname=logExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=logFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=logFormatter
args=('example.log',)

[formatter_logFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

利用するソースは以下。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import logging.config


def main():
    logging.config.fileConfig('logging.conf')
    logger = logging.getLogger('logExample')

    logger.debug('でばっぐ')
    logger.info('いんふぉ')
    logger.warning('わーにんぐ')

if __name__ == '__main__':
    main()

Handlerの利用

上記のサンプルだと「RotatingFileHandler」を利用している。これはファイルが一定量になると回転するハンドラ。
標準のハンドラでも十分な場合もあるが、PyPIで探すと他にもいろいろなハンドラが存在している。
以下とか便利かもしれない。

存在しない Handler は上記を参考に自作してみると良いかもしれない。

まとめ

logging は障害対処時に非常に有効。多用することになるので、使い方に慣れておくと良い。

2012年10月26日

Python で標準添付の Debuggerのpdb を利用してデバッグするメモ

概要

pdb を利用してみる。

ドキュメント

公式ドキュメント「pdb — The Python Debugger」に概要が書いてある。

使い方

スクリプトを直接起動してデバッグする方法と、インタラクティブshell で起動する方法があるが、ここでは、スクリプトを起動してデバッグする方法で書く。
以下がデバッグするスクリプトのサンプル「even.py」。
自分の場合、スクリプトを直接デバッグすることがあまりなく、デバッグするのはライブラリとかをunittest経由でデバッグすることがが多いのでサンプルは unittest で書いている。
以下の例はクラス内の関数が偶数だけ返す所でバグがあり、奇数を返すようになっている。そんなに良い例ではないかも。

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


class Sample(object):
    """
    テスト対象のクラス
    """
    def to_even(self, lst):
        """
        リストを渡すと、偶数だけにして返す
        """

        # 意図的にバグらせていて、奇数しか返さない
        evenlst = [v for v in lst if v % 2 == 1]
        return evenlst


class TestSample(unittest.TestCase):
    """
    テストクラス
    """

    def test_main(self):
        clazz = Sample()

        #  0 から 10 のリストを生成 [0, 1, 2, .....]
        lst = xrange(10)
        evenlst = clazz.to_even(lst)

        # 偶数かテストする
        for item in evenlst:
            num = item % 2
            self.assertEqual(0, num)


if __name__ == '__main__':
    unittest.main()

とりあえず実行してみると以下のようにエラーになる。

> python even.py 
F
======================================================================
FAIL: test_main (__main__.TestSample)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "even.py", line 33, in test_main
    self.assertEqual(0, num)
AssertionError: 0 != 1

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

pdbを起動してみる。起動は以下のようにする。

python -m pdb even.py

起動すると pdb プロンプトが起動する。

> /path/to/even.py(3)<module>()
-> import unittest
(Pdb) 

上記の出力から「3行目」の「import」で実行が停止しているのがわかる。
「list」で停止位置の周辺ソースが表示される。

(Pdb) list
  1     #!/usr/bin/env python
  2     # -*- coding: utf-8 -*-
  3  -> import unittest
  4  
  5  
  6     class Sample(object):
  7         """
  8         テスト対象のクラス
  9         """
 10         def to_even(self, lst):
 11             """
(Pdb) 

行数とともに、現在の停止場所に「->」がある。

「list」のようにデバッグに利用できるコマンドの主な物は以下になる。コマンドには省略形が存在する物もある。

コマンド 省略形 概要
help h コマンドのヘルプを表示。
「help」でコマンド一覧、
「help コマンド名」でコマンドのヘルプ
が表示される。
where w スタックトレースを表示
down d スタックトレース中に1レベル下げる
up u スタックトレース中に1レベル上げる
step s 現在行を実行し、次の行に進む
次の行が関数の場合、関数内で停止
next n 現在行を実行し、次の行に進む
次の行が関数でも、関数内では停止しない
return r 現在の関数から抜ける
list l 現在行周辺のソースを表示
デフォルトは11行表示する
args a 現在関数の引数一覧を表示
p 式 式内容を表示
pp 式 式内容を pprint で表示
quit q デバッガ終了

現在行が「import」なので「n(ext)」で進める。
間違えて「s(tep)」すると「import」を実行する Python のライブラリ内に入る。その場合は「r(eturn)」とかすると戻ってこれる。

(Pdb) n
> /path/to/even.py(6)<module>()
-> class Sample(object):
(Pdb) 

class の行になるが、実行されるわけではない。
再度「n」するが、この「n」は一度実行すると次回は「リターンキー」で同じ事を実行する。

(Pdb) 
> /path/to/even.py(18)<module>()
-> class TestSample(unittest.TestCase):
(Pdb) 

「c(ontinue)」すると、停止するまで実行する。

(Pdb) c
F
======================================================================
FAIL: test_main (__main__.TestSample)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "even.py", line 33, in test_main
    self.assertEqual(0, num)
AssertionError: 0 != 1

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)
The program exited via sys.exit(). Exit status:  True
> /path/to/even.py(3)<module>()
-> import unittest

ブレークポイント

通常 step 実行とかしないで、ブレークポイントを利用する。ブレークポイント関連のコマンドは以下。

コマンド 省略形 概要
break 行数 or 関数名 b 指定行数にブレークポイントを付ける
tbreak 行数 or 関数名 一時的ブレークポイントを付ける
一回通過すると消える
clear 行数 or bp番号 cl ブレークポイントを削除
disable bp番号 ブレークポイントを停止
enable bp番号 ブレークポイントを有効化
ignore bp番号 回数 ブレークポイントを回数分無視する
condition bp番号 ブレークポイントの状態を確認
commands bp番号 ブレークポイントを表示
continue c ブレークポイントで停止してる場合、実行を継続

「33行目」にブレークポイントを設定し「c」して「l」で確認してみる。

(Pdb) b 33
Breakpoint 1 at /path/to/even.py:33
(Pdb) c
> /path/to/even.py(33)test_main()
-> for item in evenlst:
(Pdb) l
 28             #  0 から 10 のリストを生成 [0, 1, 2, .....]
 29             lst = xrange(10)
 30             evenlst = clazz.to_even(lst)
 31  
 32             # 偶数かテストする
 33 B->         for item in evenlst:
 34                 num = item % 2
 35                 self.assertEqual(0, num)
 36  
 37  
 38     if __name__ == '__main__':
(Pdb) 

ここで「evenlst」の内容を表示してみる。

(Pdb) p evenlst
[1, 3, 5, 7, 9]
(Pdb) 

バグっている事がわかる。

「n」を2回して「l」して「p」してみる。「p」で複数表示の場合はカンマで区切る。

(Pdb) p item,num
(1, 1)
(Pdb) 

バグ原因が判明したら「q」などで終了すれば良い。

まとめ

他者の作成したライブラリ等はソース修正するとまずいので、 print を入れることができない場合が多く、そういう場合は pdb は便利。
本当はライブラリ内で Exception が発生したりするので、結構デバッグは大変だったりする。
自作のスクリプトの場合は、普通は pdb を使うよりも、いかに良くテストを書くかの方が重要な気がしている。pdbを使わなければいけないのはテストが下手だと思った方が良い。

2012年10月24日

pyregexp を利用して Emacs の検索置換に Python の正規表現を利用してみる

概要

Emacs の正規表現は結構使いずらくて、覚えられないので、「pyregexp」を利用して、Python の正規表現を利用してみる。

pyregexpに関して

Python を利用して Emacs 内の検索・置換の処理で Python の正規表現を利用できるようにする Emacs Lisp。動作には Python が必要。

設定

ソースを取得して、pathの通った所に配置。

git clone git://github.com/benma/pyregexp.git

init.el 等で以下のように設定する。

(require 'pyregexp)
(define-key global-map (kbd "C-c r") 'pyregexp-replace)
(define-key global-map (kbd "C-c q") 'pyregexp-query-replace)
(define-key esc-map (kbd "C-r") 'pyregexp-isearch-backward)
(define-key esc-map (kbd "C-s") 'pyregexp-isearch-forward)

使い方

公式サイト「pyregexp」の一部をそのまま引用する。

以下のようなテキストを用意。これの重複した文字を検索して、重複を削除してみる。

We we can delete double double words
by by using backreferences.

「C-c r」して入力を開始すると、「Regexp」になるので、「(?i)(\w+) \1」を入力。一致箇所がハイライトされるる。3箇所一致しているはず。一番最後の文字は「いち」なので間違わないように。
置換する場合は「enter」をすると「Replace」になるので、「\1」を入力。置換結果が「We we => We」のように表示される。問題なければ、enter することで置換される。

Python の式を利用することも可能。

以下のようなファイルを用意。

hoge
foo
bar

「C-c r」して「^」を入力すると、文字列の先頭が一致する。enter して Replace にして「C-c C-c」すると「Replace (using expression)」になるので「str(i+1) + ": "」を入力すると、先頭に「1: 2: 3:」のような文字が入力される。

str だけでなく、int等も利用できるのでかなり便利。

「wdired-change-to-wdired-mode」でも利用可能。かなり高度な置換ができる。

まとめ

まだ使いはじめたばかりだが、自分は Python に慣れているので、かなり便利に利用できている。

2012年10月23日

Python の lambda メモ

概要

仕事の関係で Python の lambda に関して最近質問された。lambda は結構読みずらいようで質問の回数が多い気がする。毎回似たような質問に答えるのが面倒なので、回答用に概要をメモしておく。

Python の lambda

lambda は無名関数を作成するための機能。

def a(b):
    return c

と同じ物をが、lambda だと以下のように記述できる。

a = lambda b: c

簡単なサンプル

とりあえず簡単なサンプルが以下。時々 lambda は改行ができないと思っている人がいるみたいだが、括弧でかこめば普通に改行できる。

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

class TestLambda(unittest.TestCase):

    def test_basic(self):

        # def を利用
        def a(b):
            return b * 10

        self.assertEqual(20, a(2))

        # lambda を利用
        a = lambda b: b * 2

        self.assertEqual(4, a(2))

        # 括弧を利用すると途中改行が可能
        a = (lambda b:
             b * 4)

        self.assertEqual(8, a(2))

if __name__ == '__main__':
    unittest.main()

まとめ

確かに lambda を使うべき場面もあるが、普通はlambda はそんなに多用しない方が良いと思う。利用しても、意味ない場合も多い。
変な所で、変なテクニック使ってもおもしろくともなんとも無いので、普通に読みやすく書くのが一番良い。
あと、書いたは良いが、質問される内容はもっと高度になる場合が多いので、このエントリは自分の手抜き用には役に立たないかもしれない。

2012年10月22日

Mercurial(hg) を使って GitHub に Pull Request する手順

概要

分散バージョン管理ツールとして自分は「Mercurial」を利用している。
自分にとって Git は難しすぎるので、できれば使いたくなかったので、Git を利用しないで GitHub に Pull Request する方法を調べた。

Pull Request が初めての場合

Pull Request が初めての場合、GitHub の公式マニュアルに従って練習すると良い。Pull Requestのマニュアルは「Fork A Repo · github:help」にある。

GitHub のマニュアルに従って、「https://github.com/octocat/Spoon-Knife」を Fork して練習する。失敗しても誰も怒ったりしないので、自由にやるのが良い。

hg-git のインストールと設定

hg で GitHub のソースを clone するには「hg-git」を利用する。
hg-git」は Git のバイナリがなくても、単体で Git レポジトリを扱うことができるライブラリ。pip 等で簡単にインストールできる。

pip install hg-git

hgrcに以下を設定する

[extensions]
hggit=

hg で GitHub のソースを clone する

hg-git を利用して GitHub のレポジトリを clone する。
例えばGitHub で「git@github.com:username/Spoon-Knife.git」のようなレポジトリの場合、以下のように指定すると clone できる。

hg clone git+ssh://github.com/username/Spoon-Knife.git

Pull Request用に開発

master でそのまま開発すると、fork 元の変更に追随することができなくなるので、開発用の branch を作成する。

cd Spoon-Knife
hg branch 'develop'
# コミット
hg ci -m"develop"
# 確認
hg branches

そして、開発用の branch から pull request 用の branch を作成する。

hg branch 'feature/samplespike'
# コミット
hg ci -m"feature/samplespike"
# 確認
hg branches

pull request 用の branch で開発する。

push 用の bookmark 作成

hg-git では hg の bookmark が Git の branch になる。push を本当にする場合、bookmark を作成する。

hg bookmark -r feature/samplespike pullrequest/samplespike

pull requestのために、Commit を一つに纏める

開発によって、Commit が複数に分割されている場合があるが、分割されたpull request はしない方が良い。
Git の場合 rebase でこれを実施するが、hg では「Histedit Extension」を利用する。Mercurial 2.3以降は標準添付なので以下のようにhgrcに設定する。

[extensions]
histedit=

利用しているバージョンが Mercurial 2.3 以下の場合は「histedit」からソースを取得して設定する必要がある。

hg clone https://bitbucket.org/durin42/histedit

ソースからの場合は hgrc に以下のように設定する。

[extensions]
histedit = /path/to/hg_histedit.py

histedit の使い方は「hg help -e histedit」でhg の help を見ると和訳されている。
英語だが公式のマニュアルを見ても良い「Histedit Extension」。

hg histedit まとめたいバージョン

push の前に確認する

「hg log」や「hg glog」を利用して意図した通りになっているか十分確認する。push するとhisteditでの変更ができない。

Hg-Gitプラグインは「--HG--」のようなコメントを挿入してしまう。これを抑止するにはソースを直接修正するしかない。
「hggit/git_handler.py」に該当部分が存在するので、気になる場合は、削除かコメントアウトしておくと良い。

push して pull request

hg-git でpush するのは hg で push するのと何も変らない。

hg push

あとは GitHub のWeb から該当の branch を pull request すれば良い。

本家に追随する

本家が自分のpull requestを取り込みしてくれたら、追随する必要がある。

hg pull -r master git+ssh://github.com/octocat/Spoon-Knife.git
hg up

これで取り込める。master に commit していると面倒なことになるので、master では開発しないようにした方が良い。

まとめ

これでGitHub の pull request が hg で可能になる。

2012年10月21日

munin 2 の作成した rrd ファイルを確認する

概要

munin 2 がどのような rrd ファイルを作成しているのか確認してみる。

データファイル確認

munin 2 のデータファイルはデフォルトでは「/var/lib/munin/localdomain」以下に作成されている。
rrdtool コマンド で内容を確認することが可能。

rrdtool info nginx_request-request-d.rrd

python で見る場合は以下のようにすると見れる。

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


def main():
    info = rrdtool.info('nginx_request-request-d.rrd')
    for key in sorted(info.keys()):
        print '{0} = {1}'.format(key, info[key])

if __name__ == '__main__':
    main()

設定された値はだいたいわかるが、なぜその値なのかは、もうすこし調べてみる。

2012年10月20日

munin の plugin 作成メモ

概要

Munin」の plugin 作成の手順とサンプルをメモしておく。

ドキュメント

公式ドキュメントは結構ちらばっていてやや読みずらいが、plugin を作成したいならちゃんと読んだ方が良い。

plugin サンプル

とりあえず、サンプルを見て作成するのが速い。

参考に nginx の稼動を記録する plugin を作成してみる。nginx の status を利用して監視をするので、 status は有効にしておく。
nginx の稼動確認 plugin は公式の munin plugin としてすでに存在しているが、存在している plugin を作成することで、自作の plugin が既存の plugin と同じ動作をすることが確認できるので、最初に作成する plugin はすでに存在している plugin を作成してみるのが良い。

  • /usr/share/munin/plugins/nginx_request
  • /usr/share/munin/plugins/nginx_status

munin 標準の nginx plugin は Perl で作成されており、「LWP::UserAgent」が必要な plugin になっている。動作させる場合は、「/etc/munin/plugins」に「ln -s」する。動作させて nginx を監視したグラフを生成しておく。

munin の plugin はプログラム言語を問わないので、とりあえず自分は Python で実装してみたのが以下。「nginx_request.py」の名前で作成した。
以下の plugin は正規表現を「https://github.com/samuel/python-munin/blob/master/munin/nginx.py」の物を利用して、実質30から40分程度で作成したので、あまり最適化してないし、エラー処理もいいかげん。またバグがあるかもしれないので、ちゃんと検証してから利用すること。一応 Python 2.6 と 2.7 で動作するはず。
自作の plugin も動作させる場合は、「/etc/munin/plugins」に「ln -s」すれば良い。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
import sys
import urllib


def get_env():
    """
    get env
    """
    url = os.environ.get('url')
    if url is None:
        url = 'http://localhost/nginx_status'
    return url


def get_fields():
    """
    fields
    """
    return ('request',)


def get_server_status():
    """
    server status
    """
    # https://github.com/samuel/python-munin/blob/master/munin/nginx.py
    status_re = re.compile(
        r'Active connections:¥s+(?P<active>¥d+)¥s+'
        r'server accepts handled requests¥s+'
        r'(?P<accepted>¥d+)¥s+(?P<handled>¥d+)¥s+(?P<requests>¥d+)¥s+'
        r'Reading: (?P<reading>¥d+) Writing: (?P<writing>¥d+) Waiting: (?P<waiting>¥d+)')
    try:
        status = status_re.search(urllib.urlopen(get_env()).read()).groupdict()
    except Exception, e:
        status = None
    return status


def do_data():
    """
    data
    """
    request = get_server_status()['requests']
    fields = get_fields()
    for field in fields:
        print '{0}.value {1}'.format(field, request)


def do_autoconfig():
    """
    autoconfig
    """
    if get_server_status() is None:
        print 'no (no nginx status on {0})'.format(get_env())
    else:
        print 'yes'


def do_config():
    """
    config
    """
    print 'graph_title nginx Requests'
    print 'graph_args --base 1000 -l 0'
    print 'graph_vlabel Requests per second'
    print 'graph_category nginx'

    fields = get_fields()
    for field in fields:
        print '{0}.label requests'.format(field)
        print '{0}.min 0'.format(field)
        print '{0}.type DERIVE'.format(field)
        print '{0}.draw LINE2'.format(field)


if __name__ == '__main__':
    if len(sys.argv) > 1:
        if sys.argv[1] == 'config':
            do_config()
            sys.exit(0)
        if sys.argv[1] == 'autoconfig':
            do_autoconfig()
            sys.exit(0)
    else:
        do_data()

上記の plugin は 外部設定に対応している。「/etc/munin/plugin-conf.d」に「nginx」みたいな名前で以下のようなファイルを作成すると、監視 URL を変更できる。

[nginx_*]
env.url http://domain/nginx_status

plugin のデバッグ

基本的には「munin-run」を利用するとデバッグがしやすい。
上記の plugin をデバッグする場合は、以下のようにする。

sudo munin-run nginx_request autoconfig
sudo munin-run nginx_request config
sudo munin-run nginx_request

今後の課題

munin の plugin 作成の設定値を全部認識しているわけではないので、徐々に確認していく。

2012年10月19日

Ubuntu に munin 2 をインストールする手順メモ

概要

Munin」はサーバリソースグラフツール。
最新版は2.0.7だが、Ubuntu だと munin 1.x 系しかインストールできない模様なので、インストール手順をメモする。

muninの概要

munin は サーバのリソースをグラフ化するツール。似たようなツールは cacti 等が存在する。

muninの利点は以下

  • MySQL 等のデータベース不要
  • データ記録に RRDtool を利用
  • RRDtool で生成したデータファイルは、容量が初期生成時から増えることはないので、サーバの容量管理が容易
  • Perl で記述されているので、多くの環境で動作する
  • plugin の作成のためのプログラム言語はなんでも良く、書き方も比較的容易

munin は「master」と「node」が存在する。ここでは一つのサーバに「master」と「node」をインストールしているが、通常は「node」しかインストールしないサーバ等も存在することになる。

インストール

インストール手順を書いた fabfile は以下。「fab install」でインストールできる。
この記事を書いた時点では、deb ファイルがまだ 2.0.6 しかなかったので、2.0.6 をインストールしている。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from fabric.decorators import task
from fabric.api import (
    sudo,
    run,
    cd,
    env,
)


@task
def install():
    """
    munin 2.0 インストール
    """
    # 依存ファイルのインストール
    sudo('apt-get update')
    sudo('apt-get -y install librrds-perl '
         'rrdtool '
         'liblog-log4perl-perl '
         'ttf-dejavu '
         'libdate-manip-perl '
         'libcgi-fast-perl '
         'libfile-copy-recursive-perl '
         'libio-socket-inet6-perl '
         'librrd4 '
         'libfcgi-perl '
         'libyaml-syck-perl '
         'libsocket6-perl '
         'liblist-moreutils-perl '
         'libnet-server-perl '
         'libio-multiplex-perl '
         'libnet-cidr-perl '
         'libnet-snmp-perl '
         'libhtml-template-perl '
         'liburi-perl '
         'gawk ')

    etc_dict = {
        'urlpath': 'http://ftp.jp.debian.org/debian/pool/main/m/munin/',
        'ver': '2.0.6-1',
    }
    with cd(env.path):
        deb_tpl = (
            'munin-common_{ver}_all.deb'.format(**etc_dict),
            'munin_{ver}_all.deb'.format(**etc_dict),
            'munin-plugins-core_{ver}_all.deb'.format(**etc_dict),
            'munin-node_{ver}_all.deb'.format(**etc_dict),
        )
        # ダウンロード
        for deb in deb_tpl:
            run('wget {urlpath}/{deb}'.format(deb=deb, **etc_dict))

        # インストール
        for deb in deb_tpl:
            sudo('dpkg -i {deb}'.format(deb=deb))

起動

基本的には自動起動するはず。

「/etc/init.d/munin-node」および、「/etc/cron.d/munin」、「/etc/cron.d/munin-node」を確認する。

ディレクトリ

ディレクトリを確認しておく必要がある。

  • 設定ファイル:/etc/munin
  • 記録ファイル:/var/lib/munin
  • HTMLファイル:/var/cache/munin/www
  • ログ:/var/log/munin
  • pid:/var/run/munin

細かい設定は、設定ファイルで実施するが、とりあえずこの記事ではインストールまでとする。

nginxの設定

http 経由でグラフを見えるように設定する。
以下のような設定を nginx の設定に追加すれば、「http://ip/munin/」でグラフを見ることが可能になる。

# munin
location ^~ /munin/ {
    alias   /var/cache/munin/www/;
}

以上

2012年10月18日

nginx で自己署名証明書(オレオレ証明書、self signed certificate)を設定する

概要

ngin で 自己署名証明書(オレオレ証明書)を設定する。自己署名証明書でも暗号化はされるので、自分で利用する分には問題ない。

証明書作成

fabfile の該当箇所は以下。コピペして実行しても良い。途中でパスワードなど聞かれるが、全部空で良い。

@task
def setup_ssl():
    """
    オレオレ証明書の作成
    """
    work_dir = '/opt/nginx/conf/'
    with cd('{0}'.format(work_dir)):
        # pass hoge
        sudo('openssl genrsa -des3 -out server.key 1024')
        sudo('openssl req -new -key server.key -out server.csr')
        sudo('cp server.key server.key.org')
        sudo('openssl rsa -in server.key.org -out server.key')
        sudo('openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt')

nginxの設定

設定ファイルは以下のようになる。

daemon off;
worker_processes  5;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    error_log /opt/nginx/var/log/nginx-error.log debug;

    sendfile        on;

    keepalive_timeout  65;

    gzip  on;

    gzip_comp_level 2;
    gzip_proxied    any;
    gzip_types      text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    server {
        listen       80;
        # 443 ポートを有効にする
        listen       443;
        server_name  localhost;

        # SSL を有効にして、証明書のパスを指定
        ssl          on;
        ssl_certificate /opt/nginx/conf/server.crt;
        ssl_certificate_key /opt/nginx/conf/server.key;

        charset  utf-8;

        location / {
            root   /opt/www/html;
            index  index.html index.htm index.php;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # nginx status
        location ^~ /nginx_status {
            stub_status on;
            access_log  off;
        }

    }
}

ファイアーウォールの設定等している場合は 443 ポートを開放する。
あとは再起動して、https でアクセスすれば良い。

以上

2012年10月17日

プロセス管理に daemontools ではなく Supervisor を利用してみませんか

概要

サーバでは様々なプロセスを実行する必要があるが、これをどう管理するかは悩み所になる。
ここでは強力で柔軟なプロセス管理ツール「Supervisor」に関して記述する。

Supervisorに関して

Supervisor」は Python で記述された、プロセス管理ツール。
プロセス管理ツールと言うと「upstart」や「daemontools」が利用されていると思われる。
「upstart」は強力ではあるが、柔軟性がまだそれほど無いため、高度なプロセス管理はまだ難しい状況だと思われる。
「daemontools」は ノウハウが蓄積されており、強力で柔軟性が高いツールだが、2001年以降公式では更新されておらず、動作が不安定にってしまう環境も存在している。パッチを当てれば良いが、環境によって動作が変化してしまう場合もあり、安心して利用できない状況になってしまった。
Supervisor」は積極的に開発され、非常に安定したツールとなっている。
機能的には「daemontools」とほぼ同様の事が可能。
Python で記述されることで、Unix互換システムにおいて、快適に動作する。当然 Mac でも動作する。
現在の Linux サーバは Python はほぼ必須でインストールされている、という状況なので、ツールが Python で記述されていることは利点の一つになりえる。

Supervisorの利点は以下となる。全機能はマニュアル「Supervisor」を参照。

  • 積極的に開発されている
  • 設定ファイルが比較的簡単
  • プロセスのグループ化が容易
  • プロセスの状態により処理を実行することが可能
  • サブプロセスの管理も可能
  • ログを高度に処理できる
  • Webインターフェースが存在する
  • XMP-RPCインターフェースが存在する

インストール

Supervisorをインストールする場合、通常virtualenvにより環境を構築する。
参考に、以下インストール手順を示した fabfile を配置した。以下のスクリプトはインストールディレクトリは「/opt/python」で、作業ディレクトリは「/opt/supervisor」として記述してある、また、この fabfile はあまり、汎用的には記述していない、あくまで参考である。

実行は「fab install」で実行可能

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from fabric.decorators import task
from fabric.api import (
    sudo,
    run,
    cd,
    put,
    env,
)
from fabric.contrib.files import exists


@task
def install():
    """
    Supervisor
    """
    setup_venv()
    install_supervisor()
    setup_bin()
    setup_conf()
    setup_upstart()


@task
def setup_venv():
    """
    virtualenv による Python 環境構築
    """
    with cd(env.path):
        run('wget https://raw.github.com/pypa/virtualenv/master/virtualenv.py')
        sudo('python virtualenv.py --distribute /opt/python')


@task
def install_supervisor():
    """
    Supervisor インストール
    """
    run('source /opt/python/bin/activate')
    sudo('/opt/python/bin/pip install Supervisor')


@task
def setup_bin():
    """
    実行ファイル設定
    """
    work_dir = '/opt/supervisor/bin'
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}'.format(work_dir))
    put('./fabfile{0}/*'.format(work_dir),
        '{0}/'.format(work_dir), use_sudo=True)
    sudo('chown root:root {0}/*'.format(work_dir))
    sudo('chmod 755 {0}/*'.format(work_dir))


@task
def setup_conf():
    """
    設定ファイル
    """
    work_dir = '/opt/supervisor'
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}/etc'.format(work_dir))
        sudo('mkdir -p {0}/var/log'.format(work_dir))
        sudo('mkdir -p {0}/var/run'.format(work_dir))

    work_dir = '/opt/supervisor/etc'
    conf_file = '{0}/supervisord.conf'.format(work_dir)
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}'.format(work_dir))
    put('./fabfile{0}'.format(conf_file),
        '{0}'.format(conf_file), use_sudo=True)

    work_dir = '/opt/supervisor/etc/supervisor.d'
    conf_file = '{0}/*.conf'.format(work_dir)
    if not exists('{0}'.format(work_dir)):
        sudo('mkdir -p {0}'.format(work_dir))
    put('./fabfile{0}'.format(conf_file),
        '{0}'.format(conf_file), use_sudo=True)


@task
def setup_upstart():
    """
    upstart 設定
    """
    conf_file = '/etc/init/supervisor.conf'
    put('./fabfile{}'.format(conf_file),
        '{0}'.format(conf_file), use_sudo=True)
    sudo('chown root:root {0}'.format(conf_file))

管理用スクリプトの配置

Supervisor をインストールすると、インストール場所の「bin」以下に「supervisord」と「supervisorctl」の2ファイルが生成される。
これを直接利用しても良いが、自分は、一つのサーバで複数のSupervisorを起動する要件が発生する場合があるため、以下のようにスクリプトを作成している。
このスクリプトは実行する Python の指定と、設定ファイルの指定をしているだけだが、用途によっては便利だろう。

以下が「supervisord」と同じ機能のファイル

#!/opt/python/bin/python
# -*- coding: utf-8 -*-
import os
import sys

SRC_DIR=os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.argv.extend(['-c', '{0}/etc/supervisord.conf'.format(SRC_DIR)])

import supervisor.supervisord

if __name__ == '__main__':
    supervisor.supervisord.main()

以下が「supervisorctl」と同じ機能のファイル。

#!/opt/python/bin/python
# -*- coding: utf-8 -*-
import os
import sys

SRC_DIR=os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.argv[1:1] = ['-c', '{0}/etc/supervisord.conf'.format(SRC_DIR)]

import supervisor.supervisorctl

if __name__ == '__main__':
    supervisor.supervisorctl.main(sys.argv[1:])

設定ファイル

設定ファイルの雛形は「echo_supervisord_conf」プログラムによって生成可能。
とりあえず以下のように記述すると最低限の動作はする。「%(here)s」は設定ファイルのあるディレクトリを示す。
「include」の「file」は現在のバージョンではまだ「%(here)s」が使えない。

[supervisord]
childlogdir = %(here)s/../var/log
logfile = %(here)s/../var/log/supervisor.log
logfile_maxbytes = 50MB
logfile_backups = 10
loglevel = info
pidfile = %(here)s/../var/run/supervisor.pid
umask = 022
nodaemon = false
nocleanup = false

[inet_http_server]
port = 127.0.0.1:9001
username =
password =

[supervisorctl]
serverurl = http://127.0.0.1:9001
username =
password =

[rpcinterface:supervisor]
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface

[include]
files = /opt/supervisor/etc/supervisor.d/*.conf

upstartの設定

Supervisor自身をSupervisorで管理することはできないので、Supervisorは upstartで管理する。
以下のようなファイルを「/etc/init」以下に「supervisor.conf」として配置する。

description     "supervisor"

start on runlevel [2345]
stop on runlevel [!2345]

respawn

chdir /opt/supervisor
exec sudo -i /opt/python/bin/python /opt/supervisor/bin/supervisord -n -d /opt/supervisor

pre-stop exec sudo -i /opt/python/bin/python /opt/supervisor/bin/supervisorctl stop all

プロセス管理設定

これで Supervisor は起動している。「supervisorctl status」を実行するとまだ管理対象が存在しないことがわかる。
とりあえず、nginx の管理を設定してみる。

管理対象のプロセスはフォアグラウンドで起動させる必要がある。nginx の設定を変更する。

daemon off;

Supervisor の設定ファイルで「include」の「files」で指定したディレクトリに以下のようなファイルを「nginx.conf」のような名前で配置する。

[program:nginx]
command=/opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf
redirect_stderr=true
stdout_logfile=%(here)s/../var/log/%(program_name)s_stdout.log
stderr_logfile=%(here)s/../var/log/%(program_name)s_stderr.log
autostart=true
autorestart=true

ここで注意すべきなのは「%(here)s」は、元設定ファイルのカレントディレクトリになるということ。パスには注意する。
以下のようにすることで、有効になる。

# 設定読み込み
supervisorctl reread
# 設定反映
supervisorctl update
# 確認
supervisorctl status

設定を反映するまでは、有効にならない。

以下のように、起動、終了、再起動が可能。

supervisorctl stop nginx
supervisorctl start nginx
supervisorctl restart nginx

「autostart=true」を設定しているので、Supervisor が起動すると自動起動する。
「autorestart=true」を設定しているため、異状終了しても管理対象プロセスは復旧してくる。kill などして、確認してみると良い。

Webインターフェース、XML-RPC

SupervisorはHTTPのWebインターフェエースをもっている。また XML-RPC による管理も可能。
HTTPSを利用したい場合は、nginx などの HTTPサーバを経由させれば良い。
上の設定ファイルを利用している場合はポート9001になる。

その他機能

他の機能はマニュアルを読むのが良い。機能は高機能なので、いろいろできる。

2012年10月16日

Ubuntu Server 12.04 LTS に nginx を Install したメモ

概要

Ubuntu Server 12.04 LTS に apt-get で nginx をインストールしようとしたら、nginx のバージョンが古かったのでコンパイルしてインストールした。とりあえずメモしておく。

fabfile

Fabric」で設定を書いてコンパイルした。
この fabfile だとインストール先は「/opt」の下にしてある。
ユーザは nginx ユーザを想定して作成している。
あまり綺麗な fabfile ではないが、そこまで綺麗に書いても意味ないかも。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from fabric.decorators import task
from fabric.api import (
    sudo,
    run,
    cd,
    put,
    env,
)


@task
def install():
    """
    nginx のインストール
    """

    # 依存ファイルのインストール
    sudo('apt-get install -y gcc')
    sudo('apt-get install -y make')
    sudo('apt-get install -y libpcre3-dev')
    sudo('apt-get install -y libssl-dev')

    # nginx
    with cd(env.path):
        etc_dict = {
            'urlpath': 'http://nginx.org/download/',
            'ver': '1.2.4',
            'data_dir': '/opt/nginx',
            'user': 'nginx',
            'group': 'nginx',
        }

        # ディレクトリ用意
        sudo('mkdir -p %(data_dir)s/conf' % etc_dict)
        sudo('mkdir -p %(data_dir)s/module' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/log' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/run' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/lock' % etc_dict)
        sudo('mkdir -p %(data_dir)s/var/tmp' % etc_dict)

        # ユーザ作成
        sudo('useradd -s /bin/false nginx -d /opt/nginx')

        # nginx コンパイル
        nginx = 'nginx-{ver}'.format(**etc_dict)
        run('wget {urlpath}/{nginx}.tar.gz'.format(nginx=nginx, **etc_dict))
        run('tar xvfz {nginx}.tar.gz'.format(nginx=nginx))
        with cd('{nginx}'.format(nginx=nginx)):
            run('./configure '
                '--prefix=%(data_dir)s '
                '--conf-path=%(data_dir)s/conf/nginx.conf '
                '--error-log-path=%(data_dir)s/var/log/nginx-error.log '
                '--http-log-path=%(data_dir)s/var/log/nginx-access.log '
                '--pid-path=%(data_dir)s/var/run/nginx.pid '
                '--lock-path=%(data_dir)s/var/lock/nginx.lock '
                '--user=%(user)s '
                '--group=%(group)s '
                '--with-debug '
                '--with-poll_module '
                '--with-http_gzip_static_module '
                '--with-http_realip_module '
                '--with-http_ssl_module '
                '--with-http_stub_status_module '
                % etc_dict)
            run('make')
            sudo('make install')

        # パーミッションの設定
        sudo('chown -R nginx:nginx /opt/nginx')


@task
def setup_nginx():
    """
    nginx 設定
    """
    # 設定ファイルのアップロード
    conf_path = '/opt/nginx/conf/nginx.conf'
    put('./fabfile/{conf_path}'.format(conf_path=conf_path),
        '{conf_path}'.format(conf_path=conf_path), use_sudo=True)
    # パーミッションの設定
    sudo('chown -R nginx:nginx {conf_path}'.format(conf_path=conf_path))

nginxの設定ファイル

「nginx.conf」の中身はいいかげんだが以下な感じ。

daemon on;
worker_processes  5;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    error_log /opt/nginx/var/log/nginx-error.log debug;

    sendfile        on;

    keepalive_timeout  65;

    gzip  on;

    gzip_comp_level 2;
    gzip_proxied    any;
    gzip_types      text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    server {
        listen       80;
        server_name  localhost;

        charset  utf-8;

        location / {
            root   /opt/www/html;
            index  index.html index.htm index.php;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # nginx status
        location ^~ /nginx_status {
            stub_status on;
            access_log  off;
        }

    }
}

コンパイルとインストール

fabfileの実行は以下のようにする。

fab -H ホスト install
fab -H ホスト setup_nginx

起動

nginx の起動は以下

sudo su /opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf

このままだと root ユーザでの起動になってしまう。本当は nginx ユーザで起動したいので、後で設定する。

動作確認

FW設定している場合はポートを開放する。

sudo ufw allow 80/tcp

「http://hostip/」でアクセスして、表示されたら成功。

2012年10月15日

システム構築、デプロイツール Fabric について

概要

サーバ構築等のシステム構築は多数のコマンドを打鍵することになる。そうしたコマンドをメモの形で残しておいても、コピペで実行していればミスは発生するし、安全性が低い。
プログラマーなら、手順全てをプログラムしておくのが良い。
それを可能にするツールが「Fabric」。

インストール

Fabric」は Python で作成されている。インストールは easy_install か pip 等で実施する。

pip install fabric

インストールで「fab」というコマンドがインストールされるので、PATHを通しておく。

事前準備

サーバに対して実行する場合は、サーバにログインできる状態にしておく必要がある。

簡単な使い方

以下のようなファイルを「fabfile.py」の名前で作成。

from fabric.api import run

def host_uname():
    run('uname -s')

「fabfile.py」の存在するディレクトリで、ホストを指定して、「fab」コマンドを実行する。

fab -H 192.0.2.178 host_uname

この例だと「192.0.2.178」のサーバにログインして「uname -s」を実行する。ログインする時に通常のログイン処理が実行される。

もっと自動化したい場合は、「evn」を設定することで、自動的にログインすることも可能。

from fabric.api import env

# SSH のキーのパスを設定
env.key_filename = [os.path.join(os.path.dirname(__file__), 'keys/id_rsa')]
# ログイン先の IP
env.hosts = [
    '192.0.2.178',
]
# ログインユーザ名
env.user = 'username'
# sudo する際のパスワード
env.password = 'password'

マニュアル

マニュアルは「Fabric」に存在する。 Operationsの章とかは理解しやすいかもしれない。ほとんどの事が実行できる。 あとは「fabfile.py」で検索するとサンプルが沢山あるのでそれを見るのが速い。

fabfileの分割

通常は「fabfile.py」を作成して実行するが、システム構築等を実行するとすぐに「fabfile.py」が肥大化する。
「fabfile.py」を分割したい場合は「fabfile」というディレクトリを作成し、その中に「__init__.py」ファイルを作成。
同じディレクトリに Python ファイルを用意して、「__init__.py」で import すれば良い。

たとえば以下のようなディレクトリ構成にする。

.
├── fabfile
    ├── __init__.py
    ├── commons.py
    ├── etc
    │   └── ufw
    │       ├── before.rules
    │       └── before6.rules
    ├── ufw.py

「__init__.py」の中身は以下のような感じ。

import commons
import ufw

実行は「ufw.py」の中身が以下のようだとすると。「fab ufw.status」で実行できる。

from fabric.decorators import task
from fabric.api import sudo

@task
def status():
    """
    Firewall の状態確認
    """
    sudo('ufw status')

あとは API を使いこなせば、ほとんど何でも自動実行可能。事前に構築環境と同じOSなどを用意して、十分に動作を確認してから実行できるので、非常に便利。

まとめ

Fabric」に関しては利用してみないと便利さが良くわからないかもしれないが、かなり便利なツール。環境構築を汎用的に実施したい人には特にお勧め。