実例で学ぶDockerハンズオン

![](https://gyazo.com/edaf107dfc6483b5ae78f05951e6d84b.png)

この記事は[CAMPHOR- Advent Calender 2015](http://advent.camph.net/) 22日目の記事です。
こんにちはkohey18です！(｀･ω･´)ゞ

学生時代はCAMPHOR-の4期代表として頑張ってから上京して今に至ります(｀･ω･´)ゞ

この一年、インフラエンジニアとして業務でDockerを触る機会がたくさんあり、その中でインフラエンジニアだけでなく、サーバサイドエンジニアもアプリのエンジニアももっとDockerを手元で触ってもらってDocker力があるとどんだけ楽だろうか。。。と思うことが多々あり、週末なり時間のあるときに試して欲しいDockerハンズオンをまとめておこうと思いました。

週末とかで試してみて、「あぁなるほど」と思って貰えればと思います。

## 対象者

- Docker初心者よりちょい上(一応初心者もわかるようにDockerfileの書き方から書いています)
   - とりあえずDockerをローカルに入れているけど、何もしていない
   - なんとなくDockerfileの書き方はわかる

## Dockerとは

ここでは省略させてください！
様々な記事が上がっているかと思います！

書籍でいうと以下が良かったかも！
[Dockerエキスパート養成読本［活用の基礎と実践ノウハウ満載！］ (Software Design plus)](http://www.amazon.co.jp/dp/4774174416)

## ハンズオンゴール

![](https://gyazo.com/ce8de99e560f6b6f98ff8069ba7a10f0.png)


文字にすると

- docker-machineのポート8080のnginxコンテナにリクエストが来る(pオプションの理解)
- Sinatraのアプリケーションはnginxコンテナのproxy_pass経由でしかつながらない(linkオプションの理解)
- nginx, Sinatraのアプリケーションコンテナのログが全てfluentdのコンテナに送られる(logDriverの理解)
- fluentdのログは全てローカルPCに出力される(vオプションの理解)

とそれぞれ一応、Dockerの基本的〜応用的なことがローカルの環境で実現できるようにします。

## 環境

- docker-machine
   - 構築はこちらを → [docker-machineに変えた話](http://qiita.com/kohey18/items/b7e48ad70f97680041e5)
- docker
   - version 1.9.1
- Mac OS X 10.10.5

docker-machineの設定(ポートフォワーディング等)は今回は省かせてもらいます。

## 構築手順

![](https://gyazo.com/9c62f45782ee3c63a131940c4b826ba1.png)

### sinatra


- Sinatra
   - Dockerfile
   - app.rb

```Dockerfile:Dockerfile
FROM ruby:2.2
MAINTAINER kohey

ADD app.rb app.rb
RUN gem install sinatra

EXPOSE 4567
CMD ["ruby", "app.rb"]
```
#### Dockerfileの書き方

初学者のためにDockerファイルの説明をすると、
Sinatraを動かすためにはもちろん`ruby`がコンテナ内で動いている必要がある。
今回、このコンテナ内のrubyの環境構築を楽にするためにrubyの2.2が動いているイメージを基にして、そこに上書きするようにしてみた。
先頭の`FROM ruby:2.2`がそこを表している。

- `ADD`
   - `docker build`時に`current directory`のファイルをコンテナにぶち込む作業を行う。
- `RUN`
   - `docker build`時にコンテナに走らせたい処理を記載。
- `EXPOSE`
   - コンテナとして空けておくポートを記載。

```rb:app.rb

require 'sinatra'

set :bind, '0.0.0.0'

get '/' do
  "Hello World!"
end
```

こちらは`Hello World!`を出力するだけ

では、このSinatraアプリケーションのコンテナを動かしてみましょう！

```bash

$ docker build -t <your_name>/sinatra .
$ docker run --name sinatra -d <your_name>/sinatra
$ docker ps
```

```
kohey% docker ps                                                                   
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
acfa7757f698        kohey/sinatra       "ruby app.rb"       13 seconds ago      Up 14 seconds       4567/tcp            sinatra
```

これで問題ないです。

このSinatraのコンテナ、外からは見れない状態です。
docker-machine内部からはport`4567`が空いているだけ。

![](https://gyazo.com/239aa0ef8cc4db603680ff1626046099.png)

上図のようにdocker-machine上にただ動いているだけ。

外から見れるようにするには、`pオプション`を利用すればいいのですが、それはnginxのコンテナで試してみましょう！

### nginx

- nginx
   - Dockerfile  
   - nginx.conf
   
```Dockerfile:Dockerfile
FROM ubuntu
MAINTAINER kohey
RUN apt-get update

RUN apt-get install -y nginx
# Remove the default Nginx configuration file
RUN rm -v /etc/nginx/nginx.conf
# Copy a configuration file from the current directory
ADD nginx.conf /etc/nginx/
# Expose ports
EXPOSE 80
# Set the default command to execute when creating a new container
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
```

```nginx:nginx.conf

worker_processes  5;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for" "$upstream_cache_status"';

    access_log  /var/log/nginx/access.log main;
    sendfile        on;

    keepalive_timeout  65;

    server {
        listen 80;
        server_name localhost;

        location / {
            proxy_pass http://sinatra:4567; #Sinatraのアプリケーションにproxyする
        }
    }
}
```

動かしてみましょう！

```bash

$ docker build -t <your_name>/nginx .
$ docker run --name nginx -p 8080:80 --link sinatra:sinatra -d kohey/nginx
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
ac12ce7600cc        kohey/nginx         "/usr/sbin/nginx -g '"   6 minutes ago       Up 6 minutes        0.0.0.0:8080->80/tcp   nginx
acfa7757f698        kohey/sinatra       "ruby app.rb"            11 minutes ago      Up 11 minutes       4567/tcp               sinatra
```

### pオプション

`docker run -p docker-machineのポート番号:コンテナ内ポート番号`
となる。
例えば、docker-machineのipが`192.168.99.100`だとしたら、

docker-machineのipの調べ方は[コチラ](http://qiita.com/kohey18/items/b7e48ad70f97680041e5)

`192.168.99.100:8080`はport`80`にフォワードされるから、すなわち、docker-machine内のnginxにリクエストが飛ぶこととなる。

かなり使うオプションなので抑えておいて欲しいです。


### linkオプション

linkオプションを用いることでコンテナ間通信を可能にします。
すると、以下のようなところまで実現できました。

![](https://gyazo.com/9cd0c6cdc4dc3944d298b6543d7ed87a.png)

すると、外からnginxを通りproxy_passでsinatraのアプリケーションが見えるかと思います。

再掲: docker-machineのipの調べ方は[コチラ](http://qiita.com/kohey18/items/b7e48ad70f97680041e5)

![](https://gyazo.com/2e3cb772f1cc1350fe1cefc77732ff81.png)  

ここまで完全に理解できれば、最低限のDockerfileの書き方と最低限のオプションは理解できたかと思います。


## 応用編


Docker1.8から[Fluentd Log Driver](https://docs.docker.com/engine/reference/logging/fluentd/)が追加されました。
ローカルで試せるので少し試してみましょう！


### fluentdのコンテナを立ち上げる

- fluentd
   - Dockerfile
   - td-agent.conf

```Dockerfile:Dockerfile
FROM ubuntu
MAINTAINER kohey

RUN apt-get update && apt-get install -y --force-yes libssl0.9.8 curl

RUN curl -L http://toolbelt.treasuredata.com/sh/install-ubuntu-trusty-td-agent2.sh | sh && mkdir -p /var/log/td-agent

ENV GEM_HOME /opt/td-agent/embedded/lib/ruby/gems/2.1.0/
ENV GEM_PATH /opt/td-agent/embedded/lib/ruby/gems/2.1.0/
ENV PATH /opt/td-agent/embedded/bin/:$PATH
ADD ./config/td-agent.conf /etc/td-agent/td-agent.conf
RUN mkdir -p /var/log
EXPOSE 24224
CMD ["td-agent"]
```

- td-agent.conf

```xml:td-agent.conf

<source>
  @type forward
  @id forward_input
  port 8888
</source>

<match **.**>
   type copy
   <store>
     type file
     path /var/log/td-agent/docker
   </store>
   <store>
      type stdout
   </store>
</match>
```


ログの入力をポート8888でHTTP通信で行うようにする。
動かしてみましょう！

```bash

$ docker build -t kohey/td-agent .
$ docker run --name td-agent -v /Users/kohey/Desktop/log/:/var/log/td-agent/docker -p 8888:8888 kohey/td-agent
```


#### vオプション

vオプションを使うとホストの任意のパスをコンテナの任意のパスにマウントすることができる

`-v /Users/kohey/Desktop/log/:/var/log/td-agent`

`-v ホストの任意のパス:コンテナの任意のパス`

こんな感じで書く

※※ 今回はdocker-machineが動いているVirtualBoxとローカルPCの共有フォルダを設定しているので、`コンテナ` -> `VirtualBox` -> `ローカルMacPC`が実現可能である。


![](https://gyazo.com/6ff0112686afcf3a1197d97ab06a98a7.png)

ここまで来ました！！！

*気がつけば大作になったぞ！！！*

#### logDriverを使う

では、先ほど動かした`Sinatra`アプリケーションコンテナと`nginx`コンテナをstopしましょう。

```bash

$ docker rm -f sinatra
$ docker rm -f nginx
```

そして、再度、ログドライバーオプションをつけた`Sinatra`アプリケーションコンテナと`nginx`コンテナを動かしてみましょう！


```bash

$ docker run --name sinatra -d --log-driver=fluentd --log-opt=fluentd-address=192.168.99.100:8888 --log-opt fluentd-tag=docker.{{.Name}} kohey/sinatra
$ docker run --name nginx -p 8080:80 --link sinatra:sinatra --log-driver=fluentd --log-opt fluentd-address=192.168.99.100:8888 --log-opt fluentd-tag=docker.{{.Name}} kohey/nginx
```

`--log-driver=fluentd --log-opt=fluentd-address=192.168.99.100:8888 --log-opt fluentd-tag=docker.{{.Name}}`

- log-driver
   - コンテナのログドライバーをfluentdにする(他のサポートもある)
- log-opt
   - ログドライバーに必要な情報を与える


![](https://gyazo.com/ce8de99e560f6b6f98ff8069ba7a10f0.png)

これでここまで来たかと思います！
ローカルのPCにログが出力されているはずなので見てみましょう！

![](https://gyazo.com/324677a483c9f84d3270960a06eae9a5.gif)

下はローカルのPCに吐き出されたログをひたすら`tail`している感じです！
fluentdのログも`docker.{{.Name}}`より、`docker.sinatra`とか`docker.nginx`とかになっていると思います！

これでハンズオンのゴールが出来たかと思います！


## まとめ

最低限のオプション等々を紹介した感じの大作になってしまった。
僕は初めて触った時に
*「linkオプションを利用しただけで`http://sinatra:4567`でproxy_pass通るのかよ！」*
とか
*「vオプションの使い方さえしっかりすればデータは永続化されそう！」*
とか
*「ログドライバーでコンテナのログが簡単に集まる・・・・！！便利じゃない？？」*
とかローカルの環境上でも感動する所は多々ありました。

実際、業務でもDocker使ってProduction運用していて、ログ周りとかデプロイ周りとか詰まりどころ多々あるけど、ちょっとずつ知見が溜まっている感じがします。

docker-machine上でちょっと凝ったことするときにこのハンズオンに出会ってもらえればなーとか思いました！

明日は、後輩の[mikasakasa](https://github.com/mikasakasa)だよ！(｀･ω･´)ゞ