Goldstine研究所

mosuke5's tech blog

ブログ移転しました。5秒後にリダイレクトします。

インフラテスト(serverspec)はじめました

※執筆後、業務でもserverspecを利用し始めたのもあり、業務レベルでの実践例も追記している。

運営中のVim::Factoryでserverspecを使ったインフラテストを導入したので、 導入理由や工夫している点、悩んでいる点について記述します。

Vim::Factoryについてはこっちみてね。 mosuke5.hateblo.jp

1. serverspecってなによ

詳しくは公式サイトや書籍などを参考にして欲しいですが、
「サーバの状態をコードで自動的にテスト・確認するためのツール」です。

Serverspec - Home

例えば、ApacheでWebサーバを組んでいるサーバがあったとして、下記の要件で動いているとします。

この要件をサーバが満たしているかコードでテストします。
上記の例だとこんなコードを書きます。

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

各種テストの立ち位置

f:id:mosuke5:20170416114252j:plain:w600

  • serverspecは、サーバの状態(正しく設定されたか)を確認するためのテストツールです
  • サーバの振る舞いのテストは別のツールを使うことをおすすめします
  • また、監視も一種のテストと言えます
  • 一般的には監視はその実行頻度の高さから、振る舞いを監視することが多い
  • 監視ツールで、Configファイルが正しいかは見ない

2. なんで導入したの?

serverspecを導入したのには大きく2つの理由があります。

(その1) インフラのテスト駆動開発を支えるため

Vim::Factoryは趣味で開発・運用しているサービスで、まだ作りたての発展途上のサービスです。
(作って公開してからは日が経っている気もしますが、週1の開発なので…)
そのため、今後もインフラコードをガンガン修正していくことを想定しています。
ですので、その開発、リファクタリングを支えるべく、テスト駆動開発で行えるようにするためです。

(その2) インフラテストの必要性を深く理解するため

「インフラテストの必要性を深く理解するため」です。
必要性があるから導入したんじゃにないの?と思われるかもしれませんが、
ぼくはまだインフラテストの本当の必要性を理解できていないと思っています。

実は、はじめこんな風に思っていましたし、今でも少し思う部分もある。

  • ansibleやchefで実行結果がOKならそれでテストも兼ねているのでは?
  • 実際にどんな項目をテストすればいいか、考えてみた時にピンと来ない…

実際に試してみないと、その本質が見えてこないと思っているので、
導入してみて自分の肌で感じようと思っています。

個人的なお話なのでみなさんには関係のない話ではありますが、
Vim::Factoryの開発は企業でもなければ営利目的でもありません。
「週末にインフラ技術のインプット勉強をしていたが、それだけでは飽きてきて、
サービスを開発・運用していくなかでインフラ技術を磨いていきたい」 というものだったので、こういった実験も大歓迎だったのです。

3. 工夫している点

チーム内でインフラテストコードの「指針」を決める

Ansibleなど構成管理ツールがあるなかで、本当にインフラテストっているの?とはじめは誰もが思うはずです。
「なんとなく」、「流行っているから」という理由で導入をすると、
きっとチーム内でインフラテストを書く意味や目的が異なってきてしまいます。
また、serverspecはいろんな使い方ができてしまうので、なおさら人によって考え方が異なってしまいます。

ですので、チーム内でインフラテストコードの「指針」を決めて共有しています。
こうすることで、「なぜインフラテストコードを書くのか」の目的意識を統一しています。

この指針には、下記の本をかなり参考にしました。(serverspec作者の本です)

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は主に、インフラのテスト駆動開発のために導入したのですが、 本番環境でのデプロイ作業(インフラ設定の変更)にも大活躍しています。

デプロイ作業時に、下記ステップで行うことで、デプロイ作業の安心感を得るとともに、
作業後の確認作業を軽減できたのはとても素晴らしいことでした。

  1. serverspec実行:テスト落ちる
  2. Ansible実行:デプロイ
  3. serverspec:テスト通る

4. 悩んでいること。これからについて

serverspecのCIの方法に悩んでいます。

  • どうやってCIをやるのが効率的か?
  • Vim::FactoryはAnsibleでアプリケーションのデプロイまで行っているのだけど、
  • インフラテストとアプリのテスト同時にやってほうがよくないだろうか?
  • でも、アプリのテストをするごとにAnsibleで環境構築すると時間がかかってしまう。
  • 上記を解決するためにCI用にDockerイメージを作るという案もあるけど、そこまでするべきか??

これからは、どうCIをしていくか。
インフラテストとアプリのテストをどう結びつけていくか。
このあたりが課題と考えています。

(追記)インフラCI失敗した

よくある構成例ではあるが、下記のようにGitlabCI+ DigitalOceanを使ってインフラCIの検討を行った。
f:id:mosuke5:20170416125051j:plain:w600

しかし、結果的には運用にのるところまでいかなかった。理由はこんな感じ。
正直、趣味でやってる範囲にしては、ここを突破するモチベーションがなかった。