Docker を使ってみた

# はじめに

友人（@digital24s さん）に誘われて、クラウドサービスで仮想マシン環境を作ってみました。

[Google Compute Engine に Windows 環境を作ってリモートデスクトップ接続してみた #GoogleCloud - Qiita](https://qiita.com/tinymouse/items/2d357bc4fc2d8ad7fbc6)

続いて、コンテナ型の仮想環境の Docker も使ってみたいと思いました。

# Docker を使ってみる

## Linux マシンを用意する

Docker は Linux OS で稼動します。そのため Linux マシンを用意します。
自分は Google Cloud の Compute Engine を使って Ubuntu OS の仮想マシンを作りました。

## Docker エンジンをインストールする

Ubuntu OS マシンに Docker エンジンをインストールします。

[Docker Engine インストール（Ubuntu 向け） | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/engine/install/ubuntu/)

## Docker コンテナを作ってみる

以下のサイトを参考にしました。

[Dockerコンテナの作成、起動〜停止まで #Docker - Qiita](https://qiita.com/kooohei/items/0e788a2ce8c30f9dba53)

[Docker再入門 #Docker - Qiita](https://qiita.com/kazokmr/items/20240e8b8a847fdae877)

## コンテナイメージをダウンロードする

Docker リポジトリからコンテナイメージをダウンロードします。
今回は CentOS を使ってみようと思います。

```console
$ docker pull centos:latest
```

## コンテナを作成する

イメージからコンテナを作成します。

```console
$ docker create -it --name trydocker centos
```

`-it` オプションをつけて作成します。こうしないと、コンテナを起動した後で OS のシェルにコマンドを入力して出力結果を確認することができません。

`--name` オプションでコンテナに名前をつけておきます。名前は任意です。

作成されたコンテナが確認できます。

```console
$ docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS                      PORTS     NAMES
d6d964941a51   centos          "/bin/bash"   a minute ago     Created                               trydocker
```

## コンテナを起動する

作成したコンテナを起動します。

```console
$ docker start trydocker
```

コンテナの状態を確認してみます。

```console
$ docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS                      PORTS     NAMES
d6d964941a51   centos          "/bin/bash"   a minute ago     Up a minute ago                       trydocker
```

ステータスが「Up」になっています。つまり稼動しています。↑

## コンテナ内で操作する

コンテナ内で OS が起動してシェルが使えるようになっています。これに接続します。

```console
$ docker attach trydocker
```

コンテナ内の OS のシェルでコマンド入力できます。

```console
# whoami
root

# cat /etc/*release
中略
NAME="CentOS Linux"
後略
```

Ubuntu OS 上の Docker 環境で CentOS が起動していることが分かります。使えるコマンドも違ったりします。

コンテナ内のシェルからホストのシェルに戻ります。↓

```console
# exit
```

コンテナの状態を確認します。

```console
$ docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS                      PORTS     NAMES
d6d964941a51   centos          "/bin/bash"   a minute ago     Exited (0) a second ago               trydocker
```

ステータスが「Exited」になっています。つまり停止しています。↑

もう一度、起動して接続します。

```console
$ docker start trydocker
$ docker attach trydocker
```

以下のキー入力します。

```
ctrl+P → ctrl+Q
```

ホスト側に戻りました。コンテナの状態を確認します。

```console
$ docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS                      PORTS     NAMES
d6d964941a51   centos          "/bin/bash"   a minute ago     Up a minute ago                       trydocker
```

ステータスが「Up」になっています。つまり稼動しています。↑

## コンテナ内のコマンドを実行する

コンテナ内のシェルに接続してコマンド入力しましたが、ホスト側でダイレクトに実行することができます。

```console
$ docker exec trydocker cat /etc/*release
中略
NAME="CentOS Linux"
後略
```

参考：[Dockerコンテナ内で操作 attachとexecの違い #Docker - Qiita](
https://qiita.com/RyoMa_0923/items/9b5d2c4a97205692a560)

## コンテナの実体を確認する

ホスト側のファイルを確認してみます。

参考：[コンテナ仮想化Dockerの使い方 & 実体 | NagareLab](https://www.nagarelab.com/ja/2831)

以下のディレクトリが確認できます。

```
/var/lib/
  ├─ docker/
  │  ├─ builder/
     ├─ buildkit/
     ├─ containers/
     │  ├─ d6d964941a51.../
     │  │  ├─ checkpoints/
     │     ├─ mounts/
     │     ├─ d6d964941a51...log
     │     ├─ config.v2.json
     │     ├─ hostconfig.json
     │     ├─ hostname
     │     ├─ hosts
     │     ├─ resolv.conf
     │     └─ resolv.conf.hash
     ├─ image/
     ├─ network/
     ├─ overlay2/
     │  ├─ 6d060f2e6ecc.../
     │  │  ├─ diff/
     │     ├─ work/
     │     ├─ link
     │     └─ lower
     ├─ plugins/
     ├─ runtimes/
     ├─ swarm/
     ├─ tmp/
     ├─ volumes/
     └─ engine-id
```

コンテナ内でファイルを作成します。

```console
# mkdir /home/docker
# echo "Hello, Docker!" > /home/docker/hello.txt
```

ホスト側のファイルを確認します。

```
  ├─ docker/

     ├─ overlay2/
     │  ├─ 6d060f2e6ecc.../
        │  ├─ diff/
           │  ├─ home/
              │  └─ docker/
                    └─ hello.txt        
```

ここで確認できた `hello.txt` をホスト側で編集してみます。

```console
$ sudo echo "Hello, World!" > /var/lib/docker/overlay2/6d060f2e6ecc.../diff/home/docker/hello.txt
```

コンテナ内で確認します。

```console
# cat /home/docker/hello.txt
Hello, World!
```

### ターミナルで使えるファイル管理ソフトを導入する

`cd` や `ls` コマンドを使ってもいいのですが大変なので、ファイラ（ファイル管理ソフト）を導入します。幾つか試してみましたが `ranger` がよさそうです。

[ターミナルで使えるファイラrangerを使ってみる #Linux - Qiita](https://qiita.com/rattcv/items/caed7dd8115b294402c8)

## コンテナを停止する

実行しているコンテナを停止します。

```console
$ docker stop trydocker
```

コンテナの状態を確認してみます。

```console
$ docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS                      PORTS     NAMES
d6d964941a51   centos          "/bin/bash"   a minute ago     Exited (0) a second ago               trydocker
```

ステータスが「Exited」になっています。つまり停止しています。↑

## コンテナを削除する

コンテナが不要になったら削除できます。

```console
$ docker rm trydocker
```

## docker run で実行する

Docker の運用で、上記のように `crete` 、`start` 、`exec` することは、ほとんどないそうです。
`run` コマンドで実行します。

```console
$ docker run --name trydocker centos cat /etc/*release
中略
NAME="CentOS Linux"
後略
```

コンテナの状態を確認してみます。

```console
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                      PORTS     NAMES
746757caaf41   centos    "cat /etc/lsb-releas…"   a second ago     Exited (1) a second ago                trydocker
```

コンテナが作成されて停止しています。↑

コンテナは基本的に使い捨てるそうです。
なので、`--name` オプションをつけないで、さらにコンテナが残らないように `--rm` オプションをつけたりします。

```console
$ docker run --rm centos cat /etc/*release
```

コンテナを起動してシェルに接続してみます。↓

```console
$ docker run -it --rm centos /bin/bash
```

## コンテナのデータを永続化する

`docker run --rm` で実行されたコンテナで作成されたデータは、コンテナが使い捨てられるので残りません。
コンテナが作り直されても、データが残るようにする＝永続化する仕組が用意されています。

参考：[「そのコンテナのデータ、消えるよ……」とさせないための、イメージ図＋実践で理解するDockerストレージ【Dockerコンテナ・グレートジャーニー③】 #Docker - Qiita](https://qiita.com/Toyo_m/items/95a2543c07dc5b126208)

### ホスト側のディレクトリをコンテナにマウントする

ホスト側に共有するディレクトリを用意します。

```console
$ mkdir /home/docker/share
```

これをコンテナ内で利用できるようマウントします。

```console
$ docker run -it --mount type=bind,src=/home/docker/share,target=/home/share --name trymount centos /bin/bash
```

コンテナ内でディレクトリを確認します。

```console
# ls /home/share/
何も表示なし
```

ホスト側でファイルを作ります。

```console
$ echo "Hello, Docker!" > /home/docker/share/hello.txt
```

コンテナ内でディレクトリを確認して、ファイルを変更します。

```console
# ls /home/share
hello.txt
# cat /home/share/hello.txt
Hello, Docker!
# echo "Hello, World!" > /home/share/hello.txt
```

ホスト側でファイルの内容を確認します。

```console
$ cat /home/docker/share/hello.txt
Hello, World!
```

### ボリュームコンテナを作ってコンテナにマウントする

予めボリュームコンテナを用意します。ボリュームコンテナは使い捨てません。

```console
$ docker volume create shared_volume
```

ホスト側のファイルを確認してみます。

```
  ├─ docker/

     ├─ volumes/
     │  ├─ shared_volume/
```

これをコンテナ内で利用できるようマウントします。

```console
$ docker run -it --mount type=volume,src=shared_volume,target=/home/share --name trymount1 centos /bin/bash
```

コンテナ内でファイルを作ります。

```console
# echo "Hello, Docker!" > /home/share/hello.txt
```

上記のコンテナを破棄して、別のコンテナを実行します。

```console
$ docker rm trymount1
$ docker run -it --mount type=volume,src=shared_volume,target=/home/share --name trymount2 centos
```

コンテナ内でファイルの確認します。

```console
# cat /home/share/hello.txt
Hello, Docker!
```

# Docker を理解する

自分は仕事でも趣味でも、Docker を使う機会はないと思っていました。なので、Docker について勉強してきませんでした。
とはいうものの、周囲の知人は Docker を使っていて、会話に出てくることがあります。このとき自分が話題についていけないのは、悔しいと思っていました。

## よくある Docker の説明を見て考えたこと

入門といえる記事を検索して一通り読んでいきました。
ハイパーバイザ型の仮想マシンと比較してコンテナ型の Docker が説明されています。

![説明1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114460/9a236243-2eb7-14b7-5429-4063eb25e6c3.png)

参考：[コンテナってなんだろう― 「コンテナ」の概要を知る | Think IT](https://thinkit.co.jp/article/17301)

これらの説明を読んで、次のように考えていました。

コンテナはホストの OS の機能を呼出するという。
であれば、ホストの OS に予めミドルウェアを入れれば、コンテナで利用できるのか。

コンテナとは、実行時に生成される仮想マシンなのか。それとも、ハイパーバイザ型の仮想マシンの仮想ディスクのようなものなのか。

空のコンテナを作って、コンテナ内で操作するとホスト側で差分情報が記録されて、これを配付して再利用できるのだろうか。

これらの疑問を Doker を利用している知人に聞いてみたが、どうも違っているらしい。けれど、説明を聞いてもよく分からない。使ってみれば分かるようになると言う。
自分は、Docker を使いたいのでなく、Docker がどんなものか分かりたいだけなので、上手い説明が欲しいと思っていました。

## 試してみて分かったこと。記事によっては書かれていなかったこと

実際に使って試しながら、他の記事を読んでいきました。その結果、分かったことがありました。いわゆる「入門」記事には書かれていなかったり、書かれていても分かりづらかったりしたことでした。

まず、UNIX 系 OS なら使えると思っていたが、Linux OS しか Docker は使えない。

ベースになるコンテナイメージが予め用意されていて、それを使ってコンテナを作る。

コンテナ内に OS のカーネルはなくて、ホスト機の OS のカーネルを呼出する。
ホスト機の OS のユーティリティソフトやミドルウェアは、コンテナから見えるようになっていない。コンテナ内にユーティリティソフトが用意される。
自分はユーティリティソフトを含めて OS（「広義の OS」）と言うスタンスなので、コンテナ内に OS がない説明を見て、勘違いしていた。

Docker のコンテナは、ホスト OS の所定のディレクトリにあるファイル群である。
コンテナ内で作成されたデータは、このディレクトリに作成される。

コンテナは基本的に使い捨てる運用する。
永続化したいデータはコンテナの外に置くようにする。

![説明2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114460/40b96902-92eb-a9e7-0054-3271ba6d0f82.png)