はじめてのDocker for Mac

# この記事の内容、執筆者、対象読者
以下のDocker for Macの「はじめに」を読みほどきつつ、Dockerの仕組みを正しく理解する。
https://docs.docker.com/docker-for-mac/

執筆者は、Dockerのことを何も知らない、文系出身のエンジニア2年生。
対象読者は、同じようなタイプの人を想定しています。できるかぎり間違いのないように

ちなみに、2017年1月時点の「はじめに」を元に書いています。
また、次のことが完了していることが前提です。

1. https://docs.docker.com/docker-for-mac/ から、最新のDocker for Macをダウンロードしてインストールする。
2. ```$ docker --version```が正常に実行できること


# DockerイメージからDockerコンテナを新規作成する

Docker for Macをインストールして、--versionオプションなどが正常に反応することを確認したら、
まず次のコマンドを打ってみる。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                         NAMES
```

psコマンドは、動いているDockerコンテナ（後述）のリストを取得する。
いまは何も動かしていないので、ヘッダが出るだけ。

つぎに下のコマンドを打ってみる。

```
$ docker run hello-world
```

runコマンドは、あるDockerイメージ（すぐあとで述べる）をもとにDockerコンテナを新規作成してコマンドを実行する。
正常に動けば、次のようなメッセージが出るはず。

```
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/
```


## hello-worldで、Dockerがやっていること

hello-worldを実行すると、次の4ステップで行われる。

1. Dockerクライアントが、Dockerデーモンを呼び出す（contactする）
2. Dockerデーモンが、Dockerハブから”hello-world”イメージをローカルに落とす
3. Dockerデーモンが、取得したイメージから新しいコンテナを生成する。このイメージは、今あなたが読んでいる文を出力する実行ファイルを実行する。
4. Dockerデーモンが、その出力（文章）をDockerクライアントに渡す。クライアントは、その出力をターミナルに送る。

「Dockerクライアント」、「Dockerデーモン」、「Dockerハブ」、「Dockerイメージ」の関係性は、ここで理解できる。
より詳しい知識については、「Docker 仕組み」とかでググったほうが良さそう。

なお「デーモン」とは、バックグラウンドで動くプログラムだという認識。

# ubuntuを実行してみる

hello-worldにも書かれているように、つぎのコマンドを実行してみる
```
$ docker run -it ubuntu bash
```
-itオプションは「コンテナを作ってログインする」ためのもの。
bashコマンドは、「bashを起動する」ためのもの。（まだ何も理解してません。）


# Docker化されたWebサーバを始動する

下のコマンドを実行して、nginx（Webサーバ）を起動する。
そのあと、http://localhost にアクセスすると、「Welcome to nginx!」ページが表示される。
```
$ docker run -d -p 80:80 --name webserver nginx
```

-dオプションは「デタッチド・モード」で起動するためのもの。バックグラウンドで実行されるので、標準入出力や標準エラーがコンソールに表示されない。
ちなみに、これまでの実行は「フォアグランド・モード」と呼ばれ、コンテナの中でプロセス（※1）を開始し、プロセスの標準入出力や標準エラーをコンソールに送る。

（※1）
> プロセスとは、OSからメモリ領域などの割り当てを受けて処理を実行しているプログラムのこと。
（http://e-words.jp/w/%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9.html）

-pオプションは、コンテナにポートフォワーディングでアクセスできるようにするためのもの。
「ポートフォワーディング」については、以下を引用。

> ポートフォワーディングの機能を一言でいうと、インターネットから
特定のポート番号宛にパケットが届いたときに、あらかじめ 
設定しておいたLAN側の機器にパケットを転送する機能です。
（http://www.itbook.info/study/nat6.html）

引数には、「外部からアクセスされるポート番号 : コンテナ側（nginx）のポート番号」を設定する。
これにより、localhostのポート番号80にアクセスすると、nginxのポート番号80に繋がるようになる。

--nameオプションは、コンテナの名前を定義する。今回はwebserverと設定している。
注意点として、nginxはイメージ名。webserverがコンテナ名。runコマンドに与える引数はイメージを指定する、ということがわかった。


# Dockerコンテナの再起動、停止
ここまで、hello-world、ubuntu、nginxイメージをそれぞれ起動した。
この時点で、psコマンドを打つと、次のような出力が出るはず。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                         NAMES
(適当な文字列)        nginx               "nginx -g 'daemon off"   2 hours ago         Up 2 hours          0.0.0.0:80->80/tcp, 443/tcp   webserver
```

