背景と動機
Webアプリで複数バージョンのMySQLを使い分けたい
- 古いアプリは5.1系
- 新しいアプリは5.6系
- iPhone絵文字に対応させたいけど、5.1だとBMPしか扱えないお(´・ω・`)
- あるテストでは、MySQL5.1に繋ぎにいって、別のテストでは5.6に繋ぎにいくようにさせたい
- mysqlenvで複数バージョンのMySQLをテスト毎にswitchする?
- ローカル環境構築大変
- Jenkins Slaveにもmysqlenv入れるの?
- いずれにしても、5.1と5.6両方に繋ぐアプリの場合はうまくいかない
仮想環境を用意する
開発環境を容易に構築するために、VMWareとかの仮想イメージを用意していた。mysqldやQ4M、必要なPerlモジュールなどが一通り入っているくん。
- 開発環境の更新に追従させていくのが難しい
- いざ使おうと思ったら、用意した仮想イメージにまたなにか追加でインストールしないと使えない
- イメージの共有が難しい
- 仮想マシン起動すると重い、起動に時間かかる
ってことで、VMメンドイし重いので、ローカルのMacOSXで開発したい。MacVim使えるし、MacVim (ry
開発環境にMiddlewareを一式用意しないと
MySQL, memcached, redisとか自力で入れないとねー
- ローカルにmysqldを入れて、Test::mysqld使おうとした場合、ローカルにQ4MやHandlerSocketを入れないといけない
- OSXのバージョンが上がるたびに、結構苦労して自力コンパイル&インストールしないといけない
- mysql-build使えば、比較的簡単にインストールできるけども…
Docker imageにM/W構築して再利用
アプリ自体をDocker imageにするのはちょっとハードル高そう(?)なので、M/WだけでもDockerImageにして共有できればいいんじゃね?
DockerImage作って、docker hubに上げた
(memcachedも入れてもいいかも。別imageでもいいかと思うんでいれてない)
boot2docker start
docker pull iwata/centos6-mysql51-q4m-hs
docker pull iwata/centos6-mysql56-q4m-hs
docker run -d -t --name=mysql51 -p 3306 iwata/centos6-mysql51-q4m-hs
docker run -d -t --name=mysql56 -p 3306 iwata/centos6-mysql56-q4m-hs
docker ps
boot2docker ip
# The VM's Host only interface IP address is: 192.168.59.103
docker port mysql51 3306
# 0.0.0.0:49223
mysql -uroot -h192.168.59.103 -P49223 -e 'show plugins;select @@version;'
docker port mysql56 3306
# 0.0.0.0:49224
mysql -uroot -h192.168.59.103 -P49224 -e 'show plugins;select @@version;'
docker kill mysql51 && docker rm mysql51
docker kill mysql56 && docker rm mysql56
Testの中でこのimageをdocker run
して繋ぎにいって、test終わったら、docker kill
&&docker rm
してくれればいい。
ってことで、そんな感じのことをやってくれるCPAN Module書いた。
Test::Docker::Imageを使えば、よしなにやってくれるYO!
Test::Docker::Imageを使ってみる
use Test::Docker::Image;
my $mysql_image_guard = Test::Docker::Image->new(
container_ports => [3306],
tag => 'iwata/centos6-mysql51-q4m-hs',
);
my $port = $mysql_image_guard->port(3306);
my $host = $mysql_image_guard->host; # Docker's host IP
`mysql -uroot -h$host -P$port -e 'show plugins'`;
undef $mysql_image_guard; # destroy a guard object and execute docker kill and rm the container.
docker imageの任意のtagを指定してnew
すると、guard objectが返ってくるので、このobject経由で接続情報を取得できます。
new
した時点で、docker run
が実行されてcontainerが立ち上がった状態になります。
guard objectが破棄されるタイミングで、docker kill
とdocker rm
を呼んで不要になったcontainerも破棄してくれます。
この辺は、Test::mysqld
がDESTROY
でmysqldをkillしているのと似たような感じです。
Harrietを使うと以下のような感じになります。
$ENV{TEST_MYSQLD} ||= do {
require Test::Docker::Image;
my $mysql_image = Test::Docker::Image->new(
container_ports => [3306],
tag => 'iwata/centos6-mysql51-q4m-hs',
);
$HARRIET_GUARDS::MYSQLD = $mysql_image;
my $port = $mysql_image->port(3306);
my $host = $mysql_image->host;
"dbi:mysql:dbname=test;host=$host;user=root;port=$port";
};
上記のコードでは、MySQL用のimageを使いましたが、Redisやmemcached用のimageが用意されていれば、ローカルにはクライアントだけ入っていれば、色んなM/W使い回せるようになります。
将来的にはApp::Prove::Plugin::MySQLPoolのように、docker containerをpoolingするModuleを作ることで、並列実行できるようにできればと思います。
(誰か作って><)