殿堂入り記事

開発環境で使うDocker入門

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

モバイルアプリサービス部の五十嵐です。

Dockerは軽量でポータビリティに優れ、環境を汚さないので開発環境には無くてはならない存在ですよね!今回はまだDockerを使っていない人向けに、私が自分が辿ってきた経験を元に、『こういうシーンでDockerを使うと便利』というのをステップ・バイ・ステップで説明していきます。

対象読者

Dockerはなんとなく知っていて興味はあるけど、使い所や導入するメリットが分からないという人を対象にしています。

環境

  • Docker for Mac(Docker 1.12.3)

Dockerについて

Dockerは複数のコンポーネントからなるプラットフォームですが、本記事では主にDocker EngineのことをDockerと呼びます。 Dockerはコンテナと呼ばれる仮想化技術を用いています。従来の仮想化技術と異なる点は、ホストのカーネルをコンテナと共有し、ホストOS上で独立したプロセスとして実行されるという点です。これにより軽量化・高速化が図られています。また、Dockerは特定のインフラに依存せず、Linux・Mac・Windowsなど様々なOS上で動作することができ、ポータビリティに優れています。

What is Docker?より。

初めての人のための記事

初めてDockerを使うという人は、まずは以下の記事を一読されると良いと思います。

Step.1 公開されているイメージを使う

一番簡単な利用例としては、Docker Hub というDockerイメージのリポジトリサービスで公開されているイメージを利用する方法です。

例えば、あるプロジェクトでは MySQL5.6 を利用していてローカルマシンに MySQL5.6 をインストールして開発をしていましたが、次のプロジェクトでは MySQL5.7 を利用することになったとします。ローカルマシンに MySQL5.6/5.7 を両方インストールして必要に応じてサービスを切り替えて利用するのは骨の折れる作業だと思います。

そこでDockerの出番です。MySQLのようなメジャーなサービスであれば Docker Hub でイメージが公開されてるので、それをpullしてローカルマシン上で起動することですぐに必要なミドルウェアを構築することができます。

# MySQL5.6の起動・停止
docker pull mysql:5.6
docker run -d -e MYSQL_ROOT_PASSWORD=password --name mysql5.6 mysql:5.6
docker stop mysql5.6

# MySQL5.7の起動・停止
docker pull mysql:5.7
docker run -d -e MYSQL_ROOT_PASSWORD=password --name mysql5.7 mysql:5.7
docker stop mysql5.7

公開されているリポジトリのうちDocker社が公式として公開しているものには official と書かれています。それ以外にも一般ユーザが公開しているリポジトリもたくさんあります。

Docker HubでMySQLを検索した結果

また最近ではDocker StoreというDockerイメージのマーケットプレイスでも提供されているようです。

Docker StoreでMySQLを検索した結果

Step.2 自分でイメージを作成して使う

次に自分でイメージを作成する例を紹介します。

例えばローカル開発用に対向システムのWebAPIのモックをRubyで作成したとします。プロジェクトでは数人のメンバーが共同で開発しているため、このモックをメンバー全員が使えるようにする必要があります。ところが各メンバーのローカル環境のRubyバージョンはばらばらで環境構築からする必要があります。

そこでDockerの出番です。Dockerfileを作成して作成したモックアプリケーションを配布可能な状態にします。実際には公開されているイメージをベースにしてカスタマイズしていくことになります。

以下はSinatraで作ったRubyアプリケーションをDockerfileでイメージ化した例です。

アプリケーションのコードはこれだけです。アクセスされたパスに対応する静的なJSONをレスポンスとして返します。

require 'sinatra'
require "sinatra/json"
require 'json'

get '/sample.json' do
  json get_json("response/sample.json")
end

def get_json(path)
  File.open(path) do |file|
    JSON.load(file)
  end
end

次にDockerfileです。

FROM に設定するのがベースとなるイメージです。詳細は端折りますがアプリケーションコードをコンテナ内に COPY し、gemをインストールしてrubyアプリケーションを起動しています。モックAPIのレスポンスは静的ファイルになっており、利用者が編集したいのでホストのボリュームにマウントすることにします。

