📦

runC: 分かり易く解説 (第1回) - 概要と実現技術編

2023/11/12に公開

Dockerの中核コンテナ技術であるrunCについて、以下の通りいくつかの記事に分けて分かり易く解説します。

runCとは?

コンテナランタイムという表現をよく見ますが、イメージとしてはDockerのイメージ保存などのコンテナ管理に関するDocker固有の機能を削除した、中核のコンテナ機能を実現しているモジュールのことです。

それを少し難しく言うと、OCI仕様に基づいたコンテナ生成・実行などを行うコンテナランタイムのリファレンスと言います。Dockerがコンテナランタイムの仕様をOCI仕様として標準化して、そのリファレンスプログラムとして作ったのがrunCです。

どうやって動いているの?

runCは主にLinux Kernelのシステムコールで提供されるcgroupsとnamespace機能(名前空間)を利用して実現されています。

これらを利用して、いい具合に隔離したものをDockerとして利用していて、その中でUbuntuだったり目的のターゲットOSを動かしています。

だた、厄介な点がrunCには設定項目が多くあるという点です。しかも各種設定項目に対しての説明が不十分なため、特に初心者にとっては辛い感じになっています。この辺も分かり易く解説していきたいと思います。

cgroupsとは?

Control Groupの略で、ホストOSが持つCPUやメモリなどのリソース(CPU、メモリ、ディスクI/Oなど)に対して、グループ毎にリソースの利用を制限・隔離するLinuxカーネルの機能です。

namepsace(名前空間)とは?

namespaceは大きく分けて以下の6つに分類されます。

  • ネットワーク名前空間
  • マウント名前空間
  • PID名前空間
  • IPC名前空間
  • ユーザ名前空間
  • UTS名前空間

このnamespcaseを用途に合わせて設定することで、ネットワーク空間やプロセス、ファイルシステムなどを名前空間毎に別物として隔離していくことが可能です。

ネットワーク名前空間

ネットワークデバイスやIPアドレスなどのネットワークインタフェースのネットワークに関する機能を隔離します。これにより各名前空間でそれぞれ別の仮想ネットワークを構築することができます。

マウント名前空間

ファイルシステムを隔離します。これにより各名前空間でそれぞれ別のファイルシステムとして認識されて、基本的に相互にアクセス出来なくなります。Linuxコマンドのchrootがこの機能に近いイメージです。

PID名前空間

PID(プロセスID)空間を隔離します。これにより各名前空間でPIDがそれぞれ割り振られて、同じIDのものが存在出来ますが、別空間のプロセスとなるため、プロセス間通信は出来なくなります。隔離された空間で3rd partyのアプリケーションを動かしたいなどのセキュリティ対応のサンドボックスとして利用されたりします。

IPC名前空間

IPC(プロセス間通信)に関するリソースを隔離します。これにより各名前空間でセマフォや共有メモリが別のもになり、アクセス出来なくなります。

ユーザ名前空間

UID/GIDを隔離します。これにより各名前空間で同じUIDのユーザーを作ることが出来ます。

UTS名前空間

unameコマンドやシステムコールから返されるシステム情報を隔離します。これにより各名前空間はそれぞれ独自のホスト名とドメイン名を持つことが出来るようになります。

unshareコマンド

Linuxのunshareコマンドを利用して、namespaceの設定が可能です。※設定変更にはroot権限が必要です。

$ unshare -h

Usage:
 unshare [options] [<program> [<argument>...]]

Run a program with some namespaces unshared from the parent.

Options:
 -m, --mount[=<file>]      unshare mounts namespace
 -u, --uts[=<file>]        unshare UTS namespace (hostname etc)
 -i, --ipc[=<file>]        unshare System V IPC namespace
 -n, --net[=<file>]        unshare network namespace
 -p, --pid[=<file>]        unshare pid namespace
 -U, --user[=<file>]       unshare user namespace
 -C, --cgroup[=<file>]     unshare cgroup namespace
 -f, --fork                fork before launching <program>
     --mount-proc[=<dir>]  mount proc filesystem first (implies --mount)
 -r, --map-root-user       map current user to root (implies --user)
     --propagation slave|shared|private|unchanged
                           modify mount propagation in mount namespace
 -s, --setgroups allow|deny  control the setgroups syscall in user namespaces

 -h, --help                display this help
 -V, --version             display version

For more details see unshare(1).

Discussion