📦

runC: 分かり易く解説 (第3回) - 実際に動かしてみる

2023/11/12に公開

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

はじめに

今回は実際にrunCでUbuntu18.04を動かし、雰囲気が分かるようになることを目的として解説していきます。

Ubuntu 18.04のrootfs準備

まず、コンテナ化対象のrootfs (/) となるディレクトリを作成します。

mkdir sample-container-rootfs
cd sample-container-rootfs

次に、x64用(PC環境に合わせた適切なアーキテクチャを選択が必要)のDockerイメージのUbuntu18.04イメージを取得します。

$ wget https://partner-images.canonical.com/core/bionic/current/ubuntu-bionic-core-cloudimg-amd64-root.tar.gz
$ tar xzvf ubuntu-bionic-core-cloudimg-amd64-root.tar.gz
$ rm ubuntu-bionic-core-cloudimg-amd64-root.tar.gz
$ ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

この展開したデータを見ると分かりますが、Linuxのルートファイルシステムのデータ群をtarで固めたデータであることが分かると思います。これがDockerイメージの実態なのです。

config.jsonファイルの作成

config.jsonファイルは必ずrootfs直下に配置する必要があります。

runc spec

config.jsonファイルの修正

今回は細かい設定やカスタマイズはさておき、とりあえず動かす方法のconfigファイルは以下です。基本的にrunc specで作成したそのままで動かすことが可能です。

{
	"ociVersion": "1.0.1-dev",
	"process": {
		"terminal": true,
		"user": {
			"uid": 0,
			"gid": 0
		},
		"args": [
			"sh"
		],
		"env": [
			"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
			"TERM=xterm"
		],
		"cwd": "/",
		"capabilities": {
			"bounding": [
				"CAP_AUDIT_WRITE",
				"CAP_KILL",
				"CAP_NET_BIND_SERVICE"
			],
			"effective": [
				"CAP_AUDIT_WRITE",
				"CAP_KILL",
				"CAP_NET_BIND_SERVICE"
			],
			"inheritable": [
				"CAP_AUDIT_WRITE",
				"CAP_KILL",
				"CAP_NET_BIND_SERVICE"
			],
			"permitted": [
				"CAP_AUDIT_WRITE",
				"CAP_KILL",
				"CAP_NET_BIND_SERVICE"
			],
			"ambient": [
				"CAP_AUDIT_WRITE",
				"CAP_KILL",
				"CAP_NET_BIND_SERVICE"
			]
		},
		"rlimits": [
			{
				"type": "RLIMIT_NOFILE",
				"hard": 1024,
				"soft": 1024
			}
		],
		"noNewPrivileges": true
	},
	"root": {
		"path": "",      ← ここを修正。runcコマンド実行ディレクトリからの相体位置を意識して下さい。
		"readonly": true
	},
	"hostname": "runc",
	"mounts": [
		{
			"destination": "/proc",
			"type": "proc",
			"source": "proc"
		},
		{
			"destination": "/dev",
			"type": "tmpfs",
			"source": "tmpfs",
			"options": [
				"nosuid",
				"strictatime",
				"mode=755",
				"size=65536k"
			]
		},
		{
			"destination": "/dev/pts",
			"type": "devpts",
			"source": "devpts",
			"options": [
				"nosuid",
				"noexec",
				"newinstance",
				"ptmxmode=0666",
				"mode=0620",
				"gid=5"
			]
		},
		{
			"destination": "/dev/shm",
			"type": "tmpfs",
			"source": "shm",
			"options": [
				"nosuid",
				"noexec",
				"nodev",
				"mode=1777",
				"size=65536k"
			]
		},
		{
			"destination": "/dev/mqueue",
			"type": "mqueue",
			"source": "mqueue",
			"options": [
				"nosuid",
				"noexec",
				"nodev"
			]
		},
		{
			"destination": "/sys",
			"type": "sysfs",
			"source": "sysfs",
			"options": [
				"nosuid",
				"noexec",
				"nodev",
				"ro"
			]
		},
		{
			"destination": "/sys/fs/cgroup",
			"type": "cgroup",
			"source": "cgroup",
			"options": [
				"nosuid",
				"noexec",
				"nodev",
				"relatime",
				"ro"
			]
		}
	],
	"linux": {
		"resources": {
			"devices": [
				{
					"allow": false,
					"access": "rwm"
				}
			]
		},
		"namespaces": [
			{
				"type": "pid"
			},
			{
				"type": "network"
			},
			{
				"type": "ipc"
			},
			{
				"type": "uts"
			},
			{
				"type": "mount"
			}
		],
		"maskedPaths": [
			"/proc/kcore",
			"/proc/latency_stats",
			"/proc/timer_list",
			"/proc/timer_stats",
			"/proc/sched_debug",
			"/sys/firmware",
			"/proc/scsi"
		],
		"readonlyPaths": [
			"/proc/asound",
			"/proc/bus",
			"/proc/fs",
			"/proc/irq",
			"/proc/sys",
			"/proc/sysrq-trigger"
		]
	}
}

