1-1. コンテナとは何か - プロセスのパッケージング

所要時間: 40-50分(がっつりなら2セッション分) ゴール: コンテナとVMの違い、namespaces/cgroupsの役割、なぜDockerが流行ったのかを自分の言葉で説明できる コミット内容: このノートの「今日の発見」欄を埋める。手を動かすコマンドは少なめ、概念理解中心の回


このレッスンのゴール

  • コンテナとVMの違いを「OSカーネルを共有するかどうか」で説明できる
  • namespaces と cgroups の役割を別々に言える
  • Mac の Docker が裏で Linux VM を動かしている事実を腹落ちしている
  • Docker / containerd / runc / OCI の関係を1分で説明できる
  • 「コンテナ=軽量VM」という誤解を捨て、自分の言葉で言い換えられる

なぜ学ぶか(実務悩みベース)

  • 「ローカルでは動くのに本番で動かない」依存地獄に毎回振り回される
  • Docker入門記事は手順だけで「裏で何が起きてるか」が分からない
  • Kubernetes / ECS / Cloud Run の議論で前提知識として要求される
  • セキュリティレビュー(コンテナエスケープ・特権昇格)の議論についていけない

前章とのつながり

Linux 編で学んだ「プロセス」「ファイルシステム」「ユーザー / 権限」の3点が、本章の namespaces / cgroups の議論にそのまま地続きで効く。「コンテナは特別な何かではなく、Linux プロセスの応用形」 という見方ができれば、本章は半分終わったようなもの。


大前提: なぜコンテナを学ぶか

2020年以降、バックエンドエンジニアの求人で 「Docker使えること」 が必須に近い扱いになった。理由はシンプルで:

  • 環境差異が消える: 「自分のMacでは動くけど本番で動かない」が劇的に減る
  • デプロイが速くなる: コンテナイメージ1個を本番にコピーするだけで動く
  • マイクロサービス・Kubernetes時代の前提: 分散システムを組むなら、まずアプリをコンテナ化する

逆に言うと、コンテナを理解していないと「現代のバックエンド」のスタート地点に立てない。Linux/Goを学んだ後、最初にコンテナを叩き込むのはそのため。

ただ、「Docker入門」系の記事は手順だけ書いてあって「何が起きてるか分からないまま動かす」になりがち。このレッスンは 手を動かす前に、まず仕組みから やる。なぜなら:

コンテナで詰まる人は、ほぼ全員「コンテナをVMだと思ってる」か「コンテナをただのzipファイルだと思ってる」かのどちらか。

この誤解を最初に潰しておくと、後がめちゃくちゃ楽になる。


セッション①: コンテナとは何か、VMとの違い(25分)

1. コンテナの一言定義

コンテナの本質

「隔離された環境で動く、ただのLinuxプロセス」

これが結論。コンテナは魔法でも仮想マシンでもなく、Linuxカーネルの機能を使って 「他のプロセスから見えないように隔離されたプロセス」 を作っているだけ。

ホストOS(Mac/Linux)から見れば、コンテナの中で動いているプログラムは普通の ps で見えるプロセスのひとつ。ただし、コンテナ内のプロセスからは「自分が隔離された世界にいる」ように見える。

「軽量なVM」という説明は不正確。コンテナはVMではない。OSを丸ごと持っているわけでもない。

2. VMとコンテナのアーキテクチャ比較

┌─────────────────────────────────┐    ┌─────────────────────────────────┐
│       仮想マシン (VM)            │    │        コンテナ                 │
├─────────────────────────────────┤    ├─────────────────────────────────┤
│  App A    App B    App C        │    │  App A    App B    App C        │
│  ─────    ─────    ─────        │    │  ─────    ─────    ─────        │
│ Bin/Lib  Bin/Lib  Bin/Lib       │    │ Bin/Lib  Bin/Lib  Bin/Lib       │
│ ─────── ─────── ───────         │    │ ──────── ──────── ────────      │
│ GuestOS GuestOS GuestOS         │    │   (ゲストOSは無い!)          │
│ ─────── ─────── ───────         │    │                                 │
│        Hypervisor               │    │     Container Runtime           │
│  (VMware/VirtualBox/KVM)        │    │   (Docker / containerd)         │
├─────────────────────────────────┤    ├─────────────────────────────────┤
│         Host OS                 │    │         Host OS (Linux)         │
├─────────────────────────────────┤    ├─────────────────────────────────┤
│         Hardware                │    │         Hardware                │
└─────────────────────────────────┘    └─────────────────────────────────┘

VMとコンテナの決定的違い

