Dockerの使い方

#**はじめに**
[株式会社サイシード](http://sciseed.jp)のインターンシップにて、**Docker**を導入しました。
本稿が少しでもみなさまの参考になれば幸いです。

#####**導入環境**

・windows7
・vagrant

#**Dockerの使い方**
##**Dockerコンテナへのアクセス**
vagrant側のポートフォワードを正しく設定して、ホスト側（PC側）からvagrantの
特定のポートにアクセスできるようにする。
コンテナへのアクセスは、以下のように行われる。
**PC　⇔　vagrant　⇔　Dockerコンテナ**
　
例えば、
######**PC　⇔　vagrant**
PCとvagrantのIPアドレスとポートフォーワードを以下のように設定

```txt:Vagrantfile 
　config.vm.network "private_network", ip: "192.168.33.10"
　config.vm.network "forwarded_port", guest: 8888, host: 8080
```

######**vagrant　⇔　Dockerコンテナ**
vagrantとDockerコンテナのポートフォワードを以下のように設定

```txt:vagrant
$ sudo docker run -d -p 8080:3000 tera_shin/centos_test:latest
```

　
#####**PCからDockerコンテナへのアクセス**
上記のような設定になっている時、**PC**からvagrantのIPアドレス（192.168.33.10）またはsshアドレス（127.0.0.1）の**8888番ポート**にアクセスすると、
**vagrant**の**8080番ポート**を介して、**Dockerコンテナ**の**3000番ポート**にアクセスすることができる。
　
##**Dockerfileについて**
参照：[Dockerfile のベストプラクティス](http://docs.docker.jp/engine/articles/dockerfile_best-practice.html)
####**Dockerfileとは**
Dockerfileは、Dockerイメージの構成内容をまとめて記述できるファイル。
"docker build"を行う際に参照され、このファイルに記述された内容が実行された上で、
Dockerイメージが作成される。
設定をコードとして明示的に記述することで、ミスを防ぎ変更を容易にすることできる。

####**１．基本**
**Dockerfileの基本的な書き方**
１行につき１つの操作を"命令"と"引数"でスペース区切りで記述する。
`命令 引数`
"#"から始まる行はコメントとして処理される。
"docker build"コマンドによって、Dockerfileファイルの上から順番に処理が実行される。
######**docker build**

```txt:vagrant
$ sudo docker build -t Dockerhubのユーザー名/任意のイメージ名 Dockerfileのディレクトリ（今いるディレクトリなら".（ピリオド）"）
```

　
######**よく用いる命令の一覧**

|命令                 |用途                |
|:------------------:|:------------------:|
|FROM	             |元となるDockerイメージの指定 | 
|MAINTAINER	     |作成者の情報                |
|RUN	             |コマンドの実行              |
|COPY	             |ファイル／ディレクトリの追加 |
|CMD	             |コンテナーの実行コマンド 1   |
|ENTRYPOINT	     |コンテナーの実行コマンド 2   |
|WORKDIR	     |作業ディレクトリの指定       |
|ENV	             |環境変数の指定              | 
|USER	             |実行ユーザーの指定          |
|EXPOSE	             |ポートのエクスポート        |
|VOLUME	             |ボリュームのマウント        |


######**記述例：**
具体的には以下のように記述する。

```txt:Dockerfile 
FROM centos:centos6
MAINTAINER tera_shin <tera_shin@gmail.com>

RUN yum -y update && \
    yum install -y \
        sudo \
        git \
        make \
        gcc \
        gcc-c++ \
        httpd

RUN groupadd -r developers && \
    useradd -r -g developers developer && \
    echo 'developer:1234' | chpasswd && \
    echo "developer ALL=(ALL) ALL" >> /etc/sudoers && \
    chown -R developer ~/ && \
    chown -R developer /usr/share/nginx/html

ENV HOME=/home/developer
WORKDIR $HOME

ENV APP_ROOT=/var/www/html
VOLUME $APP_ROOT

COPY my_app $APP_ROOT/my_app

EXPOSE 80
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
```

　
####**２．"命令"の説明**
"命令"のいくつかについて説明します。

**（１）FROM**
　"docker build"でdockerイメージを作成する際の元となるdockerイメージを指定する。
　**記述例**では`centos:centos6`を指定している。
　dockerイメージは、[DockerHub](https://hub.docker.com/)で探すこともできる。
　
**（２）MAINTAINER**
　Dockerfileの作成者についての情報を記述する。
　DockerHubのユーザー名とメールアドレスを記述するのが一般的。

**（３）RUN**
　buildしたときに元となるdockerイメージに対して実行する処理を記述する。
　`\（バックスラッシュ）`を用いて、複数行に分けることもできる。

　最新バージョンのパッケージをインストールする際は、
　`yum update`と`yum install -y`を同じ`RUN` 命令文で連結するようにする。

　**記述例**の２つめの`RUN`では、グループとユーザーを追加している。
　ユーザーを追加しておくと、dockerイメージに対して`RUN`などの"命令"を
　実行するユーザーを"root"以外にも指定することができる。
　実行ユーザーを指定する際は、`USER`を用いる。

```txt:Dockerfile
USER ユーザー名

# 例： 
USER developer
```

**（４）ENV**
　作成するdockerイメージの環境変数を設定することができる。

**（５）WORKDIR**
　処理を実行するディレクトリを指定する。

**（６）VOLUME**
　dockerイメージ内のディレクトリを`VOLUME`に指定することができる。
　`VOLUME`に指定されたディレクトリ内に対して行われるすべての変更は
　dockerイメージには含まれない。

　作成したdockerイメージを`run`する際に、
　`VOLUME`に指定されたディレクトリに対してマウントを行うと、
　コンテナの停止とともにコンテナの`VOLUME`内の変更は破棄される。

　また、`VOLUME`に指定されたディレクトリに対してマウントを行っている状態で
　コンテナをイメージ化すると、
　コンテナ内の`VOLUME`に指定されたディレクトリはイメージに含まれず、
　それ以外がイメージ化されるため、
　開発中のアプリケーションと開発環境を分離することができる。

**（７）COPY**
　dockerイメージ内に追加するディレクトリを指定することができる。
　以下のように記述する。

```txt:Dockerfile
COPY ホスト側のディレクトリ dockerイメージ側のディレクトリ
```

　※ホスト側のディレクトリは、
　　Dockerfileのあるディレクトリからの**相対パス**でも指定できる。
　　dockerイメージ側のディレクトリは、**絶対パス**で記述する。

**（８）EXPOSE**
　コンテナが外部に露出するポートを指定することができる。
　`EXPOSE`に露出ポートを指定するだけでは、コンテナにアクセスすることはできず、
　コンテナの実行時に、さらに`-p`または`-P`フラグでポートを公開することで、
　コンテナへアクセスできるようになる。

**（９）CMD**
　`run`時にコンテナ内で実行する処理を記述することができる。
　Dockerfileに１つだけ記述できる。

　CMDでは、配列内にコンマ区切りでコマンドを記述する。
　**記述例**の`CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]`は、以下と同じ意味。

```
$ /usr/sbin/httpd -D FOREGROUND
```
　
##**Dockerfileで使用しないファイルおよびフォルダの除外方法**
Dockerfileからイメージをビルドする場合、Dockerfileの存在するディレクトリの中身は
Dockerに渡される。
不要なファイルまたはフォルダまでも渡してしまうと、その分だけビルドに余計な時間が
かかってしまう。
そこで、 .dockerignoreを利用して、不要なファイルまたはフォルダを指定する。

**例:**

```txt:.dockerignore
.rspec
log
tmp
db/*.sqlite3
db/*.sqlite3-journal
vendor/assets
node_modules
```

※.dockerignoreファイルは、ファイル名を"**.dockerignore.**"と指定しすれば作成できる。
　
##**docker-composeについて**
####**docker-composeとは**
docker-composeとは複数のコンテナからなるdockerアプリケーションを定義・実行するツールで、docker-compose用の設定ファイルを用意してコマンドを実行することで、記述されたコンテナを一気に起動することができる。
　
####**１．docker-composeのインストール**
参照：[Install Docker Compose](https://docs.docker.com/compose/install/#alternative-install-options)
Docker Toolboxをインストールしている場合は、docker-composeもインストールされるので、
そのまま利用できる。
そうでない場合は、以下のようにインストールを行う。
**（１）docker-composeをインストール**

```txt:vagrant
$ curl -L "https://github.com/docker/compose/releases/download/1.11.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
```

**（２）パーミッションを与える**

```txt:vagrant
$ chmod +x /usr/local/bin/docker-compose
```
　
####**２．docker-composeの書き方**
"**docker-compose.yml**"というファイルを作成し、YAML形式で記述する。

######**よく用いる構文の一覧**

|構文                 |用途                                |
|:------------------:|:-----------------------------------:|           
|services            |各serviceの設定を束ねる               |
|image	             |元となるDockerイメージの指定           | 
|environment	     |環境変数の指定                        |
|volumes	     |ボリュームの指定                         |
|ports               |ポートフォワードの指定                 |
|extends	     |共通の設定を利用するサービスを指定      |
|command             |デフォルトのコマンドを上書きする
|links	             |リンクするサービスを指定               |
|build	             |Dockerfileのあるディレクトリのパスを指定|
|logging	     |ログを出力する場所を指定               |


######**記述例：**

```yml:docker-compose.yml
version: '2'
services:
  db:
    image: mysql:5.7.17
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
    volumes:
      - db-data:/var/lib/mysql
  redis:
    image: redis:3.2.8
    volumes:
        - ./data/redis:/data
    ports:
      - '6379:6379'
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    environment:
      HOME: $HOME
      APP_ROOT: $APP_ROOT
      MYAPP_DATABASE_USERNAME: root
      MYAPP_DATABASE_HOST: db
      REDIS: "//redis:6379"
      REDIS_HOST: redis
      REDIS_PORT: 6379
  web:
    extends:
      service: app
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    links:
      - db
      - redis
  worker:
    extends:
      service: app
    command: bundle exec sidekiq -q notification
    volumes:
      - .:/myapp
    links:
      - db
      - redis
volumes:
  db-data:
    driver: local
```
　
####**３．構文の説明**
構文のいくつかについて説明します。
**（１）services**
　各サービスの設定を束ねる。
　各サービスの設定は`services`構文の中に記述する。

**（２）image**
　各サービスで`run`する時に利用するdockerイメージを指定することができる。
　`image`でdockerイメージを指定するか、`build`でDockerfileを実行して、
　docker-composeの実行とともにdockerイメージを作成し、それを利用する。

**（３）volumes**
　各サービスでrunする時に、マウントするボリュームを指定することができる。
　データベースなどのデータを永続化したい場合は、必ず指定する。
　**記述例**では、dockerが管理するボリュームを指定している。
　以下のように記述する。

```yml:docker-compose.yml
volumes ホスト側のディレクトリ:dockerコンテナのディレクトリ
```

　※ホスト側のディレクトリは、
　　docker-compose.ymlのあるディレクトリからの**相対パス**でも指定できる。
　　dockerコンテナ側のディレクトリは、**絶対パス**で記述する。

　※ボリュームについての詳しい情報を表示したい時は、以下のようにして確認できる。
######**　（３－１）ボリューム一覧を表示**
```txt:vagrant
　$ sudo docker volume ls
```

######**　（３－２）ボリュームの詳細を表示**
```txt:vagrant
　$ sudo docker volume inspect ボリューム名
```

**（４）ports**
　各サービスで`run`する時に、ホスト側（vagrant）とコンテナ側でポートフォワードする
　ポートを指定することができる。
　以下のように記述する。

```yml:docker-compose.yml
ports ホスト側のポート:dockerコンテナのポート
```

**（５）extends**
　サービス間で設定が共通している場合には、`extends`を利用して共通化することができる。
　`extends`で指定したサービスの設定を継承できる。

**（６）links**
　`links`を設定すると、そのサービスのIPアドレスのエイリアスとして、サービス名を利用
　できるようになる。
　コンテナ内の `/etc/hosts` の中にサービス名が追加される。
　**例：**

```text:/etc/hosts
　172.17.2.186  db
　172.17.2.187  redis
```

**（７）build**
　Dockerfile のあるディレクトリのパスを指定できる。
　docker-compose.ymlがあるディレクトリからの相対パスで指定することもできる。
　以下のように指定した場合は、docker-compose.ymlがあるディレクトリを基準に
　`./Dockerfile`を参照する。

```yml:docker-compose.yml
build: .
```
　
　"Dockerfile"以外のファイル名のDockerfileを指定したい場合は以下のように記述する。

```yml:docker-compose.yml
context: .
dockerfile: Dockerfile.prod
```

　contextで、ファイルのパスを指定し、`dockerfile`でDockerfileの名前で指定する。
　この例では、`./Dockerfile.prod`を参照する。


**（８）command**
　デフォルトのコマンドを上書きすることができる。
　

**（９）logging**
　特に設定しない場合は`driver: json-file`となり、ホストの
　`/var/lib/docker/containers/*/*-json.log`
　にログが出力される。
　
##**Dockerコンテナを使う**
#####**１．Dockerコンテナを作る**
実際にDockerを利用する場合は、作成したイメージからまずDockerコンテナを作る。

#####**２．Dockerコンテナにプログラムをマウントする**
作成したDockerコンテナに開発中のプログラムをDockerコンテナのボリュームにマウントする。

#####**３．プログラムの開発を行う**
ホスト側のプログラムを編集する。
編集内容は、Dockerのマウント機能によってコンテナ側に反映されるため、
Dockerコンテナにアクセスすることで、プログラムの実行内容を確認しながら、
開発を進めることができる。

#####**４．開発環境の共有**
開発中に開発環境の変更があった場合も、Dockerコンテナをイメージ化し、
[DockerHub](https://hub.docker.com/)にあげることで、他の開発者と開発環境を共有することができる。
Dockerイメージはボリュームの中身を含まないため、DockerHubに上げられた
Dockerイメージを引っ張ってくれば、再び同じようにコンテナを作成し、
開発中のプログラムをマウントすることで、簡単に新しい開発環境で開発を
進めることができる。
　
　
　
参考
http://dotinstall.com/lessons/basic_docker
http://docs.docker.jp/engine/articles/dockerfile_best-practice.html
http://www.atmarkit.co.jp/ait/articles/1407/08/news031.html
http://paiza.hatenablog.com/entry/docker_intro
http://qiita.com/munisystem/items/b0f08b28e8cc26132212
http://qiita.com/pokohide/items/7397b92a188da841b435
http://qiita.com/oshin/items/0d011610238eb4df83fd
http://knowledge.sakura.ad.jp/knowledge/5021/
http://qiita.com/zembutsu/items/9e9d80e05e36e882caaa
　
　
**最後に**
オリジナル音楽の投稿配信サービス[Micstack](https://www.micstack.com/)を運営しています。
よろしければご利用ください。