DockerでローカルにDBを立ててテストをもっと快適に!

こんにちは、Airシリーズ海外開発チーム 15新卒フロントエンドエンジニアの蔀です。
普段は見習いFEとして、画面を作っています。

Airシリーズ海外開発チームでは、DockerでアプリケーションやDBのコンテナを立て、継続的インテグレーションに役立てています。

チーム内では自動テストが文化として定着しつつある一方で、共有しているMac mini上のDBコンテナに負荷が集中して、テスト実行時間が長くなっていました。
そこで、Docker Registryという技術を用いて、最新のDDLを適用したDBのイメージをチームメンバーに配布し、各自のローカルでDBを用いたテストを実行できるようにしてみました。

背景

チームの環境

  • バックエンドのエンジニアは4名
  • DBはOracle
  • 3名がMacで、1名がWindows
  • チーム内で1台のMac miniを共有し、その上にDockerコンテナを立ててCIをしている

チームの課題

開発が進み、自動テストをする人が増えた時に、チームメンバーから次のような声がありました。

「なんかテスト失敗すると思ったら、他の人とテスト実行被ってDBの中身変わってるじゃん……」 「Mac miniが他の作業してるからDBコンテナが重い……」 「そっかDDLがもう変更されてるんだ……知らなかった……」

このままではCIが捗らない!俺たちは爆速で開発がしたい!
そんなチームメンバーの思いに答え、自分が立ち上がりました。

やったこと

  • 各DDLのバージョン毎のDBイメージを、共有Mac miniに置いたRegistoryを使ってチーム内に向けて共有した
  • 各エンジニアがRegistoryからもってきたDBイメージを起動するだけで、共有のMac mini上ではなく、各自のローカルにDBコンテナを立てることができるようにした

Docker Registryって何?

簡単に言うと、無料のプライベートなDocker Hubです。
チーム内や社内のみで共有したいDockerイメージを保存・共有することができます。
Docker Registry公式ドキュメント

通常のDocker Hubだと、イメージを世間に公開することになり、外部に公開したくないイメージの管理には不向きです。
Docker Trusted Registryという、社内ネットワーク上あるいはVPC上にDocker Hubを構築してくれる有料サービスもあるのですが、 チームのみで使うということもあり、今回は無料で使えるDocker Registryを選択しました。

対象環境

  • Mac OSX 10.10.5以上
    (Windowsでも可能ですが、今回はMac OSXユーザを対象に記事を書きました。)
  • Docker Toolbox 1.9.1以上

構成

今回の構成は、以下のようになっています。 Docker Registry 構成図 Docker RegistryのサーバとしてMac miniを使用しています。
サーバ(Mac mini)も、クライアント(チームメンバー所有のMac Book Pro)も、どちらもMac OSXの上にVirtualBoxを載せ、その上でDocker VMを動かしています。
VirtualBoxでポートフォワーディングをしています。

ローカルマシンでテストDBのイメージを作成したら、Mac mini上のRegistryにpushし、
それを他の人がpullして使う、という形式にしました。

DBのイメージを作る

DBのイメージはOracle、 MySQLなどありますが、私のチームではOracleイメージにすることにしました。
最初にチームで共有したいDBのイメージを作成します。 DBイメージ作成過程

  1. Dockerfileを作ってベースとなるコンテナをビルド

  2. DDLをコンテナにマウントして起動→DDL適用

  3. docker commitでイメージを作成

Dockerfileは以下のようにします。
起動時のコマンドをENTRYPOINTにしているのは、毎回起動するたびにDDLを流すのを防ぐためです。

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
FROM wnameless/oracle-xe-11g
ADD scripts /scripts
# scriptsというディレクトリにddl.shというシェルスクリプトを用意するとします。
RUN cp /scripts/ddl.sh /usr/sbin/ddl.sh && chmod +x /usr/sbin/ddl.sh
EXPOSE 22
# Oracle接続用のデフォルトポートを開ける
EXPOSE 1521
EXPOSE 8080
ENTRYPOINT ["/usr/sbin/ddl.sh"]