項目VMコンテナ
OSカーネル各VMが自前で持つホストOSを共有する
起動時間30秒~数分数百ms~数秒
サイズGB単位(OS込み)MB単位(アプリ+ライブラリのみ)
リソース消費重い(OS分も含む)軽い(プロセス1個分)
隔離レベル強い(ハードウェアレベル)弱め(カーネルは共有)
OS選択LinuxホストにWindows VMもOKホストと同じカーネルのみ

「コンテナ内のOS」と呼ばれるもの(例: Alpine Linux のイメージ)は、実はOSではなく 「ファイルシステムの中身(コマンドやライブラリ群)だけ」。カーネルは持っていない。だから軽い。

3. なぜ「カーネル共有」で隔離できるのか

ここがコンテナの肝。Linuxカーネルが提供する隔離機能 を使っている。

Linux Namespaces(名前空間)

「プロセスから見える世界」を分離するカーネル機能。種類ごとに別々の「見え方」を作れる。

namespace隔離するもの効果
pidプロセスIDコンテナ内では自分が PID 1。ホストの他プロセスは見えない
mount (mnt)ファイルシステム/ 以下が別の世界。ホストの /etc は見えない
netネットワーク専用のIPアドレス、ポート、ルーティングテーブル
userUID/GIDコンテナ内の root が、ホストの一般ユーザーにマップされる
utsホスト名hostname が違って見える
ipcプロセス間通信共有メモリ等の隔離

これは Docker 専用機能ではない。Linuxカーネル自体の機能で、Dockerはそれを使って隔離環境を組み立てているだけ。手動で unshare コマンドを叩けば、Dockerなしでもnamespace隔離プロセスは作れる。

cgroups(コントロールグループ)

「プロセスが使えるリソース量を制限する」カーネル機能。namespacesが「見える範囲」の隔離なら、cgroupsは「使える量」の制限。

制限できるもの:

  • CPU: 「このコンテナはCPU 0.5コアまで」
  • メモリ: 「512MBまで。超えたら kill」
  • ディスクI/O: 帯域制限
  • プロセス数: 「100プロセスまで」(fork bomb対策)

Docker の --cpus=0.5--memory=512m オプションは、内部的に cgroups の設定を書いているだけ。

実務インパクト: 1台のサーバーに10個のコンテナを乗せて、それぞれリソース上限を決められる。これがクラウド(AWS Fargate、Kubernetes)の基盤。

コンテナの実体(要点まとめ)

コンテナ = namespaces で隔離された + cgroups で制限された Linux プロセス
        + ファイルシステム(イメージ)を chroot 的に切り替えたもの

「軽量VM」と呼ばれることもあるが、内部実装は全然違う。VMは「仮想ハードウェア」を作るが、コンテナは「カーネル共有のままプロセス隔離」をやる。

4. macOS で Docker が動く仕組み(疑問の解消)

ここで気づく人が出てくる。「namespaces はLinuxカーネルの機能なら、Macで動くDockerは何してるの?」

Mac で Docker Desktop を使うと裏で Linux VM が動いている

Mac(macOS)のカーネルは XNU(BSD系)で、Linuxカーネルではない。だから namespaces も cgroups も無い。

Docker Desktop for Mac は内部で 軽量Linux VM を起動して、その中でコンテナを動かしている。表向きは「Macで直接コンテナが動いてる」ように見えるが、実態は:

[macOS] → [Docker Desktop] → [Linux VM] → [containerd] → [コンテナ]

影響

  • Mac だと Linux 本番より遅い(VM経由のオーバーヘッド)
  • ファイル I/O(bind mount)が遅い問題が有名
  • Apple Silicon (arm64) と本番 (x86_64) でアーキ違いが発生 → --platform 指定が必要なケース

Windows も似ていて、WSL2(裏でLinuxが動く)を使ってDockerを動かしている。


セッション②: Docker と OCI、なぜ流行ったか(20分)

5. Docker、containerd、OCI の関係

「コンテナ = Docker」と思いがちだが、実は層が分かれている。

┌────────────────────────────────────────┐
│  ユーザー (CLI / GUI)                  │
│  docker run, docker build              │
├────────────────────────────────────────┤
│  Docker Engine (dockerd)               │  ← Dockerの「上の層」。CLIや高レベルAPI
├────────────────────────────────────────┤
│  containerd                            │  ← コンテナのライフサイクル管理
├────────────────────────────────────────┤
│  runc                                  │  ← 実際にコンテナを起動する低レベルランタイム
├────────────────────────────────────────┤
│  Linux カーネル (namespaces / cgroups) │
└────────────────────────────────────────┘

OCI(Open Container Initiative)

「コンテナの規格」を決める業界団体。Docker、Red Hat、Google などが参加。

決めている主な仕様:

  • Image Spec: コンテナイメージの形式(どんなレイヤー構造か、メタデータの書き方)
  • Runtime Spec: コンテナの起動方法
  • Distribution Spec: レジストリ(Docker Hub等)のAPI

