Skip to content
Published on

[containerd] コンテナライフサイクル管理

Authors

containerdコンテナライフサイクル管理

containerdにおけるコンテナの作成から終了までの全ライフサイクルを分析します。コンテナメタデータと実行プロセス(Task)の分離、shimによるプロセス管理、各種ランタイムクラスの統合方式を見ていきます。


1. ContainerとTaskの分離

1.1 核心概念

containerdはコンテナのメタデータと実行状態を分離します:

Container(メタデータ):
  - ID、イメージ参照、スナップショットキー
  - OCIランタイムスペック
  - ラベル、拡張データ
  - BoltDBに永続保存

Task(実行状態):
  - 実際に実行中のプロセス
  - PID、状態(created/running/stopped)
  - stdin/stdout/stderr
  - shimプロセスが管理

1.2 分離の利点

分離設計の利点:

1. コンテナメタデータはTaskなしで存在可能
   - コンテナを作成して後から起動可能
   - 停止したコンテナのメタデータを保持

2. containerd再起動に独立
   - Taskはshimが管理するためcontainerd再起動でも維持
   - 再起動後に既存shimに再接続

3. 多様なランタイムサポート
   - Containerオブジェクトはランタイムに非依存
   - Task作成時にランタイムを選択

2. コンテナ作成

2.1 作成プロセス

コンテナ作成フロー:

1. イメージからOCIスペック生成
        |
        v
2. スナップショット準備
   - イメージスナップショットチェーンにActiveスナップショット追加
   - コンテナの書き込み可能レイヤー
        |
        v
3. コンテナメタデータ保存
   - BoltDBにContainerレコード作成
   - ID、イメージ、スナップショット、ランタイム、スペックを保存
        |
        v
4. コンテナオブジェクト返却
   (まだプロセスは開始されていない)

2.2 OCIランタイムスペック

containerdはOCIランタイムスペックを生成してコンテナ実行環境を定義します:

OCIランタイムスペック主要セクション:

ociVersion: "1.0.2"

process:
  terminal: false
  user: uid=0, gid=0
  args: ["/bin/sh"]
  env: ["PATH=/usr/local/sbin:..."]
  cwd: "/"
  capabilities: ...
  rlimits: ...

root:
  path: "rootfs"
  readonly: false

hostname: "container-abc"

mounts:
  - destination: "/proc"
    type: "proc"
    source: "proc"
  - destination: "/dev"
    type: "tmpfs"
    source: "tmpfs"

linux:
  namespaces:
    - type: "pid"
    - type: "network"
    - type: "ipc"
    - type: "uts"
    - type: "mount"
  resources:
    memory:
      limit: 536870912
    cpu:
      shares: 1024
      quota: 100000
      period: 100000
  cgroupsPath: "/kubelet/pod-abc/container-xyz"

2.3 スペックジェネレーター(Spec Opts)

containerdのスペック生成パターン:

Spec OptsはOCIスペックを段階的に構築する関数チェーンです:

WithImageConfig(image)     -> イメージのCMD、ENV、WORKDIRを適用
WithHostNamespace(ns)      -> ホストネームスペース共有
WithMemoryLimit(limit)     -> メモリ制限設定
WithCPUs(cpus)             -> CPU制限設定
WithMounts(mounts)         -> マウントポイント追加
WithProcessArgs(args)      -> プロセス引数設定
WithRootfsPropagation(p)   -> rootfsマウント伝播設定
WithSeccompProfile(p)      -> Seccompプロファイル適用
WithApparmorProfile(p)     -> AppArmorプロファイル適用

3. Task実行

3.1 Task作成

Task作成フロー:

1. コンテナのランタイムタイプ確認
   (例:io.containerd.runc.v2)
        |
        v
2. shimバイナリ実行
   (containerd-shim-runc-v2 start)
        |
        v
3. shimがttrpcソケットアドレスを返す
        |
        v
4. containerdがshimにCreateリクエスト
   - OCIスペック転送
   - バンドルパス転送
        |
        v
5. shimがrunc createを実行
   - ネームスペース作成
   - cgroup設定
   - rootfsマウント
   - プロセス作成(まだ開始していない)
        |
        v
6. Task状態:Created

3.2 Task開始

Task開始:

1. containerdがshimにStartリクエスト
        |
        v
2. shimがrunc startを実行
   - コンテナプロセスのinitプロセス開始
   - exec.fifoで同期
        |
        v
3. Task状態:Running
   - PID割り当て
   - stdin/stdout/stderr接続

3.3 Task状態遷移

Task状態マシン:

  Created
     |
     | Start()
     v
  Running
     |
     +-- Kill(signal) -> シグナル送信
     |
     +-- Pause()  -> Paused
     |                 |
     |                 +-- Resume() -> Running
     |
     +-- プロセス終了 -> Stopped
     |
     v
  Stopped
     |
     | Delete()
     v
  (削除済み)

3.4 Exec(追加プロセス)

Exec動作:

既に実行中のコンテナに新しいプロセスを追加:

1. ExecProcess作成
   - 新しいプロセスのスペック定義(args、env、user)
   - execID割り当て
        |
        v
2. shimにExecリクエスト
        |
        v
3. runc exec実行
   - 既存コンテナのネームスペースに参入
   - 新しいプロセス開始
        |
        v
4. 独立してstdin/stdout/stderrを管理

使用例:kubectl exec、docker exec

4. Shimライフサイクル

4.1 Shim起動

Shim起動プロセス:

1. containerdがshimバイナリをfork/exec
   containerd-shim-runc-v2 -namespace k8s.io \
     -id container-abc \
     -address /run/containerd/containerd.sock \
     start
        |
        v
