Slide 1

Slide 1 text

詳説 OCIコンテナランタイム youki
 第15回 コンテナ技術の情報交換会
 うたもく(@utam0k)
 1

Slide 2

Slide 2 text

自己紹介
 ● うたもく(@utam0k)
 ● 普段はWeb企業のバックエンドエンジニア(Scala)
 ● 2021年からコンテナ真面目に入門
 ● containersのメンバー
 2

Slide 3

Slide 3 text

知ってもらいたいこと
 3 youkiの面白いポイント
 ● Rustとコンテナランタイムってどうなの?
 ● runCとの違いは?
 ● 処理の流れは?
 
 コンテナを作るまでの流れがコードレベルでわかる
 ● youkiの詳細な処理の流れ
 ● runCの処理の流れ
 


Slide 4

Slide 4 text

目次
 1. youkiの紹介
 2. youkiの大まかな処理の流れ
 3. コンテナランタイムのコードを追っていく!
 4

Slide 5

Slide 5 text

目次
 1. youkiの紹介
 2. youkiの大まかな処理の流れ
 3. コンテナランタイムのコードを追っていく!
 5

Slide 6

Slide 6 text

● Rust製の低レベルコンテナランタイム
 ● podmanなどを開発しているcontainersで開発中
 ○ https://github.com/containers/youki ● うたもくを中心にリリースに向けて開発を進めている
 ● githubで⭐数が2.2K
 ● youki = 容器 = コンテナ
 youkiとは?
 6

Slide 7

Slide 7 text

Kubelet(K8s)
 Linuxなど
 High-Level Runtime
 CRI
 Low-Level Runtime
 OCI
 7 CRI − Container Runtime Interface
 OCI − Open Container Initiative
 Container Runtime


Slide 8

Slide 8 text

Kubelet(K8s)
 Linuxなど
 High-Level Runtime
 CRI
 Low-Level Runtime
 OCI
 ● runc
 ● youki
 ● crun
 
 8

Slide 9

Slide 9 text

youkiの存在意義
 ● コンテナランタイムの多様性
 ● Rustコンテナ界隈でのライブラリへの貢献
 ○ https://github.com/containers/oci-spec-rs ● 既存の低レイヤのソフトウェアをRustで書き換える 一例
 9

Slide 10

Slide 10 text

目次
 1. youkiの紹介
 2. youkiの大まかな処理の流れ
 3. コンテナランタイムのコードを追っていく!
 10

Slide 11

Slide 11 text

コンテナ技術の軽い復習
 ● コンテナを支える技術
 ○ chrootpace - ルートディレクトリを変更
 ○ namespace - 操作可能なリソースの隔離
 ○ cgroup ace - 使用可能なリソースの設定
 
 ● コンテナ界隈を支える仕様
 ○ CRI - Container Runtime Interface
 ○ OCI - Open Container Initiative
 11

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

● コンテナのプロセスができるまでには double-fork が必要
 ○ 1st fork 
 ■ 中間プロセス
 ■ initプロセスを作るための間のプロセス
 ○ 2nd fork
 ■ initプロセス
 ■ 実際にコンテナのinitプロセスになる 
 ○ 理由は後ほど...


Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

● $ docker create --name test busybox hostname
 ● 高レベルランタイムからコンテナを作る命令を受付
 ○ 命令を受け付ける...? 
 ■ 単なるコマンド
 ■ $ youki create -b $(bundle_path) $(container_name)
 ● fork(2) - Intermediate Processを生成する


Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

● unshare(CLONE_NEWUSER) - ユーザー名前空間を分離
 ○ 新しい名前空間で特権ユーザーになれる
 ● host ↔ containerのユーザーとグループのマッピング
 ○ 例) host(uid 1000) ↔ container(uid 0)
 中間 create

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

● 中間プロセスでPID名前空間だけを先に分離
 ○ PID名前空間は発動した次のプロセスから適用されるため
 ● fork(2) - 中間プロセスからinitプロセスを生成
 ● initプロセスで残りの名前空間を分離
 ○ unshare(2)を発動した瞬間から分離される
 init 中間

Slide 21

Slide 21 text

