Tech Beans

Web企業で働くエンジニアのBlog


Capistranoでオペレーション自動化をしようとして諦めた

社内のチームで数十台~のサーバーを管理していて、その上でElasticSearchやHadoopを構築している。 これまで、そのクラスタのオペレーションをほぼ手作業なりシェルスクリプトでやっていて、 手順の再利用性もないし何よりめんどくさいので、 何か自動化できるツールを導入したいと思った。

そこで注目したのがCapistranoだった。

Capistranoはデプロイツールであるけれども、シェルコマンドの自動化にも使えるらしい。

qiita.com

以下、実験的にいくつかオペレーションをCapistrano3に移行してみて、結果挫折した話。

なぜCapistranoを選んだか

チームにRubyエンジニアが多く、また構成管理はChefがデファクトになりつつあって、 Rubyで書ける仕組みが欲しかった。

デプロイ作業もrsync上等!でやっていて、将来的にきちんとCI環境を整えたく、 後々デプロイツールとしても利用できるものを使いたかった。

Capistranoの壁

実際にタスクをCapistranoにしてみて、いくつか壁があった。

学習コストが高い

Capistranoは学習コストが高い。 確かにきちんと環境とタスクを切り離して管理したり、HostFilterの仕組みも便利だった。

しかし、仕組みがしっかり作られることによって、逆に学習コストが高くなってしまっている。 Chefと同じくタスクの記述がDSLになっていて、いちいち覚える事が多い。

設定ファイルのローディング順やCapistranoがどうやってタスクを生成しているか理解していないと、

  • setした値がfetchできない
  • setできてるのにタスクに反映されない

みたいなことが頻繁にあり、

「あれ、この記述どこに書けばいいんだっけ」

となることが多かった。

さらに、Capistrano3になってからオプションや記述方法の変更が入っていて、 ネット上のノウハウも2と3が混在してしまっている状況。

別に自分たちがバージョンを上げなければ問題ない話なのだけれど、せっかく積み上げたネット上のノウハウが 陳腐化したりバージョンで分断されたりするのはもったいない。。

チームで使っていくにあたって、ここらへんの学習コストなりツールの更新によるメンテナンスが問題になりそうだなぁとは初期の段階から感じた。

並列志向であること

もともとデプロイツールということを考えると仕方がないところだけど、Capistranoは並列志向だ。 原則、タスクは並列に実行される。

デプロイツールではそれで問題ないというか、そのほうが反映ラグが少なくていいのだろうけど、 サーバーオペレーションだと並列に実行するというシチュエーションはそれほど多くないと思う。

むしろ1台ずつ状況を確認しながら実行したいし、 やりたかったのがESのローリングリスタートだったので、なおさら直列にしか実行してはいけないものだった。

一応Capistranoでも、タスクにin: sequenceとかけばを直列実行できるオプションは用意されている。

でもこいつが曲者。

例えば、あるタスク(parent task)から別のタスク(child task)を呼び出すような場合

role :web, %w{host1, host2}

task :parent do
  on roles(:web), in: :sequence do |host|
    puts 'parent start@%s' % host
    invoke 'child'
    puts 'parent end@%s' % host
  end
end


task :child do
  on roles(:web) do |host|
    puts 'child exec@%s' % host
  end
end


"""
$ cap production parent
parent start@host1,
child exec@host1,
child exec@host2
parent end@host1,
parent start@host2
parent end@host2
"""

host02のchildがparentより先に呼ばれてしまっている。

最初にあれ?っと思ったけど、よくよく調べればinvoke 'child'の時点でhost02も呼ばれてしまうのだった。 これはchildにin: :sequenceを指定しても変わらない。

んー、仕組みがわかれば納得なんだけど、全然直感的じゃないというか、 普通にtask AからBを呼ぶ、みたいなことはやるわけで、それがさくっとできないのはつらい。

capistrano2ではinvoke 'child', hosts=>hostとすれば実現できた。なぜなくなったし。。

カスタマイズしづらい

上の入れ子タスクの問題をなんとか実行時に解決するすべはないかとコードを追ってみたけれど、 Capistranoはタスク設定時にroleやhostを決めてしまっていて、 実行時にset :hosts, XXXでむりやり実行サーバーを限定する、のようなことができなかった。

残念

結論: Capistranoはデプロイツールであってオペレーションツールではない

そもそもCapistranoはデプロイツールなわけで、がんばってオペレーションもこれでできなくはないけど、 そこまでする意味はなかったかなと。。

すでにCapistranoガッツリ使ってるわけでないのであれば、わざわざ学習してまで導入するメリットはなさそう。 もちろんCapistranoのが悪いわけではなく、デプロイツールとしては実績あるいいフレームワークだと思う。

そしてFabricへ

結局、Capistranoは諦めてFabricを導入することにした。

FabricはCapistranoに比べて学習コストが低く、チームで導入しても負担が少ない。 (そもそもほとんどシェルコマンドだし)

薄いフレームワークだけ提供されているので、 コードを読むのも楽だし、独自の処理を差し込むのが簡単だ。

実際、CapistranoにあるHostFilter、RoleFilterの機能をデコレーターを独自に実装して導入している。

今回学んだことは、

  • ツールは本来の目的に使用してこそ力を発揮する
    • いろんなツールが混在するのもよくないが、かといって1つのツールでなんでもやろうとしてはいけない。
  • 学習コストを慎重に検討するべき。まず試すならシンプルなものを。 simple is best

ということ。

しばらくFabricを試してみよう。