一歩すすんだ Fabric のタスク定義のしかた

Backlog ではユーザのみなさまに安定して Backlog をご利用いただけるよう 定期的にサーバやストレージの増強などのメンテナンスを行っています。 そして気付けば現在では数十台のホストで構成されるサービスとなっています。

さて、それだけのホストを運用していると、例えばアプリケーションを配備したり、 ミドルウェアを再起動するのも手動で行うには限界があります。 こういった作業に対して Backlog では Fabric を利用しています。
fabric

Fabric の魅力の一つはそのシンプルさです。例えばリモートホストに接続してコマンドを実行するには、以下のようなタスクを定義し、

fab コマンドで以下のように呼び出すだけです。

Fabric を使い出した頃、コマンドを列挙していくだけのシンプルさのおかげで、それまで shell スクリプトや手順書として Wiki に書いていたものをスムーズに移行することができました。

さて、この task デコレータを使ってタスクを定義する方法の他に、Task のサブクラスを利用する方法 がドキュメントでは紹介されています。例をそのまま引用すると、以下のようにサブクラスを定義すると、

以下の task デコレータを使った定義と同等になります。

実際 task デコレータも内部的には WrappedCallableTask という Task のサブクラスを 生成しています。ただ、これだけを見ると、ぶっちゃけデコレータを使った方法のほうが短くてシンプルに見えますね。

ですが、この Task をサブクラス化する方法は、

  • 複数のサービス間でタスクを共有したい
  • 定型的なタスクを沢山生成したい
  • 環境毎の切り替えを見通しよく実装したい

といった場合に活躍してくれます。では、実際にその内容をみていきましょう。

同様のタスクを別サービスでも使い回したい

チームで共通的に利用するミドルウェアやアプリケーションフレームワークを決めている場合、別のサービスでもあるサービスのタスクを使い回したくなることはままあります。実際、私たちも Backlog 以外に CacooTypetalk といったサービスを運用しており、共通的に利用したいタスクというのは少なからずあります。

そういった場合には、Task クラスを定義してライブラリにしておきます。

そして、どのサービスでも以下のように

とタスク定義をすれば、コピペ不要で使い回しがききます。また、あるサービスでは一部サービス固有の処理がある、といった場合にはクラスを拡張することで重複した記述をしなくてもよくなります。

この嬉しさはオブジェクト指向で書く事の良さそのものですね。

起動、停止タスクだらけの fabfile

冒頭でも述べましたが、Fabric の主な利用シーンの一つにミドルウェアの起動、停止、再起動があります。こういったタスクはシンプルなものの、数が多くなると fabfile の中身がそればかりで膨らんでしまうこともよくあります。以下のようなイメージです。

例では三つだけですが、これに memcached や tomcat その他のミドルも追加されると、同じような記述が沢山並んでしまい見通しが悪くなり、うっかり間違って書き変えてしまったり、新しく何かを追加するときにコピペして変更し忘れたり、といった事が起こりやすくなります。

そういった時に以下のようにタスクを自動生成するユーティリティメソッドを利用します。 ここでは、task デコレータを関数として呼び出してクラスを生成するラッパ関数を取得しています。

あとは定義する側では以下のように呼び出せば、stop / start / restart のタスクが自動的に生成されます。

ちゃんとタスクとして認識されています。

環境によるタスク実行内容の切り替え

一つのサービスで、プロダクション環境とステージング環境などと複数の環境を利用することもよくあるでしょう。そういった場合に、よく使われるテクニックとしては

といった形で、まず switch タスクで環境を変更したあとに、実際に実行するタスクを呼び出すというものです。switch タスク内でする処理の例としては env 環境変数にある role で指定するホスト名を変更したり、環境固有の値をセットアップするなど、です。

さて、sometask 側で実行する内容が共通の場合は良いのですが、場合によっては staging 環境と production 環境での処理には差がある、といった事もあるでしょう。そういった場合、sometask 側で

みたいな事をすれば通常はよいのですが、

環境毎に処理の切り替えが必要なタスクが多い
特定の環境では毎回同じ処理をしたい ( production だと必ず実行ログを残す、など )
といった場合には同様の処理が各タスクにあって見通しが悪くなりがちです。そこで、以下のように FactoryTask を作って関数呼び出しをラップしてあげることで、

環境毎の分岐をタスク側に書かなくてよい
環境固有の共通処理を FactoryTask 側に集約ができる
といったメリットがあります。

こちらを呼び出すには以下のようにします。

ここでは、production と staging の違いとして引数の数を変え、production の場合は logger で実行ログを残す形としてみました。

ということで、冒頭でも述べたように Task をサブクラス化する方法は

複数のサービス間でタスクを共有したい
定型的なタスクを沢山生成したい
環境毎の切り替えを見通しよく実装したい
といった場合に、コピペをなくし、見通しを良くするといった所で活躍してくれます。このエントリで書いたサンプルコードは以下から取得が可能です。

https://github.com/nulab/fabric-sample
上記のページに記載のある必要なツール ( Vagrant、Ansible/、Fabric ) をインストールした上で、以下の下準備をしていただければ、本エントリで紹介した各タスクを実際に実行することが可能ですので、実際に動かしてみてイメージを掴んでいただければと思います。

Fabric はシンプルなツールですが、こういったちょっと踏み込んだ使い方が出来るのも魅力ですね。また、Vagrant や ansible といったツールのおかげで簡単にこういったツールのテスト環境が共有出来るようになった事も素敵ですね。

ということで、Let’s enjoy Fabric !

より良いチームワークを生み出す

チームの創造力を高めるコラボレーションツール

製品をみる