※執筆後、業務でもserverspecを利用し始めたのもあり、業務レベルでの実践例も追記しました。
運営中のVim::Factoryでserverspecを使ったインフラテストを導入したので、 導入理由や工夫している点、悩んでいる点について記述します。
Vim::Factoryについてはこちらを参照。
DockerとWebSocketを使って、vimの設定をブラウザで即体感できるサービスを作った - Goldstine研究所
1. serverspecとはなにか
詳しくは公式サイトや書籍などを参考にして欲しいですが、
「サーバの状態をコードで自動的にテスト・確認するためのツール」です。
例えば、ApacheでWebサーバを組んでいるサーバがあったとして、下記の要件で動いているとします。
- apacheがインストールされていること
- apacheが起動していること、自動起動する設定であること
- ポート80があいていること
この要件をサーバが満たしているかコードでテストします。
上記の例だとこんなコードを書きます。
describe package('httpd') do
it { should be_installed }
end
describe service('httpd') do
it { should be_enabled }
it { should be_running }
end
describe port(80) do
it { should be_listening }
end
各種テストの立ち位置
テストといってもいろんな種類のテストがありますよね。 またそれぞれの役割を理解するのも難しくなってきたので、以下のようにまとめました。
serverspecは、サーバの状態(正しく設定されたか)を確認するためのテストツールです。
もし、サーバの振る舞いをテストしたいのであれば、Infratasterなどの他のツールを使うことをおすすめします。
いわゆるテストとは言えないと思いますが、「監視」も一種のテストとも考えられると思います。
監視では、1-5分間隔など実行頻度が高いことが多く、またユーザからみて影響がないかということが重要な指標であることから振る舞いを監視することも多いです。一方で監視ではConfigファイルが正しいかはあまりみることはないかと思います。
2. なんで導入したのか
serverspecを導入したのには大きく2つの理由があります。
(その1) インフラのテスト駆動開発を支えるため
Vim::Factoryは趣味で開発・運用しているサービスで、まだ作りたての発展途上のサービスです。
そのため、今後もインフラコードをガンガン修正していくことを想定しています。
その開発、リファクタリングを支えるべく、テスト駆動開発で行えるようにするためです。
(その2) インフラテストの必要性を深く理解するため
「インフラテストの必要性を深く理解するため」です。
必要性があるから導入したんじゃにないの?と思われるかもしれませんが、
ぼくはまだインフラテストの本当の必要性を理解できていないと思っています。
実は、はじめこんな風に思っていましたし、今でも少し思う部分もある。
- ansibleやchefで実行結果がOKならそれでテストも兼ねているのでは?
- 実際にどんな項目をテストすればいいか、考えてみた時にピンと来ない…
実際に試してみないと、その本質が見えてこないと思っているので、
導入してみて自分の肌で感じようと思っています。
個人的なお話なのでみなさんには関係のない話ではありますが、
Vim::Factoryの開発は企業でもなければ営利目的でもありません。
「週末にインフラ技術のインプット勉強をしていたが、それだけでは飽きてきて、
サービスを開発・運用していくなかでインフラ技術を磨いていきたい」
というものだったので、こういった実験も大歓迎だったのです。
3. 工夫している点
チーム内でインフラテストコードの「指針」を決める
Ansibleなど構成管理ツールがあるなかで、本当にインフラテストっているの?とはじめは誰もが思うはずです。
「なんとなく」、「流行っているから」という理由で導入をすると、
きっとチーム内でインフラテストを書く意味や目的が異なってきてしまいます。
また、serverspecはいろんな使い方ができてしまうので、なおさら人によって考え方が異なってしまいます。
ですので、チーム内でインフラテストコードの「指針」を決めて共有しています。
こうすることで、「なぜインフラテストコードを書くのか」の目的意識を統一しています。
この指針には、下記の本をかなり参考にしました。(serverspec作者の本です)
テストをサーバの役割毎にまとめる
ディレクトリの主な構成は下記のようになっています。
(実際にはもっとファイルやホストが多いですがイメージです。)
├ Rakefile
├ spec/
│ └ spec_helper.rb
│ ├ base/
│ │ ├ user_spec.rb
│ │ └ selinux_spec.rb
│ ├ proxy/
│ │ └ nginx_spec.rb
│ └ app/
│ └ vimfactory-app_spec.rb
└ sshconfig
nginxなどを搭載するプロキシサーバとアプリケーションサーバがあり、それぞれにロールを割り振ります。
Rakefile内に書いてます。もし記述が増えるようだと別ファイルに切り出すと思います。
hosts = [
{
:name => "proxy.vimfactory",
:roles => %w( base proxy )
},
{
:name => "app.vimfactory",
:roles => %w( base app )
}
]
このようにすることで、役割の違うサーバでも共有するテスト項目はコードの重複なくテストできます。
また拡張性もそこそこいい感じなんじゃないかと思っています。
sshの設定ファイルを任意で指定する
serverspecを使っているとsshのクライアント側の設定ファイルを利用することがでてきます。
serverspec-init
コマンドで生成されるspec_helper.rbでは~/.ssh/config
をデフォルトで読むようになっています。
ですが個人的には~/.ssh/config
にプロジェクトの設定を書くのがあまり好きではないし、効果的でないことがあると考えています。
~/.ssh/config
は個人のPCなどの設定によって各々違うものです。
そこにプロジェクト固有の設定を書くと、設定が衝突したり、管理が難しくなります。
ですので、プロジェクトで使うsshのクライアントの設定ファイルもgit管理しておいて、
それを使うようにすることが望ましいと思っています。
そこで、spec_helper.rbのNet::SSH::Config
部分を修正し、sshの設定ファイルを任意で指定できるようにしています。
config = ENV['SSH_CONFIG']
options = Net::SSH::Config.for(host, [config])
実行時に指定します。
bundle exec rake serverspec SSH_CONFIG=sshconfig
デプロイ作業での活用
serverspecは主に、インフラのテスト駆動開発のために導入したのですが、 本番環境でのデプロイ作業(インフラ設定の変更)にも大活躍しています。
デプロイ作業時に、下記ステップで行うことで、デプロイ作業の安心感を得るとともに、
作業後の確認作業を軽減できたのはとても素晴らしいことでした。
- serverspec実行:テスト落ちる
- Ansible実行:デプロイ
- serverspec:テスト通る
4. 悩んでいること。これからについて
serverspecのCIの方法に悩んでいます。
- どうやってCIをやるのが効率的か?
- Vim::FactoryはAnsibleでアプリケーションのデプロイまで行っているのだけど、
- インフラテストとアプリのテスト同時にやってほうがよくないだろうか?
- でも、アプリのテストをするごとにAnsibleで環境構築すると時間がかかってしまう。
- 上記を解決するためにCI用にDockerイメージを作るという案もあるけど、そこまでするべきか??
これからは、どうCIをしていくか。
インフラテストとアプリのテストをどう結びつけていくか。
このあたりが課題と考えています。
(追記)インフラCI失敗した
よくある構成例ではあるが、下記のようにGitlabCI+ DigitalOceanを使ってインフラCIの検討を行った。
しかし、結果的には運用にのるところまでいかなかった。理由はこんな感じ。
正直、趣味でやってる範囲にしては、ここを突破するモチベーションがなかった。
- 実行に膨大な時間がかかる
- それゆえにたまにタイムアウトで死ぬ
- DigitalOceanインスタンスの停止に失敗した場合が面倒(笑)