Dockerを体験してみるハンズオン Part1

[前回](http://qiita.com/NewGyu/items/d0b0d6074e13acd51f3e)はdockerを使う動機ということを書いたのですが今回は実際に動かしてみるハンズオン的なものです。

ここで使用しているソースコードはこちらに格納してあります。
https://github.com/NewGyu/docker-demo
readme.mdにもある程度のことを書いてあるので参考にしてください。

## 前提

### 解説すること

* dockerコマンドの簡単な説明
* およびその概念

### 解説しないこと

* dockerのインストール方法、システム要件
    * [Docker公式ドキュメントを読んでください](https://docs.docker.com/installation/) 
* Amazon EC2の使い方

### 用意するもの

* AWSのt2.microインスタンスを起動できるAWSアカウントと少々のお金
* EC2インスタンスにssh接続できるスキル
* 少々のLinuxコマンドの知識

※Vagrantなどで自分のローカルにLinuxサーバーを立てても構いません

## まずは動かしてみよう

### 1. dockerが動くEC2インスタンスをつくる

* [ ECS-Optimized Amazon Linux AMI](https://aws.amazon.com/marketplace/pp/B00U6QTYI2)からt2.microにEC2インスタンスを作成（リッチな人はもっと良いインスタンスでも構いません）
* セキュリティグループでは80番ポートを開けておいてください

### 2. デモアプリのコンテナを動かす

```shell-session
$ docker run -it -p 80:80 newgyu/docker-sample:1.0.0

Unable to find image 'newgyu/docker-sample:1.0.0' locally
1.0.0: Pulling from newgyu/docker-sample
e2a4fb18da48: Pulling fs layer 
58016a5acc80: Pulling fs layer 
3f8d2e13b904: Pulling fs layer 
f82bfe122da5: Pulling fs layer 
0ae98a5db0c5: Pulling fs layer 
2b61a94751a3: Pull complete 
:

Digest: sha256:3137e6df67b07bd3f19ec08bdec7c9049a50a07900a45d1f2febfe9b256822a3
Status: Downloaded newer image for newgyu/docker-sample:1.0.0
Using CATALINA_BASE:   /usr/local/tomcat
Using CATALINA_HOME:   /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.111. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.111. Set the 'ServerName' directive globally to suppress this message
[Sun Oct 25 14:54:49.402642 2015] [mpm_event:notice] [pid 9:tid 140328118753152] AH00489: Apache/2.4.17 (Unix) configured -- resuming normal operations
[Sun Oct 25 14:54:49.412396 2015] [core:notice] [pid 9:tid 140328118753152] AH00094: Command line: 'httpd -D FOREGROUND'
```

さて、これで、

* http://EC2インスタンスのパブリックIP/index.jsp
* http://EC2インスタンスのパブリックIP/hello

の２つが確認できると思います。(Tomcatの起動に10秒くらいかかるので　せっかちにアクセスするとService Unavailableがでるかもしれません）

コンソールの方を見てみると、

```
111.238.90.15 - - [25/Oct/2015:15:28:35 +0000] "GET /index.jsp HTTP/1.1" 200 125
111.238.90.15 - - [25/Oct/2015:15:28:40 +0000] "GET /hello HTTP/1.1" 200 103
```

こんな感じのアクセスログが出ているかと思います。

終了は普通のフォアグラウンドで動いているプロセスを止めるのと同じで、```Ctrl+C```です。

これでdockerのコンテナを動かすことが出来ました。パチパチ。

## さて、解説

### アプリケーションの構成

![](https://cacoo.com/diagrams/V55Sg3UVjneVSwQv-EB29A.png)

* 80番ポートでhttpを受けるApache  
  /usr/local/apache2
    * apacheとtomcatを連携させる設定  
      [/usr/local/apache2/conf/extra/proxy.conf](https://github.com/NewGyu/docker-demo/blob/master/infra/apache.conf/extra/proxy.conf) 
* 8080番ポートでapacheの後ろにいるTomcat  
  /usr/local/tomcat
    * サンプルアプリケーション  
[/usr/local/tomcat/webapps/ROOT.war](https://github.com/NewGyu/docker-demo/tree/master/app/src/main)

### もう一度動かして確認

#### 今度はバックグラウンドで動かす

```shell-session
$ docker run -d -p 80:80 newgyu/docker-sample:1.0.0
```

先ほど`-it`だったところを`-d`に変えて実行します。
`docker run`のオプションの意味はこんな感じです。

|オプション|意味|
|--------|------|
|i|ターミナルからコマンドの入力ができます|
|t|標準出力に実行したコマンドの結果が出力されます。普通は`-it`とセットで使います。|
|d|バックグラウンドでデーモンの用に動きます|

See: https://docs.docker.com/reference/commandline/run/

さて、今度は先ほどのようなtomcatとapacheの起動ログは出ずにすぐに制御が返ってきましたね。

#### プロセスを確認する

```shell-session
$ ps -ef | grep -E "(httpd|tomcat)"
root     14352  2235  0 15:56 ?        00:00:00 /bin/sh -c /usr/local/bin/httpd-foreground
root     14367 14352  0 15:56 ?        00:00:00 httpd -DFOREGROUND
root     14396 14352 17 15:56 ?        00:00:37 /usr/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat/endorsed -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
bin      14405 14367  0 15:56 ?        00:00:00 httpd -DFOREGROUND
bin      14406 14367  0 15:56 ?        00:00:00 httpd -DFOREGROUND
bin      14407 14367  0 15:56 ?        00:00:00 httpd -DFOREGROUND
ec2-user 19434 13661  0 15:59 pts/0    00:00:00 grep --color=auto -E (httpd|tomcat)
```

ホストマシンにはインストールした覚えのないhttpdやjavaのプロセスが動いていますね。
このようにdocker runで動かしたコンテナは普通のプロセスとして動作します。

今度は`docker ps`というコマンドを使ってみましょう。

```
]$ docker ps
CONTAINER ID        IMAGE                        COMMAND                CREATED             STATUS              PORTS                          NAMES
84eb0313086b        newgyu/docker-sample:1.0.0   "/bin/sh -c /usr/loc   5 minutes ago       Up 5 minutes        0.0.0.0:80->80/tcp, 8080/tcp   stupefied_goodall   
```

`CONTAINER ID=84eb0313086b`これが先ほど`docker run`で起動したコンテナのIDです。このIDはdockerの世界におけるプロセス番号みたいなものでコンテナを起動するたびに変わるものです。

このようにしてdockerのコンテナの動作状況を確認することが出来ます。

#### ポートを確認する

```
$ netstat -tan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State      
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      
tcp        0      0 :::80                       :::*                        LISTEN      
tcp        0      0 :::22                       :::*                        LISTEN      

```

80番ポートが開いていますね。dockerは仮想マシンのようなものでホストのポートを勝手に開けたりしないのですがなぜでしょう？
ヒントは先ほど`docker ps`した時の`PORTS=0.0.0.0:80->80/tcp, 8080/tcp`というところにあります。そして`docker run`した時の`-p 80:80`です。

|オプション|意味|
|--------|------|
|p|ホストのポートとコンテナのポートをマッピングします。 `-p ホストのポート:コンテナのポート` |

このようにホストのポートからコンテナのポートにフォワードすることが出来ます。iptablesみたいですね。

```
$ sudo iptables -L -4
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  anywhere             ip-172-17-8-239.ap-northeast-1.compute.internal  tcp dpt:http
```

dockerが裏でiptablesにルールを追加しているのです。

See: https://docs.docker.com/userguide/dockerlinks/#connect-using-network-port-mapping

#### コンテナのIPアドレス

コンテナには仮想マシンのNATモードの様にIPアドレスがふられます。
`docker inspect`で確認できます。

```
$ docker inspect -f "{{.NetworkSettings.IPAddress}}" 84eb031
172.17.8.239
```

※84eb031はコンテナIDです。フル桁指定しなくても一意になるところまで省略可能です。

このIPはdockerホスト内だけで有効なIPなので外部から直接コンテナにアクセスすることは出来ません。そのため、上記のような`-p`でポートのマッピングを行います。
また、コンテナを起動するたびにdockerが自動で割り振りをします。

#### docker inspect

単に次のようにしてみましょう。

```shell-session
$ docker inspect コンテナId
```

長いJSONが出ましたね。 `-f`で指定しているのは[jqコマンド](http://momijiame.tumblr.com/post/51299899637/jq-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%8C%E5%BC%B7%E5%8A%9B%E3%81%99%E3%81%8E%E3%81%A6%E3%83%A4%E3%83%90%E3%81%84%E4%BB%B6)の評価式と同じようなものです。
正しくはGo言語のテンプレート式のようです。 ごめんなさいこのへんはあまり詳しくないんで[こちら](http://blog.shibayu36.org/entry/2013/12/28/145019)を参考にしてください。

See: https://docs.docker.com/reference/commandline/inspect/

----

もっと色々書こうと思っていたのですが、疲れたのでまた今度続きを書きたいと思います。
寝ます。

↓

[Part2はコチラ](http://qiita.com/NewGyu/items/e200c9fac28d4e2e3cb4)