LoginSignup
11
10

More than 5 years have passed since last update.

Perlのテスト内で、Dockerを使ってMySQLとかMiddlewareを使い回す

Last updated at Posted at 2014-10-03

背景と動機

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使おうとした場合、ローカルにQ4MHandlerSocketを入れないといけない
  • 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 killdocker rmを呼んで不要になったcontainerも破棄してくれます。
この辺は、Test::mysqldDESTROYでmysqldをkillしているのと似たような感じです。

Harrietを使うと以下のような感じになります。

t/harriet/mysqld.pl
$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を作ることで、並列実行できるようにできればと思います。
(誰か作って><)

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10