Dockerの内部構造について
基本アーキテクチャ
Dockerは大きくクライアント-サーバー構造になっている。
Docker Client: コマンドラインから操作するインターフェース部分
Docker Daemon: 裏側でコンテナを実際に管理するサーバー部分
REST API: この2つを繋ぐインターフェース
Docker Daemonがコンテナのビルド、実行、配布などの重い処理を担当していて、私たちはClientを通じて命令を出しているだけという構造。
核となる技術
1. Linux Namespaces
コンテナの分離を実現する核となる技術。プロセスに「見える世界」を制限する仕組み。
具体的には6つの名前空間で分離を実現している:
PID Namespace: プロセスID空間の分離
Network Namespace: ネットワークインターフェース、ルーティングテーブル等の分離
UTS Namespace: ホスト名とドメイン名の分離
IPC Namespace: プロセス間通信の分離
Mount Namespace: ファイルシステムマウントポイントの分離
User Namespace: ユーザーとグループIDの分離
これにより、コンテナ内のプロセスからは自分だけの世界にいるように見える。例えば、PID Namespaceによって、コンテナ内のプロセスには「自分がPID 1である」と思わせることができる。
2. Control Groups (cgroups)
リソース使用量を制限・計測・割り当てるための技術。
CPUやメモリなどの使用量を制限
コンテナごとの性能分離を実現
ホストシステムのリソース枯渇を防止
例えば、--memory=1gオプションで起動すると、そのコンテナは1GB以上のメモリを使用できなくなる。単なる論理的な区分けではなく、カーネルレベルでの制約がかかる。
3. UnionFS (Union File System)
レイヤー化されたファイルシステム技術。
イメージはレイヤーの積み重ねで構成
各レイヤーは読み取り専用
コンテナ実行時のみ書き込み可能な最上位レイヤーが追加される
Dockerでよく見る「レイヤー」という概念はここからきている。各ビルドステップごとに差分だけを保存する仕組みで、イメージの軽量化と再利用性を高めている。
実装としては、OverlayFS, AUFS, Btrfs, ZFSなど様々な選択肢があり、環境によって使われるものが変わる。多くの環境ではOverlayFSが採用されているっぽい。
4. Containerd
Docker 1.11以降、コンテナ実行部分が分離され、containerdというコンポーネントに移行した。
OCI (Open Container Initiative) 準拠のコンテナランタイム
Dockerのコア機能を担当
Kubernetes等からも直接利用可能
この分離により、Dockerを使わなくてもコンテナを扱える柔軟性が生まれた。
5. runC
低レベルのコンテナランタイム。最終的にコンテナを実行する部分。
C言語で書かれたシンプルな実装
OCI標準に準拠
namespaces, cgroupsなどを直接操作
containerdがコンテナ管理の高レベルAPIを提供し、実際の実行はrunCが担当するという階層構造になっている。
イメージ構造
Dockerイメージはレイヤー構造になっている点が重要。
1. ベースレイヤー: 通常はOSのファイルシステム
2. 中間レイヤー: アプリケーションの依存関係など
3. トップレイヤー: アプリケーションコード
docker history <image-name>で各レイヤーとそのサイズを確認できる。これらのレイヤーがイミュータブル(不変)であることが、Dockerの再現性の高さを支えている。
ネットワーク
コンテナのネットワークも興味深い仕組みになっている:
Bridge: デフォルトのネットワークドライバ。仮想ブリッジを作成
Host: ホストのネットワークスタックを直接使用
Overlay: 複数のDockerホスト間でのネットワーク
Macvlan: 物理的なネットワークインターフェースのように見せる
None: ネットワークなし
内部的にはiptablesやLinux BridgeといったLinuxの標準的なネットワーク機能を組み合わせて実現している。Docker NetworkのBridgeモードでは、実際にはホスト上に仮想的なブリッジインターフェースが作られ、各コンテナの仮想インターフェースがそこに接続される形になっている。
Docker Engineの内部構造
最新のDocker構造は主に次のコンポーネントからなる:
1. Docker CLI: ユーザーインターフェース
2. dockerd: Docker Daemon
3. containerd: コンテナライフサイクル管理
4. containerd-shim: コンテナとrunCの間のプロセス
5. runC: 実際にコンテナを実行するランタイム
この階層化された設計により、各コンポーネントの役割が明確になり、他のツールとの互換性も高まっている。Kubernetesなどはcontainerdを直接使うことができるようになった。
セキュリティ
Dockerのセキュリティモデルも複雑:
Capabilities: Linux上の細かい権限制御
Seccomp: システムコールのフィルタリング
AppArmor/SELinux: アクセス制御の強化
Rootless mode: 非root権限でのコンテナ実行
コンテナはプロセスの分離であって仮想化ではないので、カーネルは共有されている。そのため、ホストシステムとの分離レベルはVMほど強くない点に注意が必要。
結論
Dockerは表面上はシンプルに見えるが、内部ではLinux kernelの高度な機能をフル活用した複雑なシステム。
特にNamespaces, cgroups, UnionFSという3つの技術を組み合わせることで、軽量で高速なコンテナ環境を実現している。
claude学習