Youkiの基礎知識とcontribution環境構築

# Youkiとは

runc同様、OCI runtime specである。Rustで書かれている。日本語の容器が名前の由来。

なお、OCI runtime specは、1. 隔離環境の作成, 2. コンテナ実行, 3. プロセスのKill, 4. コンテナの削除、に関する明文化された仕様を指す。

### メリット

Rustで実装するメリットは、Goよりsystem callを扱いやすい(Cを呼び出すオーバーヘッドが少ない、ゼロコスト抽象化などの仕様のおかげ?)、Cと比べてメモリ安全、が挙げられていた。そのおかげか、Youkiは、runcより高速に動作するらしい。

### 仕様

現在はLinux環境しかサポートしていない。Linux以外の環境で動かしたい場合、VagrantでVMを利用する必要がある。

Youkiは低レベル container runtimeなので、Docker, Podmanなどの高レベル container runtimeと組み合わせて使うことになる。

Youkiには、ルート権限が必要なrootful modeと、不要なrootless modeがある。ルート権限とは、コンテナ内のプロセス権限がルートユーザーと同等かどうか。ルート権限がある場合、セキュリティ面での問題が懸念される。このあたりは[コンテナセキュリティ本]([https://www.amazon.co.jp/コンテナセキュリティ-コンテナ化されたアプリケーションを保護する要素技術-Liz-Rice/dp/4295016403/)を読むと、雰囲気がわかるはず。

### Dockerで動かす方法

`etc/docker/daemon.json`を書き換えることで、Dockerでもyoukiもruntimeとして使える。

```json
{
"default-runtime": "runc",
"runtimes": {
"youki": {
"path": "/path/to/youki/youki",
"runtimeArgs": [
"--debug",
"--systemd-log"
]
}
}
}
```

## レポジトリの説明

### libcgroups

Linux cgroupとやり取りしたりするための機能を担っている。common, stats, systemd, test_manager, v1, v2といったmoduleを持つ。例えば、cgroup fileの読み書きを簡単にしてくれるuser interfaceなどが提供されている。

### libcontainer

containerの作成・管理を担う。apparmor, container, hook, namespacesなどの様々なmoduleがある。

### liboci-cli

OCI container runtimeのためのCLIを提供している。

### libseccomp

libseccompへのRust FFI bindingを提供する。FFIとは、あるプログラミング言語から他プログラミング言語を利用するための機構。この文脈でいうと、RustからC言語で書かれたlibseccompを扱うということだと思う。

### Webasssembly

runwasiのように、WebAssembly moduleをYoukiで実行することが可能。

## control flow

以下のような流れでYoukiは動作するらしい。
![スクリーンショット 2024-02-26 23.39.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/371779/f724c6e8-56b5-4c83-8543-fed8b77afb4b.png)


# Contribution環境を作るときに試行錯誤したメモ

## Rustに関するメモ

コードリーディング・contributionをする際に、分からなかったところのメモ。

### Builder

以下のstruct Linuxの場合、ビルダーパターンを利用して初期値を設定することができる。ビルダーパターンを使うと、オブジェクトの構築をstep by stepで行えるようになり、オブジェクトの初期化を扱いやすくしてくれる。コード内でLinuxBuilderが出てきたが、LinuxBuilderというオブジェクトを定義している箇所はなく、LinuxオブジェクトにBuilderがderiveされているだけ。

```rust
#[derive(Builder, Clone, Debug, Deserialize, Eq, Getters, Setters, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
#[builder(
    default,
    pattern = "owned",
    setter(into, strip_option),
    build_fn(error = "OciSpecError")
)]
#[getset(get = "pub", set = "pub")]
/// Linux contains platform-specific configuration for Linux based
/// containers.
pub struct Linux {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    /// UIDMappings specifies user mappings for supporting user namespaces.
    uid_mappings: Option<Vec<LinuxIdMapping>>,
```

Linux Builderの初期化例

```rust
// Linux構造体のビルダーを使用して、uid_mappingsに値を設定
let linux = Linux::builder()
    .uid_mappings(vec![uid_mapping]) // ここでuid_mappingsにベクターを設定
    .build() // ビルダーパターンを完成させ、Linuxインスタンスを生成
    .expect("Failed to build Linux"); // build()メソッドがエラーを返す可能性があるため、unwrap()やexpect()で処理
```

### enum型

以下のようなenum型があるとする。

```rust
#[derive(Debug, thiserror::Error)]
pub enum InitProcessError {
    #[error("failed to set sysctl")]
    Sysctl(#[source] std::io::Error),
    #[error("failed to mount path as readonly")]
    MountPathReadonly(#[source] SyscallError),
```

enum型は、以下のように、InitProcessError::Sysctl (各enumの値)、のように指定して使う。

```rust
fn set_sysctl() -> Result<(), InitProcessError> {
    Err(InitProcessError::Sysctl(io::Error::new(io::ErrorKind::Other, "oh no!")))
}
```

## contribution環境の構築

私はLinux環境 PCでは無いので、CodeSpaceを使用した。EC2(amazon Linux)も試したが、CodeSpaceの方がラクだった。

### just

justとは、makeに影響を受けたtask runner。Makefileと違って、.PHONYが不要なので簡潔に書ける。

install方法は[document](https://just.systems/man/en/chapter_5.html)に書いてある(makeで良いのでは？と思った)

```shell
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/bin
export PATH="$PATH:$HOME/bin"
```

### golang

[runtime-tools](https://github.com/opencontainers/runtime-tools/blob/master/go.mod)を動かすために、go 1.19が必要。apt-getだとversion指定できないのでwgetでversion指定してインストールした。

```shell
wget https://go.dev/dl/go1.19.13.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.19.13.linux-amd64.tar.gz
export PATH="$PATH:/usr/local/go/bin"
```

### Rustc

CodeSpaceではすでにインストールされていて不要。EC2 amazon Linuxで動かすときはインストールが必要。

```shell
curl https://sh.rustup.rs -sSf | sh
```

### GitHubとの接続

CodeSpaceでは不要。EC2 amazon Linuxで動かすとき必要だった。

```shell
sudo yum install -y git
ssh-keygen -t rsa -b 4096
cat ~/.ssh/id_rsa.pub
# 結果をgithub settingsに貼り付ける。
ssh -T git@github.com
git clone git@github.com:hogehoge/youki.git
```

### その他のライブラリ

CodeSpaceでは不要。EC2 amazon Linuxで動かすとき必要だった。このあたりはYoukiのREADMEに書いてある。

```shell
sudo dnf install          \
      pkg-config            \
      systemd-devel         \
      elfutils-libelf-devel \
      libseccomp-devel      \
      clang-devel           \
      openssl-devel

# 以下のコマンドを実行してリンカーを入れる。
sudo dnf install glibc-static libstdc++-static
```

# 参考

[https://github.com/containers/youki:embed:cite]

[https://containers.github.io/youki/:embed:cite]