こんにちは!日々のインフラ運用やコンテナのデプロイ、本当にお疲れ様です。第一線のSREとして現場を走り回っていると、「コンテナってなんだか魔法のように動いて便利だけど、裏側で一体何が起きているんだろう?」という疑問に出会うことがよくありますよね。
Kubernetesを使っていると、マニフェストファイルを書いてkubectl applyするだけで、アプリがポコポコと立ち上がってくれます。でも、その下層にあるLinuxカーネルや、コンテナの「入れ物」を作るランタイムの世界を覗いてみると、実は非常にドラマチックな仕組みが動いているんです。
今回は、インフラやネットワークの世界に一歩踏み出したばかりのあなたに向けて、コンテナの共通規格である OCI(Open Container Initiative)標準仕様 と、実際にLinuxの機能を組み合わせてコンテナを実体化させる runcやcrunといった低レイヤランタイムの役割 について、身近な例えを交えながら優しく紐解いていきたいと思います。
一歩ずつ、リラックスして理解していきましょう!
—
1. コンテナの世界共通ルール「OCI(Open Container Initiative)」とは?
まずは、コンテナの「設計図」と「荷物の梱包方法」のルールを定めているOCIについてお話ししましょう。
皆さんは、通販で海外から届いたダンボール箱を開けたことはありますか? どこの国のどんな会社が発送したものであっても、ダンボールの材質やテープの貼り方、ラベルの貼る位置などがだいたい共通しているからこそ、世界中のどんな配達員でもスムーズに荷物を運べますよね。
もし、配送業者ごとに「うちの箱は三角形です」「うちは丸いカプセルに入れます」なんてバラバラだったら、トラックも倉庫も大パニックになってしまいます。
OCIが定めた2つの「共通規格」
コンテナ業界でも、かつてはDocker社が独自のフォーマットでイメージを作り、独自の仕組みで動かしていました。「もしDocker以外のツールを使いたいときはどうするんだ?」という業界全体の懸念から生まれたのが OCI(Open Container Initiative) です。
OCIは、大きく分けて次の2つの仕様を定めています。
1. OCI Image Specification(イメージ仕様):
アプリのコードやミドルウェア、設定ファイルなどを、どういう形式でひとまとめにして「コンテナイメージ(ダンボール箱)」にするかのルール。
2. OCI Runtime Specification(ランタイム仕様):
そのダンボール箱を解体し、Linuxカーネルの機能を使って「実際にどうやってプロセスを立ち上げる(部屋を割り当てる)」かのルール。
このOCIという「共通の世界共通規格」があるおかげで、私たちはDockerだけでなく、PodmanやCRI-O、さらにはKubernetesといった様々なツールを自由に行き来して使えるようになっているんです。
—
2. 低レイヤランタイム (runc / crun) の正体: Linuxの魔術師たち
OCIという「共通の設計図」が手に入ったら、次はそれを実際に形にする「職人」が必要です。ここで登場するのが、低レイヤランタイム(Low-level Runtime) と呼ばれるプログラムたちです。代表的なものに runc(ランシー) や crun(シラン) があります。
普段、私たちがコンテナを操作するときによく耳にする Docker Daemon や Kubernetes の kubelet は、いわば「現場監督」です。彼らは人間にとって分かりやすい指示を出しますが、直接Linuxカーネルを操作してコンテナの檻を作るわけではありません。
実際の「檻(コンテナ)の鍵を閉める」という泥臭い作業を行っているのが、この runc や crun なのです。
例え話:アパートの管理人さんとrunc
Linuxカーネルという巨大なビル(ホストOS)の中に、新しく入居者(コンテナ)を迎える場面を想像してください。
現場監督(Dockerなど)がやってきて、「この設計図(OCIバンドル)通りに、外から一切見えない独立した部屋を作ってくれ!」と管理人さんに頼みます。
この管理人さんこそが runc や crun です。
彼らはLinuxカーネルが持つ、次の2つの強力な機能(マジック)を巧みに使って、あっという間に安全な個室を作り上げます。
- Namespaces(名前空間):
部屋ごとにカーテンを閉めるような機能です。同じビルの中にいるのに、隣の部屋の住人の顔(プロセスID)や、冷蔵庫の中身(ネットワークインターフェース)が一切見えなくなります。「自分はこの世界の住人だ」と思い込ませる技術です。
- cgroups(コントロールグループ):
お部屋の「リソース制限」です。「この部屋で使える電気(CPU)は全体の20%まで、水道(メモリ)は512MBまでね」とガチガチに制限し、使いすぎたらピピーッとアラートを鳴らす仕組みです。
runc はGo言語で書かれた非常に信頼性の高い標準的なランタイムですが、最近注目されている crun は C言語で書かれており、メモリ消費が少なく起動速度が圧倒的に速いという特徴があります。SREの現場では、コンテナの起動レイテンシを極限まで削りたいときに crun が選ばれることが増えています。
—
3. 実践:実際にOCIの仕組みを覗いてみよう!
「理屈はわかったけど、実際にどう動いているの?」という声が聞こえてきそうですね。
ここでは、Dockerなどの重厚な仕組みを通さず、OCIの仕様に沿った形で低レイヤランタイムを直接触るハンズオン風のコードを見ていきましょう。
※実際のLinux環境(Ubuntuなど)で試すことができます。
ステップ1: コンテナのルートファイルシステムを用意する
まずは、コンテナの「中身(OSの最小限のファイルたち)」を適当なディレクトリに展開します。これを rootfs と呼びます。
# 作業用のディレクトリを作成
mkdir -p my-container/rootfs
# 最小限のUbuntuイメージをダウンロードして rootfs に展開する
# (※宿主がUbuntuの場合のイメージ取得と展開のイメージです)
docker export $(docker create ubuntu:22.04) | tar -C my-container/rootfs -xvf -
ステップ2: OCIコンテナの「設計図(config.json)」を作る
次に、この rootfs をどのように起動するかを定義する OCIランタイム仕様に沿った設定ファイル config.json を生成します。
# runcを使って自動で雛形の config.json を生成する
cd my-container
runc spec
このコマンドを実行すると、ディレクトリ内に config.json というファイルが作成されます。中身を覗いてみましょう。
{
"ociVersion": "1.0.0",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sh" // コンテナが起動した瞬間に実行するコマンド
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
]
},
"root": {
"path": "rootfs", // 先ほど用意したファイルシステムへのパス
"readonly": false
},
"linux": {
"namespaces": [
{"type": "pid"}, // プロセスIDを隔離する
{"type": "network"},// ネットワークを隔離する
{"type": "ipc"},
{"type": "uts"},
{"type": "mount"}
]
}
}
*(※上記設定ファイルは、分かりやすく簡略化しています)*
ステップ3: runc で直接コンテナを起動する!
さあ、準備は整いました。Dockerデーモンを介さず、直接 runc コマンドを叩いてコンテナ(名前空間とcgroupsで守られた個室)を起動してみましょう!
# "test-container" という名前でコンテナを起動する
sudo runc run test-container
ジャジャーン!
画面のプロンプトが変わり、コンテナ内部のシェル (sh) にログインした状態になりました。
ここで ps aux や ip a コマンドを叩いてみてください。
ホストOSのプロセスやネットワークデバイスとは完全に切り離され、このコンテナの中だけの世界が広がっていることが確認できるはずです。ここが、まさに runc が魔法をかけた瞬間です。
—
まとめ:レイヤの底を知ると、トラブルに強くなる!
今回は、OCI標準仕様と、低レイヤランタイム(runc / crun)の役割について、郵便配達やアパートの管理人に例えながら解説しました。
- OCI標準仕様 は、コンテナの荷物や設計図の世界共通ルール。
- 低レイヤランタイム(
runc/crun) は、そのルールに従ってLinuxのNamespacesやcgroupsを操作し、実際にコンテナの檻(個室)を作り上げる職人。
「KubernetesやDockerが裏側で何をやっているのか?」という足元をこうして覗いておくと、万が一「コンテナのメモリ制限が効かない!」「プロセスが見えない!」といったディープなトラブルシューティングに直面したときでも、「あぁ、あのあたりの設定やランタイムの挙動が怪しいな」と自信を持って原因にたどり着けるようになります。
インフラの世界は、一見難しそうに見えても、紐解いていくと非常に筋書きがしっかりしていてロマンに満ちています。
あなたのSRE・インフラエンジニアとしての旅路が、これからもワクワクするものでありますように!
それでは、また次回の技術解説でお会いしましょう!
コメント