出力文については一旦保留。ここでは、なぜか（nginxイメージから生成した）webserverコンテナだけが表示さえていることに注目。
webserverの情報しか出ないのは、psコマンドが「現在起動しているコンテナ」を出力するため。
つまり、hello-worldとubuntuイメージから生成されたコンテナは、知らぬ間に停止していたことになる。**なぜ停止したかはまだわからない。**
もし停止したコンテナも含めたすべてのコンテナを表示させるためには、-aオプションをつけて実行する。

```
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                         PORTS                         NAMES
(適当な文字列)        ubuntu              "bash"                   44 minutes ago      Exited (0) 39 minutes ago                                    (適当な文字列)
(適当な文字列)        nginx               "nginx -g 'daemon off"   2 hours ago         Up 2 hours                     0.0.0.0:80->80/tcp, 443/tcp   webserver
(適当な文字列)        hello-world         "/hello"                 2 hours ago         Exited (0) 2 hours ago                                       (適当な文字列)
```

つぎに、現在起動しているnginxコンテナを停止させる。stopコマンドで実施できる。
停止させるとlocalhostに接続できなくなり、psコマンドを打っても対象にならない。

```
$ docker stop webserver
```

停止させる対象として、コンテナ名（webserver）を指定している点に注目。
runコマンドはイメージを指定し、停止や再開（startコマンド）はコンテナを指定する必要がある。

# Dockerコンテナの削除
1つのイメージから、コンテナはいくつでも作成できる。
たとえば、すでにhello-worldイメージからコンテナを生成している状態で、もう一度

```
$ docker run hello-world
```

を実行すると、あたらしいコンテナが生成される（※）。ここで```$ docker ps -a```を実行すると、

```
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                         PORTS               NAMES
(適当な文字列)        hello-world         "/hello"                 About an hour ago   Exited (0) About an hour ago                       (適当な文字列)
(適当な文字列)        hello-world         "/hello"                 2 hours ago         Exited (0) 16 minutes ago                          (適当な文字列)
```

のように、新しいコンテナ名でコンテナが生成されていることがわかる。
今更だが、--nameオプションをつけずにrunコマンドを実行すると、適当な英単語のコンテナ名がつくみたい。

（※）このDockerコンテナの生成に利用するDockerイメージは、すでにDockerハブから取得済み。こういう場合は、Dockerハブから取得せずにローカルから取得する。

つぎに、いま作成したコンテナを削除したい。そのときはrmコマンドを実行する。

```
$ docker rm （コンテナ名、またはコンテナID）
```

さらに、起動している状態のwebserverコンテナを削除しようとする。すると、次のようなエラーが出る。
（もし停止中ならstartコマンドで再開してから下のコマンドを行う。）

```
$ docker rm webserver
Error response from daemon: You cannot remove a running container (コンテナID). Stop the container before attempting removal or use -f
```

つまり、削除前に停止をしないといけない。面倒なので、-fオプションをつけることで停止・削除を同時に行える。

```
$ docker rm -f webserver
```

# Dockerイメージの削除
rmコマンドを使って、これまでに作ったコンテナを削除したとする。
しかし、イメージはずっと残っていることに注意したい。（最初に書いた4ステップを見返すとよく理解できる。）

そこで、最後にDockerハブから取得したDockerイメージを削除する。
まず、imagesコマンドでこれまで取得したイメージの一覧を表示させる。

```
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              （適当なID）        10 days ago         181.6 MB
ubuntu              latest              （適当なID）        3 weeks ago         129 MB
hello-world         latest              （適当なID）        6 months ago        1.848 kB
```

削除するには、rmiコマンドを利用する。たとえば、hello-worldイメージを削除してみる。

```
$ docker rmi hello-world （またはDockerイメージIDでも可）
```

なお、まだコンテナが存在する（停止しても「存在」はする）場合はエラーが起きて削除できない。
イメージを削除するときは、まず該当イメージから生成されたコンテナをすべて削除してから実施するように。

すべてのDockerイメージを削除すると、当然ながらすべてのDockerコンテナも削除されていることになるので、一番最初の状態になる。

# おわりに
とりあえず、Dockerに対する「恐怖心」のようなもの無くなった。
まだ具体的なDockerの活用はできないが、おそらくこれが基礎になるはず。

## まだ理解できていないところ
```$ docker run hello-world```を実行すると、10行ほどのメッセージが出ることを確かめた。
このメッセージを、同じコンテナから再度出力するときにはどうしたらいいだろうか？