Dokcer入門

# Dokcerとは
Dokcer社が提供する「コンテナ型仮想化技術」で、コンテナ仮想化を用いてアプリケーションを開発・配置・実行をするためのオープンプラットフォームである。
アプリケーションを基盤から分離し、アプリケーションのコードの転送、テスト、デプロイ、コードの記述とコードの実行におけるサイクルを短くできるもので、基盤から分離することにより、OSやバージョンの違いといった開発環境及び動作環境と異なる環境においてでも、アプリケーションの動作が容易に実現できる技術である。


## Dokcerの概念

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/7ce7b411-1fc0-e697-b389-e3839e4ad647.png" width=70%)>

従来の仮想化技術では、ホストOS(Windows、MacOS等)上で、仮想化ソフト/ハイパーバイザー(VirtualBox等)により別のOSを立ち上げるということをしていました。この場合、1台のPCで並列してOSを起動させており、動作が重くなるという欠点があります。また、チーム内でのファイル共有も手間がかかります。
<br></br>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/0571b233-31cd-e83a-07d0-d848eee8fef3.png" width=70%)>

Dokcerでは、それぞれ異なったホストOS上でDokcerエンジンと呼ばれるLinux仮想マシンのカーネル(OSの中核部で、システムのリソースやハードウェアとソフトウェアの連携を管理)を裏で動かすことで、各コンテナのアプリケーションを動かしています。

ホストOSのカーネルを各コンテナが共有することで、ホストマシンのリソース(CPU、メモリ)を節約しながら動作が可能になります。
これにより、動作が軽量になるということになります。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/1ec5a5de-7b8f-1cf0-9748-b199a34877f8.png" width=70%)>

また、Dokcer HubといったコンテナネットワークにDockerイメージ（コンテナ）をPush(アップロード)及びPull(ダウンロード)することで、コンテナの共有を開発者同士で行うこともできます。

## なぜ個々の環境が必要なのか

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/2bdbf2fa-58df-f6f1-f02e-f628031f8707.png" width=70%)>

例えば、図のように環境AでアプリAを動かそうとしても、そもそもPythonのバージョンの違いではじかれ、アプリCではNumpyのバージョンが異なると言われるような事態が起こりえます。
逆のケースもあり、昔のソースコードを実行しようとしたらバージョンが新しくて実行できないことなどもあります。

この問題は、ローカル（個人）で開発環境を切り替えることで、基本的に解決できます。

- Anaconda：https://www.anaconda.com/products/individual
- pyenv　　：https://github.com/pyenv/pyenv

しかし、複数人での開発の場合、それぞれ同じ開発環境に合わせていたとしても、ホストOSが異なっていたり、コンフィグ（設定）を多少いじっていたりと、ちょっとした依存関係の変化により、正常に起動しないことが発生します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/e9d1f0b4-63f4-ad14-ec19-a93c5fcb3d4d.png" width=70%)>

開発者にとっての問題だけではなく、ユーザ側も同じ環境構築をする場合があります。
提供者視点では、ユーザ側への環境構築サポートをしなければならないですし、
ユーザ視点では、アプリケーションを要求しているのに、環境構築も行なければならないのか、そのサポート費用も取られるのかといった疑問もあります。（ケースバイケース）

この状態では双方のコストパフォーマンスが悪いということです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/b2788f1c-79b4-dcc4-7d36-3018a5c15f3d.png" width=70%)>

Dokcerを用いて、同じDokerイメージを共有することで、開発、運用を容易に行えるようになります。

要は、__Dockerを使用することで開発環境を徹底的に揃えることができる__ ということです。

## Dockerイメージとは
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/5b94ec2a-9908-39ac-3b31-08d149c2cb66.png" width=70%)>

Dockerイメージは、何層にもなるレイヤが存在し、もとになるベースイメージにイメージ・レイヤを重ねることで、新たなDockerイメージを作成することができます。ベースイメージとはCentOSやUbuntuなどのOSやNginxやApacheなどのWebサーバなどになります。

Dockerコンテナの実行に必要な概念としてのパッケージ（ファイルやメタ情報の集まり）であり、仮想マシンイメージのように、実体としての１ファイルではありません。また、それぞれのレイヤに親子関係を持っています。

通常、Dockerイメージを自動構築できるように定義するのが Dockerfile と呼ぶ設定ファイルであり、このファイル内でイメージを構成する命令を書きます。この命令の1つ1つが、概念上のイメージレイヤに相当します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/658afc15-9bf8-78cc-978a-7a62e2a0a135.png" width=70%)>

