Docker & Docker-Composeの基本的な使い方

[サーバサイドプログラミング関連 Advent Calendar 2016](http://qiita.com/advent-calendar/2016/serverside)の4日目の記事です。
Docker使ったことない方がすぐにコンテナを構築をできるように説明がんばります。
環境はMacをベースに説明します(Win機持ってない)が、install時以外は基本的には違いは無いかと思います.

# Dockerとは

> 
> Docker provides a way to run applications securely isolated in a container, packaged with all its dependencies and libraries. Because your application can always be run with the environment it expects right in the build image, testing and deployment is simpler than ever, as your build will be fully portable and ready to run as designed in any environment. And because containers are lightweight and run without the extra load of a hypervisor, you can run many applications that all rely on different libraries and environments on a single kernel, each one never interfering with the other. This allows you to get more out of your hardware by shifting the “unit of scale” for your application from a virtual or physical machine, to a container instance.
> [Docker Doc](https://docs.docker.com/)より

とある. 概要だけ訳すと、  
・Dockerはアプリをホストから分離してコンテナ内で実行するので安全  
・ビルドに移植性があるのでテスト-デプロイが楽に  
・アプリの最小単位(スケール単位)をマシンレベルからコンテナのインスタンスレベルまで下げることでマシンをもっと活かせる  

などと書かれている.概念をつかむ為に正確性に欠ける表現をすると、コンテナはCで言うa.outだったりJavaのjarだったり、一つのコンテナが1プロセスとして動くアプリのようなものだと考えると最初はとっつきやすいかと思う.  
コンテナの元となる"イメージ"から"コンテナ"を作成する.  
また、ビルドに移植性があるという点についても上記の説明にのっかるとJVM上だから動くよね☆のようなものだと(最初は)考えてほしい.  
スケール単位という考え方については、設計思想の項で説明する.  

## 設計思想
というか運用上のお勧めの考え方、くらいに捉えてほしい.  
###containerはミニマルであるべき  
例えばLAMP環境を構築する場合,Linux上にapache, mysql, phpが必要となる.スケールを考えると、今まではデータベース用サーバとアプリケーションサーバ(+バランサー)といった感じに変更を加えると思う.  
Dockerを用いる場合には、apache+phpコンテナ,mysqlコンテナといったようにスケールした際の単位を最初から用いることで変更・メンテし易いようにすることが推奨されている.


## セットアップ

環境は  
macOS Sierra version 10.12.1
16GB of RAM
を用いる.  
Win/Mac/Linuxに共通して要求される環境は、
・4GB of RAM
・64bit OS
である。
さて、セットアップといっても、
[docker.com](https://www.docker.com/)
から`Get Docker for Mac`もしくは`Get Docker for Windows`を選べばよい.
Docker Toolboxというのもあるが、主流はDocker for (win/mac)であると思う.  

Linuxについては、CentOSを例とするが、daemon起動以外はshを叩くだけなので問題ないかと思う.  


```sh
sudo yum update
# docker.repoを追加しDockerインストールするscriptを叩く
curl -fsSL https://get.docker.com/ | sh
# daemon追加
sudo systemctl enable docker.service

# daemon起動
sudo systemctl start docker

```

[docker installation](https://docs.docker.com/engine/installation/)

## 実行

`docker run`でコンテナのプロセスを実行する.
docker run実行時に永続プロセスを指定してあげないと、処理が終了した時点でコンテナもexitしてしまうので注意.  
以下に簡単な例を示す.

```sh
# image(コンテナ)の実行 centosの場合
docker run -it centos /bin/bash
```
上記コマンドはcentosのイメージをdockerのrepoから取得し、コンテナとして起動したあとに/bin/bashに接続することを意味する.  
dockerは基本的にstdin/stdoutのような標準ストリームを全てアタッチするので、-it オプションを用いることで/bin/bashのようなシェル(インタラクティブなプロセス)を起動できる.

以下に `docker run` 時のオプションを示す.

```sh
--name # imageに名前をつける
-v /path/to/host:/path/to/container  # hostのディレクトリをコンテナにマウントする 
-p 8888:80 # hostの8888番ポートをコンテナの80番ポートにポートフォワードする.
```
とりあえずはこの３つさえ知っておけば最小限はなんとかなる.

## 各種コマンド

#### コンテナの確認
```sh
docker ps

# docker ps -a でexit状態のコンテナも表示される.

```

#### イメージの確認

```sh
docker images
```

#### コンテナの削除

```sh
# docker ps で削除したいコンテナのidを確認.
docker rm container_id

# 止まっているコンテナを全て削除したい場合には以下が便利.
# docker rm `docker ps -a -q`

```

#### イメージの削除

```sh
docker rmi image_id

# image全削除したい場合には以下.
# docker rmi `docker images`
```

## Dockerfile
dockerのイメージは `Dockerfile`というファイルから生成することができる.  
centosにnginxをインストールしたイメージのDockerfileの例を用いて説明する.

```sh
# Dockerfileでは#でコメントアウト

# FROM *** で使用するベースイメージを指定.
FROM centos

# コマンドを実行する場合にはRUNを用いる.
# RUN で実行するコマンドは && や \ を用いてなるべく連結させる.
RUN yum update && yum install -y \
    epel-release \
    nginx

# COPY は指定したホストのディレクトリをコピーする.
COPY path/to/host /usr/share/nginx/html

# EXPOSE は開けるポートを指定する.
EXPOSE 80

# CMD はソフトウェアの実行に用いる.
# CMD ["command", "引数","引数"]
# のように記述する.
# ここでコマンドをforegroundで実行させる必要がある.
CMD ["nginx", "-g", "daemon off;"]
# apacheの場合は以下.
# CMD ["apache2","-DFOREGROUND"]
# CMD命令は最後に記述されたものだけが有効となるので注意.


```

これで最小限のdockerの使い方がわかるかと思う.足りない部分ももちろんあるのですが許してください)

# Docker-Compose
さて、ではどうやってコンテナ同士を連携させるか?に移る.それには `docker-compose` というものを用いる.

docker-composeとは予想の通り、コンテナのビルドから連携までをyml形式のファイルベースで行う事ができるツールである。  
そのため開発環境の共有もymlファイルを渡してビルドしてもらうだけなのでとても楽.  
サーバで利用する場合にも`git pull`ですむので通信量の削減に繋がる.
何よりファイルベースなので環境の見通しがよく、開発環境のズレが生じないためとても幸せになれる.

## セットアップ
Win/MacではDocker for Win/Mac に入ってると思う.
Linuxでのインストールを示す.
pipを用いる方法とscriptからインストールする方法がある.

```sh
# pipの場合
pip install docker-compose

# scriptの場合
curl -L https://github.com/docker/compose/releases/download/1.9.0/run.sh > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
```

(doc Docker)[https://docs.docker.com/compose/install/]

## 実行
上で述べたように、 `docker-compose` は `docker-compose.yml`という名前のymlファイルを作成し、ビルド、実行を行う.それではymlファイルの書き方を示す.


```yaml
version: '2'

services:
    app:
        # depends_onで依存関係を示す.appでmysqlを使いたいのでdbコンテナを指定.
        depends_on:
            - db
        # build はDockerfileからビルドすることを示す.
        # pathは相対パスになるので、build/app/Dockerfileが読まれる.
        build: "./build/app"
        # ports でポートフォワーディングを指定.
        ports:
            - "8888:80"
        # volumes でマウントの指定.こちらも相対パス.
        volumes:
            - "./volumes/app:/var/www/html"
        # links で連携させるコンテナを示す.
        # depends_onはビルド時に依存関係を考慮してビルドさせるために指定.
        # links は appコンテナの/etc/hostsにdbのipを記述してくれる.
        links:
            - db
    db:
        # image はbuildをせず既存イメージを用いる場合に使う.
        image: mysql
        # environment は環境変数などのexportをしてくれる.
        # mysqlのようにenvironmentで指定が必須の変数もあるので、公式イメージを利用する際には注意.
        environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_DATABASE: database
        ports:
            - "13306:3306"
        # 永続化が必要なものに対してはvolumesなどでホスト側に残す
        volumes
            - "./data/mysql:/var/lib/mysql"
            -


```

次に作成したymlからコンテナを実行する.
今回の例ではbuildするコンテナがあるので、まずは
`docker-compose build`
を実行.
buildコンテナが無い場合や、buildが終わったら
`docker-compose run -d`
でバックグラウンドでコンテナたちを走らせることができる.

## 各種コマンド

```sh
# docker-compose.ymlからビルドする
docker-compose build 

# ymlに書かれてるコンテナを起動する　
docker-compose run
# backgroundで起動する場合、
# -dオプションをつける

# コンテナの確認
docker-compose ls

# コンテナの終了
docker-compose kill

#コンテナの削除

docker-compose rm
```

これで最小限のdocker-composeの使い方がわかるかと思う.


## まとめ
dockerコンテナの構築を `Dockerfile`で行い、`docker-compose`でオーケストレーションすると楽.
(拙い文章ですみません...!)