こんにちは、WEBエンジニアのチョウです。
1つのサービスに新機能がどんどん追加されると、ロジックが複雑になり、開発やメンテナンスは難しくなります。その解決方法はいつくかあると思いますが、1つの方法として、1つのサービスを複数サービスに分割して別々で開発するというやり方があります。複数サービスへの分割には、スケールアウトに優れているなどのメリットがありますが、テストの実施やインフラの準備は以前よりも難しくなります。今回、複数サービスにおけるローカルテストについてすこし共有したいと思います。
普通のローカルテストはアプリケーションを立ち上げて、ブラウザでテストするのです。複数サービスの場合は、必要なサービス一つ一つを起動しないといけないです。そこで、設定の問題だったり、サービスが依頼する環境の問題だったり、一回だけならいいですが、毎回やらないといけないと無駄に時間かかります。もし設定は事前に用意できて、環境はちゃんと管理されているツールがあれば一番いいかなと思う人はきっといますでしょう。
AWSにすでにAMIみたいなサーバーインスタンスのsnapshotを取る仕組みがあります。ローカルの仮想マシンでsnapshotを取ってテストするのも可能ですが、パソコンで同時に起動できる仮想マシンが限られてるようで、AMIのやり方でテストするのは非現実的だと思います。もう1つの方法は、dockerなどのコンテナ技術です。
コンテナ技術というのはcgroupsなどの仕組みを利用して、サーバー一台で複数サーバーのように、1サーバー1アプリケーションを起動します。cgroups自体は「プロセスグループのリソース(CPU、メモリ、ディスクI/Oなど)の利用を制限・隔離するLinuxカーネルの機能」です。コンテナ技術の中で一番有名なのはdockerです。
つまり、dockerを利用すれば、一台サーバー(dockerの仮想マシン)で複数サービスを起動することが可能になります。もっと正確にいえば、dockerで各サービスのimageを作って、docker-composeを使って複数サービスを起動してテストすることができます。
最近こちらが携わったプロジェクトを紹介します。サービスの構成
nginx ----> Service A ----> Database A
|
--> Service B ----> Database B
元々これらのサービスを起動するには
- Service Aを起動する
- Service Bを起動する
- ローカルのnginxを設定し、再起動する
- もちろん、ローカルにDBなども起動
dockerを使う場合
docker-compose up -d
で一気に起動できる。
もちろん、設定ファイルが必要です。docker-compose.ymlでこんな設定があります。
version: '2'
services:
haproxy:
image: 'nginx'
ports:
- '8081:8081'
depends_on:
- service-a
- service-b
service-a:
image: 'service-a'
command: rails s -p 3000
volumes:
- /path/to/service/a:/usr/src/app
depends_on:
- service-db
environment:
RAILS_ENV: development
service-b:
image: 'serivce-b'
command: rails s -p 3001
volumes:
- /path/to/service/b:/usr/src/app
depends_on:
- service-db
environment:
RAILS_ENV: development
service-db:
image: 'postgres:9.4'
volumes:
- data-service-db:/var/lib/postgresql/data
volumes:
data-service-db:
docker-compose.yml自体は複数のdocker containerを組み合わせる設定ファイルで、パラメータ名は基本dockerがcontainer作る時のパラメータと同じです。docker containerというのは、docker imageのインスタンスです。docker imageはアプリケーションの稼働環境と理解してもいいです。docker imageを作るには、Dockfileを利用し、アプリケーションが依頼してるソフトウェアのインストールしたり、アプリケーションのファイルをコピーしたりして、アプリケーションバイナリのように1つコマンドで起動できるリソースグループを作ります。
Railsアプリケーションにとっては、ローカルで開発する場合が多いですので、Gemfile/Gemfile.lockだけでdocker imageを作って、docker-compose.ymlの中でソースコードをマッピングするほうがおすすめします。
例えば、こんなDockerfile
FROM ruby:2
RUN echo 'deb http://http.debian.net/debian jessie main' >> /etc/apt/sources.list && apt-get update && apt-get install -y nodejs && apt-get clean && rm -rf /var/lib/apt/lists/*
RUN echo "Asia/Tokyo" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY Gemfile /usr/src/app/Gemfile
COPY Gemfile.lock /usr/src/app/Gemfile.lock
RUN bundle install
ちなみに、複数サービスではなくても、application + databaseみたいなパターンも使えます。そして、多数なサービスを起動するのもしんどいかもしれません。一部のサービスだけ変更したり、影響されるサービスが少なかったりする時、Dockerでテストするのは楽になると思います。サービスの数が多くなれば、リモートでサーバーの立ち上げて、リモートのサーバーでテストするほうがいいかもしれません。
いかがでしょうか。複数サービスがかかわるプロジェクトでDockerを使ってみませんか。