1つのミドルウェアをインストールするたび、レイヤが積み重ねっていくイメージで、それぞれのイメージレイヤは編集不可(Read Only)であり、Dockerイメージからコンテナを起動した際に作成される、コンテナレイヤのみ編集可能になっています。

これは、コンテナ内で何かしらのミドルウェアをインストールした際に、そのコンテナレイヤに保存し、新たなイメージレイヤとするためであります。

# 実践
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/2c7b0a95-c8c6-343f-dd93-457c04adca9e.jpeg" width=70%)>

Macは[こちら](https://hub.docker.com/editions/community/docker-ce-desktop-mac/)、Windowsは[こちら](http://hub.docker.com/editions/community/docker-ce-desktop-windows)からDockerをインストール


「WSL 2 installation is incomplete.」が表示された場合
以下をインストールし、Dockerを再起動

「Docker Desktop starting...」が表示されれば問題なし

Windows Subsystem for Linux
https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi

以下はWSLについてのドキュメント、見なくてもいい
WSLドキュメント
https://docs.microsoft.com/ja-jp/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package

## 動作確認

```
$ docker --version

Docker version 20.10.13, build a224086
```

## 動作テスト

基本的なコマンドとして
```
$dockerコマンド サブコマンド イメージ
```
これをターミナル、コマンドプロンプト等にて打ち込むことでコンテナを実行します。
テストとして、「hello-world」というコンテナを実行します（Dockerは常駐している上で）。
- https://hub.docker.com/_/hello-world
```
$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:f861ba8563637d693e3043069b0a4ebf3d92c2e865b541379f6a6b8164b7c8bb
Status: Downloaded newer image for hello-world:latest 

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.
    (amd64)
 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 ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```
この一文が出力されていれば、「hello-world」のコンテナが実行できていることになります。
```
Hello from Docker!
```
コンテナが存在するのか、もう一度確認します。
```
# ダウンロードしたイメージの一覧を表示します
$ docker image ls

REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello-world   latest    feb5d9fea6a5   5 months ago   13.3kB
```

```
# すべてのコンテナを表示します（デフォルトは実行中のコンテナのみ）
$ docker ps -all

CONTAINER ID   IMAGE         COMMAND    CREATED        STATUS                    PORTS     NAMES
56f95902920e   hello-world   "/hello"   34 hours ago   Exited (0) 34 hours ago             mystifying_jemison
```
コマンドが返ってきて、hello-worldの表記があれば、問題ないです。

# Dockerfileからイメージ作成
Dockerfileとは、Dockerイメージを作成する為に実行するコマンドをコード化して、一つのファイルにまとめたものです。
適当なディレクトリに"Dockerfile"という名前のファイルを作成します。以下のコードを記述し、保存します。
```Dockerfile
FROM centos:centos7
RUN yum -y install httpd php
COPY test.php /var/www/html/
CMD ["/usr/sbin/httpd","-DFOREGROUND"]
```
画面表示用に、phpファイルを同じ階層に作成し、以下のコードを記述します。
```test.php
<?php
  echo "Hello World!";
?>
```
この時点でのファイルの配置はこのようになっています。フォルダ名の"Docker"は任意です。
```
Docker/
   ├ Dockerfile
   ├ test.php
```
コマンドプロンプト、スクリプトを開いて、上記のDockerfileと同じ階層に行き、以下のコマンドを打ち込みます。
ここで、設定ファイルであるDockerfileからDockerイメージを作成します。

```
$ docker build -t testphpimg .

[+] Building 1.9s (9/9) FINISHED
 => [internal] load build definition from Dockerfile                                                                        0.0s
 => => transferring dockerfile: 164B                                                                                        0.0s
 => [internal] load .dockerignore                                                                                           0.0s
 => => transferring context: 2B                                                                                             0.0s
 => [internal] load metadata for docker.io/library/centos:centos7                                                           1.8s
 => [auth] library/centos:pull token for registry-1.docker.io                                                               0.0s
 => [internal] load build context                                                                                           0.0s
 => => transferring context: 29B                                                                                            0.0s
 => [1/3] FROM docker.io/library/centos:centos7@sha256:c73f515d06b0fa07bb18d8202035e739a494ce760aa73129f60f4bf2bd22b407     0.0s
 => CACHED [2/3] RUN yum -y install httpd php                                                                               0.0s
 => CACHED [3/3] COPY test.php /var/www/html/                                                                               0.0s
 => exporting to image                                                                                                      0.0s
 => => exporting layers                                                                                                     0.0s
 => => writing image sha256:3e6d74481e3e3c1bbf796c8bf640c656d6515bf4b88efe95188292eb16c9b9bc                                0.0s
 => => naming to docker.io/library/testphpimg                                                                               0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
```
Dockerイメージが作成されていることを確認します。
```
$ docker images

REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
testphpimg    latest    xxxxxxxxxxxx   47 minutes ago   414MB
hello-world   latest    xxxxxxxxxxxx   5 months ago     13.3kB
```

次に、作成したDockerイメージをコンテナとして動かします。
```
$ docker run -d -p 8080:80 --name testweb testphpimg:latest

//64文字の文字列が表示される
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
起動を確認したら、以下にアクセス
- http://localhost:8080/test.php

|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/5f2b1a6e-ed2c-0e56-1663-2840076242b5.jpeg" width=80%)>|
|:--|

Hello World!が表示されていれば、正常にコンテナが動作しています。
コマンドからも動作していることが確認できます。
```
$ docker ps -all

CONTAINER ID   IMAGE               COMMAND                  CREATED          STATUS          PORTS                  NAMES
xxxxxxxxxxxx   testphpimg:latest   "/usr/sbin/httpd -DF…"   43 minutes ago   Up 43 minutes   0.0.0.0:8080->80/tcp   testweb
```
間違った場合には、イメージとコンテナの削除コマンドを使用します。
```
//Dockerイメージの削除
$ docker rmi [IMAGE ID] 

//コンテナを削除
$ docker rm [CONTAINER ID]
```
順序はコンテナを立ち上げている場合には、コンテナ削除→Dockerイメージを削除の順番となります。

## Dockerfileの記述
### FROM 
```
FROM [イメージ名]:[タグ名]
```

FROMで指定したDockerイメージをベースイメージとして用いることを宣言するコマンドです。
```Dockerfile
FROM centos:centos7
```

先ほどの例では、CentOSをベースイメージとして使用しています。

### RUN
```
RUN [実行するコマンド]
```
FROMで指定したベースイメージに対してコマンドを実行させるために記述します。

```Dockerfile
RUN yum -y install httpd php
```
ここでは、ApacheとPHPの両方をインストールしています。

### COPY
```
COPY [イメージ内にコピーしたいファイルのパス][コピー先のベースイメージ内のファイルパス]
```
作成したDockerイメージ内にホストPC上にあるファイルを転送したい場合に使用します。

```Dockerfile
COPY test.php /var/www/html/
```
ホストPCで作成したtest.phpをCentOS内の/var/www/html/にコピーしています。

### CMD
```
CMD ["実行コマンド","引数"]
```
docker runコマンドで実行する際（立ち上げ後）のコマンドを指定します。
CMDコマンドはイメージからコンテナ作成時に1度だけ実行されます。

```
CMD ["/usr/sbin/httpd","-DFOREGROUND"]
```

ここでは、Apacheを起動させ、コマンド引数に-DFOREGROUNDを持たせています。

## Dockerfileの挙動

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/3cf61586-4d11-c8cd-649b-d74f10146f9a.png" width=70%)>

これらの一連の動作を示した図です。
それぞれのコマンドは実行タイミングによる違いで、docker buildにより、Docker HubからCentOSのDockerイメージをピックし、このイメージをベースイメージとして、新たなイメージのtestphpimgを作成します。作成時に、RUN、COPYコマンドが実行され、testphpimgからコンテナを起動させる際に、CMDコマンドが実行されます。

つまりは、CentOSをベースイメージに、ApacheとPHPをインストールし、/var/www/html/にtest.phpをコピーしたイメージがtestphpimgであり、コンテナ作成時に、Apacheを起動させるといった流れになります。

# Docker Hub
Docker Hubには様々なミドルウェアがインストールされたDockerイメージが存在します。
- https://hub.docker.com/
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388964/d60cb781-2bf0-dd51-eef4-0019ef1a8410.jpeg" width=70%)>

以下のコマンドを使用することで、Docker Hub上のDockerイメージを取得できます。
```
$ docker pull イメージ名
```

# 参考文献
http://docs.docker.jp/get-started/overview.html

https://ja.wikipedia.org/wiki/Docker

https://tech-lab.sios.jp/archives/19191

https://tech-lab.sios.jp/archives/18811

https://aiacademy.jp/media/?p=1106

https://aiacademy.jp/media/?p=1386

https://kitsune.blog/docker-summary

https://qiita.com/etaroid/items/b1024c7d200a75b992fc

https://qiita.com/zembutsu/items/24558f9d0d254e33088f

https://qiita.com/tifa2chan/items/e9aa408244687a63a0ae