(No.10) おじさんが、LINE風アプリを開発する - Dockerを使用してコンテナを作成＆実行してみる

# 前回までのあらすじ
- GitHubを利用して、おじさんの成果物を公開する準備が整いつつあるよ
    - [(No.1) おじさんが、Git・GitHubを利用できるようにしてみる - GitHubにユーザを登録する](https://qiita.com/resojisan/items/4cd09b7a5f33ca8a925d)
    - [(No.2) おじさんが、Git・GitHubを利用できるようにしてみる - GitHubを利用してみる](https://qiita.com/resojisan/items/75dcc83ea55af0db329f)
    - [(No.3) おじさんが、Git・GitHubを利用できるようにしてみる - GitHubからリポジトリを複製（クローン）する](https://qiita.com/resojisan/items/f119d8b71e3bba9eadec)
- 目標を立てたよ、LINE風アプリが作れるようになることを目指すよ
    - [(No.4) おじさんが、何を開発するのか目標を立てるよ](https://qiita.com/resojisan/items/6a8690d21fe9ab00189d)
    - [(No.5) おじさんが、LINE風アプリを開発する - メッセージの送受信を考えてみるよ](https://qiita.com/resojisan/items/6418171fcc80bc3122cf)
- 開発環境の差異を軽減することが出来ないか考えてみるよ
    - [(No.6) おじさんが、LINE風アプリを開発する - 仮想機械、コンテナについて調べてみるよ - その１](https://qiita.com/resojisan/items/b08490e013067994d11e)
    - [(No.7) おじさんが、LINE風アプリを開発する - 仮想機械、コンテナについて調べてみるよ - その２](https://qiita.com/resojisan/items/e9d84723286a297f9a72)
    - [(No.8) おじさんが、LINE風アプリを開発する - Docker Desktopをインストールする](https://qiita.com/resojisan/items/c2e44b85c2657ea5b4b1)
    - [(No.9) おじさんが、LINE風アプリを開発する - VS Code(Visual Studio Code)をインストールする](https://qiita.com/resojisan/items/1f65437d0c504e8a8e61)

# 今回やること
- コンテナを実際に作成＆起動してみるよ。
- Dockerのことを調べながら作業を進めていくよ。

# Dockerでコンテナを実行するまでの流れ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/74a46264-ae2d-23c8-b657-6b92f9928bfd.png)

- [Docker overview](https://docs.docker.com/engine/docker-overview/)に概要が書いてあるよ。
- （１） [Dockerfile](https://docs.docker.com/glossary/?term=Dockerfile)を書く。
    - Dockerfileとは・・・
        - アプリの実行環境（コンテナ）を作成するための手順＆指示書のようなもの。
        - コンテナ内で何（アプリ等）をどのように実行するのかについても書く。
    - [Dockerfile reference](https://docs.docker.com/engine/reference/builder/)
        - Dockerfileの書き方が書いてあるよ。
    - [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
        - 効率的？最良？なDockerfileの作成方法が書かれているよ。
        - このような推奨される方法をベストプラクティスなんて呼ぶらしいよ。（なんだかかっこいいね）
        - 一読した方が、無駄が省けるかもしれないね。おじさんの場合、どうしてそうするべきなのか、なんて考えながら読むから大変だよ。
- （２） Dockerfileを元にイメージを作成する。
    - [イメージ](https://docs.docker.com/glossary/?term=image)とは・・・
        - コンテナの元。
        - コンテナの実行に必要なものが梱包されたファイル。
        - Dockerfileを元にイメージファイルを作成することが出来る。
        - 「docker build」コマンドを使用する。
        - イメージは[ベースイメージ](https://docs.docker.com/glossary/?term=base%20image)を元に作成する。作成元となったイメージをベースイメージと呼ぶみたい。
        - ベースイメージは、[Docker Hub](https://hub.docker.com/)などの[レジストリ(誰かが作成したイメージのカタログ集のようなもの)](https://docs.docker.com/glossary/?term=registry)に登録されている。
        - [ベースイメージ自体も自分で作成することが出来る](https://docs.docker.com/develop/develop-images/baseimages/)みたいだよ。
- （３） イメージを元にコンテナを作成＆実行する。
    - 「docker run」コマンドを使用する。


# Dockerfileの書き方
- コンテナを作成＆実行するには、イメージが必要だってわかったよ。
    - イメージを作成するには、Dockerfileが必要だってわかったよ。
        - だから、おじさん、Dockerfileを書けるようになろうと思うんだ。
- 書き方
    - [Dockerfile reference](https://docs.docker.com/engine/reference/builder/)に書いてあるよ。

## 形式（フォーマット）
- Dockerfileの1行は、「命令(INSTRUCTION)　半角スペース　引数（arguments）」の形式で書くみたいだよ。  
（補足） 引数とは、命令のパラメータのようなものだよ。
- 「#」の行は、コメント行として解釈するので、「#」以降は、メモや説明文など、好きに書いて良いみたいだよ。

```dockerfile

## Comment
INSTRUCTION arguments
```

## ベースイメージの決定
- イメージの基礎（もと）となるベースイメージを決定する必要があるみたいだよ。
- ベースイメージには、コンテナ内で使用するLinuxディストリビューション(Linuxカーネルを除く)の一部が含まれるため、**想定する本番環境を見据えてベースイメージを選択するべき**だと、おじさんは考えたよ。
    - Linuxカーネルは互換性を考慮して開発されているらしいので、ホストOSのLinuxカーネルと、ベースイメージが想定するLinuxカーネルが異なっていたとしても、正常に動作する可能性があるかもだけど、おじさんは小心者なので、誰かが保証してくれるような組み合わせが良いと思うんだよ。
- 最終的に本番環境をどのサーバにするか考えないとね・・・
    - DockerCE（無償版）の場合、公式サイトで2つのクラウド環境にインストールする解説があるよ。
        - [Azure](https://docs.docker.com/docker-for-azure/why/)
        - [AWS(Amazon Web Services)](https://docs.docker.com/docker-for-aws/why/)
            - ひとまず[AWS(Amazon Web Services)](https://aws.amazon.com/jp/)を本番環境の候補として考えておくよ。
                - AWSの環境に最適化されたLinuxがあるみたい。
                    - [Amazon Linux 2](https://aws.amazon.com/jp/amazon-linux-2/)
                        - Amazon Linux 2に対応するベースイメージが用意されているみたい。
                            - [amazonlinux](https://hub.docker.com/_/amazonlinux/)
- ベースイメージの指定をDockerfileに書く。
    - 「[FROM](https://docs.docker.com/engine/reference/builder/#from)」で指定する。
        - ベースイメージを指定する。
        - Dockerfileの冒頭に書く必要（ARGを除く）がある。  
        - ひとまず、エイヤで、AWSを本番環境にすることを見据えて、ベースイメージに「amazonlinux」を指定するよ。
            - このあたりは、開発が進むにつれて、検証してみたいな。


```dockerfile

FROM amazonlinux:2.0.20190508
```
- FROM以降の「amazonlinux」が、イメージの種類を表す名前。
- 「:」（コロン）以降が、バージョン（タグと呼ぶ）を表すらしいよ。
    - おじさんが、作業した時点の最新版が「2.0.20190508」だったんだ。
    - 「2.0.20190508」の代わりに、「latest」と書くと、最新版を指定することになるみたいだよ。
    - バージョンや更新履歴は[Docker HubのWebサイト](https://hub.docker.com/_/amazonlinux/)から確認することが出来たよ。

## ベースイメージに関して気になること
- [Amazon Linux2に対応するベースイメージ](https://hub.docker.com/_/amazonlinux/)を今回は使用することにしたよ。
- だけど、Docker Hub上には様々な開発者が作成&登録したベースイメージが保管されているみたいだよ。
- 小心者のおじさんの場合、誰が作成したものか不安になっちゃうよ・・・。
    - おじさんの場合は、[「Docker Official Images」（Docker公式イメージ）](https://docs.docker.com/docker-hub/official_images/)と、お墨付きのあるものだけを使用することにしたよ。
    - 公式イメージの場合、[セキュリティの専門家も含む、Dockerのチームがレビューするみたいだよ](https://docs.docker.com/docker-hub/official_images/)みたいだよ。
    - 公式イメージのガイドラインも用意されていて、非公式のイメージと比較すると、何だか手厚そうな気がするよ・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/15e802c4-13f9-14cd-c37d-e477ea4391f2.png)


## 実行するプログラム（アプリやサービス）の決定
- まだおじさんは何も開発していないから、Linuxに元から存在するコマンドをコンテナ内で起動出来るか試してみるよ。  
- macOSのターミナルでも試すことが出来るけど、次のコマンドをコンテナ内で実行することにしたよ。
    - [「uname」](https://en.wikipedia.org/wiki/Uname)がコマンドの名前だよ。OSの種類や名前などを表示するコマンドだよ。
    - 「-a」は、コマンドに指定するパラメータで、全ての情報を表示するように指示するものだよ。

```sh

uname -a
```

- コンテナ内で実行するプログラムを、Dockerfileに書く。
    - 「[CMD](https://docs.docker.com/engine/reference/builder/#cmd)」で指定する。
    - 書き方は次の通り。

```dockerfile

# パラメータはカンマ区切りで複数書くことが可能だよ。不要ではれば省略も出来るよ。
CMD ["実行するプログラムの名前","パラメータ"]
```

- 最終的なDockerfileは次のように書く予定だよ。

```dockerfile

FROM amazonlinux:2.0.20190508
CMD ["uname", "-a"]
```

## 実際にDockerfileを作成する
### 作業用のフォルダ（ディレクトリ）を作成する
- ターミナルを起動する（Launchpad内の「その他」の中にあるよ）  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/c5cba762-eafe-f278-1a19-f2d74a9acd4b.png)
- 次のコマンドを入力してエンターキーを押下。 

```sh

mkdir ~/dockertest1
```

- [mkdir](https://ss64.com/osx/mkdir.html)はフォルダ（ディレクトリ）を作成するコマンドだよ。
- 「~/」はログインユーザの個人フォルダ（[ホームディレクトリ](https://ja.wikipedia.org/wiki/%E3%83%9B%E3%83%BC%E3%83%A0%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA)と呼ぶらしいよ）の場所を表す省略形だよ。
    - コマンドを実行すると、ホームディレクトリ直下に「dockertest1」フォルダ（ディレクトリ）が作成されるよ。

### Dockerfileを作成する
- VS Codeにて、「ファイル」ー「新規ファイル」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/81a8f321-355a-10c1-21cc-1d48eabaf008.png)

- 空のファイルが作成されるよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/20e16acc-8a5f-45a8-5486-b05ca770c34a.png)

- 「ファイル」ー「保存」
- 保存先とファイル名を尋ねられるけど、「~」を入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/6fcb68c0-dd64-9b21-ae38-49941cfbb851.png)
- 「~」を入力すると、フォルダの場所を尋ねられる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/680f29ee-4223-0f0b-5fa3-b1c2bc4b7f1b.png)
- 先ほど作成したフォルダを入力して「移動」を押下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/0daa73ca-b477-3fd0-1243-18507649973e.png)
- ファイル名に「Dockerfile」を入力して、「保存」を押下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/71980bfd-e37f-21f3-3443-608ead682c40.png)
- Dockerfileを作成することが出来たよ。これからDockerfileを編集していくよ。
    - Docker関連のファイルであることをVS Codeも自動的に認識するみたいだね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/617445be-a71f-e77a-8c85-bb8a4cc106fa.png)

### VS Codeを使用して、Dockerfileに指示を書く
- 次の通りDockerfileを編集するよ。

```dockerfile

FROM amazonlinux:2.0.20190508
CMD ["uname", "-a"]
```

- VS Codeではこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/a4969ca3-c72d-bbc0-48cd-2dbcde30800a.png)

- VS Codeで編集したら、保存するのを忘れないようにしないとね。（macOSの場合、コマンドキー + Sでも保存できるよ）  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/b1ba752a-d6ca-e0bd-9d7b-51fa33d78800.png)


## Dockerfileからイメージを作成する
- 作成したDockerfileを元に、イメージを作成するには、「[docker build](https://docs.docker.com/engine/reference/commandline/build/)」コマンドを使用するよ。
- まずはターミナルで次のコマンドを実行するよ。
    - 作業するフォルダ（ディレクトリ）をホームディレクトリ直下に作成した「dockertest1」に移動。

```sh

cd ~/dockertest1
```

- 続けて、ターミナルで次のコマンドを実行するよ。

```sh

docker build -t ojisanimg1:1.0 .
```

- 「-t ojisanimg1:1.0」は、作成するイメージにつけるタグ（目印や名前のようなもの）を指定しているよ。
    - [タグの詳細はここ](https://docs.docker.com/engine/reference/commandline/tag/)に書いてあるよ。
    - タグは、コンテナを作成するときなどに、どのイメージから作成するのか、イメージを特定するために利用することが出来るよ。
    - タグは、「リポジトリ名:バージョン」の形式で書くよ。
    - リポジトリ名は、おじさんも何を指すのかよくわかってないけど、イメージを表す名前のようなものと解釈しているよ。
        - 今回、「ojisanimg1」をリポジトリ名に指定してみたよ。
    - バージョンはイメージのバージョンを指定するよ。
        - 「ojisanimg1:latest」のように書くことも可能だよ。
    - タグは「docker build -t ojisanimg1:1.0 -t ojisanimg1:latest .」のように複数書くことも出来るよ。
- 末尾の「.」（ドット）は、あとでおじさんの理解を書くことにするよ。
- コマンドを実行すると、次のように処理が実行されるよ。

```xxx

Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM amazonlinux:2.0.20190508
2.0.20190508: Pulling from library/amazonlinux
72d97abdfae3: Pull complete 
Digest: sha256:730fae68c6a180e8006443b0f090e56419da5ceb5d11250d96e627660ffbc674
Status: Downloaded newer image for amazonlinux:2.0.20190508
 ---> b94321659aca
Step 2/2 : CMD ["uname", "-a"]
 ---> Running in b7c713fd524a
Removing intermediate container b7c713fd524a
 ---> 272308db775e
Successfully built 272308db775e
Successfully tagged ojisanimg1:1.0
```

- 「docker build」の末尾の「.」（ドット）は、イメージの作成に必要なファイル（Dockerfileも含めて）の場所を指定するものだよ。
- macOS／Windows／Linuxなどの一般的なOSでは、「.」（ドット）は、現在作業中のフォルダ（ディレクトリ）の場所を意味するよ。
- 次のコマンドで、作業場所をホームディレクトリ直下の「dockertest1」フォルダ（ディレクトリ）に変更したので・・・  

```sh

cd ~/dockertest1
```
- 今回の「.」は、「~/dockertest1」と同じ意味になるよ。（cdで作業場所を~/dockertest1に変更しているので）
    - つまり、「~/dockertest1」以下にあるファイルを利用して、イメージを作成するように、指示したってことなんだよ。

- 詳しくは[ここに書いてある](https://docs.docker.com/engine/reference/commandline/build/)よ。
    - macOSにインストールしたdockerコマンドを利用して、イメージ作成を指示することが出来るよ。
    - イメージの作成は、macOSのなかで実行されるのではなく、ハイパーバイザー上で動作するLinuxのなかでイメージは作成されるんだよ。
    - macOSと、Linuxの仲介役が「[Docker daemon](https://docs.docker.com/engine/docker-overview/)」と呼ばれるプログラム（サービス）だよ。
    - Docker daemonは、イメージ作成の指示の他に、イメージを作成するために必要なファイル(Dockerfileなど）を受け取り、イメージを作成するよ。
    - 以上から、docker build実行時の作業場所「.」に、巨大なファイルが無数に置いてある状況の場合、処理に時間がかかる可能性があると思うよ。（おじさんは試してないけどね・・・）
        - 「docker」コマンドを実行するコンピュータと、「docker」コマンドから指示を受け取る「Docker daemon」は、それぞれ異なるコンピュータ上で動作するシチュエーションもあるみたいだよ。 異なるコンピュータ間のファイル転送は、インターネットや、何らかのネットワークを通じて、ファイルを伝送する必要が出てくるので、その時に実感することかもしれないよね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419842/9e6e52d4-33a1-a50f-c5eb-1b1111690d68.png)

## イメージを作成したことを確認する
- 「[docker images](https://docs.docker.com/engine/reference/commandline/images/)」コマンドを使用するよ。
- 次のコマンドを実行することで、Docker daemonに作成済みのイメージ一覧を問い合わせすることが出来るよ。
- 次の通り、ターミナル上で入力してみたよ。

```sh

docker images
```

- 実行結果だよ。

```xxx

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ojisanimg1          1.0                 272308db775e        About an hour ago   162MB
amazonlinux         2.0.20190508        b94321659aca        12 days ago         162MB
```

- 「amazonlinux:2.0.20190508」はベースイメージだね！
- おじさんが作成したイメージは、タグで「ojisanimg1:1.0」指定した通りに作成されていることがわかるね！
    - amazonlinuxのベースイメージと、サイズが同じなので、ベースイメージを元に作成されたことが読み取れるね。

## 作成したイメージからコンテナを作成&実行する
- 「[docker run](https://docs.docker.com/engine/reference/run/)」コマンドを使用するよ。
- 作成したイメージから、コンテナを作成＆実行するために、次のコマンドをターミナルで実行してみるよ。

```sh

docker run ojisanimg1:1.0
```

- 実行結果だよ。

```xxx

Linux 3140d1e9940c 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
```

- uname（OS名などの情報表示）コマンドの実行結果が表示されたよ！！！！
- Linuxと表示されている（macOSではない）ので、コンテナ内で実行されたことがわかるね！　ハッピー。

## コンテナの存在を確認する
- コンテナの一覧を表示するには、「[docker ps](https://docs.docker.com/engine/reference/commandline/ps/)」コマンドを使用するよ。
- 次のコマンドをターミナルで入力すると、Docker daemonに実行中のコンテナの一覧を問い合わせすることが出来るよ。

```sh

docker ps
```

- 実行結果だよ。

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

- CONTAINER IDなど、列名が出てきただけで、コンテナがまるで表示されていないよ・・・・
- 「-a」をつけて、「[docker ps](https://docs.docker.com/engine/reference/commandline/ps/)」をつけると、実行中か否かに関わらず、作成されたコンテナを確認することが出来るみたいだよ。

```sh

docker ps -a
```

- 実行結果だよ。

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
3140d1e9940c        ojisanimg1:1.0      "uname -a"          About a minute ago   Exited (0) About a minute ago                       vibrant_ptolemy
```

- 1つのコンテナの存在を確認することが出来たよ。
    - STATUS列をみると、「Exited(0)」と書いてあるよ。
    - [exited](https://docs.docker.com/engine/reference/commandline/ps/)はコンテナ内で実行するプログラム（メインプロセス）が終了したことを表すみたいだよ。
    - つまり、今回の場合は、「uname」コマンドが（適切に実行された後に）終了したことを意味するよ。
        - STATUS（状態）は、「created, restarting, running, removing, paused, exited, dead」などの状態があるみたい。
            - [exitedとdeadの違い](https://success.docker.com/article/what-is-the-difference-between-dead-and-exited-containers)
- 「docker ps」では、実行中のコンテナのみがリストアップされるけど、既にコンテナが終了していたために、何も表示されなかったんだね。
- 「docker ps -a」とすることで、停止したコンテナの存在を確認することが出来たよ。

## 停止したコンテナを再実行してみる
- まずは「docker ps -a」で、コンテナの一覧を確認するよ。

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
3140d1e9940c        ojisanimg1:1.0      "uname -a"          About a minute ago   Exited (0) About a minute ago                       vibrant_ptolemy
```

- 停止したコンテナのコンテナID(CONTAINER ID)を確認するよ。おじさんの環境では「3140d1e9940c」だったよ。
- 「[docker start](https://docs.docker.com/engine/reference/commandline/start/)」コマンドで、作成済みのコンテナを起動することが出来るよ。

```sh

docker start 3140d1e9940c
```

-- 実行結果だよ。

```xxx

3140d1e9940c
```

- あれれ・・・先程のように、unameの実行結果が表示されずに、コンテナIDが表示されただけだよ・・・。
- 「docker ps -a」で、もう一度、コンテナを確認してみるよ。

```sh

docker ps -a
```

- STATUS(状態)をみると、「5 seconds ago」とあり、コンテナが5秒前に実行されたことがわかるよ。なので、再実行されたことは間違いないみたいだよ。

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
3140d1e9940c        ojisanimg1:1.0      "uname -a"          2 hours ago         Exited (0) 5 seconds ago                       vibrant_ptolemy
```

- [docker startの資料](https://docs.docker.com/engine/reference/commandline/start/)をみると、「-a」のパラメータを指定することで、おじさんのターミナルに、コンテナが出力する結果を表示させることが出来ることがわかったよ。

```sh

docker start -a 3140d1e9940c
```

- 実行結果だよ。今度はちゃんと、ターミナルに実行結果が表示されたよ。

```xxx

Linux 3140d1e9940c 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
```

## 再びdocker runしてみる
- おじさん、素朴な疑問として、docker runと、docker startの違いがよくわからなくなってきたんだ。
- もう一度、docker runしてみるよ。

```sh

docker run ojisanimg1:1.0
```

- 実行結果だよ。

```xxx

Linux f84e04390638 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
```

- docker ps -aで、コンテナの一覧を問い合わせてみるよ。

```sh

docker ps -a
```

- 実行結果だよ。
    - コンテナが2つに増えたことがわかるよ！！

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
f84e04390638        ojisanimg1:1.0      "uname -a"          4 seconds ago       Exited (0) 3 seconds ago                       optimistic_meninsky
3140d1e9940c        ojisanimg1:1.0      "uname -a"          2 hours ago         Exited (0) 9 minutes ago                       vibrant_ptolemy
```

- もう一度、docker run後に、docker ps -aしてみるよ！

```sh

docker run ojisanimg1:1.0
```

```sh

docker ps -a
```

- 実行結果だよ。
    - コンテナは3つに増えたよ。このことからも、docker runは、イメージを元に新規にコンテナを作成して、そのコンテナを実行していることが読み取れるよ。
    - 一方のdocker startは、作成済みのコンテナを起動するコマンドであり、コンテナの作成までは出来ないみたいだよ。

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
9713cd6301ac        ojisanimg1:1.0      "uname -a"          4 seconds ago       Exited (0) 2 seconds ago                        confident_lamport
f84e04390638        ojisanimg1:1.0      "uname -a"          4 minutes ago       Exited (0) 4 minutes ago                        optimistic_meninsky
3140d1e9940c        ojisanimg1:1.0      "uname -a"          2 hours ago         Exited (0) 14 minutes ago                       vibrant_ptolemy
```

- [docker runの説明](https://docs.docker.com/engine/reference/commandline/run/)にも書いてあるように、新規に作成したコンテナを実行するコマンドだって理解したよ。
- [docker startの説明](https://docs.docker.com/engine/reference/commandline/start/)にもあるように、作成済み（停止中）のコンテナを実行するコマンドだって理解したよ。

## おじさんの疑問点、コンテナは実行してもすぐに停止する
- [コンテナの状態(exited）に関して書かれている記事](https://success.docker.com/article/what-is-the-difference-between-dead-and-exited-containers)を読むと、コンテナは、そのコンテナ内で実行されるプログラム（メインプロセス）が終了すると、コンテナは停止(exited状態になる)するものと読みとったよ。
- おじさんの妄想だけど、Dockerfile内の「CMD」で、コンテナ内で実行するプログラムを指定することが出来たけど、このプログラムが動き続けるようなものであれば、コンテナも動作し続けるのだろうと予想したよ。
    - このあたりは、開発が進むにつれて、少しずつ理解・体験していこうと考えているよ。

## 不要になったコンテナを削除してみる
- まずは、docker ps -aで、コンテナの一覧を問い合わせするよ。

```sh

docker ps -a
```

- 結果だよ。

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
9713cd6301ac        ojisanimg1:1.0      "uname -a"          25 minutes ago      Exited (0) 25 minutes ago                       confident_lamport
f84e04390638        ojisanimg1:1.0      "uname -a"          30 minutes ago      Exited (0) 30 minutes ago                       optimistic_meninsky
3140d1e9940c        ojisanimg1:1.0      "uname -a"          3 hours ago         Exited (0) 39 minutes ago                       vibrant_ptolemy
```

- コンテナを削除するには、「[docker container rm](https://docs.docker.com/engine/reference/commandline/container_rm/)」コマンドを使用するよ。
- 次のように、削除したいコンテナのコンテナIDを指定するよ。

```sh

docker container rm 9713cd6301ac
```

- 実行結果だよ。

```xxx

9713cd6301ac
```

- 再び、docker ps -aしてみるよ。

```sh

docker ps -a
```

- 実行結果だよ。コンテナが1つ消えたことがわかるね！

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
f84e04390638        ojisanimg1:1.0      "uname -a"          31 minutes ago      Exited (0) 31 minutes ago                       optimistic_meninsky
3140d1e9940c        ojisanimg1:1.0      "uname -a"          3 hours ago         Exited (0) 41 minutes ago                       vibrant_ptolemy
```

- ひとつひとつ削除するのも面倒。「[docker container prune](https://docs.docker.com/engine/reference/commandline/container_prune/)」コマンドを使用すると、停止中の全てのコンテナを一括で削除することが出来るよ。

```sh

docker container prune
```

- 実行すると、警告の表示とともに、処理を続けるかどうか尋ねられる。
    - 続行するので、「y」と入力してエンターキーを押下するよ。

```xxx

WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
```

- すると、次のように表示され、コンテナが削除されるよ。

```xxx

Deleted Containers:
f84e043906380f4df6b7c756a28743370bbfd8a798f86db19a9687454f9c6683
3140d1e9940c8d942160a592dc1d86d746d10f6726383d09d5ed93053ed7ba93

Total reclaimed space: 0B
```

- 本当に削除されたのか、docker ps -aで確認するよ。

```sh

docker ps
```

- 実行結果だよ。 停止中の全てのコンテナが削除されたよ。

```xxx

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

## 不要になったイメージを削除してみる
- まずは、docker imagesで、作成済みのイメージ一覧を問い合わせするよ。

```sh

docker images
```

- 実行結果だよ。

```xxx

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ojisanimg1          1.0                 272308db775e        4 hours ago         162MB
amazonlinux         2.0.20190508        b94321659aca        12 days ago         162MB
```

- イメージの削除は、「[docker rmi](https://docs.docker.com/engine/reference/commandline/rmi/)」コマンドを使用するよ。
    - 削除するイメージIDをパラメータに指定してコマンドを実行するよ。

```sh

docker rmi 272308db775e
```

- 実行結果だよ。

```xxx

Untagged: ojisanimg1:1.0
Deleted: sha256:272308db775e4c0e34fd4a80da0162d60ccd8571fa2c79cd10f6a820a130025a
```

- 本当に削除出来たか、docker imagesしてみるよ。

```sh

docker images
```

- 実行結果だよ。 削除出来た!

```xxx

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
amazonlinux         2.0.20190508        b94321659aca        12 days ago         162MB
```

# 今回のまとめだよ
- コンテナを実行するには、アプリやアプリの実行環境を梱包したイメージが必要だったよ。
- イメージを作成するには、Dockerfileにコンテナの実行環境や、実行するアプリを指定する必要があったよ。
- 実際にDockerfileを書いてみたよ。 イメージを作成するには、その元となるベースイメージが必要だったよ。
- docker buildで、Dockerfileを元にイメージを作成することに成功したよ。
- docker runで、イメージからコンテナを作成＆実行することが出来たよ。
- コンテナは、コンテナ内のアプリ（メインプロセス）が終了すると、コンテナも停止するみたいだよ。
- 停止したコンテナは、docker startで再び実行することも出来たよ。
- 不要なコンテナや、イメージを削除する方法がわかったよ。

# あとがき
- いよいよ、次回はWebサーバとやらの準備に入るよ！　なかなかアプリの開発に到達しないね・・・。
- どこかの誰かも同じように手を動かして、アプリを開発していると思うと、おじさんも一歩前進したのかな、なんて思ったりもしたよ。
- [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)に書いていることが気になるよ。進めていくうちに、再び登場するかもしれないよ。
    - 特に、[この引用](https://12factor.net/ja/processes)が気になるんだよ、おじさん。
- イメージから、コンテナ（アプリの実行環境）を増殖することが出来る点に、何だかワクワクするものを感じたよ。


# 参照・メモ
[Docker overview](https://docs.docker.com/engine/docker-overview/)
[Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
[Dockerfile reference](https://docs.docker.com/engine/reference/builder/)
[Docker CLI reference](https://docs.docker.com/engine/reference/commandline/docker/)
[Amazon Linux 2](https://aws.amazon.com/jp/amazon-linux-2/)
[Docker Official Images - amazonlinux](https://hub.docker.com/_/amazonlinux/)
[Twelve-Factor App](https://12factor.net/ja/)