Docker内部構造を理解しよう!namespace・cgroup・runcで学ぶコンテナ技術

「Dockerってなんで仮想マシンより軽いの?」
「namespaceとかcgroupって何をしているの?」
「runcって聞くけど、Dockerとどう違うの?」

Dockerを使い始めた方なら、一度はこんな疑問を抱いたことがあるのではないでしょうか。

Dockerコンテナが軽量で高速な理由は、Linux カーネルの機能を巧妙に組み合わせた仕組みにあります。その中核となるのが、namespacecgroup、そしてruncという技術です。

この記事では、技術評論社の「[改訂新版]イラストでわかるDockerとKubernetes」を参考に、Dockerの内部構造とコンテナ技術の仕組みについて、初心者にもわかりやすく解説していきます!

[改訂新版]イラストでわかるDockerとKubernetes (Software Design plusシリーズ)
created by Rinker
¥2,363 (2025/10/01 00:01:09時点 楽天市場調べ-詳細)

「なんとなく使えているけど、中身がよくわからない」から「仕組みを理解して自信を持って使える」へ、一緒にステップアップしていきましょう!

目次

Dockerコンテナの軽量さの秘密

仮想マシンとの比較

まずは、なぜDockerコンテナが軽量なのかを理解するために、従来の仮想マシンと比較してみましょう。

【仮想マシンの構成】
┌─────────────────────────────────────┐
│        物理サーバー                    │
│ ┌─────────────────────────────────┐ │
│ │      ホストOS                    │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────┐ │ │
│ │ │ハイパー   │ │VM1      │ │VM2  │ │ │
│ │ │バイザー   │ │ ┌─────┐ │ │┌──┐│ │ │
│ │ └─────────┘ │ │ゲストOS│ │ ││OS││ │ │
│ │             │ │ ┌───┐ │ │ │┌┐││ │ │
│ │             │ │ │APP│ │ │ │││││ │ │
│ │             │ │ └───┘ │ │ │└┘││ │ │
│ │             │ └─────┘ │ │└──┘│ │ │
│ │             └─────────┘ └─────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
【Dockerコンテナの構成】
┌─────────────────────────────────────┐
│        物理サーバー                    │
│ ┌─────────────────────────────────┐ │
│ │      ホストOS(Linux)             │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────┐ │ │
│ │ │Docker   │ │Container1│ │Cont2│ │ │
│ │ │Engine   │ │ ┌─────┐ │ │┌──┐│ │ │
│ │ └─────────┘ │ │ APP │ │ ││APP││ │ │
│ │             │ └─────┘ │ │└──┘│ │ │
│ │             └─────────┘ └─────┘ │ │
│ │          共有カーネル              │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘

重要な違い:

  1. 仮想マシン: 各VMが独自のOSを持つ
  2. Dockerコンテナ: ホストOSのカーネルを共有

この違いにより、Dockerコンテナは:

  • 起動が高速(数秒)
  • メモリ使用量が少ない
  • オーバーヘッドが最小

コンテナが軽量である理由

Dockerコンテナが軽量な理由は、Linux カーネルの以下の機能を活用しているからです:

  1. namespace: プロセスの隔離
  2. cgroup: リソース制御
  3. Union ファイルシステム: レイヤー化されたファイルシステム
  4. copy-on-write: 効率的なデータ管理

Linux カーネル機能の活用

重要なポイントは、Dockerは「新しい技術を発明した」わけではなく、既存のLinux機能を組み合わせて使いやすくしたということです。

【Dockerの技術スタック】
┌─────────────────────────────────────┐
│         Docker CLI                  │ ← ユーザーインターフェース
├─────────────────────────────────────┤
│       Docker Engine                 │ ← コンテナ管理
├─────────────────────────────────────┤
│      containerd / runc              │ ← ランタイム
├─────────────────────────────────────┤
│   namespace + cgroup + ...          │ ← Linux カーネル機能
├─────────────────────────────────────┤
│        Linux Kernel                 │ ← OS基盤
└─────────────────────────────────────┘

つまり、Dockerはこれらの複雑な機能を抽象化し、簡単なコマンドで使えるようにしてくれているのです!

namespaceによるプロセス隔離

namespaceとは何か

namespace(ネームスペース)は、Linuxカーネルの機能の一つで、プロセスが見える世界を制限する仕組みです。

簡単に言うと、「コンテナの中から外の世界が見えないようにする魔法」のようなものです!

各種namespaceの役割

Linuxには複数のnamespaceが存在し、それぞれ異なる側面でプロセスを隔離します:

1. PID namespace

プロセスIDの隔離を行います。

