コンテンツにスキップ

Docker@コンテナ型仮想化

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. Dockerの仕組み

アーキテクチャ

Docker は、ホスト OS、Docker コンテナ (ベースイメージ、コンテナイメージレイヤー、コンテナレイヤー) といったコンポーネントから構成される。

docker_architecture


Dockerコンテナ

▼ Dockerコンテナとは

namespace と cgroups を使用して、ホスト OS を分割した領域である。

namespace では、カーネルを分離できるため、カーネルの要素 (プロセス、マウントポイント、ネットワーク、ファイル構造、ユーザー、グループなど) を独立させられる。

▼ ベースイメージ

ベースイメージは、実行 OS に依存せず一貫してビルドできる。

そのため、配布できる。

各イメージレジストリ (例:DockerHub、Amazon ECR、Google Container Registry、Artifactory、Harbor など) には、カスタマイズするうえでのベースとなるベースイメージが提供されている。

▼ イメージレイヤー

イメージレイヤーからなるコンテナイメージのビルド

イメージレイヤーの実体は、/var/lib/docker/overlay2 ディレクトリ配下にハッシュ値の名前からなるファイルとして保管されている。

Docker は、オーバーレイファイルシステムを使用して、各ファイルを層状に管理する。

1 つの命令につき、1 つのコンテナイメージレイヤーを積み重ねるようになっている。

$ docker container inspect foo-container -f "{{json .GraphDriver.Data}}" | jq .

{
  "LowerDir": "/var/lib/docker/overlay2/*****-init/diff:/var/lib/docker/overlay2/*****/diff:/var/lib/docker/overlay2/*****/diff:/var/lib/docker/overlay2/*****/diff:/var/lib/docker/overlay2/*****/diff",
  "MergedDir": "/var/lib/docker/overlay2/*****/merged",
  "UpperDir": "/var/lib/docker/overlay2/*****/diff",
  "WorkDir": "/var/lib/docker/overlay2/*****/work"
}

▼ コンテナレイヤー

コンテナイメージからコンテナを作成するときに、コンテナイメージレイヤーのうえにコンテナレイヤーが積み重ねられる。

コンテナイメージ上へのコンテナレイヤーの積み重ね


権限別コンテナの種類

▼ 特権コンテナ

root ユーザー権限の Capability (CHOWN、NET_RAW、CAP_SYS_BOOT、CAP_AUDIT_WRITE など) のすべて持つユーザーで実行したコンテナのこと。

例えば、特権コンテナの実行ユーザーは、ルートファイルシステム上の /proc へ書き込む権限を持つ。

/proc 配下には Node へのアクセスを中継するファイルがあるため、特権コンテナの実行ユーザーは Node 上で任意のコマンドを実行できる。

▼ 通常コンテナ

root ユーザーで実行したコンテナのこと。

▼ 非rootコンテナ

コンテナランタイムとコンテナ自体の両方を非 root ユーザーで実行したコンテナのこと。

これらのユーザーは、Capability を全く持たない。


02. Dockerクライアント

dockerクライアント

▼ dockerクライアントとは

docker クライアントは、docker コマンドを使用して docker デーモン API をコールできる。

docker-daemon


dockerデーモン

▼ dockerデーモンとは

ホスト側に常駐し、コンテナの操作を担うデーモン。

docker クライアントに docker デーモン API を公開する。

クライアントが docker コマンドを実行することにより、docker デーモン API がコールされ、コマンドに沿ってコンテナが操作される。

docker-daemon


03. cgroups

コンテナが使用できるハードウェアリソースを分離する。


04. namespace

namespaceとは

各 namespace を使用して、ホストのカーネルを論理的に分離する。

論理的に分離された各領域をコンテナとして使用する。

コンテナ間では、ハードウェアリソース要求やプロセス間通信が独立している。

注意点として、Kubernetes とは namespace の種類が異なる。


IPC namespace

SysV IPC オブジェクト、POSIX メッセージキューを分離する。

コンテナは、同じ IPC namespace に属するほかのプロセスと通信できる。


Network namespace

ネットワークデバイス、アドレス、ポート、ルーティングテーブル、フィルタを分離する。

各コンテナが独立した、ネットワークデバイス、アドレス、ポート、ルーティングテーブル、フィルタを持てるようになる。


Mount namespace

マウントに関する処理を分離する。

各コンテナが独立してマウントを処理できるようになる。


PID namespace

プロセス ID を分離する。

各コンテナが独立した PID を持てるようになる。

逆に言うと、同じ PID namespace に属するプロセスの PID は同じになる。


User namespace

ユーザーID (UID)、グループ ID (GID)を分離する。

各コンテナが独立した UID や GID を持てるようになる。

ただし、Docker では機能が制限されることを嫌って、User Namespace をデフォルトで無効化している。

