Open13

containerd/runcまわり

okazuokazu

https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/runtime/v2/runc/container.go#L359-L387
ここから
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/runtime/v2/runc/container.go#L360
ここに来て、すでにここでコンテナ側のプロセスを開始してそうだが、そうなるとこれがruncのプロセス?
そして、この辺でcgroupsの名前が出ているが(何をやっているかは知らない)、そういうのはruncの仕事では?

okazuokazu

r.command(context, "start", id) がどうやらrunc startコマンドを呼んでそうだぞ、ということで細かく見ていく
まず、 command() はここ。
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/vendor/github.com/containerd/go-runc/command_linux.go#L27-L42
このなかでも29-32行目に、ダイレクトにexec.CommandContext() している

	if command == "" {
		command = DefaultCommand
	}
	cmd := exec.CommandContext(context, command, append(r.args(), args...)...)

では、この command がruncなのであろう、ということは30行目のDefaultCommandが以下の通り runc であることからも予測可能。

https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/vendor/github.com/containerd/go-runc/runc.go#L58-L59

これで、以下の第二引数が(少なくともデフォルトでは) runc であることはわかった。
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/vendor/github.com/containerd/go-runc/command_linux.go#L32
では、第三引数の append(r.args(), args...)...) を見ていく。
r.args() は以下で、オプションを主に管理していそう。
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/vendor/github.com/containerd/go-runc/runc.go#L768-L793
では、もう片方の args はなにか。これは、この関数((*Runc).command())の第二引数(可変長なので複数の可能性がある)。
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/vendor/github.com/containerd/go-runc/command_linux.go#L27
つまり、一個前のポストにあるように今回のコンテキストでは "start", id の2つ。

ここまでの話を総合して大まかに以下のようなコマンドが発行されると予測される
runc SOME_OPTS start CONTAINER_ID

okazuokazu

ここまででコンテナの起動まではわかった気がするので、起動したコンテナに対してどうやってstdout/stderrを吸い上げるパイプを紐付けるのか

okazuokazu

とりあえずctrの --log-uriオプションに binary://$FILEPATH な感じでログハンドラを渡したときに来る場所がここっぽい
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L108-L109
これはここに飛ぶ
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L247-L309

ここで cmd.ExtraFiles いくつかのパイプを渡して実行しているがなぜか書き込みのパイプは閉じてしまっている。意図は不明(入力が何もない、という状態を作りたいとか?
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L283-L296

開いたパイプ、結局deferで関数出るときにcloseしてるけどそれも謎
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L252-L264

ExtraFilesにパイプいくつか追加してるが、これはfd3以降としてアタッチされる。
https://gist.github.com/smoser/b5925ab57e87423a46056e9d47e6afc8

https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L303-L307

okazuokazu

https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L109
pio.io がどう使われるかを追えば良さそう

pioがなにか、という定義が以下(processIO struct)
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L53-L59

ちなみに、binaryIOrunc.IO のinterfaceを満たすためのメソッドはこのあたりだが、Write/ReadCloserを返す処理は全部nilになっている
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/io.go#L389-L408

createIO()自体が呼ばれる箇所はいくつかあるが、とりあえず Init.Create() を見ていく。
以下でcreateIO()の戻り値全体をInit.ioとして格納してる
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/init.go#L124-L127

okazuokazu

https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/init.go#L137-L139

ここでopts.IOp.io.IO() (InitのprocessIOがrunc.IOを返してる。命名が下手?????)を格納してる
では、この opts がなにかというと、 runc.CreateOpts struct
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/vendor/github.com/containerd/go-runc/runc.go#L130-L141

このoptsは、作られた直後でここに渡されて、これは runc.Create() なのでここらへんからcontainerd -> runcにコンテキストが移って行く雰囲気がある
https://github.com/containerd/containerd/blob/e0912c068b131b33798ae45fd447a1624a6faf0a/pkg/process/init.go#L143-L145