なぜ重要か: 規格があるから、Docker 以外のツールでも同じイメージが使える

  • イメージは Docker でビルド → 実行は Kubernetes 上の containerd で、というのが普通にできる
  • GitHub Container Registry、AWS ECR、GCR、Quay など、レジストリが多数あっても同じ仕様で動く

containerd と runc

  • containerd: 「コンテナの実行・管理を担う常駐プロセス」。イメージのpull、コンテナの起動、ライフサイクル管理。Docker からも Kubernetes からも使われる。
  • runc: 「実際にプロセスを namespaces + cgroups で隔離して起動する」低レベルのコマンド。OCI Runtime Spec の参照実装。

普段は意識しないが、docker run の裏側でこれらが順番に呼ばれている。本番のトラブルシュート(コンテナが起動しない、停止しないなど)でログを追う時に出てくる名前なので、関係性だけ覚えておく。

6. なぜコンテナがここまで流行ったか

技術的な背景を3つに整理する。

流行った理由①: 「環境差異」問題の解消

「自分のMacでは動くけど本番Linuxでは動かない」 はバックエンド開発の永遠の悩みだった。

原因: Python のバージョン、システムライブラリ(libssl、libxml2)、環境変数、設定ファイルのパス、OSディストリの違い……

従来の対策(どれも辛い):

  • 開発環境でも本番と同じ Linux を VM で立ち上げる(重い)
  • 「環境構築手順書」を書く(メンテ地獄、すぐ古くなる)
  • Vagrant + VirtualBox で開発環境を統一(VMなので重い、遅い)

Docker の解:

  • アプリ + 全依存物を1個のイメージにパッケージング
  • そのイメージは Mac/Linux/Windows どこでも同じように動く
  • Dockerfile に手順が書かれているので「手順書」がそのままコードとして残る

流行った理由②: 軽量で、起動が速い

VMは数秒~数分かかる。コンテナは数百ms。

この差が効くのは:

  • オートスケーリング: 負荷が上がった瞬間にコンテナを増やす → トラフィックに追従できる
  • CI/CD: テストごとにクリーンな環境を作って捨てる → 1日数百回ビルドしてもコスト低
  • マイクロサービス: アプリを細かく分けると数十・数百のサービスになる → VM だと重すぎて成立しない

流行った理由③: クラウドとの相性

AWS、GCP、Azure すべてが「コンテナを動かすサービス」を提供している:

  • AWS: ECS, EKS, Fargate
  • GCP: GKE, Cloud Run
  • Azure: AKS, Container Apps

コンテナイメージ1個を作れば、どのクラウドにも持っていける。ベンダーロックインを回避しやすい。

さらに Kubernetes(コンテナオーケストレーター)が事実上の標準になり、「コンテナを前提とした世界」が完成した。

7. 「コンテナ ≠ 仮想化」を再確認

よくある誤解

  • 「コンテナは軽量なVM」 → 違う。OSカーネルは共有していて、VMとは仕組みが別物
  • 「コンテナは安全に隔離されている」 → カーネルを共有しているので、カーネル脆弱性ではホストまで突破される可能性あり。VM より隔離は弱い
  • 「コンテナを使えばどんな環境でも動く」 → Linuxカーネル前提。Windows コンテナを Linux ホストで動かすことはできない(逆も)
  • 「コンテナはアプリだけ入っている」 → 厳密には「OSのユーザー空間(コマンド・ライブラリ)」も入っている。カーネルだけ無い

正しい理解

  • コンテナは 「namespaces + cgroups で隔離された Linux プロセス」
  • イメージは 「アプリ + 必要なライブラリ + 最小限のユーザー空間ツール」 のパッケージ
  • 隔離は 「アプリレベル」。ハードウェアレベルではない
  • 同じOSカーネル上で動かす技術。WindowsアプリをLinuxサーバーで動かすような魔法ではない

やらかし事例: コンテナ理解の落とし穴

事例1: 「VM だと思って ssh で入って vim で本番修正」

コンテナを軽量VMだと誤解すると、本番コンテナに docker exec -it で入って設定ファイルを書き換えてしまう。次にデプロイすると 書き換えは全部消える(コンテナ = イミュータブル)。教訓: 変更は必ず Dockerfile / 設定マウントで。

事例2: 「コンテナはセキュア」と信じてroot で動かす

「コンテナで隔離されてるから何やっても安全」と root で全機能を動かすと、コンテナエスケープ脆弱性が出た時にホストまで取られる。VMより隔離が弱いという事実を忘れない。

事例3: Mac で動いたから本番で動くはず

Apple Silicon (arm64) でビルドしたイメージを x86_64 本番にデプロイ → exec format error。Mac の Docker Desktop は arm64 ネイティブで動くが、本番は amd64 が多い。--platform linux/amd64 の指定が必要なケースを知らないとハマる。

