runcを使ってみる


こんにちは.前回のポストからだいぶ時間が空きましたが,相変わらず緊急事態宣言中で自宅待機なので時間を持て余しています. ONE PIECEを読み返していたんですが,無料で読める分を読み終わってしまったので暇です. 今回はdockerに使用されているコンテナランタイムであるruncを使ってみました. では,いきます.

runcを使ってみる

runcとは #

runcとは,dockerなどのバックエンドで使用されるコンテナランタイムの一つで,dockerのデフォルトのランタイムです. コンテナプロセスの作成やリソースの制限を実行します. 実装はgolangです.

使ってみる #

早速使ってみましょう.実験環境にはVagrantで作成したubuntuを使用します. dockerが動く環境であればruncは入っているはずなのでインストールなどは省略します.

概要 #

runc - Open Container Initiative runtime runc is a command line client for running applications packaged according to the Open Container Initiative (OCI) format and is a compliant implementation of the Open Container Initiative specification.

runc integrates well with existing process supervisors to provide a production container runtime environment for applications. It can be used with your existing process monitoring tools and the container will be spawned as a direct child of the process supervisor.

Containers are configured using bundles. A bundle for a container is a directory that includes a specification file named “config.json” and a root filesystem. The root filesystem contains the contents of the container.

To start a new instance of a container: # runc run [ -b bundle ]

Where “” is your name for the instance of the container that you are starting. The name you provide for the container instance must be unique on your host. Providing the bundle directory using “-b” is optional. The default value for “bundle” is the current directory.

使用しそうなコマンドは以下の通り

  • create
  • delete
  • exec
  • kill
  • ps
  • run
  • spec
  • start

では早速使ってみましょう.

rootfsの準備 #

runcでは,dockerと違い作成するコンテナのファイルなどを用意してくれません.そこをいい感じにやってくれているのがdockerということですね. というわけで自分で用意します.

$ mkdir runc-test
$ cd runc-test

rootfsディレクトリを作成します.

$ mkdir rootfs

今回は作成するコンテナのファイルたちをdockerを経由してエクスポートしてきます. そのために,一旦dockerでコンテナを起動します.今回はcentosイメージを使用しました.(なんでもいいです)

$ sudo docker run -it centos /bin/sh

別ターミナルで

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
bc36fee690d6        centos              "/bin/sh"           6 seconds ago       Up 5 seconds                            suspicious_keller

NAMESを記録して,以下コマンドを実行してcentosのファイルシステムをtarファイルにexportします.

$ sudo docker export suspicious_keller > runc-test-centos.tar

すると,runc-test-centos.tarなるファイルが作成されるので展開しましょう.

$ tar -xvf runc-test-centos.tar
$ rm runc-test-centos.tar

とするとcentosのファイルシステムがカントディレクトリ(/runc-test/rootfs)に展開されます.

$ ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

これでrootfsの準備が完了しました.

config.jsonの準備 #

コンテナはconfig.jsonから設定を読み込んで起動されます.ですので,config.jsonを準備しましょう. config.jsonrunc specコマンドで生成されます.

$ cd ..
$ sudo runc spec

生成されたconfig.jsonをみてみましょう.

{
	"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": "rootfs",
		"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"
		]
	}
}

capabilitiesnamespacesなどいろいろ設定されています.

process -> args

に起動コマンドとしてshが登録されているのでコンテナプロセスがshを実行して起動するようです.

コンテナの起動 #

それではコンテナプロセスを起動してみます. コンテナを起動するにはrunc run [container-id]です.

$ sudo runc run runc-test-centos
sh-4.4#

シェルが立ち上がり,無事起動できているようです. これでruncでコンテナを起動することができました.

その他のコマンドを使ってみる #

この調子で他のコマンドを使用してみます.

create #

createはコンテナを作成サブコマンドです.

$ sudo runc create runc-test-centos2
cannot allocate tty if runc will detach without setting console socket

単純に実行するとエラーになりました. デタッチするときにconsole-socketを設定してないとttyを割り当てることができないと言われているので,とりあえずconfig.jsonprocess>terminalfalseに更新して再度実行すると作成できました.

list #

listで作成されたコンテナを確認することができます.

$ sudo runc list
ID                  PID         STATUS      BUNDLE                    CREATED                          OWNER
runc-test-centos2   3609        created     /home/vagrant/runc-test   2020-04-29T09:35:09.882689575Z   root

無事作成できているようです.

ps #

psで指定したコンテナの状態をみることができます. 先ほど作成したrunc-test-centos2の状態をみてみましょう.

UID        PID  PPID  C STIME TTY          TIME CMD
root      3609     1  0 09:35 ?        00:00:00 runc init

このコンテナはまだスタートしていないのでCMDの項目がrunc initとなっていますね.

start #

では,先ほど作成したコンテナを起動してみましょう. startでコンテナを起動します.

$ sudo runc start runc-test-centos2
sh: cannot set terminal process group (-1): Inappropriate ioctl for device
sh: no job control in this shell
sh-4.4# vagrant@ubuntu-xenial:~/runc-test$

起動しているようですが,シェルのエラーが出ています. これはおそらくcreateするときにterminal: falseにしたからですね.別の方法がありそうです. とりあえず起動はできたのでよしとします.(怠慢)

delete #

deleteで作成したコンテナを削除します.

$ sudo runc delete runc-test-centos2

何も出てこなければ正常に削除できています.

まとめ #

今回はコンテナランタイムのデファクトスタンダードであるruncについて一通り使用してみました.dockerでも使用されているのでdockerでコンテナを使用する際に内部でどんなことをしているのかが少し理解できました.

参考 #


See also