内容と関係ないのですが地味にハマったのはsinatraアプリケーションはdevelopment環境起動では外部からのアクセスを受け付けないようになっているので -o 0.0.0.0 で外部からのアクセスを許可するようにするオプションが必要なことでした。

FROM ruby:2.3.2

WORKDIR /app
COPY app.rb /app/app.rb
RUN mkdir /app/response

RUN gem 'sinatra'
RUN gem 'sinatra-contrib'

EXPOSE 80

CMD ["ruby", "app.rb", "-p", "80", "-o", "0.0.0.0"]

Dockerfileからイメージを作成するには docker build コマンドを実行します。

docker build -t mock-api .
docker run -d -p 8080:80 -v ./response:/app/response mock-api

作成したDockerfileをGitHubなどで共有すればメンバーへの展開も容易です。以下のイメージでは、DockerfileをGitHubへpushし、別のメンバーがDockerfileをpullしてイメージをビルドし実行する流れを示しています。

Step.3 イメージを組み合わせて使う

次にDocker同士をネットワークで接続したい場合についてです。例としては以前に書いたエントリー

AWS Elasticsearch Serviceと同じバージョンの環境をDockerで作る方法 | Developers.IO

の中でElasticsearchとKibanaを別々のコンテナとして起動し、KibanaのコンテナからElasticsearchのコンテナに接続する例を紹介しました。

docker run コマンドを実行時にオプションで --link [接続したいイメージ] を設定することでDocker同士をネットワークで接続した状態にすることができます。

Step.4 アプリケーション本体もDockerで起動する

さらに、アプリケーション本体もDockerコンテナとして起動することでアプリケーションに依存するミドルウェアもコンテナに含めることができますし、他のリソース(MySQLやmock-apiなど)とlinkさせることで、ホスト側にバインドされるポートはアプリケーション本体だけで済むのでホストマシン上でのポートの競合が少なくなり幸せになれそうです。

やることとしては、Step.2 と同じ要領でアプリケーション本体をイメージ化するためのDockerfileを作ってコンテナとして起動して、Step.3 と同様に必要なリソースとlinkさせるだけです。

Step.5 DockerComposeで構成管理をする

さて、Dockerに慣れてくると1つのプロジェクトでいくつものDockerコンテナを利用する状態になってきます。MySQL、Elasticsearch、Kibana、Redis、DynamoDB、fakeS3、fakesmtpなどが起動しています。しかもそれがプロジェクトごとにあります。

こうしたいくつものDockerコンテナをまとめて管理できるコンポーネントとしてDocker Composeがあります。Docker Composeは docker-compose.yml という設定ファイルを書くことで複数のDockerコンテナをまとめて管理することできます。

例として実際に使っている docker-compose.yml からいくつかの設定をピックアップしました。Dockerの色々なコマンドを設定ファイルとして記述できるものと思ってもらえれば良いと思います。Dockerfileからイメージの作成する場合は、 build: にDockerfileのパスを書けばビルドから起動まで1コマンドでやってくれます。

# MySQL
mysql57:
  image: mysql:5.7
  ports:
    - 3306:3306
  environment:
    - MYSQL_ROOT_PASSWORD=password
    - TZ=JST

# Elasticsearch1.5
elasticsearch:
  image: elasticsearch:1.5
  ports:
    - 9200:9200
    - 9300:9300

# Kibana4.0.3
kibana:
  image: kibana:4.0.3
  ports:
    - 5601:5601
  environment:
    - ELASTICSEARCH_URL=http://elasticsearch:9200
  links:
    - elasticsearch

# Mock API
mock-api:
  build: ./mock/api
  ports:
    - "8080:80"
  volumes:
    - ./mock/api/response:/app/response

docker-compose.yml ファイルがあるディレクトリで docker-compose コマンドを実行してコンテナを起動・停止します。

# 全てのコンテナを起動
docker-compose up -d

# 全てのコンテナを停止
docker-compose stop

まとめ

私が実際に利用しているパターンを紹介しました。まだDockerを導入していないという人は是非使ってみてください。開発環境の構築にかかる時間が劇的に減りますよ!

また今後は、本番環境でもDocker(AWS ECS)を使っていきたいと考えているので、本番環境で使う上で必要なことを調査していきたいと思います。