FORCIA CUBEフォルシアの情報を多面的に発信するブログ

Serverspecを導入し既存業務を効率化 サーバー構築後の確認作業コストを大幅改善

2018.12.06

アドベントカレンダー2018

システム部の黒坂です。日頃の業務では主に顧客サーバー、社内サーバーのインフラ周りの構築・運用を担当しています。

フォルシアのシステム部では、既存業務の効率化を進めており、その一環でサーバーのOS・MW周りの設定確認を自動化するツールであるServerspecの導入しました。今回は、Serverspecの導入の効果や課題をご紹介します。

これまでフォルシアで行っていたサーバー構築テスト

フォルシアのアプリが乗っているサーバーには、アプリを乗せる前に以下のようなインフラ構築を行っています。

  • ミドルウェアのインストールに必要なyumパッケージを入れる
  • 不要パッケージの削除・不要デーモンの停止
  • 運用上必要なユーザーの作成
  • 各種ミドルウェア等のインストール
  • sudoersの設定
  • その他

フォルシアでは、このようなインフラ構築にAnsibleを利用していました。Ansibleの利用には、実際にサーバーが意図した状態になっているか、Ansibleが意図通りに動いているかを確認する必要性があり、都度シェルでコマンドを叩いて結果が適切かどうかを目視で確認する作業を行っていました。実際に、下記のようなExcelファイルを約20ページ作成し、該当箇所について記録をしていました。

64hbj0FWNdN7q361543553976_1543554114.png

この目視確認は、作業コストが大きく、高い集中力が長時間求められる課題がありました。そこで、導入を進めたものが「Serverspec」です。再掲になりますが、Serverspecとは、サーバが想定通りに構築されたかを自動でテストするオープンソースのツールです。 RubyのテストフレームワークRspecに準拠して実装されています。仕組みとしては、実行環境からリモートサーバーにsshでコマンドを投げて、想定通りの結果が返ってくるかをチェックします。

<Serverspec 公式HP>
https://www.postgresql.jp/document/9.6/html/pgprewarm.html

FORCIAでの活用例紹介

ディレクトリ構成

ディレクトリ構成.png

実行準備

やること

Serverspecを流すために必要なものは次の3つです。

  • 全体の制御:Rakefile
  • 流すテスト:common/base以下のspec
  • 必要な変数:app以下

フォルシアではRakefileとcommon/base以下は共通の設定を用意しているため、app以下を実装すれば各アプリのサーバーに対してServerspecが流せる状態です。

実行までに考えること

実行の際に考えることは次の2つです。

  1. どのサーバーに対してどのテストを流すか
  2. どういう設定になっているか

この2つの観点で、実際のコードをベースに解説していきます。

1. どのサーバーに対してどのテストを流すか

  • ディレクトリ構成で説明したapp以下に記述します。
  • 考えることは次の4つです。
    • アプリ名(例では"sample")
    • どんな環境があるか(例では"prod"と"stg")
    • 各環境にはどのサーバーがあるか
    • 各サーバーにはどのテストを実施するか

app/sample/hosts/hosts_stg.yml を例に取ります。

webdb:  # servertype = webdb に関する記述が下記
  :hosts:   # 該当するホストはwebdb1
    - webdb1   # webdb1に実行するテストが下記(common/spec/xx_spec.rbに対応)
  :roles:  # servertype = webdbに実行するテストが下記(common/spec/xx_spec.rbに対応)
    - base
    - forcia
    - python
    - sudoers

batch:   # servertype = batch に関する記述が下記
  :hosts:  # servertype = batch に関する記述が下記
    - batch1  # 該当するホストはbatch1
  :roles:   # servertype = batchに実行するテストが下記(common/spec/xx_spec.rbに対応)
    - base
    - forcia
    - python
    - sudoers

2. どういう設定になっているか

  • app以下のvarsで、変数で指定します。
  • common/default-vars/default_vars.yml にデフォルトの変数があり、上書きが必要な変数だけ設定します。

以下、app/sample/vars/app_vars.yml の一部を抜き出した設定例です。

# app共通の変数
#---------------
# python
#---------------
python_major_version: '3'
python_minor_version: '6'
python_maintenance_version: '3'

python_modules:
  # python3系で導入するモジュールを記入
  - 'pip'
  - 'mako'
  - 'markupsafe'
  - 'psycopg2'
  - 'setuptools'

各Spec

  • テストの内容を記載します。
  • 確認していることは主に次の3つです。
    • 想定したファイルが存在するかやファイルタイプなどが想定したものかの確認
    • 想定した文字列がファイルの中に含まれているか
    • コマンドの結果の確認

python.rbの一部を例にとり、できることの一例を紹介します。

# frozen_string_literal: true     ##rubocopというRuby用のlinterで構文チェックのために内容を文字列化

require 'spec_helper'      ##変数の読み込みの順番など、各テストで共通の設定が記載
rspec_configure('forcia')  ##テスト実行ユーザーをforciaに設定

## 導入したpythonのフルバージョンの変数の設定
PYTHON_VERSION = "#{property['python_major_version']}.#{property['python_minor_version']}.#{property['python_maintenance_version']}"

