Capistranoでオペレーション自動化をしようとして諦めた
社内のチームで数十台~のサーバーを管理していて、その上でElasticSearchやHadoopを構築している。 これまで、そのクラスタのオペレーションをほぼ手作業なりシェルスクリプトでやっていて、 手順の再利用性もないし何よりめんどくさいので、 何か自動化できるツールを導入したいと思った。
そこで注目したのがCapistranoだった。
Capistranoはデプロイツールであるけれども、シェルコマンドの自動化にも使えるらしい。
以下、実験的にいくつかオペレーションを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の機能をデコレーターを独自に実装して導入している。
今回学んだことは、
- ツールは本来の目的に使用してこそ力を発揮する
- 学習コストを慎重に検討するべき。まず試すならシンプルなものを。 simple is best
ということ。
しばらくFabricを試してみよう。