ENTRYPOINTに渡しているDDL用のシェルスクリプトは、以下のようにしてみます。 Oracleを起動し、DDLを流しています。

scripts/ddl.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
set -e
echo "Starting Oracle..."
export ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe
export PATH=$ORACLE_HOME/bin:$PATH
export ORACLE_SID=XX
# Oracle起動
sh -c "/usr/sbin/startup.sh"
echo "Oracle startup complete"
# create schema & table space
if [ "$1" = 'runscript' ]; then
# ddlというディテクトリの下に3つのDDLを用意しており、これがコンテナ起動時に流れます。
cd /ddl
sh -c setup_users.sh
sh -c setup_items.sh
sh -c setup_shops.sh
fi
exec /usr/sbin/sshd -D

このイメージをビルドします。これが今後作っていくDBイメージのベースになります。

1
$ docker build -t basedb .

先ほど作ったイメージを元にコンテナを起動します。
-vオプションでDDLの入ったディレクトリをコンテナにマウントしましょう。
Dockerコンテナを起動するとDDLが流れるので、docker commit して再びイメージにします。

1
2
$ docker run -d --name basedb -v $PWD/ddl:/ddl  basedb runscript
$ docker commit basedb db:v1

最新のDDL適用済みのDBイメージができました。
これをチームメンバーと共有したいですね。
そこでDocker Registryの出番です!

サーバにDocker Registryを導入する

証明書をサーバに置く

(この作業はサーバ管理者の方のみが行ってください!)

Registryにイメージをpull/pushするには、サーバ証明書が必要になります。
証明書を無視するオプションをつけてやる方法もあるのですが、動作が不安定なようですので、証明書を置きましょう。

今回はMac miniをサーバに見立てているので、Mac miniにsshでログインし、
自己署名証明書(オレオレ証明書)を作成します。

1
2
3
4
$ ssh <Mac mini環境>
$ mkdir -p certs && openssl req \
  -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
  -x509 -days 365 -out certs/domain.crt

途中でCommon Nameを聞かれるので、忘れずにサーバのホスト名を設定してください。
以降、Common Nameyourserver.comだと仮定します。

Docker Registryにサーバ証明書を渡して起動

それでは早速、Registryを起動してみましょう。

1
2
3
4
$ docker run -d -p 5000:5000 --restart=always --name registry \
  -v $PWD/certs:/certs -v $PWD/registry:/var/lib/registry \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key registry:2

以上でサーバ側の設定は終了です。次はローカルマシンの設定をしましょう。

ローカルマシン側の設定

証明書のコピー

(この作業はRegistry利用者全員が行なう必要があります!)

先ほどサーバに置いた証明書を、Docker Registryを利用するローカルマシンにコピーしましょう。
/Users/yourname/docker/certsにコピーしたとします。
証明書をコピーしたら、ローカルのDockerクライアントに証明書を信頼させる必要があります。 公式Document

1
2
3
$ docker-machine ssh default
$ sudo cp /Users/yourname/docker/certs/domain.crt /etc/docker/certs.d/yourserver.com:5000/ca.crt
$ sudo vi /var/lib/boot2docker/bootsync.sh 

bootsync.shはDocker デーモン起動時に実行されます。
中身は以下のようにしましょう。

bootsync.sh

1
2
3
#!/bin/sh
cat /var/lib/boot2docker/server.pem >> /etc/ssl/certs/ca-certificates.crt

bootsync.shを作り終わったら、以下のコマンドでDockerクライアントにRegistryを信用させ、 Dockerデーモンを再起動します。

1
2
3
4
$ openssl s_client -connect yourserver.com:5000 -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee -a var/lib/boot2docker/server.pem
$ sudo chmod 755 /var/lib/boot2docker/bootsync.sh
$ sudo /var/lib/boot2docker/bootsync.sh
$ sudo /etc/init.d/docker restart