# ホスト側から見たプロセス
$ ps aux | head -3
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  167872 11532 ?        Ss   Oct01   0:01 /sbin/init
root         2  0.0  0.0      0     0 ?        S    Oct01   0:00 [kthreadd]

# コンテナ内から見たプロセス(PID 1から開始)
$ docker exec -it mycontainer ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4624   828 ?        Ss   10:00   0:00 /bin/bash
root        12  0.0  0.0  34400  2812 ?        R+   10:01   0:00 ps aux

重要なポイント

  • コンテナ内のプロセスは、自分たちだけしか見えない
  • コンテナ内の最初のプロセスがPID 1になる
  • ホスト側では別のPIDを持つ
[改訂新版]イラストでわかるDockerとKubernetes (Software Design plusシリーズ)
created by Rinker
¥2,363 (2025/10/01 00:01:09時点 楽天市場調べ-詳細)

2. Network namespace

ネットワークインターフェースの隔離を行います。

# ホスト側のネットワークインターフェース
$ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP

# コンテナ内のネットワークインターフェース
$ docker exec -it mycontainer ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP

3. Mount namespace

ファイルシステムのマウントポイントを隔離します。

# ホスト側のマウント情報
$ mount | grep "^/"
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
/dev/sda2 on /home type ext4 (rw,relatime)

# コンテナ内のマウント情報(異なるルートFS)
$ docker exec -it mycontainer mount | head -2
overlay on / type overlay (rw,relatime,lowerdir=...)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

4. UTS namespace

ホスト名とドメイン名を隔離します。

# ホスト側
$ hostname
my-server

# コンテナ内(異なるホスト名)
$ docker exec -it mycontainer hostname
a1b2c3d4e5f6

5. User namespace

ユーザーIDとグループIDを隔離します。

# コンテナ内ではroot(UID 0)でも
# ホスト側では一般ユーザーとして実行される

6. IPC namespace

プロセス間通信を隔離します。

実際のコマンド例と動作確認

namespaceの動作を確認してみましょう:

# 新しいPID namespaceでbashを起動
$ sudo unshare --pid --fork --mount-proc bash

# この新しい環境内でpsコマンドを実行
$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  22796  5164 pts/0    S    10:00   0:00 bash
root         7  0.0  0.0  38460  3336 pts/0    R+   10:00   0:00 ps aux

このように、unshareコマンドを使うことで、Dockerと同様のnamespace隔離を手動で作成できます!

cgroupによるリソース制御

cgroupの基本概念

cgroup(Control Groups)は、プロセスが使用できるリソースを制限・監視するLinux カーネルの機能です。

namespaceが「見えるものを制限」するのに対し、cgroupは「使えるリソースを制限」します。

【cgroupで制御できるリソース】
┌─────────────────────────────────────┐
│           cgroup                    │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐│
│ │CPU制限  │ │メモリ制限│ │I/O制限  ││
│ │         │ │         │ │         ││
│ │50% CPU  │ │512MB    │ │100MB/s  ││
│ └─────────┘ └─────────┘ └─────────┘│
│ ┌─────────┐ ┌─────────┐            │
│ │ネットワーク│ │プロセス数│            │
│ │帯域制限   │ │制限     │            │
│ └─────────┘ └─────────┘            │
└─────────────────────────────────────┘

CPU・メモリ・I/Oの制限方法

CPU制限の例

# CPUを50%に制限してコンテナを起動
$ docker run -d --name cpu-limit --cpus="0.5" nginx

# CPU使用率を確認
$ docker stats cpu-limit
CONTAINER ID   NAME        CPU %     MEM USAGE / LIMIT
a1b2c3d4e5f6   cpu-limit   45.67%    10.5MiB / 1.944GiB

メモリ制限の例

# メモリを512MBに制限
$ docker run -d --name mem-limit --memory="512m" nginx

# メモリ使用量を確認
$ docker stats mem-limit
CONTAINER ID   NAME        CPU %     MEM USAGE / LIMIT
a1b2c3d4e5f6   mem-limit   0.01%     5.2MiB / 512MiB

I/O制限の例

# ディスク読み書き速度を制限
$ docker run -d --name io-limit \
    --device-read-bps /dev/sda:50mb \
    --device-write-bps /dev/sda:10mb \
    nginx
[改訂新版]イラストでわかるDockerとKubernetes (Software Design plusシリーズ)
created by Rinker
¥2,363 (2025/10/01 00:01:09時点 楽天市場調べ-詳細)

Dockerでのcgroup活用例

Dockerは内部的にcgroupを使用してリソース制御を行っています。実際のcgroup設定を覗いてみましょう:

# 実行中のコンテナのIDを取得
$ docker ps --format "table {{.ID}}\t{{.Names}}"
CONTAINER ID   NAMES
a1b2c3d4e5f6   my-nginx

