Docker tutorial


社内学習向けに作った [Docker] の 簡易tutorial です。

## 環境

Amazon Linux AMI 2016.09.1 (HVM), SSD Volume Type
(ami-56d4ad31)


## Docker Installation

[Docker] をインストールします。

```
$ sudo yum install -y docker
$ sudo service docker start
```

いちいち ``sudo`` するのが面倒なので、alias にしておく。

```
$ echo "alias docker='sudo docker'" >> ~/.bash_profile
$ source ~/.bash_profile
```


## Docker Image を取得する

[Alpine Linux] の Docker Image を使用します。4MBしかない驚異のLinuxです (@_@)

```
$ docker pull alpine

Using default tag: latest
latest: Pulling from library/alpine
Digest: sha256:58e1a1bb75db1b5a24a462dd5e2915277ea06438c3f105138f97eb53149673c4
Status: Image is up to date for alpine:latest
```

取得した Docker Image を一覧します。

```
$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
alpine              latest              4a415e366388        13 days ago         3.984 MB
```


## コンテナを動かしてみる

以下のコマンドで [Alpine Linux] の ``/bin/sh`` プロセスの Docker Container を動かしてみます。

```
$ docker run -it alpine /bin/sh
/ #          # <-- コンテナの中に入った。 (正確には /bin/sh)
```

option とか引数の意味は↓の通りです。


```
$ docker run --help

Usage:	docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

  -i, --interactive    Keep STDIN open even if not attached
  -t, --tty            Allocate a pseudo-TTY
```

> 第1引数は 「Dockerのコマンド名」
> 第2引数は 「どの Docker Image から？」
> 第3引数は 「何のプロセスを？」
> option ``-it`` を外すとバックグラウンドでコンテナが動きます。


## curl をインストールしてみる

コンテナ (``/bin/sh``プロセス) を動かせたので、試しに [curl] をインストールして見ましょう。
[Alpine Linux] には [apk] という Package Management が入っています。

```
/ # apk update     # <-- 最初の1度は必ずやる
/ # apk add curl
```

インストールできました。実際に動かしてみる。

```
/ # curl google.com

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.jp/?gfe_rd=cr&amp;ei=sDjLWNm5L63EXre3rJgO">here</A>.
</BODY></HTML>
```

コンテナを出ます。``exit`` をすると ``/bin/sh`` プロセスが死ぬのでコンテナから放り出されます。

```
/ # exit
$         # <-- ホストOSに戻ってきた。
```


## コンテナを一覧する / 削除する

以下のコマンドで今あるコンテナを一覧します。

```
$ docker ps -a
CONTAINER ID     IMAGE       COMMAND       CREATED             STATUS                      PORTS       NAMES
1c2e6e1af35a     alpine      "/bin/sh"     4 minutes ago       Exited (0) 29 seconds ago               sad_torvalds
```

STATUS="Exited (0)" なのは、このコンテナのメインプロセスである ``/bin/sh`` が死んでしまった (先程``exit``したので) からです。
もうこのコンテナはいらないので削除しましょう。

```
$ docker rm sad_torvalds
sad_torvalds
```

もし、削除したいコンテナがまだ動いている場合は先に停止をしましょう。

```
$ docker stop sad_torvalds
```

> コンテナの名前は作る度に変わりますので注意


## Dockerfile でコンテナを自動構築する！

Docker では [Dockerfile] というテキストファイルにコンテナの構築手順を落とせます。所謂 [Infrastructure as Code] ですね。↓ は [Nginx] プロセスのコンテナを自動構築する [Dockerfile] です。

```
FROM alpine:latest
MAINTAINER Yusuke Kawatsu "your.email.address@domain"

# Set correct environment variables.
ENV HOME /root
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8

# Install packages.
RUN apk update && \
    apk add --no-cache nginx

# make dir for Nginx PID file.
RUN mkdir -p /run/nginx

# open container's ports.
EXPOSE 80

# container's main process.
ENTRYPOINT /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
```

Docker Image を上記 [Dockerfile], (ファイル名 "Dockerfile" でカレントに保存しておく) を元にビルドします。``-t`` でイメージ名を指定できる。

```
$ docker build . -t yourname/nginx
```

ちゃんと Docker Image が出来ているか、確認しましょう。

```
$ docker images

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
yourname/nginx      latest              3ab8a7ccbe71        36 minutes ago      6.358 MB
alpine              latest              4a415e366388        13 days ago         3.984 MB
```

ビルドした ``yourname/nginx`` Docker Image からコンテナを動かします。

```
$ docker run -d -p 8888:80 yourname/nginx

426d43364fb11137098c8663fe9e6730b78b719c07fffc553292bfaf175fc346
```

> ``-d`` はデーモン (バックグラウンド) の意味です
> ``-p`` はポートフォワーディングの意味で、ホストOSの ``:8888`` を コンテナの ``:80`` にフォワードします。

コンテナを一覧します。動いていますね。

```
$ docker ps -a

CONTAINER ID    IMAGE             COMMAND                  CREATED           STATUS          PORTS                  NAMES
426d43364fb1    yourname/nginx    "/bin/sh -c '/usr/sbi"   3 seconds ago     Up 3 seconds    0.0.0.0:8888->80/tcp   admiring_hopper
```


## コンテナ (nginx) にアクセスする

コンテナ (``nginx``プロセス) に ``curl`` を使ってHTTPアクセスをしてみましょう。以下の2つの方法でそれぞれアクセスしてみます。

1. ``localhost:8888`` にアクセスする (※前述の通りポートフォワーディングしている)
2. ``コンテナのIP:80`` にアクセスする

まずは ``localhost:8888`` の方から。

```
$ curl localhost:8888

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
```

> [nginx] が初期設定のままなので ``404`` が返ってきていますが、ちゃんと [nginx] プロセスと疎通はできています

簡単にアクセスできました。次は ``コンテナのIP:80`` をします。コンテナのIPは以下の様に取得できます。

```
$ docker inspect admiring_hopper
...
            "Networks": {
                "bridge": {
                    "IPAddress": "172.17.0.2",
```

コンテナのIPが分かったので直接アクセスしてみます。

```
$ curl 172.17.0.2:80

<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
```


## 既に動いているコンテナに入りたい！

コマンド ``docker exec ...`` でコンテナに侵入 (新しいプロセス) できます。

```
$ docker exec -it admiring_hopper /bin/sh
```

> 引数/option は ``docker run ...`` の時と同じ意味です

コンテナのプロセスを見てみます。ホストOSと完全に隔離されていますね！

```
/ # ps aux
PID   USER     TIME   COMMAND
    1 root       0:00 /bin/sh -c /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
    5 root       0:00 nginx: master process /usr/sbin/nginx -g daemon off; -c /etc/nginx/nginx.conf
    6 nginx      0:00 nginx: worker process
    7 root       0:00 sh
   13 root       0:00 ps aux
```


[Docker]:https://www.docker.com/
[Dockerfile]:https://docs.docker.com/engine/reference/builder/
[Alpine Linux]:https://alpinelinux.org/
[apk]:https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management
[curl]:https://curl.haxx.se/
[Infrastructure as Code]:https://en.wikipedia.org/wiki/Infrastructure_as_Code
[nginx]:https://nginx.org/en/