ストーリー: なぜ Docker が「魔法に見えるか」

2013年、Solomon Hykes が DockerCon で見せたデモは衝撃だった。「Python アプリをコンテナに包んで、別のマシンで起動したら、まったく同じように動いた」。当時は VM が主流で、同じことをやるには:

  1. Vagrantfile を書いてVM定義
  2. Chef/Puppet で構成管理
  3. VM image を作って数GB転送
  4. 起動に数分

これが Docker だと「Dockerfile 1個・数MB・数秒」。「環境差異の悩みが消える」 という福音は、当時のエンジニア社会に怒涛のように受け入れられた。

ただ、その魔法の正体は 「Linuxカーネルが10年以上前から持っていた namespaces/cgroups を、使いやすくラップしただけ」。Docker が革新だったのは技術ではなく 「使いやすい UX とイメージ流通の仕組み(Docker Hub)」 だった。これを知ると、コンテナの本質が「Linuxカーネル機能の合成」だと素直に受け止められる。


練習課題: 概念チェック

このレッスンに手を動かす要素は少ない。代わりに、自分で言語化してみる。

自分の言葉で説明してみる

以下の3つを、エンジニアじゃない友人に説明するつもりで書いてみる(このノートの「今日の発見」欄に):

  1. コンテナとVMの違いを3行で
  2. namespaces と cgroups がそれぞれ何を担当するかを1行ずつ
  3. なぜDockerが流行ったのかを1つだけ理由を選んで説明

書けない箇所があれば、その部分を読み直す。書けない = 理解できていない、のサイン。

# 一応 Docker が動くか確認だけしておく(次回から使うので)
docker --version
docker info

Docker Desktop が動いていない場合

Cannot connect to the Docker daemon のようなエラーが出たら、Docker Desktop アプリを起動する。Mac だとメニューバーのクジラアイコンが表示されていれば動いている状態。


締め: 振り返り(10分)

1. 今日の発見(このノートに追記)

- コンテナとVMの違いを3行で:
  -
  -
  -
- namespaces と cgroups の役割:
  - namespaces:
  - cgroups:
- 「Dockerが流行った理由」のうち、自分にとって一番納得した理由:
- 詰まった/モヤッとしたところ:
- 次回やりたいこと:

自己評価チェックリスト

知識レベル(説明できるか)

  • コンテナがVMとどう違うか、自分の言葉で説明できる
  • namespaces と cgroups の役割を区別できる
  • Docker / containerd / runc / OCI の関係を1分で説明できる
  • Mac の Docker Desktop が裏で Linux VM を動かしていることを知った
  • 「コンテナはセキュア」が条件付きである理由を1つ挙げられる

実行レベル(動かせるか)

  • docker --version が通る状態にした
  • docker info の出力で OS / Kernel / Storage Driver を読める

メタ認知(理解度の自己診断)

  • 「自分が一番モヤッとしたのは何か」を言語化した
  • 「次のレッスンに進む前に確認したい疑問」を1個書き出した

次のレッスンとのつながり

ここで身につけた「コンテナ=namespaces+cgroupsで隔離されたLinuxプロセス」という世界観が、次の 1-2 で docker run を打った瞬間に「裏で何が起きているか」を見える化する助けになる。docker exec が「動いているプロセスの namespace に入る」と腹落ちすれば、もう詰まらない。


詰まった時のチートシート

質問答え
コンテナとは?namespaces + cgroups で隔離されたLinuxプロセス
VM と何が違う?ゲストOSが無い。ホストOSのカーネルを共有
なぜ軽い?OS分のオーバーヘッドが無いから
namespaces とは?プロセスから見える世界の隔離(PID、ファイル、ネットワーク)
cgroups とは?プロセスが使えるリソース量の制限(CPU、メモリ)
Mac でもLinux必要?必要。Docker Desktop が裏でLinux VMを動かす
Docker と OCI の関係?OCI は規格、Docker はその規格を実装した代表的ツール
containerd とは?コンテナ管理の常駐プロセス。Docker や K8s から使われる

「実務OK」基準

このレッスンで身に付くべき感覚:

  • **「コンテナ = 隔離されたプロセス」**の世界観を持てた
  • 「VMの軽量版」という誤解を捨てた
  • Mac の Docker は裏で Linux が動いていることを知っている
  • Docker 以外にも containerd / runc / OCI など層があると認識した

ここを越えられれば、次回からの docker run が「ただの呪文」ではなく「何が起きているか分かる操作」になる。


次のレッスン

1-2. docker run で実際にコンテナを動かす へ。

次回は手を動かす回。docker run の主要オプションを叩いて、コンテナがプロセスとして動く感覚を体験する。