# cgroupの設定ファイルを確認(CPU制限)
$ cat /sys/fs/cgroup/cpu/docker/a1b2c3d4e5f6.../cpu.cfs_quota_us
50000

# cgroupの設定ファイルを確認(メモリ制限)
$ cat /sys/fs/cgroup/memory/docker/a1b2c3d4e5f6.../memory.limit_in_bytes
536870912

リソース制限の実践例

本番環境でよく使われるリソース制限の例:

# Webサーバーコンテナ
$ docker run -d \
    --name web-server \
    --cpus="1.0" \                    # CPU 1コア
    --memory="2g" \                   # メモリ 2GB
    --memory-swap="2g" \              # スワップ無効
    --restart=unless-stopped \
    nginx:alpine

# データベースコンテナ
$ docker run -d \
    --name database \
    --cpus="2.0" \                    # CPU 2コア
    --memory="4g" \                   # メモリ 4GB
    --memory-swappiness=10 \          # スワップ使用を最小限に
    --ulimit nofile=65536:65536 \     # ファイルディスクリプタ制限
        postgres:13
[改訂新版]イラストでわかるDockerとKubernetes (Software Design plusシリーズ)
created by Rinker
¥2,363 (2025/10/01 00:01:09時点 楽天市場調べ-詳細)

cgroupバージョンの違い

#### cgroupバージョンの違い

最新のLinuxディストリビューションでは、cgroup v2が使用されています:

bash

cgroup バージョンの確認

$ mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)

cgroup v2での制限確認

$ cat /sys/fs/cgroup/docker/containers/*/memory.max
536870912

## runcとコンテナランタイムの仕組み

### runcの役割と位置づけ

**runc**は、OCIランタイム仕様に準拠した**低レベルコンテナランタイム**です。実際にコンテナを起動・管理する最終的な責任を持ちます。

【Dockerのアーキテクチャ】
┌─────────────────────────────────────┐
│ Docker CLI │ ← ユーザーコマンド
└─────────────┬───────────────────────┘
│ REST API
┌─────────────▼───────────────────────┐
│ Docker Engine │ ← イメージ管理、API提供
│ (dockerd) │
└─────────────┬───────────────────────┘
│ gRPC
┌─────────────▼───────────────────────┐
│ containerd │ ← 高レベルランタイム
│ │ (ライフサイクル管理)
└─────────────┬───────────────────────┘

┌─────────────▼───────────────────────┐
│ runc │ ← 低レベルランタイム
│ │ (実際のコンテナ実行)
└─────────────┬───────────────────────┘
│ システムコール
┌─────────────▼───────────────────────┐
│ Linux Kernel │ ← namespace, cgroup等
│ (namespace, cgroup, capabilities) │
└─────────────────────────────────────┘

### OCIランタイム仕様

**OCI**(Open Container Initiative)は、コンテナの標準仕様を策定する団体です。主要な仕様:

1. **Runtime Specification**: コンテナ実行時の仕様
2. **Image Specification**: コンテナイメージの形式
3. **Distribution Specification**: イメージ配布の仕様

runcは、OCI Runtime Specificationに準拠した参照実装です。

#### OCIランタイム設定の例

runcは、以下のようなJSONファイルでコンテナの設定を受け取ります:

json
{
“ociVersion”: “1.0.0”,
“process”: {
“terminal”: false,
“user”: {
“uid”: 0,
“gid”: 0
},
“args”: [“/bin/sh”],
“env”: [
“PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin”
],
“cwd”: “/”
},
“root”: {
“path”: “/var/lib/container/rootfs”
},
“linux”: {
“namespaces”: [
{“type”: “pid”},
{“type”: “network”},
{“type”: “mount”},
{“type”: “uts”},
{“type”: “ipc”}
],
“resources”: {
“memory”: {
“limit”: 536870912
},
“cpu”: {
“quota”: 50000,
“period”: 100000
}
}
}
}

### Dockerエンジンとruncの関係

実際にDockerコマンドが実行される流れを追ってみましょう:

bash

1. Dockerコマンドの実行

$ docker run -it ubuntu bash

2. Docker Engine (dockerd) が処理

3. containerdにコンテナ作成を依頼

4. containerdがruncを呼び出し

5. runcがnamespace、cgroupを設定してコンテナを起動

#### runcを直接使用してみる

runcは単独でも使用できます:

bash

1. コンテナイメージからrootfsを展開

$ mkdir -p /tmp/container/rootfs
$ docker export $(docker create ubuntu) | tar -C /tmp/container/rootfs -xf –

2. OCI設定ファイルを生成

$ cd /tmp/container
$ runc spec

3. runcでコンテナを起動

$ sudo runc run mycontainer

これでubuntuコンテナが起動!

root@container:/# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4624 828 ? Ss 10:00 0:00 /bin/bash

#### 他のOCIランタイム

runc以外にも、様々なOCIランタイムが存在します:

- **gVisor (runsc)**: Googleが開発したサンドボックス化されたランタイム
- **Kata Containers**: 軽量仮想マシンベースのランタイム
- **Firecracker**: AWS Lambdaで使用される超軽量ランタイム

bash

gVisorを使用してコンテナを起動

$ docker run –runtime=runsc -it ubuntu bash

Kata Containersを使用してコンテナを起動

$ docker run –runtime=kata-runtime -it ubuntu bash

各ランタイムは異なる特徴を持ちます:
- **runc**: 標準的で高性能
- **gVisor**: セキュリティ重視(パフォーマンスはやや劣る)
- **Kata Containers**: VM級の隔離性能

## 実践!コンテナ技術の理解を深める

ここまでで、Dockerの内部構造について理解が深まったでしょうか?

**今回学んだ要点をまとめると:**

1. **namespace**: プロセスの見える世界を制限(隔離)
2. **cgroup**: プロセスが使用できるリソースを制限
3. **runc**: OCIランタイム仕様に基づいてコンテナを実際に起動
4. **Docker**: これらの複雑な機能を使いやすく抽象化

### 実際の業務での活用

この知識は、以下のような場面で役立ちます:

#### トラブルシューティング

bash

コンテナのリソース使用状況を詳細に確認

$ docker stats –no-stream

cgroupの制限値を直接確認

$ cat /sys/fs/cgroup/memory/docker/*/memory.limit_in_bytes