お疲れ様でした。以上でクライアント側の設定は終了です。

Registryにpush & pull

タグを付ける

いよいよサーバのRegistryにイメージをpushしてみます。
先ほど作ったDBイメージは、そのままではpushできません。タグ付けをする必要があります。
例えば、あなたの使っているサーバのホスト名がyourserver.comで、Registry用のポートを5000番で用意しているとすると、

1
$ docker tag db:v1 yourserver.com:5000/db:v1

となります。 詳細は公式Documentでご確認ください。

pushする

それでは、docker pushしてみます。

1
$ docker push yourserver.com:5000/db:v1

何やらたくさんの出力が出てきたと思います。

1
2
3
The push refers to a repository [yourserver.com:5000/db:v1] (len: 1)
975b84d108f1: Image already exists
3f12c794407e: Image already exists

成功しているのか心配だ!という方は、実際にpull出来るかどうか試してみましょう。

pullする

一旦、先ほどのimageを削除します。

1
2
$ docker rmi yourserver.com:5000/db:v1
$ docker rmi sharedb:v1

それではpullしてみます。

1
$ docker pull yourserver.com:5000/db:v1

以下のような出力が出て、Pull completeと書かれていれば完了です。

1
2
3
4
v1: Pulling from db
a326237e967d: Pull complete
Digest: sha256:92fe60c546be9c450d96ad4b3d20faffb9dba06df61861b0d4654d80a19382
Status: Downloaded newer image for yourserver.com:5000/db:v1

docker imagesで、きちんとyourserver.com:5000/db:v1があるか確認しましょう。

以上で、イメージの共有は終了です。
お疲れ様でした。

あとは、好きなだけイメージをpush/pullして、ローカル環境でガンガンテストを走らせましょう。
もう「他の人がテスト実行してないかな?」と気にする必要もありません。
テスト時間が長いことによるイライラも軽減されます。

結果

「もう他の人がテストしてるか気にしなくていい!自由!」 「何かミスってもコンテナ潰して立て直せばいい!楽!!!」 「テストの実行速度が早くなった!」

なんとかチームの皆さんにテストを気持ちよく実行してもらえるようになりました!
テスト自動化が文化として定着しつつあるなか、快適にテストできる環境の構築は大切ですね。

(おまけ)Registry内にどんなイメージがあるか見たい!

チームでRegistryの活用が進むと、イメージが増えてゆき「どんなイメージがあったっけ?」と気になることもあるでしょう。
そこで、GUIでイメージを確認できるツールを導入してみましょう。
docker registry frontendというgit hub上のプロジェクトが、便利なGUIを提供してくれています。

docker registry frontendのキャプチャ

使い方

サーバ上でツールを起動してみます。
サーバ証明書と鍵の位置の指定に気をつけてください。

1
2
3
4
5
6
7
8
9
10
11
$ git clone https://github.com/kwk/docker-registry-frontend.git
$ cd docker-regisry-frontend
$ sudo docker run \
  -d \
  -e ENV_DOCKER_REGISTRY_HOST=yourserver.com \
  -e ENV_DOCKER_REGISTRY_PORT=5000 \
  -e ENV_USE_SSL=yes \
  -v $PWD/certs/domain.crt:/etc/apache2/server.crt:ro \
  -v $PWD/certs/domain.key:/etc/apache2/server.key:ro \
  -p 5080:80 \
  konradkleine/docker-registry-frontend:v2

yourserver.com:5080でアクセスできます(ポートフォワーディング設定を忘れずに!)

これで「DBって今バージョンいくつだっけ?」となっても安心ですね!

おわりに

メインの開発をMac OSXでしているエンジニアの方も多いと思います。
Docker Registryの仕組みをチームで使えるようになると非常に便利なのですが、
Mac OSXを前提にしたDocumentが少ないので、変なところで詰まったりすることも多いです。
この記事を元に素敵なDocker Lifeを送っていただければ幸いです!