なぜdouble-forkが必要なのか?
 ● PID名前空間の分離は発動した次のプロセスから適用される ○ fork回数 += 1 ● PID名前空間の分離にはCAP_SYS_ADMINが必要 ○ ホストが特権ユーザーではない場合は...? ○ ユーザー名前空間の分離(一般ユーザーで実行可能) ■ 中間Process内で特権ユーザーになる
 ■ fork回数 += 1
 ● ユーザー名前空間 → PID名前空間 → 残りの名前空間 21

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

● 諸々コンテナになるのに必要な処理をinitプロセスに施す
 ○ pivot_root(2) - `/` の切り替え
 ○ setup capability - HCRから受け取ったケーパビリティにする
 ● 諸々準備が終わったことを最初のプロセス(youki create)に通知
 ● initプロセスはstartのシグナルが来るまで「待て」
 create 中間 init

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

● cgroupを諸々適用する
 ● 受け取ったinitプロセスのPIDをpidファイルに書き込む
 ○ HCRはこのpidファイルのPIDを元に色々管理していたりするっぽい
 create 中間

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

● initプロセスはデーモンプロセスとなり、コンテナのエントリポイントを実行 するのを待つ
 ● 高レベルコンテナランタイムからスタートの合図
 ○ $ docker start test
 init

Slide 28

Slide 28 text

処理の流れのまとめ
 ● プロセスに対して細かく色々している
 ○ 名前空間の分離
 ○ pivot_root
 ○ capability
 ○ cgroups
 
 ● double-forkが必要
 28

Slide 29

Slide 29 text

処理の流れのまとめ
 ● プロセスに対して細かく色々している
 ○ 名前空間の分離
 ○ pivot_root
 ○ capability
 ○ cgroups
 
 ● double-forkが必要
 29 Rust 得意分野っぽい!


Slide 30

Slide 30 text

目次
 1. youkiの紹介
 2. youkiの大まかな処理の流れ
 3. コンテナランタイムのコードを追っていく!
 30

Slide 31

Slide 31 text

Let’s dive into youki

Slide 32

Slide 32 text

● 流れ自体は大きく変わらない
 ● 使用言語
 ○ runC - GoとCのハイブリッド
 ○ youki - Rust(一部FFI)
 runCではどうなっているのか
 32

Slide 33

Slide 33 text

GoとCのハイブリッドだと...?
 ● Goの言語ランタイムの制約
 ○ fork(2) / clone(2) が難しい
 ■ おそらくGoroutineの関係
 ○ 言語ランタイムがマルチスレッド
 
 ● setns(2) 😢 A multithreaded process may not change user namespace with setns().


Slide 34

Slide 34 text

● Goで扱えない部分はCで扱う
 ○ サブコマンドのinitがCを扱う
 ○ 言語ランタイムが起動する前にCの処理を呼び出す
 GoとCのハイブリッドだと...?


Slide 35

Slide 35 text

● 大雑把にrunCがコンテナを作る流れ
 1. runC create(Golang)
 ● Goでも可能な処理を行いinitサブコマンドを実行
 
 2. runC init(Golang & C)
 ● Goの言語ランタイムが起動する前にCの処理(double-fork)を 行う
 
 3. double-fork(C言語)
 ● fork(2)、setns(2)、uid/gidのマッピングなどなど...
 GoとCのハイブリッドだと...?


Slide 36

Slide 36 text

Let’s dive into runC

Slide 37

Slide 37 text

runCのコード読み
 ● startContainer() - create.go
 ○ createContainer()
 ■ loadFactory()
 ● libcontainer.New()
 ○ /proc/self/exec init(= runc init)をコマンドとして container にセットしている
 
 ○ runner.run()
 ■ container をスタートする


Slide 38

Slide 38 text

コードリーディングのまとめ
 ● runCはGoとCのハイブリッド
 ○ マルチスレッドでは処理ができない部分があるのでCを 使う
 ○ createサブコマンド(Go) → initサブコマンド(C)
 ● youki
 ○ Rustのみでよりシンプルにコードがかける


Slide 39

Slide 39 text

Thanks to all the people who already contributed to youki :)
 39

Slide 40

Slide 40 text

Thanks you! Any questions?
 40