Rust no_stdで作るコンテナエンジン第15回 コンテナ技術の情報交換会@オンラインTakashi Iiguni (@guni1192)Kagawa University10/9/2021 1
View Slide
Profile• 氏名: 飯國 隆志(Takashi Iiguni)• 香川大学大学院工学研究科信頼性情報システム工学専攻M2• Favorite: Container, Security, Rust, eBPF• Twitter: guni1192• GitHub: guni1192• 来年から東京でSoftware Engineer10/9/2021 2
今日の話RustのミドルウェアもGoみたいにポータブルなバイナリを作って配りたい10/9/2021 3
Agenda• 今日話すこと• Rustでポータブルなシングルバイナリ作る話• Rustからlibcなしでシステムコールを呼び出す話• Rustの標準ライブラリ縛りでコンテナもどきを作る話• 話さないこと• OCI Runtimeの話10/9/2021 4
Rust and Go• Goは基本的にstatic link• 標準ライブラリはあまりCに依存しないようにできているからstatic linkしやすい• システムコールの呼び出しすらGoとアセンブラ• Rustは基本的にdynamic link(が多い気がする)• 標準ライブラリ(std)がlibcに依存しているからstaticlinkしにくい• musl libcを使うことでstatic linkできる• 依存ライブラリの再コンパイルが必要になる場合もある10/9/2021 5
例えばyoukiの場合10/9/2021 6
Rust library for implementing containers• libc: libc(7)をRustから呼び出すFFI binding• nix: libc crateのRust likeなwrapper• ResultによるError Handleなど10/9/2021 7
libc(7)• C言語の標準ライブラリ• printf(3)やfopen(3)などのライブラリ関数を提供• write(2), open(2)などのシステムコールを呼ぶための関数も提供• 実装例• glibc(GNU C Library): GNU Projectのlibc• musl libc: MITライセンスのlibc. Alpine Linuxなどで利用されている10/9/2021 8
Rustからlibc経由でシステムコールを発行する• Pros• 既存の実装を利用して多くの関数が利用できる• 成熟していて多くのArchに対応している• Cons• Rustの型に合わせた実装が難しい• Resultなどが使いたかったらnixのような抽象化ライブラリが必要• libcの脆弱性や更新などが必要10/9/2021 9
Issue syscall in Linux x86_64• システムコールの発行はレジスタに値を格納し,syscall命令を実行する• 結果はraxレジスタに格納Register write(2) execve(2) clone(2)syscall number rax 1 59 56arg1 rdi fd cmd flagsarg2 rsi buf args child stackarg3 rdx buf size env TID field in parentarg4 r10 TID field in childarg5 r8 thread pointerarg6 r910/9/2021 10
Rustから直接システムコールを呼ぶRustのinline assemblyを使う(asm!マクロ)10/9/2021 11
余談: 僕らがシステムコールと思っていたものはシステムコールじゃなかった• wait系のシステムコール• Linux x86_64はwait4しかない• libcのwaitpidはwait4システムコールを呼んでいる(歴史的経緯やアーキテクチャの違いがありそう)• $ man 2 waitpidが引けたからといってシステムコールとは限らない10/9/2021 12
苦労話: libc とシステムコール• cloneシステムコール• libcの引数とカーネル側のレジスタの値が違う• コールバック関数があるのはlibc側の仕様musl libcのコードは読みやすかったglibcはコメントに重要なことがたくさん書いてあった10/9/2021 13
余談: libcを経由しないシステムコールの発行について• OpenBSDはlibc以外からのシステムコールをブロックする仕組みがある• 信頼できない経路からのシステムコールの発行を止めたい• この機構の導入によってGoのようなlibcを経由しない言語がブロック• GoはOpenBSDの場合はCGOを利用するようになった10/9/2021 14
試しにシングルバイナリのコンテナエンジンを作ろう!Rustからシステムコールを呼べた10/9/2021 15
シングルバイナリなコンテナエンジンの要件• std crateを使用しない• std crateはlibcに依存している• Dynamic linkを一切しない• std crate• Rustの標準ライブラリ• Vec, Option, Boxといったプリミティブなライブラリを提供• 標準マクロ,IO,マルチスレッドなどの機能10/9/2021 16
Rust no_std• std crateを用いないモード• stdの代わりにcore crateを使用できる• core crate• プラットフォームに依存しないポータブルな標準ライブラリ• 浮動小数点,文字列,スライス,アトミック操作やSIMD命令などのAPIを提供• 一般的にカーネルやブートローダ,ベアメタルな環境で用いるモード• 動的メモリ確保などの昨日は備考: Rustのno_std環境をちゃんと触るなら「Writing an OS in Rust」https://os.phil-opp.com がおすすめ10/9/2021 17
Nosc(No std container)• Portable and Tiny Container Engine• without std crate• without dynamic link binary• Environment• Linux >= 5.7• Arch: x86_64• Cgroup v2• https://github.com/guni1192/nosc10/9/2021 18
Nosc Feature• プロセスのNamespaceの分離• プロセスを親と異なるCgroupへ所属• 必要なシステムコール• clone3• execve• write• open• wait4• mkdir10/9/2021 19
clone3• 子プロセスを作るシステムコール• Linux 5.3で追加された• Linux 5.7以降で子プロセスを親とは別のcgroupに配属可能に• clone_args.cgroupに配属させるcgroupディレクトリのfdを与えて実行• clone_args.flagsにCLONE_INTO_CGROUPを指定10/9/2021 20
Nosc Run container10/9/2021 21
Binary sizeContainer Engine Size(Bytes)Nosc (static link + stripped) 24,904go tiny container (static link + stripped) ※ 1,584,856youki (striped) 2,574,792runc (striped) 11,445,576※go tiny containerは即興で作ったnoscと似たような挙動をするコンテナエンジン10/9/2021 22
Future work• libcに依存しないAlternative nixみたいなものを作ってみても面白そう• Cに依存しないコンテナエンジンとかも面白そうSummary• Rustでもポータブルなバイナリを作りたかった• libc無しでシステムコールを発行するライブラリを作っている• no_std環境で動くコンテナエンジンもどきを作ってみた• 失って初めて分かるstdとlibcの便利さ10/9/2021 23