Dockerの基礎をまとめてみた ~ 第一章：コンテナ？Dockerとは？

# はじめに
本記事では、私がDockerを学習する中で得られた知識をまとめています。対象範囲は、コンテナ技術の背景からDockerの基本的な使い方までです。ベストプラクティスやオーケストレションなどについては対象外としています(別のタイミングでまとめられたらと思っています)。

## 背景
私がDockerの学習を始めようと思った背景は、単純に今所属している会社での使用が必須となり、危機感を覚えたためです。また、[こちらの記事](https://qiita.com/poly_soft/items/fb649573c19b7a5c0227#docker)でも、Dockerの基礎部分については最低限知っておくべき、と言及されております。

この記事では、以下のような形で順次執筆していこうと考えております。

- Dockerの基礎をまとめてみた ~ 第一章：コンテナ？Dockerとは？
- [Dockerの基礎をまとめてみた ~ 第二章：Dockerの基本操作](https://qiita.com/supaiku2452/items/eefdb8ebf106ac579bfa)
- [Dockerの基礎をまとめてみた ~ 第三章：Dockerコマンド - image編](https://qiita.com/supaiku2452/items/cd3d4e2ccd5215f3b6d9)
- [Dockerの基礎をまとめてみた ~ 第四章：Dockerコマンド - container編](https://qiita.com/supaiku2452/items/da44b05c4ba4ec13bccf)

# 第一章 : コンテナ？Dockerとは？
本章では、仮想化を実現する技術の簡単な紹介とDocker本体について記載します。

## 仮想化とは？
まずDockerについて調べる前に、仮想化技術について簡単に触れておきます(Dockerも仮想化技術の一つです)。

**仮想化** とは、1台の物理的なコンピュータの上で、複数の仮想的なコンピュータを実行させる技術・仕組みのことです。仮想化技術を利用すると、それぞれのコンピュータが、個別にOSやアプリケーションなどを実行することが可能となります。

仮想化を実現する技術は、 **ホスト型** 、 **ハイパーバイザー型** 、 **コンテナ型** があります。それぞれについてみてみたいと思います。

## ホスト型
ホスト型は、ホストOSの上に、仮想化を立ち上げるための専用ソフトウェアを導入し、その上で仮想環境を実行する仕組みのことです。こちらの仕組みを実現するものの代表としては、

- VMware Player
- VirtualBox

などが有名です。

![HostOfVm_image.jpg](https://qiita-image-store.s3.amazonaws.com/0/170140/cf18ebd5-ebd0-00db-0d4a-a33bdb036e48.jpeg)


ホスト型の仮想化技術には、以下のようなメリット・デメリットがあります。

### メリット
- ソフトウェアの導入〜仮想環境の構築までの手順が簡単

### デメリット
- ゲストOSの起動に時間がかかる
- ゲストOS→仮想化されたハードウェア→ホストOS→実ハードウェアの順序でハードウェアにアクセスするため、オーバーヘッドがかかる

## ハイパーバイザー型
ハイパーバイザー型は、物理的なハードウェアの上に仮想化を実現するための専用の領域を作り、その上で仮想環境を実行する仕組みのことです。こちらの仕組みを実現するものの代表としては、

- VMware vSphere
- Hyper-V
- KVM

などが有名です。

![HyperVisorOfVm_image.jpg](https://qiita-image-store.s3.amazonaws.com/0/170140/520513a9-daf1-06e1-8dc1-26b846257a51.jpeg)

ハイパーバイザー型の仮想化技術には、以下のようなメリット・デメリットがあります。

### メリット
- オーバーヘッドが小さい(直接ハードウェアを操作できるため)

### デメリット
- ホスト型に比べ、手軽に仮想環境を作れない
  - 専用ハードウェアやソフトウェアが必要

## コンテナ型
コンテナ型は、ホストOS上にコンテナ専用の領域を作り、その上でアプリケーションを実行する仕組みのことです。こちらの仕組みを実現するものの代表としては、

- Docker
- Linux Container

などが有名です。

![ContainerOfVm_image.jpg](https://qiita-image-store.s3.amazonaws.com/0/170140/3ee05659-c440-b2f8-e767-e40aaf5d2b64.jpeg)


コンテナ型の仮想化技術には、以下のようなメリット・デメリットがあります。

### メリット
- アプリケーションの起動が早い
  - ゲストOSの起動を必要としないので、起動が早い
- 環境構築や環境のコピーが簡単
- コストパフォーマンスが良い
  - アプリケーション毎にOSを必要としないため、ライセンス費用を抑えることができる
  - 省スペース(メモリ、ディスク)で実行することができる

### デメリット
- ホストのカーネルに依存するため、コンテナごとで異なるのカーネルを利用することができない
  - Windows と Linuxのような関係
  - Linux同士であれば、カーネルは部分は似ているため、利用することができる(はず)

## Dockerとは？
コンテナ型の仮想化環境を提供するオープンソースのプラットフォームです。

Dockerには、主に3つの機能が存在します。

1. Dockerイメージを作成する機能(Build)
アプリケーションを動かすために必要な、コード/ライブラリ/ミドルウェア/OSなどをひとまとめにしたものをDockerイメージとして作成します。Dockerイメージは、コマンドラインから作成したり、Dockerfileから作成することが可能です。

2. Dockerイメージを共有する機能(Ship)
Dockerレジストリ(Docker Hub)を利用し、イメージを管理します。Docker Hubでは、イメージのアップロードやダウンロード、検索などが行えます。

3. Dockerコンテナを実行する機能(Run)
Dockerイメージをビルドし、コンテナとして実行することができます。コンテナは、開始/停止/削除が行えます。

Dockerは、以下のコンポーネントを利用して上記機能を実現しています。

- Docker Engine
Dockerイメージ作成やコンテナの操作(起動/停止/削除)などを行うためのDockerのコア機能です。
上記の *Build* と *Run* の役割を果たします。

- Docker Registry
Dockerイメージを管理(公開/共有)する機能です。
上記の *Ship* の役割を果たします。

- Docker Compose
複数のDockerコンテナを使うDockerアプリケーションを、定義・実行するためのツールです。

- Docker Machine
仮想マシン上でDocker Engineを管理(インストール/開始/停止)するためのツールです。

- Docker Swarm
Docker用のクラスタリング用のツールです。最近は、kubernetesが主流になっていますので、割愛します。

## ちょっと蛇足、Dockerの歴史
こちらにDockerの歴史がまとめられています。

- [Dockerの歴史から紐解く、コンテナ型仮想化の「今まで」と「これから」](https://www.sbbit.jp/article/cont1/34350)

## Dockerが動く仕組み
Dockerに関する簡単な説明をしましたが、Dockerがどのような仕組みで動いているか簡単にみてみたいと思います。
Dockerを実現する技術は、Linux Containerで採用されていた技術がベースになっています。それが、 *namespace* と *cgroups* です。

## namespace
プログラム言語におけるnamespaceは、関数やクラスなどの名前衝突を避けるための仕組みです。Linuxにおけるnamespaceは、Dockerホスト上のリソースを区切って、独立した空間を擬似的に作り出すための仕組みです。

Linuxでは、以下のリソースに対してnamespaceを設定することができます。

### PID namespace
PIDは、プロセスに割り当てられたユニークなIDのことをさします。PID namespaceは、PIDとプロセスを切り離すことで、互いのプロセスにアクセスすることを避けることができます。

### Network namespace
Network namespaceは、ネットワークリソース(ネットワークデバイス/IPアドレスなど)をリソース毎に独立して持つことができます。

### UID namespace
UIDは、ユーザIDのことをさし、GIDはグループIDのことをさします。UID namespaceは、UIDとGIDをリソース毎で独立して持つことができます。namespaceを利用するとホストとリソース内のユーザを紐つけることができ、ホストは一般ユーザで、リソース内はrootユーザなどと設定することができるため、ホストとリソース間のセキュリティをあげることができます。

### MOUNT namespace
MOUNT namespaceでは、リソース毎でマウントされたファイルシステムを独自のツリーとして持つことができます。そのため、別のリソースからはそのファイルシステムに対してはアクセスできない仕組みとなっています。

### UTS namespace
UTSは、Unix Time Sharingのことをさします。UTS namespaceは、リソースごとで独自のUTSを持つことができます。

### IPC namespace
IPCは、Inter Process Communicationのことをさします。IPC namespaceは、リソースごとで独自のIPCを持つことができます。

### リソース管理の仕組み(control groups)
Dockerでは、リソース管理の仕組みをLinuxカネールにある *control groups(cgroups)* を利用して実現しています。

cgroupsは、2006年に開発が始まり、2008年にLinuxカーネルにマージされた機能で、プロセスグループのリソースを利用を制限、隔離を管理する機能です。

cgroupsでは、以下のリソースグループを管理することができます。

- blkio : このサブシステムは、物理ドライブ (例: ディスク、ソリッドステート、USB) などのブロックデバイスの入出力アクセスの制限を設定する
- cpu : このサブシステムは、スケジューラーを使用して cgroup タスクに CPU へのアクセスを提供する
- cpuacct : このサブシステムは、cgroup 内のタスクで使用される CPU リソースについての自動レポートを生成する
- cpuset : このサブシステムは、個別の CPU (マルチコアシステム上) およびメモリーノードを cgroup 内のタスクに割り当てる
- devices : このサブシステムは、cgroup 内のタスクによるデバイスへのアクセスを許可または拒否する
- freezer : このサブシステムは、cgroup 内のタスクを一時停止または再開する
- memory : このサブシステムは、cgroup 内のタスクによって使用されるメモリーに対する制限を設定し、それらのタスクによって使用されるメモリーリソースについての自動レポートを生成する
- net_cls : このサブシステムは、Linux トラフィックコントローラー (tc) が特定の cgroup から発信されるパケットを識別できるようにするクラス識別子 (classid) を使用して、ネットワークパケットにタグを付ける
- net_prio : このサブシステムは、ネットワークインターフェース別にネットワークトラフィックの優先度を動的に設定する方法を提供する

上記内容は、コンテナ内の `/sys/fs/cgroup` で確認することができます。

```
# ls -l /sys/fs/cgroup
total 0
drwxr-xr-x 2 root root 0 Sep 26 15:02 blkio
drwxr-xr-x 2 root root 0 Sep 26 15:06 cpu
drwxr-xr-x 2 root root 0 Sep 26 15:06 cpuacct
drwxr-xr-x 2 root root 0 Sep 26 15:06 cpuset
drwxr-xr-x 2 root root 0 Sep 26 15:06 devices
drwxr-xr-x 2 root root 0 Sep 26 15:06 freezer
drwxr-xr-x 2 root root 0 Sep 26 15:06 hugetlb
drwxr-xr-x 2 root root 0 Sep 26 15:06 memory
drwxr-xr-x 2 root root 0 Sep 26 15:06 net_cls
drwxr-xr-x 2 root root 0 Sep 26 15:06 net_prio
drwxr-xr-x 2 root root 0 Sep 26 15:06 perf_event
drwxr-xr-x 2 root root 0 Sep 26 15:06 pids
drwxr-xr-x 2 root root 0 Sep 26 15:06 systemd
```

また、リソースに対する設定情報は、各ディレクトリ内にあります。例えば、CPUの場合は以下のような設定になっています。

```
# cat /sys/fs/cgroup/cpu/cpu.shares
1024
```

CPUの割り当ての優先度が設定されており、デフォルト値が1024となっています。
詳細は、[こちらを見ると参考になります](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu_and_memory-use_case)

```
# cat /sys/fs/cgroup/cpuset/cpuset.cpus
0-1
```

これは、CPUが使用するコア数を指定しています。今回は、0~1個です。

上記まででcgroupsのリソースについて簡単に説明しました。cgroupsでは、上記のリソースをグループとして管理しますことができ、さらに、これらのグループを階層として持つことができます。
階層を持つことでグループの親子関係を実現でき、親子関係も持つと、親のリソースに対する制限が子に引き継がれ、子が親の制限(親>子)を超えることが無くなるため、親プロセスに影響を最小限にすることができます。

### ネットワーク構成(仮想ブリッジ/仮想NIC)
DockerEngineをインストールすると、ホストマシンにブリッジが作成されます。また、コンテナが外部と通信を行う場合は、仮想ブリッジと物理NICでパケットの転送が行われます。そこでは、IPマスカレードによってパケットの転送が行われています。

![Dockerネットワーク構成.jpg](https://qiita-image-store.s3.amazonaws.com/0/170140/5a9bf30b-04cc-0b04-4790-d0d30fcab033.jpeg)


Dockerで利用されるネットワークは、 `docker network ls` で確認することができます。

```
docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
5b68b954416c        bridge              bridge              local
f2052da2f19d        host                host                local
aafd8be32b96        none                null                local
```

上記のネットワーク設定の内容を確認したい場合は、 `docker network inspect <NAME>` コマンドで確認することができます。以下は、bridgeの設定内容です。設定内容を確認すると `172.17.0.0/16` のサブネットが作成されていることがわかります。

```
docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "5b68b954416cb87f896f4a32c7bed9de2c495ad567316f638e562fbf820de1db",
        "Created": "2018-09-29T13:38:39.283310293Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
```

コンテナを起動すると、 **Containers** に記載されているように、 `172.17.0.0/16` のサブネットを持つプライペードIPがeth0に割り当てられます。以下がコンテナに割り当てられたIPアドレスです。

```
        // 省略
        "Containers": {
            "d5ac0d89c95aa61c1e0243f7ea8a7347a6d12ff6387b15471b0889e73f098483": {
                "Name": "ubuntu-demo03",
                "EndpointID": "e91df89f6f4a2e49d5da2b4f1411c0142d28996844a1c3fd27366d33aa2705f2",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        // 省略
    }
]
```

実際にコンテナから外部のサイト(例えば www.google.com)にアクセスすると、ブリッジを経由してホスト側のネットワークを利用してアクセスしていることがわかります。

```
root@38392afdb408:/# traceroute www.google.com
traceroute to www.google.com (172.217.27.164), 30 hops max, 60 byte packets
 1  172.17.0.1 (172.17.0.1)  0.337 ms  0.050 ms  0.109 ms // Gatewayを通じて、ホストのネットワークを経由していることがわかる
 2  192.168.11.1 (192.168.11.1)  4.681 ms  4.623 ms  4.561 ms
 // 以下、省略
```

補足ですが、コンテナはeth0の内容は以下のような状態です。

```
root@38392afdb408:/# ip -d address // ubuntuイメージのコンテナです
// 省略
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
    veth numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
```

### 補足
今回は、Ubuntuのイメージを利用して確認しています。デフォルトだとネットワーク周りを確認するコマンドがないため、コンテナ起動後に以下コマンドを実行しています。

```
apt-get update
apt-get install -y iproute2 traceroute
```

### イメージ管理の仕組み
Dockerイメージは、イメージレイヤとコンテナレイヤに分かれます。イメージレイヤは、ホスト内で共有されるイメージで読み込み専用となっています。コンテナレイヤは、書き込み可能であり、イメージレイヤとの差分がコンテナレイヤに書き込まれます。
こちらについては、翻訳ドキュメントがありますので、[そちら](http://docs.docker.jp/engine/userguide/storagedriver/imagesandcontainers.html)を参考にすると良いと思います（すごくわかりやすい）。

長くなりましたが、Dockerの概要は以上になります。
これからは、よく利用するコマンドについてまとめていきたいと思います。

## まとめ一覧
Dockerの基礎をまとめてみた ~ 第一章：コンテナ？Dockerとは？
[Dockerの基礎をまとめてみた ~ 第二章：Dockerの基本操作](https://qiita.com/supaiku2452/items/eefdb8ebf106ac579bfa)
[Dockerの基礎をまとめてみた ~ 第三章：Dockerコマンド - image編](https://qiita.com/supaiku2452/items/cd3d4e2ccd5215f3b6d9)