業務で詰まっていたので展開します
環境背景
Test Kitchen & Ansible という構成の、サーバ構築自動化テストの仕組みがある。
具体的に言うと、Test Kitchen で仮想サーバ(vagrant)を作成し、
それに対して Ansible を走らせる、という仕組み。
テストは github の CI/CD で走らせていた。
Test Kitchen について
Test Kitchen は、Chef によるサーバ構築などのレシピを
効率よくテストするためのツール。
テスト用の VM 作成、レシピの適用、テスト実施、VM 削除を
一度に実行することができる。
基本、yaml ファイルでテストの定義を記述する。
※公式サイト
※書き方次第では Ansible のテストにも使えます
環境構成
※業務に関係する情報であるため、本記事に関連する最低限の内容を記載
- OS(Linux Distribution): CentOS Linux release 7.7
- Test Kitchen: version 2.1.0
- Vagrant: version 2.2.16
やろうとしたこと
以下の様なホストを作り、テストを通そうとした。
- Instance 名(仮称)
- ins-centos
- stg-ins-centos
* .kitchen.yml テスト部分の抜粋
driver:
name: vagrant
pre_create_command: <構築前に実行するコマンド>
~中略~
suites:
- name: ins-centos
driver:
vm_hostname: ins-centos.local
provisioner:
extra_vars: <vars file 名>
- name: stg-ins-centos
driver:
vm_hostname: stg-ins-centos.local
provisioner:
extra_vars: <vars file 名>
起きたエラー
stg-ins-centos のテストは成功するが、
ins-centos のテストで毎回 fail する。
ERROR: Job failed: exit status 1
テストで実行しているコマンドは以下。
bundle exec kitchen test ins
Ansible の部分や、test spec では特にエラーが出ていなかった。
調査
CI/CD のログを確認
CI/CD の実行結果途中に以下の例外が出ていた。
>>>>>> ------Exception-------
>>>>>> Class: Kitchen::UserError
>>>>>> Message: Unknown lifecycle hook target {}
>>>>>> ----------------------
>>>>>> Please see .kitchen/logs/kitchen.log for more details
>>>>>> Also try running `kitchen diagnose --all` for configuration
Exception 欄に、
Unknown lifecycle hook target {}
という kitchen のエラーが出ている。
エラー内容を確認
早速、このエラーを検索してみると、
github の test-kitchen の issue に同様の問題の記事を発見した。
https://github.com/test-kitchen/test-kitchen/issues/1498
どうやら、.kitchen.yml で定義している pre_create_command が原因らしい。
複数の suites(複数インスタンスへのテスト)を同時に実行する場合、
pre_create_command を指定するとエラーになるようだ。
* kitchen.yml の設定(再掲)
driver:
name: vagrant
pre_create_command: <構築前に実行するコマンド>
◆↑ここが問題の箇所
~中略~
suites:
- name: ins-centos
driver:
vm_hostname: ins-centos.local
provisioner:
extra_vars: <vars file 名>
- name: stg-ins-centos
driver:
vm_hostname: stg-ins-centos.local
provisioner:
extra_vars: <vars file 名>
ただ、今回の場合、
テストするインスタンス名を個々に指定していたにも関わらず、このエラーが出た。
(インスタンス毎にテストを実施しているので、エラーは出ないはず)
CI/CD のログを再確認
不思議に思ってログを見てみると、以下の記述を発見した。
$ bundle exec kitchen destroy ins
-----> Starting Kitchen (v2.1.0)
-----> Destroying <ins-centos>...
Finished destroying <ins-centos> (0m0.00s).
-----> Destroying <stg-ins-centos>... =>指定してないインスタンスがいる
Finished destroying <stg-ins-centos> (0m0.00s).
-----> Kitchen is finished. (0m2.75s)
最後の destroy コマンドにて、
テストをした ins-centos の destroy だけでなく、
stg-ins-centos の destroy まで走っている。
どうやら、kitchen コマンドで指定するインスタンス名は
REGEX(正規表現)として扱われるようで、
ins が stg-ins-centos にも部分一致していたらしい。
その結果、テストが2台のインスタンスに対して実施され、
前述の pre_create_command を2台に適用しようとしてエラーになっていたようだ。
エラーの原因
エラーの原因は2つ
1. 直接の原因
複数の suites(複数インスタンスへのテスト)を同時に実行する場合、
pre_create_command を指定するとテストがエラーになる。
(kitchen create の時点でエラーになるようだ)
2. 間接的な原因(今回の真因)
今回、Test Kitchen で実行したコマンドは以下の通り。
bundle exec kitchen test ins
意図としては、ins(ins-centos)にだけテストを行う予定だったが、
kitchen コマンドで指定したインスタンス名は部分一致になるので、
このコマンドで実行されるインスタンスは以下の2台になる。
- ins-centos
- stg-ins-centos
さらに、本テストには pre_create_command が指定されていたので、
エラー原因1により、テストが失敗していた。
対策
1. インスタンス名が部分一致で被らないようにする(これが多分正解)
- 例:インスタンス名を prd-ins-centos, stg-ins-centos の様にする
vagrant destroy prd-ins
vagrant destroy stg-ins
◆同じような名前のホストが多い場合は、接頭辞やホスト番号をちゃんとつける。
2. kitchen コマンドに渡すインスタンス名を正規表現で完全一致にする
- 例:
bundle exec kitchen destroy "^ins-centos$"
◆確実ではあるが、長いインスタンス名を毎回記述するのは面倒でもある
3. kitchen コマンドに渡すインスタンス名は省略しないで指定する(非推奨)
- 例:
bundle exec kitchen destroy ins-centos
◆上記の例の通りに実行しても、stg-ins-centos に後方一致してしまうため、結局解決にならない
教訓
ログの最後の部分にエラーが見つからなくても、
よーく途中経過を見てみると、想定外の挙動をしていることがある。
それをログから読み取ることが大事。
(UNIX の文字色でエラーを判断している場合は見逃しに注意)
今回で言うなら、
vagrant destroy でテスト対象外のホストを削除しようとしていた部分。
自分では kitchen コマンドに1台のインスタンスを指定したはずなのに、実は(正規表現で)2台指定されていた。
※気づくのに2時間かかりました;
番外:Test Kitchen で複数の suite をテストするメリット
本記事の趣旨とは違う為、深掘りはしませんが、
同じような構成の複数ホストに対してテスト行う際は、便利だと思います。
ただ、本記事の不具合の原因でもある、
pre_create_command での事前準備ができない、という欠点もあるため、
個人的には shell などで wrap し、
Test Kitchen を1 suite(ホスト)ずつ叩いた方が良いのではないか、
と思います。