ユニファ開発者ブログ

ユニファ株式会社システム開発部メンバーによるブログです。

ローカルでdockerを使って複数サービスをテストする

こんにちは、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

元々これらのサービスを起動するには

  1. Service Aを起動する
  2. Service Bを起動する
  3. ローカルのnginxを設定し、再起動する
  4. もちろん、ローカルに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を使ってみませんか。