はじめてのDocker 導入から開発の流れまで

# さぁDockerを始めよう
Dockerに関しての入門記事。ここにあるfileは動作確認はしておりません。

## こんなあなたに

* 開発環境を構築するのが非常にしんどい
* グループで開発しているが後輩がまだステージング領域の環境構築ができない
* グループで開発環境を一致させたい

# Dockerならできること

* apacheなどミドルウェアのインストールが楽チン
DockerHubから公式のイメージを引っ張ってきてコンテナを作るだけ。

* あ、このミドルウェア削除したい。...けど色々依存関係を削除するの面倒い。
コンテナを削除するだけで、依存関係をすべて削除できる。クリーンな環境を提供。

* Dockerの知識を覚えるのが面倒？Dockerなら次の1行でnginxを導入できます

```terminal
docker container run -d -p 80:80 nginx
```

利点に関してはこちらの記事にとてもよくまとまっています。
[Docker導入のための、コンテナの利点を解説した説得資料](https://qiita.com/niisan-tokyo/items/88a53a1b4aa7ad60723e)

# Web開発知識
[おうちで学べるデータベースの基本](https://books.google.co.jp/books?id=JCjABgAAQBAJ&pg=PT103&lpg=PT103&dq=データベース%E3%80%80アーキテクチャ%E3%80%80歴史&source=bl&ots=WlZo24Q0qf&sig=nEHr9Hh9Nwd1iHmJTTLkxPX7SNA&hl=ja&sa=X&ved=0ahUKEwjX0rLTi7TbAhVFWbwKHaeTBlYQ6AEINzAB#v=onepage&q=データベース%E3%80%80アーキテクチャ%E3%80%80歴史&f=false)
[エンジニアHub](https://employment.en-japan.com/engineerhub/entry/2017/11/22/110000)

# Docker概要
Dockerの仕組み自体は以下のサイトで確認してください。~~ぶっちゃけ下のサイトみたらこの記事読まなくても良い。~~

* [オブジェクトの広場](https://www.ogis-ri.co.jp/otc/hiroba/technical/docker/part1.html)
* [qiita Docker入門#1](https://qiita.com/wMETAw/items/b9bc643ded4b92bf6add)
* [さくらナレッッジDocker入門](https://knowledge.sakura.ad.jp/13265/)
* [Dockerドキュメント日本語化プロジェクト](http://docs.docker.jp/index.html)
* [@IT 超入門Docker](http://www.atmarkit.co.jp/ait/series/5086/index.html)

# CentOSへの導入の仕方
CentOS7への導入の仕方は3通りあります。詳しくは[公式のホームページ](http://docs.docker.jp/engine/installation/linux/docker-ce/centos.html)を参照ください。こんかいは1のリポジトリを使ったインストールを前提とします。解説は優秀な記事に丸投げで。

1. リポジトリを使ったインストール
2. パッケージからのインストール
3. 便利なスクリプトを使ったインストール

## リポジトリを使ったインストール
[公式のホームページ](http://docs.docker.jp/engine/installation/linux/docker-ce/centos.html)
[qiita CentOS7にDockerをインストールする](https://qiita.com/inakadegaebal/items/be9fecce813cebec5986)

# Docker Hello World
ダウンロードしたら早速Dockerが使えるか``Hello World``をしましょう。

```terminal
docker run hello-world
```
すると下のような画面がでたらインストール成功です.

```terminal
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c04b14da8d14: Pull complete
Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
```

# Dockerを構成するものたち

## Container
Dockerの肝となる機能。OS上に論理的な区画分けを作る。そこでアプリケーションやライブラリを1つにまとめ、あたかも1つのサーバーであるように動かす。

## Image
昔IT関連の本にはソフトをダウンロードするCDが付いていた。ImageとはそのようなCDを想像してほしい。コンテナを作るひな形になる。その中身は、アプリケーションの実行に必要なファイル群を含むディレクトリである。Dockerコマンドを使うとimageをtarファイルに出力できる。

環境構築の方針としては、まず必要なイメージ(railsやmysqlなど)をDockerHubから引っ張ってきてそれを元にコンテナを作っていく。このとき後述のDockerComposeを使う。

## Volume
containerは簡単に作成・削除ができるようになっており、containerを停止すると初期ファイル以外の内容が飛んでしまう。つまり途中で付け加えるデータ(データベースなど)はcontainer以外の部分に避難させなければならない。それがVollumeである。ちなみにこのVolumeに接続することをマウントするという。

また同様の理由から、railsの``bundle install``はサーバーを再起動させる(Dockerのcontainerをリビルドする)必要がある。そうしないとせっかく``bundle install``してもcontainerを停止するとライブラリの内容は飛んでしまう。

## Dockerfile
DockerfileとはDockerのコンテナの設計図。Dockerで``Hello world``したようにDockerは設計図を書かなくてもコマンドからコンテナを作成できる。しかし現在の構成を全体で共有する時やメンテナンスの点から見てもDockerfileを書くべき。以下の命令リストを見ながら実際のDockerfileを解読してみよう。

コマンドの説明は「プログラマのためのDocker教科書 第2版」を参照。

|命令|説明|
|---|---|
|FROM|ベースイメージの指定|
|RUN|コマンド実行|
|CMD|コンテナの実行コマンド|
|LABEL|ラベルを指定|
|EXPOSE|ポートのエクスポート|
|ENV|環境変数|
|ADD|ファイル/ディレクトリの追加|
|COPY|ファイルのコピー|
|ENTRYPOINT|コンテナの実行コマンド|
|VOLUME|ボリュームのマウント|
|USER|ユーザーの指定|
|WORKDIR|作業ディレクトリ|
|ARG|Dockerfile内の変数|
|ONBUILD|ビルド完了後に実行される命令|
|STOPSIGINAL|システムコンールシグナルの設定|
|HEALTHCHECK|コンテナのヘルスチェック|
|SHELL|デフォルトシェルの設定|

railsコンテナ

```dockerfile
FROM ruby:2.5.1
RUN apt-get update -qq && \
    apt-get install -y apt-utils \
                       build-essential \
                       libpq-dev \
                       nodejs \
                       mysql-client
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install -j4
ADD . /app

EXPOSE 3000
```

mysqlコンテナ

```dockerfile

FROM mysql:8.0

RUN apt-get update && \
    apt-get install -y apt-utils \
                       locales && \
    rm -rf /var/lib/apt/lists/* && \
    echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
    locale-gen ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ADD ./docker/mysql/charset.cnf /etc/mysql/conf.d/charset.cnf
```

nginxコンテナ

```dockerfile
FROM nginx:1.11
RUN apt-get update && \
    apt-get install -y apt-utils \
                       locales && \
    sed -i -e 's/# ja_JP.UTF-8/ja_JP.UTF-8/g' /etc/locale.gen && \
    locale-gen ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LC_TIME C
ADD ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
ADD ./docker/nginx/rails.conf /etc/nginx/conf.d/default.conf
```

# Docker composeを使う
通常web開発するとき以下のものが必要になってくる。

* ウェブサーバー
* アプリケーションサーバー
* railsなどのフレームワーク
* データベース
* redis
* elasticsearch

これらを同一のDockerfileに書き込むとメンテンアンス性も下がる膨大なコンテナができてしまう。公式でも1つのコンテナに1つのミドルウェアを推奨している。そして、複数のコンテナを利用するのに便利なソフトがDockerComposeだ。DockerComposeファイルにそれぞれcontainerの起動の仕方をyaml形式で書いていく。Dockerfileを利用もできる。基本的なキーの意味は以下の通り。

```
version: '2'
services:
  db:
    image: postgres
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```

|キー|説明|
|---|---|
|version|Compose定義ファイルのバージョン。DockerEngineのバージョンに依存する.|
|services|管理したいコンテナのサービスを定義|
|networks|管理したいコンテナのネットワークを定義|
|volumes|管理したいコンテナのボリュームを定義|
|image|imageを指定|
|build|イメージの構成をDockerfileに記述している場合、それを自動でビルドしてベースイメージに指定するときはbuildを指定する。Dockerfileのパスを指定する。|
|command|コンテナ内で動かすコマンドの指定をする。
|entrypoint|entrypointを上書きする。|
|links|コンテナ間の連携。連携先のコンテナ名を記述する。サービス名に別名をつけたいときは「サービス名：別名」を設定|
|volumes|コンテナのデータを管理する。接続先のボリュームのパスを指定する。|
|volumes_from|別のコンテナからすべてのボリュームをマウントするときに、コンテナ名を指定する。|
|ports|コンテナ間の通信。公開するポート番号を指定するか、「ホストマシンのポート番号:コンテナのポート番号」とする。ホストマシンのポート番号を指定しない場合、ランダムな値が設定される。|
|exports|ホストマシンへポートを公開せず、リンク機能を使って連携するコンテナのみポートを後悔するときに指定する。ログサーバーなど、webアプリケーションサーバー機能を持つコンテナを介してのみアクセスを行いたいときに使う。|
|depends_on|サービスの依存関係を定義。依存先コンテナ名を書いておくことで順番に起動される。しかしコンテナの開始の順序を制御するだけで依存先のアプリケーションが利用可能になるまで待つわけではない。|
|environment|コンテナの環境変数を定義|
|env_file|環境変数ファイルを読み込む|
|container_name|コンテナの情報設定。DockerComposeで生成されるコンテナに名前をつける。|
|labels|コンテナにラベルをつける|


# DockerComposeのインストール
[公式サイト](http://docs.docker.jp/compose/install.html)参照

# Dockerとrailsとmysql
この記事を参考に初心者向けに解説。ちなみにミドルウェアがどれが良いかは省略。
[nginx + Rails (unicorn) + MySQL 環境を Docker で構築する](https://qiita.com/utahkaA/items/772cd80b893cd5367f38)

なおwebサーバーやappサーバーの違いはジャスティンさんの記事が秀逸.
[A Web Server vs. an App Server](https://www.justinweiss.com/articles/a-web-server-vs-an-app-server/)


# その他応用的な知識

* docker machine
* docker swarm