Ubuntu18をコンテナを動かす - 方法その①

$ sudo runc run ubuntu18
# ls
bin   config.json   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  config.json~  etc  lib   media  opt  root  sbin  sys  usr
# uname -a
Linux runc 5.0.0-27-generic #28~18.04.1-Ubuntu SMP Thu Aug 22 03:00:32 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
# hostname
runc

終了方法

runc runで開始した場合、exitで終了した時点でコンテナは破棄されます。

# exit

Ubuntu18をコンテナを動かす - 方法その②

コンテナに実際に入らずに、コンテナはバックグラウンドで動かしながらバッチ処理などをそのコンテナ内で実行したい場合に利用する方法です。

$ sudo runc create ubuntu18
$ sudo runc list
ID          PID         STATUS      BUNDLE                                CREATED                          OWNER
ubuntu18    3927        created     /home/xxxxx/sample-container-rootfs   2019-11-20T11:03:43.234637306Z   root

config.jsonファイルの修正

上記方法①に対して、process -> terminalの部分をfalseに修正します。また、argsの部分も修正し、コンテナがずっと生き続けるようにダミーのsleepを入れておきます。

config.json
{
	"ociVersion": "1.0.1-dev",
	"process": {
		"terminal": false, ← 修正
	"user": {
         	"uid": 0,
        	"gid": 0
	},
	"args": [
        	"sleep","infinity" ← 修正
	],

コンテナを作成し、そのコンテナの中でコマンドを実行します。

$ sudo runc create ubuntu18
$ sudo runc start ubuntu18
$ sudo runc exec ubuntu18 ls
bin
boot
config.json
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
$ sudo runc exec ubuntu18 pwd
/
$ sudo runc exec ubuntu18 hostname
runc

終了方法

無限sleepでコンテナを動かしているため、通常のrunc killでコンテナが停止してくれないため、強制終了で対応します。

$ sudo runc list
ID          PID         STATUS      BUNDLE                                CREATED                          OWNER
ubuntu18    4398        running     /home/xxxxx/sample-container-rootfs   2019-11-20T11:23:32.631134894Z   root
$ sudo runc kill ubuntu18
$ sudo runc list
ID          PID         STATUS      BUNDLE                                CREATED                          OWNER
ubuntu18    4398        running     /home/xxxxx/sample-container-rootfs   2019-11-20T11:23:32.631134894Z   root
$ sudo runc kill ubuntu18 KILL
$ sudo runc list
ID          PID         STATUS      BUNDLE                                CREATED                          OWNER
ubuntu18    0           stopped     /home/xxxxx/sample-container-rootfs   2019-11-20T11:23:32.631134894Z   root
$ sudo runc delete ubuntu18
$ sudo runc list
ID          PID         STATUS      BUNDLE      CREATED     OWNER

Discussion