## pythonのバージョンが導入する想定だったものと一致するかを確認
describe command('source /home/forcia/.bash_profile; python --version') do
  its(:stdout) { should match("Python #{PYTHON_VERSION}") }    ##コマンドの結果が設定した変数を示すバージョンと一致しているかを確認
end

## 導入したpythonにシンボリックリンクが貼られていること
describe file('/usr/local/python') do
  it { should be_symlink }      ##symlinkであるかを確認
  it { should be_linked_to format('/usr/local/python-%s', version: PYTHON_VERSION) }       ##symlinkの向き先が導入したpythonのバージョンであることを確認
end
## ----続く-------

実行と出力結果の確認

実行コマンド

{APP=(app name)} {SPEC_ENV=(prod/stg/fdev)} rake serverspec[:all/(server name)]
{}: 必須
[]: 任意
(): 特に意味なし
※どのアプリのどの環境のどのサーバーに実行するか、を指定している

出力結果

あるサーバー(server1)のpythonについて、上述の変数を設定した上でのチェックの出力結果の例を記載します。

Command "source /home/forcia/.bash_profile; python --version"  //記載のコマンドを実行
  stdout     //標準出力を確認
    should match "Python 3.6.3" (FAILED - 1)    //標準出力が、変数で設定している3.6.3ではないのでFAILED

File "/usr/local/python"    //記載のファイルを確認
  should be symlink   //symlinkなのでOK
  should be linked to "/usr/local/python-3.6.3" (FAILED - 2)    //symlinkの張られている先が/usr/local/python-3.6.3ではないのでFAILED


---略------

Failures:

  1) Command "source /home/forcia/.bash_profile; python --version" stdout should match "Python 3.6.3"
     On host 'server1'
     Failure/Error: its(:stdout) { should match("Python #{PYTHON_VERSION}") }
       expected "Python 3.6.2\n" to match "Python 3.6.3"
       Diff:
       @@ -1,2 +1,2 @@
       -Python 3.6.3     //Serverspecの実行環境に記載している変数では3.6.3
       +Python 3.6.2     //対象サーバーに実際に導入されているバージョンは3.6.2
       env PATH="/sbin:/usr/local/sbin:/usr/sbin:$PATH" /bin/sh -c source\ /home/forcia/.bash_profile\;\ python\ --version    //チェックの際に対象上に投げている実行コマンド
       Python 3.6.2    //チェックの際のコマンドの標準出力

  2) File "/usr/local/python" should be linked to "/usr/local/python-3.6.3"
     On host 'server1'
     Failure/Error: it { should be_linked_to format('/usr/local/python-%s', version: PYTHON_VERSION) }
       expected `File "/usr/local/python".linked_to?("/usr/local/python-3.6.3")` to return true, got false   //ディレクトリが存在せず何も返ってきていないの意
       env PATH="/sbin:/usr/local/sbin:/usr/sbin:$PATH" /bin/sh -c test\ x\"\$\(readlink\ /usr/local/python\)\"\ \=\ x\"/usr/local/python-3.6.3\"   //チェックの際に対象上に投げている実行コマンド

---略------

Serverspec導入を振り返って

良かった点

作業時間の短縮

これまでは手動で確認していた部分が多かったために、app以下を正しく書くだけでチェックができる状態になったことで、サーバー構築後のチェックの作業コストは大きく下がりました。特に、サーバーの台数の多いアプリになると、短縮幅が大きく感じられます。
例えば、5台の構成のアプリのセットアップがあると、丸々1週間サーバー構築に全リソースを注いでいたところ、手動チェックが減った分、一週間のうち1/3くらいは他のことができるという感覚です。

ヒューマンエラーの防止

当たり前ですがチェックが自動化されているために、手動でのチェックよりもミスが出にくいと感じています。標準出力が想定と一致しているか確認するという作業は基本は不要のために 長時間集中する必要がないことも、ミスが出ない原因になっていると思います。

フォルシアのServerspecの課題

Ansibleとの対応のためのメンテナンスコスト

Ansibleの設定変更があった際には、都度Serverspecも改修しなければなりません。二重管理に近い形になってしまっていることが課題です。今後は、AnsibleとServerspecのCIを回すことでAnsibleの設定変更を追いやすくすることを考えています。

全ての項目を網羅していないこと

サーバー間疎通など、構築時に設定するアプリ固有の内容の一部については、現状実装していません。実装したとしても、app以下を書くコストが大きくてServerspecよりも手動で確認した方が楽なので、フォルシアでServerspecを導入している目的である確認のコスト削減に繋がらないためです。
しかし、Serverspecで何が確認できて何は手動で確認しないといけないか、という管理が必要になるという問題があります。サーバー構築のたびに「あれ、この項目はServerspecで確認できるんだっけ?」と考える必要があり、Serverspecで全てが自動化できているわけではない点が惜しいと感じています。
アプリ固有の内容は都度実装する必要がありますが、各アプリレベルで見ると一度に構築する台数は一桁のケースが多く、この部分の自動化の旨味は大きくありません。今後、規模の大きい案件を対応していく中で、コストパフォーマンスを考えながら検討を進めていきたいです。

この記事を書いた人

黒坂遼

2017年新卒入社、エンジニア。
システム部にて、顧客・社内サーバーのインフラ領域の構築・運用の業務を主に担当。
最近業務の中でクラウドの接点・知識が増加中。