背景
今まで何度かDockerに触ろうとしては挫折してきました。Dockerを学ぶ難しさは、どの入門記事を見ても書いてあることが違う、その通りに書いても何らかのエラーが出て止まる、どこを参考にすればいいのか分からない、といったところにあると思います。半ば学ぶことを諦めていたのですが、ある時「Dockerは現代サーバーサイドエンジニアの必須科目だ」と言われてしまい、もう一度挑戦してみた時の記録です。
対象読者
- 「Dockerって何? docker-compose って何?」な読者
- 「とりあえず自分のプロジェクトをDockerで動かしてみたい」な読者
参考
環境
$ docker -v
Docker version 18.06.0-ce
$ docker-compose -v
docker-compose version 1.22.0
前提
- Dockerのインストールについては他記事を参照。
はじめに
Dockerとは
Dockerは「コンテナ」という技術を用いて環境構築を助けてくれるツールです。通常、アプリケーションの開発環境を用意する際、自分のPCのローカル環境が他の開発者のローカル環境と差がある場合があり、その差に起因して同じ手順での環境構築がうまくいかなかったりします。それを防ぐために、DockerがPCの中に全く新しい別の仮想的なコンピュータを作ってくれます。その作り方の手順を Dockerfile
というファイルに記述して他の開発者に配布することで、皆が同じ環境を作ることができるということです。
docker-composeとは
Dockerは1つの仮想的なコンピュータを立ち上げ、その中でアプリケーションを動かします。しかし実際の開発現場では、複数のアプリケーションを必要とすることが多いです。例えばRailsなら、APIサーバーにRails、DBサーバーにPostgreSQL、フロントエンドにReactといったように。
Dockerでこれらを構築しようとすると、1つのDockerfileで1つのサーバーを担当することになります。そこで、その複数のDockerがどのように関連・構成されているのかを定義する役割を負うのが docker-compose です。その構成の定義ファイルが docker-compose.yml
になります。
手順
それでは実際に Docker / docker-compose を用いて Rails + PostgreSQL + React の構成で開発環境を構築してみましょう。
ディレクトリ構成
下記のような構成でアプリケーションを構築していきます。
. # プロジェクトルート
|- api # Railsアプリケーションを置くディレクトリ
|- front # Reactアプリケーションを置くディレクトリ
|- docker-compose.yml # アプリケーションの構成を定義するファイル
事前準備
Railsプロジェクトを生成
$ rails new api --api -d postgresql
データベースへの接続情報を記述する
default: &default
adapter: postgresql
encoding: unicode
host: db
username: postgres
password:
pool: 5
development:
<<: *default
database: api_dev
test:
<<: *default
database: api_test
production:
<<: *default
database: api_production
username: api
password: <%= ENV['API_DATABASE_PASSWORD'] %>
gemをインストール
$ bundle install --path vendor/bundle
普通に bundle exec rails s
で起動できる状態にしておく。
React + TypeScriptプロジェクトを生成
$ npm install -g create-react-app
$ create-react-app front --scripts-version=react-scripts-ts
普通に npm start
で起動できる状態にしておく。
Rails用のDockerfileを用意
Railsプロジェクトのルートに下記のDockerfileを設置する。
FROM ruby:2.5.1
WORKDIR /app
COPY Gemfile /app
COPY Gemfile.lock /app
COPY . /app
RUN apt-get update -qq && apt-get install -y build-essential nodejs libpq-dev postgresql-client
RUN gem update bundler && bundle install
解説
-
FROM ruby:2.5.1
: 公式が用意した既存のRuby 2.5.1用の環境をテンプレートとしてダウンロードする。 -
WORKDIR /app
: コンテナ内のワーキングディレクトリを/app
に設定する。 -
COPY Gemfile /app
: ホスト側のGemfileをコンテナ側の/appディレクトリ内にコピーする。 -
COPY Gemfile.lock /app
: ホスト側のGemfile.lockをコンテナ側の/appディレクトリ内にコピーする。 -
RUN apt-get update -qq && apt-get install -y build-essential nodejs libpq-dev postgresql-client
: Railsの起動に必要なライブラリを apt-get 経由でインストールする。 -
RUN gem update bundler && bundle install
: gemをインストールする。
React用のDockerfileを用意
FROM "node:6-alpine"
WORKDIR /app
docker-composeを定義
version: '3.5'
services:
db:
image: postgres:10.4
volumes:
- postgres-data:/var/lib/postgresql/data
api:
build: ./api
command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
image: rails:dev
volumes:
- ./api:/app
- ./api/vendor/bundle:/app/vendor/bundle
environment:
TZ: Asia/Tokyo
RAILS_ENV: development
ports:
- 3000:3000
depends_on:
- db
front:
build: ./front
command: yarn start
volumes:
- ./front:/app
ports:
- 8000:3000
volumes:
postgres-data:
driver: local
解説
- docker-compose は1つのDockerをserviceという単位で管理する。
- 今回はPostgreSQL用のサーバーを
db
、Rails用のサーバーをapi
、React用のサーバーをfront
と名付けて設定していく。 - PostgreSQLは公式が用意した
postgres
という環境(イメージ)があれば、追加でインストールすべきもの等も特に無い。そのためDockerfileを用いず、docker-composeにイメージ名を指定するだけで環境を構築できる。 - RailsとReactについてはDockerfileを用意したので、それぞれ
build
という項目にDockerfileの位置を記入する。 -
command
: 実際にサーバーを起動する時のコマンドを記述する。Rails起動時にrm -f /app/tmp/pids/server.pid
が挟まっているのは、 docker-compose を停止する度に server.pid が削除されないまま残ってしまう問題に対処するため。 -
volumes
: 記憶領域の指定。ディレクトリ名で指定するパターンと、ボリューム名で指定するパターンがある。ボリューム名を用いる場合は最後に出てくるvolumes
を用いる。ディレクトリ名で指定する場合、<ホスト側のディレクトリ>:<対応するコンテナ側のディレクトリ>
と書けば、ホスト側でファイルが変更された時にコンテナ側にすぐ反映されるようになる。 -
environment
: 環境変数の設定。プロパティ名だけ書いて値を書かなければ、コンテナ起動時のホスト側の環境変数が自動で引き継がれるため、SECRET等はこのような方法でコンテナに渡す。 -
posts
: ホストからのアクセスをコンテナ側へ渡すプロキシを設定する。例えば8000:3000
なら、ホスト側が localhost:8000 でアクセスしてきた場合、コンテナ側の localhost:3000 に渡す。これでコンテナ内のアプリケーションにアクセスできるようになる。 -
depends_on
: 依存関係を設定する。今回の場合、例えばRailsだけを起動しようとしても、依存関係のあるdbも自動で一緒に起動される。
起動
$ docker-compose build
$ docker-compose run front yarn
$ docker-compose run api bundle exec rails db:create
$ docker-compose up
解説
-
docker-compose build
: Dockerfileを読み込んで必要なイメージをダウンロード&構築 -
docker-compose run <コマンド>
: コンテナ側のワーキングディレクトリ上で<コマンド>
を実行 -
docker-compose up
: servicesに定義された各コンテナを起動 -
docker-compose stop
: servicesに定義された各コンテナを停止
Reactアプリケーションが http://localhost:8000 、Railsアプリケーションが http://localhost:3000 で起動するはず。