こんにちは.前回のポストからだいぶ時間が空きましたが,相変わらず緊急事態宣言中で自宅待機なので時間を持て余しています. 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
1
2

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

$ mkdir rootfs
1

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

$ sudo docker run -it centos /bin/sh
1

別ターミナルで

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

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

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

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

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

とすると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
1
2

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

# config.jsonの準備

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

$ cd ..
$ sudo runc spec
1
2

生成された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"
		]
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

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

process -> args

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

# コンテナの起動

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

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

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

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

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

# create

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

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

単純に実行するとエラーになりました. デタッチするときに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
1
2
3

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

# ps

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

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

このコンテナはまだスタートしていないので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$
1
2
3
4

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

# delete

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

$ sudo runc delete runc-test-centos2
1

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

# まとめ

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

# 参考