Upgrade to Pro — share decks privately, control downloads, hide ads and more …

runcの実装

Ryotaro
December 30, 2024

 runcの実装

runcの実装です。詳細はこちらの記事をご確認ください。

Ryotaro

December 30, 2024
Tweet

More Decks by Ryotaro

Other Decks in Technology

Transcript

  1. 抽象度の低いrunc イメージは不要、コンテナのルート/で十分 mkdir rootfs # busybox の/以下を rootfs にコピー docker

    export $(docker create busybox) | \ tar -C rootfs -xvf - # コンテナを起動するパラメタの雛形を./config.json に出力 runc spec # コンテナ ID を cid としてコンテナを起動 runc --root /tmp/runc run cid # /bin/sh を実行中のコンテナにアタッチ runc でコンテナの sh を開始 3
  2. 資料の目的 UNIXプログラミングの基本的な道具を知る Infrastructure Plumbing Manifesto2 When you need to create

    new plumbing, make it easy to re- use and contribute improvements back. Follow the unix principles: several simple components are better than a single, complicated one. Define standard interfaces which can be used to combine many simple components into a more sophisticated system. runc は UNIX の原則による小さな機能を組合せ 2docker のブログ Introducing runC: a lightweight universal container runtime より 4
  3. runc runの手続き runの呼出すinitがコンテナになる SIGCHLDをうける run initのptsと通信するチャネルをつくる run initを非同期実行 パイプからの通信を待つ Cのnsexec関数で名前空間を切替

    execveでinitをshに置換 疑似端末をつくる exit init コンテナの構造体をつくる コンテナの起動を通知する名前付きパイプをつくる sh 正常な起動をパイプに通知 exit run と init コマンドの手続き init はコンテナの PID 1 のプロセスになる 5
  4. 疑似端末 pty ptyはキャラクタデバイスのマスタ、スレーブの組[1] ∘ マスタ/dev/ptmx を開くと/dev/pts 下にスレーブができる ∘ 一方の入力が他方の出力になる ∘

    ptsname 関数にマスタのファイル記述子を渡すとスレーブの パスがわかる ∘ 通常、プロセスはマスタを開いて fork する。子プロセスはス レーブを複製して標準 I/O, エラーにする[2]3 3ls -l /proc/<pid>/fd で記述子 0, 1, 2 がスレーブへのリンクか分かる 7
  5. runcでのptyの準備 initはrunにマスタのファイル記述子を送る4 1. run は socketpair でソケットの組をつくる。双方向通信 できる 2. run

    は Cmd.ExtraFiles でソケットを 1 つ init にわたす 3. init は/dev/ptmx を開く 4. init は sendmsg でソケットを通してマスタを run に送る 5. run はマスタの入出力を標準 IO にコピーする 4次ページより上の手続きの実装を確認する 8
  6. init マスタを開き、その記述子をrunに送る pty, slavePath, err ^:= console.NewPty() ^// 中略 if

    err ^:= utils.SendRawFd( socket, pty.Name(), pty.Fd()); err ^!= nil { return err } setupConsole 関数の抜粋 9
  7. init dup3でスレーブを複製 fd, err ^:= unix.Open(slavePath, unix.O_RDWR, 0) ^// 中略

    for _, i ^:= range []int{0, 1, 2} { if err ^:= unix.Dup3(fd, i, 0); err ^!= nil { return err dupStdio 関数の抜粋 コードの 0, 1, 2 は標準 IO, エラー出力の記述子 10
  8. run ホストの標準入力をマスタに書き、マスタの出力を ホストの標準出力に送る f, err ^:= utils.RecvFile(socket) ^// 中略 socket

    からマスタの記述子を取得 go func() { _, _ = io.Copy(epollConsole, os.Stdin) }() t.wg.Add(1) go t.copyIO(os.Stdout, epollConsole) recvtty 関数の抜粋 epoll でマスタの入出力を監視 12
  9. 名前空間[4] 空間のプロセスには自分達がリソースを占有したよう に見える ∘ マウントポイント、プロセス ID など 6 種類の名前空間がある ∘

    名前空間の間でリソース名が重複しても互いに影響しない ∘ API clone 新しいプロセスを作る。子プロセスを新しい名前空 間に入れることもできる setns 呼びだしたスレッドを既存の名前空間に入れる。用 途はコンテナ間でのネットワークの共有など unshare 指定した種類の名前空間を作り、呼びだしたプロセ スを移動する 14
  10. nsexecでinitの名前空間を移動 Goの前にCのnsexec関数を実行する ^/* #cgo CFLAGS: -Wall extern void nsexec(); void

    <中略> init(void) { nsexec(); } ^*/ import "C" nsenter.go の抜粋 Go のランタイムには複数のスレッドがあるが、setns は呼びだ したスレッドの名前空間のみを移動する[5] 15
  11. nsexec cloneでpid 1を割り当てる ∘ clone に CLONE_PARENT を 渡すことで、新しい init

    の 親プロセスを常に run にし、 SIGCHLD を run に届ける ∘ uid_map, gid_map には、 親と子の名前空間のユーザ、 グループ ID の写像を書く[6] init runからsetns対象の 名前空間を受信 clone setns pid /proc/pid/uid_map /proc/pid/gid_map 更新 unshare clone setuid(0) setgid(0) init init returnしてGoのmainへ exit exit nsexec の処理手順 unshare では呼出元のプロセス名前空間を変更できない[7] 16
  12. initのルートファイルシステムを変更 pivot_rootでファイルシステムを変更する cd rootfs ^&& mkdir put_old # 新しいマウントポイントの名前空間に移動 unshare

    -mpfr /bin/sh # pivot_root の引数はマウントポイントでないとだめ mount --bind $(pwd) $(pwd) # put_old にもとのルートをおく。後で unmount 可 pivot_root $(pwd) $(pwd)/put_old pivot_root の実行例 18
  13. 実装とPlumbling Manifestoをくらべて コンテナの基礎の実装に特別なからくりはない ∘ 標準のインターフェース ∘ 端末, 名前空間、マウントは互いに異質だが、どれ も/proc 下のディレクトリ

    (ns, fd, mounts) にファイル として、その状態が公開されている ∘ ファイル開き、返った記述子を関数に渡して操作できる ∘ 小さな機能の組合せ ∘ コンテナに用途を限らない機能で実装されている5 ∘ runc 自体も抽象度の高い API から組合せられて使われる 5clone の CLONE_NEWIPC などコンテナのために用意された機能も一部ある[8] 20
  14. 参考資料 [1] M. Kerrisk, ``pty(7) - Linux manual page,'' (2024),

    [Online]. Available: https://man7.org/linux/man-pages/man7/pty.7.html (visited on 11/29/2024). [2] V. G. Teodorovici, ``Advanced programming in the unix environment, third edition by w. richard stevens and stephen a. rago,'' SIGSOFT Softw. Eng. Notes, vol. 38, no. 6, p. 45, Nov. 2013, ISSN: 0163-5948. DOI: 10.1145/2532780.2532798. [Online]. Available: https://doi.org/10.1145/2532780.2532798. [3] M. Kerrisk, ``TIOCSCTTY(2const) - Linux manual page,'' (2024), [Online]. Available: https://man7.org/linux/man- pages/man2/TIOCSCTTY.2const.html (visited on 11/30/2024). 21
  15. 参考資料 [4] M. Kerrisk, ``namespaces(7) - Linux manual page,'' (2024),

    [Online]. Available: https://man7.org/linux/man- pages/man7/namespaces.7.html (visited on 11/30/2024). [5] Open Container Initiative, ``Runc,'' (2015), [Online]. Available: https://github.com/opencontainers/runc (visited on 11/23/2024). [6] M. Kerrisk, ``user_namespaces(7) - Linux manual page,'' (2024), [Online]. Available: https://man7.org/linux/man- pages/man7/user_namespaces.7.html (visited on 06/15/2024). 22
  16. 参考資料 [7] M. Kerrisk, ``unshare(2) - Linux manual page,'' (2024),

    [Online]. Available: https://man7.org/linux/man-pages/man2/unshare.2.html (visited on 06/15/2024). [8] M. Kerrisk, ``clone(2) - Linux manual page,'' (2024), [Online]. Available: https://man7.org/linux/man-pages/man2/clone.2.html (visited on 12/03/2024). 23