2. shimが自身をデーモン化
   - 親プロセスから分離(setsid)
   - containerdと独立して実行
        |
        v
3. ttrpc Unixソケット作成
   /run/containerd/s/abc123...
        |
        v
4. ソケットアドレスをstdoutに出力
   containerdがこのアドレスを読んで接続

4.2 Shimの役割詳細

Shimの主要責任:

1. プロセス管理:
   - コンテナプロセスの親役割
   - wait4()で終了ステータス収集
   - OOMイベント検知と報告

2. I/O管理:
   - stdin/stdout/stderr FIFO管理
   - ログドライバーとの接続
   - I/Oコピー(containerProcess <-> FIFO)

3. containerdとの通信:
   - ttrpcによるコマンド受信
   - イベント報告(TaskExitなど)
   - ステータス照会応答

4. containerd再起動対応:
   - containerd再起動時も実行継続
   - 再起動されたcontainerdが既存shimに再接続
   - 状態復旧

4.3 Shimシャットダウン

Shimシャットダウン:

1. Task Deleteリクエスト受信
        |
        v
2. コンテナリソースクリーンアップ
   - cgroup削除
   - ネームスペースクリーンアップ
   - rootfsアンマウント
        |
        v
3. ttrpcソケットクローズ
        |
        v
4. shimプロセス終了

5. Checkpoint/Restore

5.1 Checkpoint

Checkpoint動作:

実行中のコンテナの状態をスナップショットとして保存:

1. CRIU(Checkpoint/Restore in Userspace)呼び出し
        |
        v
2. プロセスメモリダンプ
   - メモリページ保存
   - ファイルディスクリプタ状態保存
   - ネットワーク接続状態保存
        |
        v
3. チェックポイントイメージ作成
   - CRIUイメージファイルセット
   - コンテナスペックと一緒に保存
        |
        v
4. オプションでコンテナ停止

使用事例:
  - ライブマイグレーション
  - 高速起動(事前ウォームアップ状態から復元)
  - デバッグ(特定時点の状態キャプチャ)

5.2 Restore

Restore動作:

1. チェックポイントイメージ読み込み
        |
        v
2. 新しいコンテナ環境準備
   - ネームスペース作成
   - rootfsマウント
        |
        v
3. CRIU restore実行
   - メモリページ復元
   - プロセス状態復元
   - ファイルディスクリプタ再接続
        |
        v
4. プロセス実行再開

6. ランタイムクラス

6.1 多様なランタイムサポート

containerdはshimインターフェースを通じて多様なランタイムをサポートします:

ランタイムクラス別比較:

+----------+---------------+-----------+----------+-----------+
| ランタイム| 分離レベル    | オーバヘッド| 起動時間| 互換性    |
+----------+---------------+-----------+----------+-----------+
| runc     | ネームスペース | 最小      | 高速    | 最高      |
| kata     | 軽量VM        | 中程度    | 中程度  | 高い      |
| gVisor   | ユーザーカーネル| 低い     | 高速    | 中程度    |
| Wasm     | Wasmサンドボックス| 最小   | 非常に速い| 限定的  |
+----------+---------------+-----------+----------+-----------+

6.2 runc

runc:

- デフォルトOCIランタイム
- Linuxネームスペースとcgroupベースの分離
- ホストカーネルを直接使用
- 最小オーバーヘッド
- すべてのLinuxコンテナワークロードに適合

shim: containerd-shim-runc-v2
config.toml:
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
    runtime_type = "io.containerd.runc.v2"

6.3 Kata Containers

Kata Containers:

- 軽量VM内部でコンテナを実行
- QEMU/Cloud-Hypervisor/Firecrackerを使用
- 別のゲストカーネルで強力な分離
- マルチテナント環境に適合
- VMオーバーヘッドあり

shim: containerd-shim-kata-v2
config.toml:
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
    runtime_type = "io.containerd.kata.v2"

6.4 gVisor

gVisor(runsc):

- ユーザースペースカーネル(Sentry)
- システムコールをインターセプトして再実装
- ホストカーネルの攻撃面を縮小
- ptraceまたはKVMベースで動作
- 一部システムコール未サポート

shim: containerd-shim-runsc-v1
config.toml:
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
    runtime_type = "io.containerd.runsc.v1"

6.5 WebAssembly(Wasm)

Wasmランタイム:

- WebAssemblyバイナリをコンテナとして実行
- Wasmtime、WasmEdgeなどを使用
- 非常に高速な起動時間(ミリ秒単位)
- 最小メモリ使用
- ポータブルバイナリ
- 制限されたシステムアクセス(WASI)

shim: containerd-shim-wasm
config.toml:
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wasm]
    runtime_type = "io.containerd.wasm.v1"

6.6 RuntimeClass選択

Kubernetes RuntimeClass連携:

1. RuntimeClassリソース定義:
   apiVersion: node.k8s.io/v1
   kind: RuntimeClass
   metadata:
     name: kata
   handler: kata

2. PodでRuntimeClassを指定:
   spec:
     runtimeClassName: kata
     containers:
       - name: app
         image: nginx

3. containerdがhandlerにマッチするランタイムを選択:
   handler "kata" -> containerd.runtimes.kata設定
   -> containerd-shim-kata-v2を実行

7. まとめ

containerdのコンテナライフサイクル管理はContainer(メタデータ)とTask(実行)の分離、shimによるプロセス分離、多様なランタイムクラスサポートが核心です。shimのデーモン化設計によりcontainerd再起動でもコンテナが維持され、OCIランタイムスペックベースの標準化されたインターフェースでrunc、Kata、gVisor、Wasmなど多様なランタイムを統合します。