そのため、コンテナとホストの UID/GID が同じになっている。

これに伴い、コンテナを root ユーザーで実行することに脆弱性がある。


UTS namespace

ドメインを分離する。

各コンテナが独立したドメインを持てるようになる。


03. ロギング

ロギングドライバー

▼ ロギングドライバーとは

コンテナ内の標準出力 (/dev/stdout) と標準エラー出力 (/dev/stderr) に出力されたログを、ファイルや API に対してフォワーディングする。

$ docker run -d -it --log-driver <ロギングドライバー名> --name  <コンテナ名> <コンテナイメージ名>:<バージョンタグ> /bin/bash

▼ json-file

標準出力/標準エラー出力に出力されたログを、/var/lib/docker/containers/<コンテナID>/<コンテナID>-json.log ファイルにフォワーディングする。

デフォルトの設定値である。

labels キーで文字列リストを設定すると、属性キーを付与できる。

{
  "log-driver": "json-file",
  # ドライバーのオプション
  "log-opts": {
      "max-size": "10m",
      "max-file": "3",
      # JSONに付与する属性キー
      "labels": "app,env",
    },
}

▼ fluentd

構造化ログに変換し、サイドカーとして稼働する Fluentd コンテナに送信する。

Amazon ECS コンテナの awsfirelens ドライバーは、fluentd ドライバーをラッピングしたものである。

 {
   "log-driver": "fluentd",
    # ドライバーのオプション
   "log-opts": {
     "fluentd-address": "<Fluentdコンテナのホスト名>:24224"
   }
 }

▼ none

標準出力/標準エラー出力に出力されたログを、ファイルや API にフォワーディングしない。

ファイルに出力しないことにより、開発環境のアプリケーションサイズの肥大化を防ぐ。

▼ awslogs

標準出力/標準エラー出力に出力されたログを Amazon CloudWatch-API に送信する。

{
  "log-driver": "awslogs",
  # ドライバーのオプション
  "log-opts": {"awslogs-region": "us-east-1"},
}

▼ gcplogs

標準出力/標準エラー出力に出力されたログを、Google Cloud Logging の API にフォワーディングする。

{
  "log-driver": "gcplogs",
  # ドライバーのオプション
  "log-opts": {"gcp-meta-name": "example-instance-12345"},
}


各ベンダーのコンテナイメージのログ出力先

▼ コンテナの標準出力/標準エラー出力

Linux では、標準出力は『/proc/<プロセスID>/fd/1』、標準エラー出力は『/proc/<プロセスID>/fd/2』である。

コンテナでは、『/dev/stdout』が『/proc/self/fd/1』のシンボリックリンク、また『/dev/stderr』が『/proc/<プロセスID>/fd/2』のシンボリックリンクとして設定されている。

[root@<コンテナID>:/dev] $ ls -la

total 4
drwxr-xr-x 5 root root  340 Oct 14 11:36 .
drwxr-xr-x 1 root root 4096 Oct 14 11:28 ..
lrwxrwxrwx 1 root root   11 Oct 14 11:36 core -> /proc/kcore
lrwxrwxrwx 1 root root   13 Oct 14 11:36 fd -> /proc/self/fd
crw-rw-rw- 1 root root 1, 7 Oct 14 11:36 full
drwxrwxrwt 2 root root   40 Oct 14 11:36 mqueue
crw-rw-rw- 1 root root 1, 3 Oct 14 11:36 null
lrwxrwxrwx 1 root root    8 Oct 14 11:36 ptmx -> pts/ptmx
drwxr-xr-x 2 root root    0 Oct 14 11:36 pts
crw-rw-rw- 1 root root 1, 8 Oct 14 11:36 random
drwxrwxrwt 2 root root   40 Oct 14 11:36 shm
lrwxrwxrwx 1 root root   15 Oct 14 11:36 stderr -> /proc/self/fd/2 # 標準エラー出力
lrwxrwxrwx 1 root root   15 Oct 14 11:36 stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root   15 Oct 14 11:36 stdout -> /proc/self/fd/1 # 標準出力
crw-rw-rw- 1 root root 5, 0 Oct 14 11:36 tty
crw-rw-rw- 1 root root 1, 9 Oct 14 11:36 urandom
crw-rw-rw- 1 root root 1, 5 Oct 14 11:36 zero

▼ nginxイメージ

公式の nginx イメージは、/dev/stdout というシンボリックリンクを、/var/log/nginx/access.log ファイルに作成している。

また、/dev/stderr というシンボリックリンクを、/var/log/nginx/error.log ファイルに作成している。

これにより、これらのファイルに対するログの出力は、/dev/stdout/dev/stderr にフォワーディングされる。

▼ php-fpmイメージ

記入中...