namespaceの確認

$ sudo lsns -t pid

#### セキュリティ対策

bash

User namespaceを使用してrootレスコンテナを実行

$ docker run –user 1000:1000 -it ubuntu bash

読み取り専用ファイルシステムで起動

$ docker run –read-only -it ubuntu bash

capability を制限

$ docker run –cap-drop=ALL –cap-add=NET_BIND_SERVICE nginx

#### パフォーマンス最適化

bash

CPUアフィニティを設定

$ docker run –cpuset-cpus=”0,1″ -it ubuntu bash

メモリスワップを無効化

$ docker run –memory=1g –memory-swap=1g nginx

I/O優先度を設定

$ docker run –blkio-weight=500 nginx
“`

[改訂新版]イラストでわかるDockerとKubernetes (Software Design plusシリーズ)
created by Rinker
¥2,363 (2025/10/01 00:01:09時点 楽天市場調べ-詳細)

さらなる学習への道筋

コンテナ技術の理解をさらに深めたい方は、以下のトピックを学習することをおすすめします:

1. コンテナセキュリティ

  • AppArmor/SELinux: 強制アクセス制御
  • seccomp: システムコール制限
  • capabilities: 権限の細分化

2. コンテナネットワーク

  • CNI (Container Network Interface): ネットワークプラグイン
  • iptables: ネットワークルール管理
  • overlay network: 複数ホスト間のネットワーク

3. ストレージ

  • Union FS: オーバーレイファイルシステム
  • Volume drivers: ストレージプラグイン
  • CSI (Container Storage Interface): ストレージ標準

4. オーケストレーション

  • Kubernetes: コンテナオーケストレーション
  • Docker Swarm: Docker純正クラスター
  • Nomad: HashiCorp製オーケストレーター

次のステップの紹介

今回はDockerの内部構造を中心に解説しましたが、これらの技術はKubernetesでも活用されています。

特に、KubernetesのPod内でのコンテナ実行には、今回学んだnamespace、cgroup、runcの仕組みが重要な役割を果たしています。

さらに詳しく学びたい方には、「[改訂新版]イラストでわかるDockerとKubernetes」が強くおすすめです!

この書籍では:

  • 視覚的でわかりやすいイラストで複雑な概念を理解
  • 実践的な例を通じて実際の動作を確認
  • 最新の技術動向(containerd、CRI-O等)も網羅
  • 現役エンジニアによる現場の知見が満載

著者の徳永航平さんは、CNCF containerdのレビュワーやMobyプロジェクトのBuildKitメンテナという、まさにコンテナ技術の最前線で活躍されている方です。

「なんとなく使えている」から「仕組みを理解して自信を持って使える」へ。
「トラブルが起きても慌てない」技術力を身につけましょう!

コンテナ技術は奥が深いですが、基本的な仕組みを理解することで、より効果的に活用できるようになります。ぜひ、この記事をきっかけに、さらなる学習を進めてみてください!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA



reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

目次