Go 言語で Docker を使うテストを書く時

日記。
Docker コンテナ内で立ち上げたサーバーに繋ぐテストを書く時の話。
テスト開始時にコンテナを立ち上げて、テストして、終了時に落とすみたいな奴。

コマンドや API を叩いてコンテナの実行が始まった直後に繋ぎに行くと、まだコンテナ内で listen が始まっていないのが原因で失敗する、ということがしばしば起こる。
この時の対応策は色々あるだろうけども、1番安直な方法は恐らく「成功するまで sleep とリトライを繰り返す」という類のものだと思う。
(コンテナ自体が不安定な場合は起動失敗してないか調べるのも必要そう)

この sleep の部分を「netstat の出力で listen しているか調べる」という、これまた安直な方法で実現すれば、汎用的に使えるリトライ不要な手段になるのではないだろうか? と思い、少し作ってみることにした。

https://github.com/oov/dockertest
コードの一部はこの記事を参考にさせてもらった。

パッケージを作る前に手作業で試してみた時点で「コンテナ内に netstat 自体が存在しない」という理由で上手くいかず/proc/net/tcp などを見に行くようにアプローチを変えざるを得なかったものの、Docker Hub にあるオフィシャルの MySQL, PostgreSQL, Redis, MongoDB と、dnsmasq(andyshinn/dnsmasq) に繋ぐのを試した限りでは概ね成功した。

概ね、というのは PostgreSQL は listen が始まった直後だと "the database system is starting up" と言われて接続に失敗することがあり、結局リトライする処理が必要になったため。
他のプログラムでも設定などによっては listen したあと更に準備時間が必要なケースもあるかも知れないので、結果的には思惑ほど全て上手くは行かなかったことになる。ちょっと残念。

環境変数 DOCKER_HOST の有無も考慮して動くようにしたおかげでテストに手を加えなくても Windows 環境でも Docker Quickstart Terminal とか boot2docker を経由してテストを実行できるし、パッケージとしては作ってよかったと思う。

ところで、普段 Go 言語を使って遊んでいるのは自宅サーバの Ubuntu で作曲とかに使うマシンは Windows なんだけど、このテストを両方の環境で実行したら思っていたより速度差が大きかった。

CPU: Intel Atom D510 1.66GHz / Memory: 4GB / OS: Ubuntu 14.04
=== RUN   Example_dnsmasq
--- PASS: Example_dnsmasq (2.98s)
=== RUN   Example_mongoDB
--- PASS: Example_mongoDB (4.61s)
=== RUN   Example_mySQL
--- PASS: Example_mySQL (55.38s)
=== RUN   Example_postgreSQL
--- PASS: Example_postgreSQL (17.30s)
=== RUN   Example_redis
--- PASS: Example_redis (2.46s)
CPU: Intel Core i7-4790K 4.00GHz / Memory: 32GB / OS: Windows 10
=== RUN   Example_dnsmasq
--- PASS: Example_dnsmasq (1.08s)
=== RUN   Example_mongoDB
--- PASS: Example_mongoDB (0.87s)
=== RUN   Example_mySQL
--- PASS: Example_mySQL (6.91s)
=== RUN   Example_postgreSQL
--- PASS: Example_postgreSQL (4.49s)
=== RUN   Example_redis
--- PASS: Example_redis (0.96s)

普段の環境で毎回1分近く掛かるのは結構辛いし、テスト開始時にコンテナを起動するか、名前をつけておいた起動済みコンテナを利用するのか自動で判断する仕組みが必要かもなぁ、と思ったのだった。

---- 追記 2015-11-20

起動済みのコンテナを再利用する仕組みを追加したところ、遅い環境でも1秒程度で終わるようになった。
名前しかチェックしていないのでイメージやパラメータが違う状態で起動しているのも利用できてしまったりするけど、厳密にやり過ぎても使いにくいかも知れないのでその辺りは緩めにしておくことにした。
同じパラメータで起動したい時は Container.RunArgs を利用するとコピペできて便利。