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

runcの実装

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Ryotaro Ryotaro
December 30, 2024

 runcの実装

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

Avatar for Ryotaro

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