1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-05-18 05:26:13 +02:00
youki/print.html
2024-02-12 05:10:59 +00:00

1271 lines
107 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="light" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Youki User and Developer Documentation</title>
<meta name="robots" content="noindex">
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('light')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="youki.html">Youki</a></li><li class="spacer"></li><li class="chapter-item expanded "><a href="user/introduction.html"><strong aria-hidden="true">1.</strong> User Documentation</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="user/basic_setup.html"><strong aria-hidden="true">1.1.</strong> Basic Setup</a></li><li class="chapter-item expanded "><a href="user/basic_usage.html"><strong aria-hidden="true">1.2.</strong> Basic Usage</a></li><li class="chapter-item expanded "><a href="user/crates.html"><strong aria-hidden="true">1.3.</strong> Crates provided</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="user/libcgroups.html"><strong aria-hidden="true">1.3.1.</strong> libcgroups</a></li><li class="chapter-item expanded "><a href="user/libcontainer.html"><strong aria-hidden="true">1.3.2.</strong> libcontainer</a></li><li class="chapter-item expanded "><a href="user/liboci_cli.html"><strong aria-hidden="true">1.3.3.</strong> liboci-cli</a></li><li class="chapter-item expanded "><a href="user/libseccomp.html"><strong aria-hidden="true">1.3.4.</strong> libseccomp</a></li></ol></li><li class="chapter-item expanded "><a href="user/webassembly.html"><strong aria-hidden="true">1.4.</strong> Webassembly</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><a href="developer/introduction.html"><strong aria-hidden="true">2.</strong> Developer Documentation</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="developer/basics.html"><strong aria-hidden="true">2.1.</strong> Basics</a></li><li class="chapter-item expanded "><a href="developer/unwritten_rules.html"><strong aria-hidden="true">2.2.</strong> Unwritten Rules</a></li><li class="chapter-item expanded "><a href="developer/good_places_to_start.html"><strong aria-hidden="true">2.3.</strong> Good places to start</a></li><li class="chapter-item expanded "><a href="developer/documentation_mdbook.html"><strong aria-hidden="true">2.4.</strong> This Documentation</a></li><li class="chapter-item expanded "><a href="developer/repo_structure.html"><strong aria-hidden="true">2.5.</strong> Repository Structure</a></li><li class="chapter-item expanded "><a href="developer/debugging.html"><strong aria-hidden="true">2.6.</strong> Debugging</a></li><li class="chapter-item expanded "><a href="developer/crate_specific_information.html"><strong aria-hidden="true">2.7.</strong> Crate Specific Information</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="developer/libcgroups.html"><strong aria-hidden="true">2.7.1.</strong> libcgroups</a></li><li class="chapter-item expanded "><a href="developer/libcontainer.html"><strong aria-hidden="true">2.7.2.</strong> libcontainer</a></li><li class="chapter-item expanded "><a href="developer/liboci_cli.html"><strong aria-hidden="true">2.7.3.</strong> liboci-cli</a></li><li class="chapter-item expanded "><a href="developer/libseccomp.html"><strong aria-hidden="true">2.7.4.</strong> libseccomp</a></li><li class="chapter-item expanded "><a href="developer/youki.html"><strong aria-hidden="true">2.7.5.</strong> youki</a></li></ol></li><li class="chapter-item expanded "><a href="developer/e2e/e2e_tests.html"><strong aria-hidden="true">2.8.</strong> e2e tests</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="developer/e2e/rust_oci_test.html"><strong aria-hidden="true">2.8.1.</strong> rust oci tests</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="developer/e2e/integration_test.html"><strong aria-hidden="true">2.8.1.1.</strong> integration_test</a></li><li class="chapter-item expanded "><a href="developer/e2e/test_framework.html"><strong aria-hidden="true">2.8.1.2.</strong> test_framework</a></li><li class="chapter-item expanded "><a href="developer/e2e/runtimetest.html"><strong aria-hidden="true">2.8.1.3.</strong> runtimetest</a></li></ol></li><li class="chapter-item expanded "><a href="developer/e2e/containerd_integration_test_using_youki.html"><strong aria-hidden="true">2.8.2.</strong> containerd integration test</a></li><li class="chapter-item expanded "><a href="developer/e2e/runtime_tools.html"><strong aria-hidden="true">2.8.3.</strong> runtime tools</a></li></ol></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Youki User and Developer Documentation</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="youki"><a class="header" href="#youki">Youki</a></h1>
<p align="center">
<img src="./assets/youki.png" width="450">
</p>
<p>youki is an implementation of the <a href="https://github.com/opencontainers/runtime-spec">OCI runtime-spec</a> in Rust, similar to <a href="https://github.com/opencontainers/runc">runc</a>.</p>
<h2 id="about-the-name"><a class="header" href="#about-the-name">About the name</a></h2>
<p>youki is pronounced as /joÊŠki/ or yoh-key.
youki is named after the Japanese word 'youki', which means 'a container'. In Japanese language, youki also means 'cheerful', 'merry', or 'hilarious'.</p>
<h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2>
<p>Here is why we are writing a new container runtime in Rust.</p>
<ul>
<li>
<p>Rust is one of the best languages to implement the oci-runtime spec. Many very nice container tools are currently written in Go. However, the container runtime requires the use of system calls, which requires a bit of special handling when implemented in Go. This is too tricky (e.g. <em>namespaces(7)</em>, <em>fork(2)</em>); with Rust, it's not that tricky. And, unlike in C, Rust provides the benefit of memory safety. While Rust is not yet a major player in the container field, it has the potential to contribute a lot: something this project attempts to exemplify.</p>
</li>
<li>
<p>youki has the potential to be faster and use less memory than runc, and therefore work in environments with tight memory usage requirements. Here is a simple benchmark of a container from creation to deletion.</p>
<div class="table-wrapper"><table><thead><tr><th style="text-align: center">Runtime</th><th style="text-align: center">Time (mean ± σ)</th><th style="text-align: center">Range (min … max)</th></tr></thead><tbody>
<tr><td style="text-align: center">youki</td><td style="text-align: center">198.4 ms ± 52.1 ms</td><td style="text-align: center">97.2 ms … 296.1 ms</td></tr>
<tr><td style="text-align: center">runc</td><td style="text-align: center">352.3 ms ± 53.3 ms</td><td style="text-align: center">248.3 ms … 772.2 ms</td></tr>
<tr><td style="text-align: center">crun</td><td style="text-align: center">153.5 ms ± 21.6 ms</td><td style="text-align: center">80.9 ms … 196.6 ms</td></tr>
</tbody></table>
</div><details>
<summary>Details about the benchmark</summary>
<ul>
<li>A command used for the benchmark
<pre><code class="language-console">$ hyperfine --prepare 'sudo sync; echo 3 | sudo tee /proc/sys/vm/drop_caches' --warmup 10 --min-runs 100 'sudo ./youki create -b tutorial a &amp;&amp; sudo ./youki start a &amp;&amp; sudo ./youki delete -f a'
</code></pre>
</li>
<li>Environment
<code>console $ ./youki info Version 0.0.1 Kernel-Release 5.11.0-41-generic Kernel-Version #45-Ubuntu SMP Fri Nov 5 11:37:01 UTC 2021 Architecture x86_64 Operating System Ubuntu 21.04 Cores 12 Total Memory 32025 Cgroup setup hybrid Cgroup mounts blkio /sys/fs/cgroup/blkio cpu /sys/fs/cgroup/cpu,cpuacct cpuacct /sys/fs/cgroup/cpu,cpuacct cpuset /sys/fs/cgroup/cpuset devices /sys/fs/cgroup/devices freezer /sys/fs/cgroup/freezer hugetlb /sys/fs/cgroup/hugetlb memory /sys/fs/cgroup/memory net_cls /sys/fs/cgroup/net_cls,net_prio net_prio /sys/fs/cgroup/net_cls,net_prio perf_event /sys/fs/cgroup/perf_event pids /sys/fs/cgroup/pids unified /sys/fs/cgroup/unified CGroup v2 controllers cpu detached cpuset detached hugetlb detached io detached memory detached pids detached device attached Namespaces enabled mount enabled uts enabled ipc enabled user enabled pid enabled network enabled cgroup enabled $ ./youki --version youki version 0.0.1 commit: 0.0.1-0-0be33bf $ runc -v runc version 1.0.0-rc93 commit: 12644e614e25b05da6fd08a38ffa0cfe1903fdec spec: 1.0.2-dev go: go1.13.15 libseccomp: 2.5.1 $ crun --version crun version 0.19.1.45-4cc7 commit: 4cc7fa1124cce75dc26e12186d9cbeabded2b710 spec: 1.0.0 +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +YAJL </code></li>
</ul>
</details>
</li>
<li>
<p>The development of <a href="https://github.com/oracle/railcar">railcar</a> has been suspended. This project was very nice but is no longer being developed. This project is inspired by it.</p>
</li>
<li>
<p>I have fun implementing this. In fact, this may be the most important.</p>
</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="user-documentation"><a class="header" href="#user-documentation">User Documentation</a></h1>
<p>This section provides documentation of youki and the sub crates that the youki repo contains for the users. So if you are using youki as a low level container runtime, or using any of the crates in youki workspace as dependencies for your own project, this section might be helpful for you.</p>
<p>This is divided into following sub-sections :</p>
<ul>
<li>Basic Setup : This explains how the dependencies and setup required to compile and run youki</li>
<li>Basic Usage : This explains using youki itself as a low-level container runtime, or using one of the crates as dependencies</li>
<li>crates : This section provides brief explanation of the member crates of youki repo workspace
<ul>
<li>libcgroups</li>
<li>libcontainer</li>
<li>liboci-cli</li>
<li>libseccomp</li>
</ul>
</li>
<li>Webassembly : This explains how to use webassembly module with youki.</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="basic-setup"><a class="header" href="#basic-setup">Basic Setup</a></h1>
<p>This explains the requirements for compiling Youki as a binary, to use it as a low-level container runtime, or to depend once of its crates as dependency for your own project.</p>
<p>Youki currently only supports Linux Platform, and to use it on other platform you will need to use some kind of virtualization. The repo itself provides Vagrantfile that provides basic setup to use Youki on non-Linux system using Vagrant. The last sub-section explains using this vagrantfile.</p>
<p>Also note that Youki currently only supports and expects systemd as init system, and would not work on other systems. There is currently work on-going to put systemd dependent features behind a feature flag, but till then you will need a systemd enabled system to work with Youki.</p>
<h2 id="build-requirements"><a class="header" href="#build-requirements">Build Requirements</a></h2>
<p>As Youki is written in Rust, you will need to install and setup Rust toolchain to compile it. The instructions for that can be found on Rust's official site <a href="https://www.rust-lang.org/tools/install">here</a>.
If you installed it using rustup, the correct compiler version will be setup automatically from <code>rust-toolchain.toml</code> in the repo root.</p>
<h3 id="build-with-cross-rs"><a class="header" href="#build-with-cross-rs">Build with cross-rs</a></h3>
<p>You can compile youki using <a href="https://github.com/cross-rs/cross">cross-rs</a>, which provides:</p>
<ul>
<li>Seamless compilation for different architectures (see <code>Cross.toml</code> in the repo root for the list of supported targets)</li>
<li>No build time dependencies (compilation runs in a container)</li>
<li>No runtime dependencies when building static binaries (musl targets)</li>
</ul>
<p>The only build dependency is <a href="https://github.com/cross-rs/cross?tab=readme-ov-file#installation">cross-rs</a> and its <a href="https://github.com/cross-rs/cross?tab=readme-ov-file#dependencies">dependencies</a> (rustup and docker or podman).</p>
<pre><code class="language-console">$ CARGO=cross TARGET=musl just youki-dev # or youki-release
</code></pre>
<h3 id="build-without-cross-rs"><a class="header" href="#build-without-cross-rs">Build without cross-rs</a></h3>
<p>Install the build dependencies and then run:</p>
<pre><code class="language-console">$ just youki-dev # or youki-release
</code></pre>
<p>Install the build dependencies using your distribution's package manger</p>
<h4 id="debian-ubuntu-and-related-distributions"><a class="header" href="#debian-ubuntu-and-related-distributions">Debian, Ubuntu and related distributions</a></h4>
<pre><code class="language-console">$ sudo apt-get install \
pkg-config \
libsystemd-dev \
build-essential \
libelf-dev \
libseccomp-dev \
libclang-dev \
libssl-dev
</code></pre>
<h4 id="fedora-centos-rhel-and-related-distributions"><a class="header" href="#fedora-centos-rhel-and-related-distributions">Fedora, CentOS, RHEL and related distributions</a></h4>
<pre><code class="language-console">$ sudo dnf install \
pkg-config \
systemd-devel \
elfutils-libelf-devel \
libseccomp-devel \
clang-devel \
openssl-devel
</code></pre>
<h2 id="runtime-requirements"><a class="header" href="#runtime-requirements">Runtime requirements</a></h2>
<p>The static binary (musl) builds of youki have no additional runtime requirements. Otherwise you need to install the runtime requirements using your distribution's package manager:</p>
<h4 id="debian-ubuntu-and-related-distributions-1"><a class="header" href="#debian-ubuntu-and-related-distributions-1">Debian, Ubuntu and related distributions</a></h4>
<pre><code class="language-console">$ sudo apt-get install libseccomp2
</code></pre>
<h4 id="fedora-centos-rhel-and-related-distributions-1"><a class="header" href="#fedora-centos-rhel-and-related-distributions-1">Fedora, CentOS, RHEL and related distributions</a></h4>
<pre><code class="language-console">$ sudo dnf install libseccomp
</code></pre>
<h2 id="running-youki"><a class="header" href="#running-youki">Running youki</a></h2>
<p>You can use Youki by itself to start and run containers, but it can be a little tedious, as it is a low-level container runtime. You can use a High-level container runtime, with its runtime set to Youki, so that it will be easier to use. Both of these are explained in the <a href="user/./basic_usage.html">Basic Usage</a>. For using it along with an high-level runtime, you will to install one such as Docker or Podman. This documentation uses Docker in its examples, which can be installed from <a href="https://docs.docker.com/engine/install">here</a>.</p>
<hr />
<h2 id="quick-install"><a class="header" href="#quick-install">Quick install</a></h2>
<p>Install from the GitHub release as root:</p>
<!--youki release begin-->
<pre><code class="language-console"># curl -sSfL https://github.com/containers/youki/releases/download/v0.3.2/youki-0.3.2-$(uname -m)-musl.tar.gz | tar -xzvC /usr/bin/ youki
</code></pre>
<!--youki release end-->
<h2 id="getting-the-source"><a class="header" href="#getting-the-source">Getting the source</a></h2>
<p>Currently Youki can only be installed from the source code itself, so you will need to clone the Youki GitHub repository to get the source code for using it as a runtime. If you are using any crates of Youki as dependency you need to do this step, as Cargo will automatically clone the repository for you.</p>
<p>To clone the repository, run</p>
<pre><code class="language-console">$ git clone https://github.com/containers/youki.git
</code></pre>
<p>This will create a directory named youki in the directory you ran the command in. This youki directory will be referred to as root directory throughout the documentation.</p>
<h2 id="installing-the-source"><a class="header" href="#installing-the-source">Installing the source</a></h2>
<p>Once you have cloned the source, you can build it with <a href="https://github.com/casey/just#installation">just</a> :</p>
<pre><code class="language-console"># go into the cloned directory
$ cd youki
$ just youki-dev # or youki-release
$ ./youki -h # get information about youki command
</code></pre>
<p>This will build the Youki binary, and put it at the root level of the cloned directory, that is in the youki/ .</p>
<hr />
<h2 id="using-sub-crates-as-dependency"><a class="header" href="#using-sub-crates-as-dependency">Using sub-crates as dependency</a></h2>
<p>To use any of the sub-crate as a dependency in your own project, you can specify the dependency as follows,</p>
<pre><code class="language-toml">[dependencies]
...
liboci-cli = { git = "https://github.com/containers/Youki.git" }
...
</code></pre>
<p>Here we use <code>liboci-cli</code> as an example, which can be replaced by the sub-crate that you need.</p>
<p>Then you can use it in your source as</p>
<pre><code>use liboci_cli::{...}
</code></pre>
<hr />
<h2 id="using-vagrant-to-run-youki-on-non-linux-platform"><a class="header" href="#using-vagrant-to-run-youki-on-non-linux-platform">Using Vagrant to run Youki on non-Linux Platform</a></h2>
<p>As explained before, Youki only support Linux, and to build/use it on non-Linux Platforms, you will need to use some kind of virtualization. The repo provides a Vagrantfile to do the required VM setup using Vagrant, which can be installed from <a href="https://www.vagrantup.com/docs/installation">here</a>.</p>
<p>Once installed and setup, you can run vagrant commands in the cloned directory to run Youki inside the VM created by vagrant :</p>
<pre><code class="language-console"># in the youki directory
# for rootless mode, which is default
$ vagrant up
$ vagrant ssh
# or if you want to develop in rootful mode
$ VAGRANT_VAGRANTFILE=Vagrantfile.root vagrant up
$ VAGRANT_VAGRANTFILE=Vagrantfile.root vagrant ssh
# in virtual machine
$ cd youki
$ just youki-dev # or youki-release
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="basic-usage"><a class="header" href="#basic-usage">Basic Usage</a></h1>
<p>This explains using Youki as a low-level container runtime. Youki can be used by itself to create, start and run containers, but doing so can be tedious, and thus you might want to use a higher-level runtime with Youki set as its runtime, so that you can get a convenient and easy interface.</p>
<p>You can use Youki with Docker, or Podman, but for the purpose of the examples, we will illustrate using Docker.</p>
<p>Youki can run in two modes, namely rootful mode, and rootless mode. The primary difference from the user-perspective in these is that as the name suggests, rootless mode does not require root/admin permissions, while rootful mode needs the root permissions. Both of these are shown in the examples below.</p>
<h4 id="using-youki-with-a-high-level-runtime"><a class="header" href="#using-youki-with-a-high-level-runtime">Using youki with a high-level runtime</a></h4>
<p>We will first see how to use Youki with a high-level runtime such as Docker. You can install Docker from <a href="https://docs.docker.com/engine/install/">here</a>.</p>
<p>By default, after installation the docker sets up so that its daemon process will start running in background after booting up. By default, this configures Docker to use its default low-level runtime, and to use Youki instead , we will first need to stop the running Docker daemon.</p>
<p>As Youki needs systemd to compile, this assumes that you are running on a systemd based system. So you an first check if the docker daemon is running or not by running</p>
<pre><code class="language-console">systemctl status docker
</code></pre>
<p>This will print a message showing if the daemon is active or not. If it is active, then you will need to stop it by running</p>
<pre><code class="language-console">sudo systemctl stop docker
</code></pre>
<p>After this you need to manually restart the docker daemon, but with Youki as its runtime. To do this, run following command in the youki/ directory after building youki</p>
<pre><code class="language-console">dockerd --experimental --add-runtime="youki=$(pwd)/youki" # run in the youki/scripts directory
</code></pre>
<p>This will start the daemon and hang up the console. You can either start this as a background process to continue using the same terminal, or use another terminal, which will make it easier to stop the docker daemon later.</p>
<p>In case you don't stop the original daemon, you can get an error message after previous command</p>
<pre><code class="language-console">failed to start daemon: pid file found, ensure docker is not running or delete /var/run/docker.pid
</code></pre>
<p>Now that the docker daemon is running, you can use docker normally as you will, but you will be able to specify Youki as its low-level runtime to actually create, start and stop the containers.</p>
<p>You can try running a container such as</p>
<pre><code class="language-console">docker run -it --rm --runtime youki busybox # run a container
</code></pre>
<p>This will start a busybox container, and give access to terminal inside it.</p>
<p>After you are done, you can stop the docker daemon by sending it a signal, either by using <code>Ctrl</code> + <code>C</code> if you are running the process in another terminal, or by using kill command with the pid of it, if you have started it as a background process.</p>
<p>Then to start the original/normal Docker daemon, you can run</p>
<pre><code class="language-console">sudo systemctl start docker
</code></pre>
<h4 id="let-docker-permanently-know-youki-as-a-runtime"><a class="header" href="#let-docker-permanently-know-youki-as-a-runtime">Let docker permanently know youki as a runtime</a></h4>
<p>With newer versions of docker, you can update file <code>/etc/docker/daemon.json</code> to
let docker know youki
(<a href="https://docs.docker.com/engine/reference/commandline/dockerd/#on-linux">source</a>).
You may need to create this file, if it does not yet exist. A sample content of it:</p>
<pre><code class="language-json">{
"default-runtime": "runc",
"runtimes": {
"youki": {
"path": "/path/to/youki/youki",
"runtimeArgs": [
"--debug",
"--systemd-log"
]
}
}
}
</code></pre>
<p>After this (need to restart docker at the first time), you can use youki
with docker: <code>docker run --runtime youki ...</code>. You can verify the runtime includes <code>youki</code>:</p>
<pre><code class="language-console">$ docker info|grep -i runtime
Runtimes: youki runc
Default Runtime: runc
</code></pre>
<h4 id="using-youki-standalone"><a class="header" href="#using-youki-standalone">Using Youki Standalone</a></h4>
<p>Youki can also be used directly, without a higher-level runtime such as Docker to create, start, stop and delete the container, but the process can be tedious. Here we will show how you can do that, to run a simple container with desired program running in it.</p>
<p>Note that we will still be using Docker to generate the rootfs required for running the container.</p>
<p>To start, in the youki/scripts directory, make another directory named tutorial, and create a sub-directory rootfs inside it</p>
<pre><code class="language-console">mkdir -p tutorial/rootfs
</code></pre>
<p>After that, you will need to use docker to create the required directory structure</p>
<pre><code class="language-console">cd tutorial
docker export $(docker create busybox) | tar -C rootfs -xvf -
</code></pre>
<p>This will create the required directory structure for using it as a root directory inside the container.</p>
<p>Now the any container runtime gets the information about the permissions, configurations and constraints for the container process by using a config.json file. Youki has a command which can generate the default config for you. To do this, run</p>
<pre><code class="language-console">../youki spec
</code></pre>
<p>After this, you can manually edit the file to customize the behavior of the container process. For example, to run the desired program inside the container, you can edit the process.args</p>
<pre><code class="language-json">"process": {
...
"args": [
"sleep", "30"
],
...
}
</code></pre>
<p>Here you can change the args to specify the program to be run, and arguments to be given to it.</p>
<p>After this, go back to the youki/ directory</p>
<pre><code class="language-console">cd ..
</code></pre>
<p>As the setup is complete, you can now use youki to create the container, start the container, get its state etc.</p>
<pre><code class="language-console"># create a container with name `tutorial_container`
sudo ./youki create -b tutorial tutorial_container
# you can see the state the container is `created`
sudo ./youki state tutorial_container
# start the container
sudo ./youki start tutorial_container
# will show the list of containers, the container is `running`
sudo ./youki list
# delete the container
sudo ./youki delete tutorial_container
</code></pre>
<p>The example above shows how to run Youki in a 'rootful' way. To run it without root permissions, that is, in rootless mode, few changes are required.</p>
<p>First, after exporting the rootfs from docker, while generating the config, you will need to pass the rootless flag. This will generate the config withe the options needed for rootless operation of the container.</p>
<pre><code class="language-console">../youki spec --rootless
</code></pre>
<p>After this, the steps are basically the same, except you do not need to use sudo while running youki.</p>
<pre><code class="language-console">cd ..
./youki create -b tutorial rootless_container
./youki state rootless_container
./youki start rootless_container
./youki list
./youki delete rootless_container
</code></pre>
<h4 id="log-level"><a class="header" href="#log-level">Log level</a></h4>
<p><code>youki</code> defaults the log level to <code>error</code> in the release build. In the debug
build, the log level defaults to <code>debug</code>. The <code>--log-level</code> flag can be used to
set the log-level. For least amount of log, we recommend using the <code>error</code> log
level. For the most spammy logging, we have a <code>trace</code> level.</p>
<p>For compatibility with <code>runc</code> and <code>crun</code>, we have a <code>--debug</code> flag to set the
log level to <code>debug</code>. This flag is ignored if <code>--log-level</code> is also set.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="crates-provided"><a class="header" href="#crates-provided">Crates provided</a></h1>
<p>Youki repo itself is a Cargo workspace, comprising of several sub-crates, each one for some specific functionality. The youki binary depends on this to provide the low-level functionality, and you can use these crate as a dependency for your own projects as well.</p>
<p>For more information on how to add a sub-crate as a dependency in your project, see <a href="user/./basic_usage.html">Basic Usage</a>.</p>
<p>The subsection in this part briefly explains some of the crates, and some of the functionality they expose. This should be good enough to get a general idea of each crate. To get detailed information about the structs, functions and modules each crate exposes, unfortunately, for the time being, you will need to go through the source itself, but we are working on creating better docs.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="libcgroups"><a class="header" href="#libcgroups">libcgroups</a></h1>
<p>libcgroups is the crate that contains functionality to work with Linux cgroups. This provide an easy to use interface over reading and writing cgroups files, as well as various structs that represent the cgroups data.</p>
<p>The modules that it exposes are :</p>
<ul>
<li>common</li>
<li>stats</li>
<li>systemd</li>
<li>test_manager</li>
<li>v1</li>
<li>v2</li>
</ul>
<p>Following is a short explanation of these modules.</p>
<h3 id="common"><a class="header" href="#common">common</a></h3>
<p>This module contains functionality that is general to any type of cgroup. Some of the things it provides are:</p>
<ul>
<li>
<p>trait <code>CgroupManager</code> which gives and interface for the following:</p>
<ul>
<li>add a task to a cgroup</li>
<li>apply resource restriction</li>
<li>remove a cgroup</li>
<li>control freezer cgroup state</li>
<li>get stats from a cgroup</li>
<li>get pids belonging to the cgroup</li>
</ul>
</li>
<li>
<p>functions <code>write_cgroup_file_str</code> and <code>write_cgroup_file</code> which write data to a cgroup file</p>
</li>
<li>
<p>function <code>read_cgroup_file</code> which reads data from given cgroup file</p>
</li>
<li>
<p>function <code>get_cgroup_setup_with_root</code> which returns setup of cgroups (v1,v2, hybrid) on the system with specified cgroup root path</p>
</li>
<li>
<p>function <code>get_cgroup_setup</code> which returns setup of cgroups (v1,v2, hybrid) on the system with default cgroup root path <code>/sys/fs/cgroup</code></p>
</li>
<li>
<p>function <code>create_cgroup_manager_with_root</code> which returns corresponding cgroup manager on the system with specified cgroup root path, if the passed <code>root_path</code> argument is <code>None</code>, then it's same as function <code>create_cgroup_manager</code></p>
</li>
<li>
<p>function <code>create_cgroup_manager</code> which returns corresponding cgroup manager on the system with default cgroup root path <code>/sys/fs/cgroup</code></p>
</li>
</ul>
<h3 id="stats"><a class="header" href="#stats">stats</a></h3>
<p>This module has functionalities related to statistics data of the cgroups, and structs representing it.</p>
<p>Some of the things it exposes are</p>
<ul>
<li>
<p>struct <code>Stats</code> which contains following structs:</p>
<ul>
<li>
<p><code>CpuStats</code> : contains cpu usage and throttling information</p>
</li>
<li>
<p><code>MemoryStats</code> : contains usage of memory, swap and memory combined, kernel memory, kernel tcp memory and other memory stats</p>
</li>
<li>
<p><code>PidStats</code> : contains current number of active pids and allowed number of pids</p>
</li>
<li>
<p><code>BlkioStats</code> : contains block io related stats, such as number of bytes transferred from/to a device in cgroup, number of io operations done by a device in cgroup, device access and queue information etc.</p>
</li>
<li>
<p><code>HugeTlbStats</code> : containing stats for Huge TLB such as usage, max_usage, and fail count</p>
</li>
</ul>
</li>
<li>
<p>function <code>supported_page_size</code> which returns hugepage size supported by the system</p>
</li>
<li>
<p>utility functions to operate with data in cgroups files such as:</p>
<ul>
<li>
<p><code>parse_single_value</code> : reads file expecting it to have a single value, and returns the value</p>
</li>
<li>
<p><code>parse_flat_keyed_data</code> : parses cgroup file data which is in flat keyed format (key value)</p>
</li>
<li>
<p><code>parse_nested_keyed_data</code> : parses cgroup file data which is in nested keyed format (key list of values)</p>
</li>
<li>
<p><code>parse_device_number</code> : parses major and minor number of device</p>
</li>
</ul>
</li>
</ul>
<h3 id="systemd"><a class="header" href="#systemd">systemd</a></h3>
<p>This is the module used by youki to interact with systemd, and it exposes several functions to interact:</p>
<ul>
<li>
<p>function <code>booted</code> to check if the system was booted with systemd or not</p>
</li>
<li>
<p>module <code>controller_type</code>, which contains enum <code>ControllerType</code> which is used to specify cgroup controllers available on a system</p>
</li>
<li>
<p>module <code>manager</code>, which contains <code>Manager</code> struct, which is the cgroup manager, and contain information such as the root cgroups path, path for the specific cgroups, client to communicate with systemd etc. This also implements <code>CgroupManager</code> trait, and thus can be used for cgroups related operations.</p>
</li>
<li>
<p>module <code>dbus-native</code> is the native implementation for dbus connection, which is used to interact with systemd in rootless mode.</p>
</li>
</ul>
<h3 id="test_manager"><a class="header" href="#test_manager">test_manager</a></h3>
<p>This exposes a <code>TestManager</code> struct which can be used as dummy for cgroup testing purposes, which also implements <code>CgroupManager</code>.</p>
<h3 id="v1-and-v2"><a class="header" href="#v1-and-v2">v1 and v2</a></h3>
<p>These two modules contains functionalities specific to cgroups version 1 and version 2. Both of these expose respective cgroup managers, which can be used to manage that type of cgroup, as well as some utility functions related to respective cgroup version, such as <code>get_mount_points</code> (for v1 and v2), <code>get_subsystem_mount points</code> (for v1), and <code>get_available_controllers</code> (for v2) etc.</p>
<p>The v2 module also exposes devices module, which provides functionality for working with bpf, such as load a bpf program, query info of a bpf program, attach and detach a bpf program to a cgroup, etc.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="libcontainer"><a class="header" href="#libcontainer">libcontainer</a></h1>
<p>This crate provides functionality for creating and managing containers. Youki itself uses this crate to manage and control the containers.</p>
<p>This exposes several modules, each dealing with a specific aspect of working with containers.</p>
<ul>
<li>
<p><code>apparmor</code> : functions that deal with apparmor, which is a Linux Kernel security module to control program capabilities with per program profiles.</p>
</li>
<li>
<p><code>capabilities</code> : this has functions related to setting and resetting specific capabilities, as well as to drop extra privileges from container process.</p>
</li>
<li>
<p><code>config</code> : this exposes <code>YoukiConfig</code> struct, which contains a subset of the data in the <code>config.json</code>. This is the subset that is needed when starting or managing containers after creation, and rather than parsing and passing around whole <code>config.json</code>, the smaller <code>YoukiConfig</code> is passed, which is comparatively faster.</p>
</li>
<li>
<p><code>container</code> : This is the core of the container module, and contains sub-modules and structs that deal with the container lifecycle including creating, starting, stopping and deleting containers.</p>
</li>
<li>
<p><code>hooks</code> : exposes function <code>run_hooks</code>, which is used to run various container lifecycle hooks as specified in oci-spec.</p>
</li>
<li>
<p><code>namespaces</code> : exposes <code>Namespaces</code> struct, which deals with applying namespaces to a container process.</p>
</li>
<li>
<p><code>notify_socket</code> : this contains <code>NotifyListener</code> struct, which is used internally to communicate between the main youki process and the forked container processes.</p>
</li>
<li>
<p><code>process</code> : a module which exposes functions related to forking the process, setting up the namespaces and starting the container process with correct namespaces.</p>
</li>
<li>
<p><code>rootfs</code> : this contains modules which deal with rootfs, which is minimal filesystem that is provided to the container.</p>
</li>
<li>
<p><code>user_ns</code> : this deals with running containers in with new user namespace, usually rootless containers will use this, that is running containers without needing root permissions.</p>
</li>
<li>
<p><code>seccomp</code> : this deals with setting up seccomp for container process. It uses libseccomp crate in order to do that.</p>
</li>
<li>
<p><code>signal</code> : this provides simple wrappers for unix signal, so that parsing them from their names or signal numbers is easier.</p>
</li>
<li>
<p><code>syscall</code> : this provides a trait <code>Syscall</code>, which is used to abstract over several functionalities which need to call libc functions. This allows the other parts of library to use those functions without having to deal with implementation details.</p>
</li>
<li>
<p><code>tty</code> : this deals with setting up the tty for the container process.</p>
</li>
<li>
<p><code>utils</code> : provides various utility functions, such as <code>parse_env</code> to parse the env variables, <code>get_cgroups_path</code>, <code>create_dir_all_with_mode</code> etc.</p>
</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="liboci-cli"><a class="header" href="#liboci-cli">liboci-cli</a></h1>
<p>This module provides the structs for command line arguments for OCI container runtimes as specified in the OCI Runtime Command Line Interface. The exposed structures derive <code>clap::Parser</code>, so that they can be directly used for parsing oci-commandline arguments.</p>
<h3 id="implemented-subcommands"><a class="header" href="#implemented-subcommands">Implemented subcommands</a></h3>
<div class="table-wrapper"><table><thead><tr><th style="text-align: center">Command</th><th style="text-align: center">liboci-cli</th><th style="text-align: center">CLI Specification</th><th style="text-align: center">runc</th><th style="text-align: center">crun</th><th style="text-align: center">youki</th></tr></thead><tbody>
<tr><td style="text-align: center">create</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">start</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">state</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">kill</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">delete</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">checkpoint</td><td style="text-align: center"></td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center"></td></tr>
<tr><td style="text-align: center">events</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">exec</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">list</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">pause</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">ps</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">restore</td><td style="text-align: center"></td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center"></td></tr>
<tr><td style="text-align: center">resume</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">run</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">spec</td><td style="text-align: center">✅</td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center">✅</td></tr>
<tr><td style="text-align: center">update</td><td style="text-align: center"></td><td style="text-align: center"></td><td style="text-align: center">✅</td><td style="text-align: center">✅</td><td style="text-align: center"></td></tr>
</tbody></table>
</div><div style="break-before: page; page-break-before: always;"></div><h1 id="libseccomp"><a class="header" href="#libseccomp">libseccomp</a></h1>
<p>This crate provides Rust FFI bindings to <a href="https://github.com/seccomp/libseccomp">libseccomp</a>. This is adapted from code generated using rust-bindgen from a C header file. This also manually fixes some of the issues that occur as rust-bindgen has some issues when dealing with C function macros.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="webassembly"><a class="header" href="#webassembly">Webassembly</a></h1>
<p>There are 3 things you need to do to run a WebAssembly module with youki.</p>
<ol>
<li>Build youki with wasm-wasmer feature flag enabled</li>
<li>Build a container image with the WebAssembly module</li>
<li>Run the container with youki</li>
</ol>
<h2 id="build-youki-with-wasm-wasmedge-wasm-wasmer-or-wasm-wasmtime-feature-flag-enabled"><a class="header" href="#build-youki-with-wasm-wasmedge-wasm-wasmer-or-wasm-wasmtime-feature-flag-enabled">Build youki with <code>wasm-wasmedge</code>, <code>wasm-wasmer</code>, or <code>wasm-wasmtime</code> feature flag enabled</a></h2>
<ul>
<li>
<p>Run <code>build.sh</code> with <code>-f wasm-wasmedge</code> option.</p>
<pre><code class="language-bash">./scripts/build.sh -o . -r -f wasm-wasmedge
</code></pre>
</li>
<li>
<p>Run <code>build.sh</code> with <code>-f wasm-wasmer</code> option.</p>
<pre><code class="language-bash">./scripts/build.sh -o . -r -f wasm-wasmer
</code></pre>
</li>
<li>
<p>Run <code>build.sh</code> with <code>-f wasm-wasmtime</code> option.</p>
<pre><code class="language-bash">./scripts/build.sh -o . -r -f wasm-wasmtime
</code></pre>
</li>
</ul>
<h2 id="build-a-container-image-with-the-webassembly-module"><a class="header" href="#build-a-container-image-with-the-webassembly-module">Build a container image with the WebAssembly module</a></h2>
<p>If you want to run a webassembly module with youki, your config.json has to include either <strong>runc.oci.handler</strong> or <strong>module.wasm.image/variant=compat"</strong>.</p>
<p>It also needs to specify a valid .wasm (webassembly binary) or .wat (webassembly test) module as entrypoint for the container. If a wat module is specified it will be compiled to a wasm module by youki before it is executed. The module also needs to be available in the root filesystem of the container obviously.</p>
<pre><code class="language-json">"ociVersion": "1.0.2-dev",
"annotations": {
"run.oci.handler": "wasm"
},
"process": {
"args": [
"hello.wasm",
"hello",
"world"
],
...
}
...
</code></pre>
<h3 id="compile-a-sample-wasm-module"><a class="header" href="#compile-a-sample-wasm-module">Compile a sample wasm module</a></h3>
<p>A simple wasm module can be created by running</p>
<pre><code class="language-console">rustup target add wasm32-wasi
cargo new wasm-module --bin
cd ./wasm-module
vi src/main.rs
</code></pre>
<pre><pre class="playground"><code class="language-rust">fn main() {
println!("Printing args");
for arg in std::env::args().skip(1) {
println!("{}", arg);
}
println!("Printing envs");
for envs in std::env::vars() {
println!("{:?}", envs);
}
}</code></pre></pre>
<p>Then compile the program to WASI.</p>
<pre><code class="language-console">cargo build --target wasm32-wasi
</code></pre>
<h3 id="build-a-container-image-with-the-module"><a class="header" href="#build-a-container-image-with-the-module">Build a container image with the module</a></h3>
<p>Create a Dockerfile.</p>
<pre><code class="language-console">vi Dockerfile
</code></pre>
<pre><code class="language-Dockerfile">FROM scratch
COPY target/wasm32-wasi/debug/wasm-module.wasm /
ENTRYPOINT ["wasm-module.wasm"]
</code></pre>
<p>Then build a container image with <code>module.wasm.image/variant=compat</code> annotation. <sup class="footnote-reference"><a href="#1">1</a></sup></p>
<pre><code class="language-console">sudo buildah build --annotation "module.wasm.image/variant=compat" -t wasm-module .
</code></pre>
<h2 id="run-the-wasm-module-with-youki-and-podman"><a class="header" href="#run-the-wasm-module-with-youki-and-podman">Run the wasm module with youki and podman</a></h2>
<p>Run podman with youki as runtime. <sup class="footnote-reference"><a href="#1">1</a></sup></p>
<pre><code class="language-bash">sudo podman --runtime /PATH/WHARE/YOU/BUILT/WITH/WASM-WASMER/youki run localhost/wasm-module 1 2 3
</code></pre>
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
<p>You might need <code>sudo</code> because of <a href="https://github.com/containers/youki/issues/719">#719</a>.</p>
</div>
<div style="break-before: page; page-break-before: always;"></div><h1 id="developer-documentation"><a class="header" href="#developer-documentation">Developer Documentation</a></h1>
<p>This section of the documentation is more oriented towards those who wish to contribute to youki, and contains more information and resources about the working and implementation of it. So if you are thinking of helping, this is a great place to start with.</p>
<p>Also, Thank you! If you have any issues or doubts, you can ask them on youki's discord server <a href="https://discord.gg/h7R3HgWUct">here</a>.</p>
<p>This section is split into following parts</p>
<ul>
<li>
<p>Basics : This contains general resources and information that you wold need to work with any parts of youki.</p>
</li>
<li>
<p>Unwritten Rules : This is the part to make them written! This will contain conventions and rules that were discussed and decided in PRs or just commonly followed when developing.</p>
</li>
<li>
<p>Good Places to Start : This section will contain some suggestions about the areas that will be a good place to start for new contributors.</p>
</li>
<li>
<p>Crate specific Information : This is split into one sections for each crate, and will contains information and resources specific for that crate.</p>
</li>
</ul>
<p>Happy Contributing!</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="basics"><a class="header" href="#basics">Basics</a></h1>
<p>This section has the general information and resources needed to work with any part of youki. As youki is written in Rust, you should know some basic Rust before. If you don't yet, some good resources for that can be found on the Rust's <a href="https://www.rust-lang.org/learn">official site</a>.</p>
<h2 id="youki-1"><a class="header" href="#youki-1">Youki</a></h2>
<p>Youki is a low level container runtime, which deals with the creation and management of Linux containers. Some of other such low-level runtimes are <a href="https://github.com/opencontainers/runc">runc</a> and <a href="https://github.com/containers/crun">crun</a>. These are usually used by a higher-level runtime such as Docker or Podman to actually create and manage containers, where the higher level runtime provides a much easier interface for users.</p>
<p>Before you start working on developing youki, you should go through <a href="developer/../user/introduction.html">the User documentation</a> as it specifies the requirements and setup for running youki. For developing youki, you will need to install the dependencies and clone the repo, as specified in the <a href="developer/../user/basic_setup.html">Basic Setup</a> and <a href="developer/../user/basic_usage.html">Basic Usage</a> sections.</p>
<h2 id="testing-while-developing"><a class="header" href="#testing-while-developing">Testing while developing</a></h2>
<p>While developing youki, you might need to compile and test the code from time to time, to make sure it is working and and something is not accidentally broken. Currently there are two ways to verify that:</p>
<ul>
<li>Unit tests, which test individual components of youki</li>
<li>Integration tests, which test the complete functionality of youki commands from start to end.</li>
</ul>
<p>As the steps to run these tests can be a bit tedious, a makefile in project the root provides an easy way to run these quickly. The makefile currently states three individual test :</p>
<ul>
<li>test: The unit tests</li>
<li>oci-integration-test: The integration tests provided by OCI, these are the current standard to make sure youki is OCI compliant.</li>
<li>integration-test: This is the Rust port of the OCI runtime tests, as there are some issues in the OCI tests. See <a href="developer/./integration_test.html">integration_test</a> page.</li>
</ul>
<p>All three can be run by using <code>make test-all</code>, or you can run the individual command to run specific tests.</p>
<h2 id="resources"><a class="header" href="#resources">Resources</a></h2>
<h4 id="oci"><a class="header" href="#oci">OCI</a></h4>
<p>Open containers initiative is project, which provides a standardization and standardized specification for operating-system-level virtualization. That way components that confirm to the specification provided by OCI spec, can interoperate with each other easily, and developing of new applications becomes easier. For example youki can be used inplace of runc in Docker, as all three : Docker, runc and youki are OCI compliant, and have a standard interface.</p>
<p>Their main GitHub page is at <a href="https://github.com/opencontainers">https://github.com/opencontainers</a>, and more information about the runtime specifications can be found at <a href="https://github.com/opencontainers/runtime-spec/blob/master/runtime.md">https://github.com/opencontainers/runtime-spec/blob/master/runtime.md</a>.</p>
<p>As youki needs to deal with a lot of low level programming interfaces of Linux Kernel, another good place know is the online man pages project, which can be found at <a href="https://man7.org/">https://man7.org/</a>. Man pages provide detailed information about the programming interfaces of various features of Linux Kernel. You can simply search <code>man &lt;feature-name&gt;</code> using a search engine, or you can search at the site itself, at <a href="https://man7.org/linux/man-pages/index.html">https://man7.org/linux/man-pages/index.html</a>. These can be very helpful to know about the behavior and usage of and reasoning behind various kernel features used throughout youki.</p>
<p>Happy developing!!!</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="unwritten-rule"><a class="header" href="#unwritten-rule">Unwritten Rule</a></h1>
<p>This is the place to write down rules or conventions that were discussed in PRs, so that newcomers can easily find them, without having to go through the PR history. So if you decide on any convention to follow for the project, please make sure to add them here.</p>
<h2 id="conventions-to-follow"><a class="header" href="#conventions-to-follow">Conventions to follow</a></h2>
<h4 id="errors"><a class="header" href="#errors">Errors</a></h4>
<p>Youki currently uses <a href="https://www.crates.io/crates/anyhow">anyhow</a> library to deal with errors occurring during its execution. So wherever you use fallible actions, or functions that can return <code>Result</code>, make sure you attach enough information with the errors so that error logs can be useful for debugging later. For example, if you are reading a file, or parsing something and the operation does not succeed, you can add the path from which you attempted to read the file, or the string that you attempted to parse.</p>
<p>Also for the error messages, we follow the convention all small-case letters and no period at the end, as discussed in <a href="https://github.com/containers/youki/issues/313">this PR</a>. Whenever you write error messages, please follow this convention to keep them uniform.</p>
<h4 id="logs"><a class="header" href="#logs">Logs</a></h4>
<p>youki uses <a href="https://crates.io/crates/log">log</a> crate to log information while running. Whenever adding code to interact with system or kernel features or such, make sure to add debug logs so that if youki crashes, you can trace the errors and zero-in on the reasons using logs.</p>
<h4 id="comments"><a class="header" href="#comments">Comments</a></h4>
<p>Make sure that you comment copiously, and explain the peculiar behavior of your code so that others can understand why certain code is written the way it is. Also make sure to add doc comments and examples for <code>pub</code> items in the crates, so that users can find it from the docs generated by <code>cargo doc</code>.</p>
<h4 id="scripts"><a class="header" href="#scripts">Scripts</a></h4>
<p>In any script, any makefile etc, make sure to <code>set -e</code> at the start. This will abort the script after any command fails, rather than continuing with incorrect state and creating knock-on errors.</p>
<h4 id="update-this-documentation"><a class="header" href="#update-this-documentation">Update This Documentation</a></h4>
<p>Keep this Documentation updated! Make sure you add any relevant doc-links and resources to this that you found helpful or contains background information required to understand the code, so that it can help newcomers as well as others to find the resources in one single place.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="good-places-to-start"><a class="header" href="#good-places-to-start">Good places to start</a></h1>
<p>First of all, welcome to youki! Hope you have fun while developing and contributing :)</p>
<p>This lists some of the known places that are long-running and would be useful for beginners. But as the things under development can change any time, the best place to check are the issues on the <a href="https://github.com/containers/youki/issues">GitHub repo</a>. You can find issues with labels <code>good fist issue</code> or <code>help wanted</code> and start working on them.</p>
<p>You can also search for <code>TODO</code> or <code>FIXME</code> comments in the source, and try working on them, but not all of them are easy places to start, and some of them can be particularly tricky to fix.</p>
<hr />
<p>This lists known parts of youki that can be good for beginners at the time of the writing. Please update as things change.</p>
<h4 id="documentation-comments"><a class="header" href="#documentation-comments">Documentation Comments</a></h4>
<p>Currently youki is decently commented, and those explain most of the public facing API and structs. But there are still places which can use more doc comments, and examples showing usage, so people can use the docs generated by <code>cargo doc</code> as a guide.</p>
<p>If you don't know much about container runtime or low level system working, then this can be a good place to start. While going through the code and documenting it, you can learn about it. Make sure that you update this documentation with useful links that you found while commenting some code if it has some peculiar behavior, or it is hard to understand without knowing some background.</p>
<h4 id="integration-tests"><a class="header" href="#integration-tests">Integration Tests</a></h4>
<p>You can find more detailed information about this in the <code>integration_test</code> crate, but in brief, we currently use <a href="https://github.com/opencontainers/runtime-tools">OCI-runtime-tools</a> provided integration tests to validate that youki is OCI spec compliant. But those are written in Go, which makes the developer depend on two language env to compile youki and test it. These tests also have some issues which makes them hard to use on some system setups.</p>
<p>Thus we are porting those test to Rust, so that it can be a Rust implementation of OCI-runtime integration tests, as well as be easy to run on local systems for testing. If you know Go and Rust this can be a great place to start. Check out the <a href="https://github.com/containers/youki/issues/361">tracking issue</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="this-documentation"><a class="header" href="#this-documentation">This Documentation</a></h1>
<p>This documentation is created using mdbook and aims to provide a concise reference for users and developers of youki. For more information on mdbook itself, you can check out the <a href="https://rust-lang.github.io/mdBook/">mdbook documentation</a>.</p>
<p>Please make sure that you update this documentation along with newly added features and resources that you found helpful while developing, so that it will be helpful for newcomers.</p>
<p>Currently this documentation is hosted at <a href="https://containers.github.io/youki/">https://containers.github.io/youki/</a>, using GitHub pages. GitHub CI actions are used to automatically check if any files are changed in /docs on each push / PR merge to main branch, and if there are any changes, the mdbook is build and deployed to gh-pages. We use <a href="https://github.com/peaceiris/actions-mdbook">https://github.com/peaceiris/actions-mdbook</a> to build and then <a href="https://github.com/peaceiris/actions-gh-pages">https://github.com/peaceiris/actions-gh-pages</a> GitHub action to deploy the mdbook.</p>
<p>When testing locally you can manually test the changes by running <code>mdbook serve</code> in the docs directory (after installing mdbook), which will temporarily serve the mdbook at <code>localhost:3000</code> by default. You can check the mdbook documentation for more information.</p>
<p>If you want to test it using gh-pages on your own fork, you can use following steps in the docs directory.</p>
<pre><code class="language-console">git worktree prune
# Do this if you are running this command first time after booting,
# As after shutdown /tmp files are removed
git branch -D gh-pages &amp;&amp; git worktree add /tmp/book -b gh-pages
mdbook build
rm -rf /tmp/book/* # this won't delete the .git directory
cp -rp book/* /tmp/book/
cd /tmp/book
git add -A
git commit 'new book message'
git push -f origin gh-pages
cd -
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="repository-structure"><a class="header" href="#repository-structure">Repository Structure</a></h1>
<p>This page might be the one that gets most easily outdated, as the structure might change at any time! Thus make sure to update this whenever there are any changes in the overall structure of the whole repo. For the same reason, this does not list the structure in detail but instead describes only the main directories.</p>
<h3 id="github"><a class="header" href="#github">.github</a></h3>
<p>Contains workflows and files needed by those workflows.</p>
<h3 id="crates"><a class="header" href="#crates">crates</a></h3>
<p>This is the core of youki. This contains various libraries that are developed alongside of youki and the youki binary itself.</p>
<h3 id="docs"><a class="header" href="#docs">docs</a></h3>
<p>The directory where the source of this documentation resides. The source is also divided into two parts, for developers and users. Please see <a href="developer/./documentation_mdbook.html">Documentation documentation</a> for more information.</p>
<h3 id="hack"><a class="header" href="#hack">hack</a></h3>
<p>As the name suggests, contains hack scripts for patching some issues which are currently not solvable in a straightforward way or solving issues for which we have no idea of why they occur.</p>
<h3 id="scripts-1"><a class="header" href="#scripts-1">Scripts</a></h3>
<p>Contains scripts for various purposes, such as building youki, running integration tests etc. These might be small scripts called from many other scripts, big scripts that perform a complex task or helper scripts for the main makefile.</p>
<h3 id="tests"><a class="header" href="#tests">tests</a></h3>
<p>This contains all the integration tests for validating youki. Note that these are integration tests for start-to-end testing of youki commands. Unit tests for individual parts are in their respective source files in crates.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="debugging"><a class="header" href="#debugging">Debugging</a></h1>
<p>Since Youki uses pipe and double-fork in the creating phase, it is hard to debug what happened.
You might encounter the error message, "Broken pipe ..." Unfortunately,
this error message has only information that a child process exists with an error for some reason.</p>
<p>This section will give some tips to debug youki to know what happens in the child processes.</p>
<h1 id="bpftrace"><a class="header" href="#bpftrace">bpftrace</a></h1>
<p><a href="https://github.com/iovisor/bpftrace">bpftrace</a> is an eBPF based tool.
In the case of youki, you can catch the system calls youki issued.</p>
<p>For example, if you catch write system calls, you can see the log output until the middle of process.
It allows you to do something similar to print debugging.</p>
<p><em>How to debug</em></p>
<ol>
<li>
<p>You need to install bpftrace, please refer to <a href="https://github.com/iovisor/bpftrace/blob/master/INSTALL.md">the official documentation</a> to know how to install it.</p>
</li>
<li>
<p>Before running the process or comannd you want to debug, run the following command in another terminal.</p>
<p>You need the root privilege to run it.</p>
<pre><code class="language-console">$ cd ${youki_repo}
$ just hack-bpftrace
</code></pre>
</li>
<li>
<p>Run the command you want to debug.</p>
</li>
</ol>
<p><em>For example</em></p>
<ol>
<li>
<p>Run the bpftrace script.</p>
<pre><code class="language-console">$ just hack-bpftrace
BPFTRACE_STRLEN=120 ./hack/debug.bt
Attaching 13 probes...
Tracing Youki syscalls... Hit Ctrl-C to end.
TIME COMMAND PID EVENT CONTENT
</code></pre>
</li>
<li>
<p>Run the Kubernetes cluster using kind with youki</p>
<pre><code class="language-console">$ cd ${youki_repo}
$ just test-kind
docker buildx build --output=bin/ -f tests/k8s/Dockerfile --target kind-bin .
...
Creating cluster "youki" ...
...
kubectl --context=kind-youki apply -f tests/k8s/deploy.yaml
runtimeclass.node.k8s.io/youki created
deployment.apps/nginx-deployment created
...
kubectl --context=kind-youki delete -f tests/k8s/deploy.yaml
runtimeclass.node.k8s.io "youki" deleted
deployment.apps "nginx-deployment" deleted
</code></pre>
</li>
<li>
<p>Returning to the first command executed, the system calls youki issued are caught and logged.</p>
<pre><code class="language-console">$ just hack-bpftrace
BPFTRACE_STRLEN=120 ./hack/debug.bt
Attaching 13 probes...
Tracing Youki syscalls... Hit Ctrl-C to end.
TIME COMMAND PID EVENT CONTENT
207033348942 youki 13743 open errno=2, fd=-1, file=/opt/containerd/lib/glibc-hwcaps/x86-64-v3/libc.so.6
...
207035462044 youki 13743 open errno=0, fd=3, file=/proc/self/exe
207035478523 youki 13743 write fd=4, ELF
207066996623 4 13743 open errno=2, fd=-1, file=/opt/containerd/lib/glibc-hwcaps/x86-64-v3/libc.so.6
...
207070130175 4 13743 clone3
207070418829 youki:[1:INTER] 13747 write fd=4, {"timestamp":"2023-09-24T10:47:07.427846Z","level":"INFO","message":"cgroup manager V2 will be used","target":"libcgrou
...
207084948440 youki:[1:INTER] 13747 clone3
207085058811 youki:[1:INTER] 13747 write fd=4, {"timestamp":"2023-09-24T10:47:07.442502Z","level":"DEBUG","message":"sending init pid (Pid(1305))","target":"libcontai
207085343170 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.442746Z","level":"DEBUG","message":"unshare or setns: LinuxNamespace { typ: Uts, path
...
207088256843 youki:[2:INIT] 13750 pivt_root new_root=/run/containerd/io.containerd.runtime.v2.task/k8s.io/0fea8cf5f8d1619a35ca67fd6fa73d8d7c8fc70ac2ed43ee2ac2f8610bb938f6/r, put_old=/run/containerd/io.containerd.runtime.v2.task/k8s.io/0fea8cf5f8d1619a35ca67fd6fa73d8d7c8fc70ac2ed43ee2ac2f8610bb938f6/r
...
207097207551 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.454645Z","level":"DEBUG","message":"found executable in executor","executable":"\"/pa
...
207139391811 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.496815Z","level":"DEBUG","message":"received: start container","target":"libcontainer
207139423243 youki:[2:INIT] 13750 write fd=4, {"timestamp":"2023-09-24T10:47:07.496868Z","level":"DEBUG","message":"executing workload with default handler","target"
</code></pre>
</li>
</ol>
<div style="break-before: page; page-break-before: always;"></div><h1 id="crate-specific-information"><a class="header" href="#crate-specific-information">Crate Specific Information</a></h1>
<p>This section contains subsections for each individual crate in the youki workspace. Each of the subsection will have information and resources on that particular crate.</p>
<p>In case you are working with some specific crate, you can find resources about it in its section. Also make sure you add any resources that you find when working on them as well.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="libcgroups-1"><a class="header" href="#libcgroups-1">libcgroups</a></h1>
<p>This crate provides an interface for working with cgroups in Linux. cgroups or control groups is a Linux kernel feature which can be used to fine-control resources and permissions given to a particular process or a group of processes. You can read more about them on the <a href="https://man7.org/linux/man-pages/man7/cgroups.7.html">cgroups man page</a>.</p>
<p>The initial version of cgroups is called the version 1 was implemented in kernel 2.6.24, and later in kernel version 4.5, a new version of cgroups was released, aimed to solve issues with v1, the version v2.</p>
<p>This crates exposes several functions and modules that can be used to work with cgroups :</p>
<ul>
<li>
<p>Common traits and functions which are used by both v1 and v2 such as</p>
<ul>
<li>Trait <code>CgroupManager</code>, this abstracts over the underlying implementation of interacting with specific version of cgroups, and gives functions to add certain process to a certain cgroup, apply resource restrictions, get statistics of a cgroups, freeze a cgroup, remove a cgroup or get list of all processes belonging to a cgroup. v1 and v2 modules both contain a version specific cgroup manager which implements this trait, and thus either can be given to functions or structs which expects a cgroup manager, depending on which cgroups the host system uses.</li>
<li>Apart from the trait, this also contains functions which help with reading cgroups files, and write data to a cgroup file, which are used throughout this crate.</li>
<li>Functions to detect which cgroup setup (v1, v2 or hybrid) is on the host system with/without specified mounted cgroup root path, as well as functions to get the corresponding cgroups manager w/o cgroup root path.</li>
</ul>
</li>
<li>
<p>Functions and structs to get and store the statistics of a cgroups such as</p>
<ul>
<li>CPU stats including usage and throttling</li>
<li>Memory stats including usage of normal and swap memory, usage of kernel memory, page cache in bytes etc</li>
<li>Pid stat including current active pids and maximum allowed pids</li>
<li>Block IO stats such as number of bytest transferred to/from a device in the cgroup, io operations performed by a device in the cgroup, amount of time cgroup had access to a device etc</li>
<li>Huge TLB stats such as usage and maximum usage etc.</li>
<li>Function to get pid stats</li>
<li>Function to get supported hugepage size</li>
<li>Function to parse flat keyed data and nested keyed data that can be in a cgroups file</li>
<li>Parse a device number</li>
</ul>
</li>
<li>
<p>Cgroups V1 module which deal with implementing a cgroup manager for systems which have cgroups v1 or hybrid cgroups</p>
</li>
<li>
<p>Cgroups V2 module which deal with implementing a cgroup manager for systems which have cgroups v2</p>
</li>
</ul>
<p>As youki currently depends on systemd as an init system, this crate also exposes module systemd, which provides interface for working with systemd related operations. <a href="https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html">systemd resource control</a> is a good place to read more about systemd and its involvement in resource control.</p>
<h2 id="dbus-native"><a class="header" href="#dbus-native">Dbus Native</a></h2>
<p>This module is the native implementation of dbus connection functionality used for connecting with systemd via dbus. Refer to <a href="https://github.com/containers/youki/issues/2208">this issue discussion</a> following for the discussion regarding moving away from existing dbus-interfacing library.</p>
<p>Note that this implements the minimal required functionality for youki to use dbus, and thus does not have all the dbus features.</p>
<ul>
<li>
<p>Refer to see <a href="https://dbus.freedesktop.org/doc/dbus-specification.html">dbus specification</a> and <a href="https://dbus.freedesktop.org/doc/api/html/structDBusHeader.html">header format</a> for the individual specifications.</p>
</li>
<li>
<p>For systemd interface and types, you can generate the following file and take help from the auto-generated functions
<code>dbus-codegen-rust -s -g -m None -d org.freedesktop.systemd1 -p /org/freedesktop/systemd1</code>, see https://github.com/diwic/dbus-rs</p>
</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="libcontainer-1"><a class="header" href="#libcontainer-1">libcontainer</a></h1>
<p>This crate is one of the core crates of the youki workspace, and has functions and structs that deal with the actual craetion and management of the container processes.</p>
<p>Remember, in the end, a container is just another process in Linux, which has control groups, namespaces, pivot_root and other mechanisms applied to it. The program executing has the impression that is is running on a complete system, but from the host system's perspective, it is just another process, and has attributes such as pid, file descriptors, etc. associated with it like any other process.</p>
<p>Along with the container related functions, this crate also provides Youki Config, a subset of the OCI spec config. This config contains only the essential data required for running the containers, and due to its smaller size, parsing it and passing it around is more efficient than the complete OCI spec config struct.</p>
<p>Other than that, this crate also provides a wrapper over basic Linux sockets, which are then used internally as well as by youki to communicate between the main youki process and the forked container process as well as the intermediate process.</p>
<p>This crate also provides an interface for Apparmor which is another Linux Kernel module allowing to apply security profiles on a per-program basis. More information about it can be found at <a href="https://apparmor.net/">https://apparmor.net/</a>.</p>
<h3 id="notes"><a class="header" href="#notes">Notes</a></h3>
<h4 id="some-other-modules-expose-by-this-crate-are"><a class="header" href="#some-other-modules-expose-by-this-crate-are">Some other modules expose by this crate are</a></h4>
<ul>
<li>rootfs, which is a ramfs like simple filesystem used by kernel during initialization</li>
<li>hooks, which allow running of specified program at certain points in the container lifecycle, such as before and after creation, start etc.</li>
<li>signals, which provide a wrapper to convert to and from signal numbers and text representation of signal names</li>
<li>capabilities, which has functions related to set and reset specific capabilities, as well as to drop extra privileges
<ul>
<li><a href="https://blog.container-solutions.com/linux-capabilities-in-practice">Simple explanation of capabilities</a></li>
<li><a href="https://man7.org/linux/man-pages/man7/capabilities.7.html">man page for capabilities</a></li>
</ul>
</li>
<li>tty module which daels with providing terminal interface to the container process
<ul>
<li><a href="https://man7.org/linux/man-pages/man7/pty.7.html">pseudoterminal man page</a> : Information about the pseudoterminal system, useful to understand console_socket parameter in create subcommand</li>
</ul>
</li>
</ul>
<h4 id="executor"><a class="header" href="#executor">Executor</a></h4>
<p>By default and traditionally, the executor forks and execs into the binary
command that specified in the oci spec. Using executors, we can override this
behavior. For example, <code>youki</code> uses executor to implement running wasm
workloads. Instead of running the command specified in the process section of
the OCI spec, the wasm related executors can choose to execute wasm code
instead. The executor will run at the end of the container init process.</p>
<p>The API accepts only a single executor, so when using multiple executors, (try
wasm first, then defaults to running a binary), the users should compose
multiple executors into a single executor. The executor will return an error
when the executor can't handle the workload.</p>
<h4 id="namespaces--namespaces-provide-isolation-of-resources-such-as-filesystem-process-ids-networks-etc-on-kernel-level-this-module-contains-structs-and-functions-related-to-applying-or-un-applying-namespaces-to-the-calling-process"><a class="header" href="#namespaces--namespaces-provide-isolation-of-resources-such-as-filesystem-process-ids-networks-etc-on-kernel-level-this-module-contains-structs-and-functions-related-to-applying-or-un-applying-namespaces-to-the-calling-process">Namespaces : namespaces provide isolation of resources such as filesystem, process ids networks etc on kernel level. This module contains structs and functions related to applying or un-applying namespaces to the calling process</a></h4>
<ul>
<li><a href="https://man7.org/linux/man-pages/man7/pid_namespaces.7.html">pid namespace man page</a></li>
<li><a href="https://man7.org/linux/man-pages/man2/clone.2.html">CLONE_NEWUSER flag</a></li>
</ul>
<blockquote>
<p>Note: clone(2) offers us the ability to enter into user and pid namespace by creating only one process. However, clone(2) can only create new pid namespace, but cannot enter into existing pid namespaces. Therefore, to enter into existing pid namespaces, we would need to fork twice. Currently, there is no getting around this limitation.</p>
</blockquote>
<ul>
<li><a href="https://man7.org/linux/man-pages/man2/fork.2.html">fork(2) man page</a></li>
<li><a href="https://man7.org/linux/man-pages/man2/clone.2.html">clone(2) man page</a></li>
</ul>
<h4 id="pausing-and-resuming"><a class="header" href="#pausing-and-resuming">Pausing and resuming</a></h4>
<p>Pausing a container indicates suspending all processes in it. This can be done with signals SIGSTOP and SIGCONT, but these can be intercepted. Using cgroups to suspend and resume processes without letting tasks know.</p>
<ul>
<li><a href="https://man7.org/linux/man-pages/man7/cgroups.7.html">cgroups man page</a></li>
<li><a href="https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt">freezer cgroup kernel documentation</a></li>
</ul>
<h4 id="the-following-are-some-resources-that-can-help-understand-with-various-linux-features-used-in-the-code-of-this-crate"><a class="header" href="#the-following-are-some-resources-that-can-help-understand-with-various-linux-features-used-in-the-code-of-this-crate">The following are some resources that can help understand with various Linux features used in the code of this crate</a></h4>
<ul>
<li><a href="https://dev.to/rrampage/surviving-the-linux-oom-killer-2ki9">oom-score-adj</a></li>
<li><a href="https://man7.org/linux/man-pages/man1/unshare.1.html">unshare man page</a></li>
<li><a href="https://man7.org/linux/man-pages/man7/user_namespaces.7.html">user-namespace man page</a></li>
<li><a href="https://man7.org/linux/man-pages/man3/wait.3p.html">wait man page</a></li>
<li><a href="https://man7.org/linux/man-pages/man2/pipe.2.html">pipe2 man page</a> : Definition and usage of pipe2</li>
<li><a href="https://man7.org/linux/man-pages/man7/unix.7.html">Unix Sockets man page</a> : Useful to understand sockets</li>
<li><a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl man page</a> : Process control man pages</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="liboci-cli-1"><a class="header" href="#liboci-cli-1">liboci-cli</a></h1>
<p>This crate was separated from original youki crate, and now contains a standalone implementation of structs needed for parsing commandline arguments for OCI-spec compliant runtime commandline interface. This is in turn used by youki to parse the command line arguments passed to it, but can be used in any other projects where there is need to parse OCI spec based commandline arguments.</p>
<p>This primarily uses the <a href="https://docs.rs/clap/latest/clap/index.html">crate clap-v3</a> for parsing the actual commandline arguments given to the runtime.</p>
<p>You can refer to <a href="https://github.com/opencontainers/runtime-tools/blob/master/docs/command-line-interface.md">OCI Commandline interface guide</a> to know more about the exact specification.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="libseccomp-1"><a class="header" href="#libseccomp-1">libseccomp</a></h1>
<p>Seccomp is a linux kernel feature that allows a process a one-way transition into secure mode, where restrictions are applied to the syscalls the process can make, as well as restrictions on the file descriptors. Specifically, it can exit, sigreturn and read/write already open file descriptors. This way the process can be isolated and restricted on how it interacts with rest of the system on a kernel level.</p>
<p>This crate does not actually implement any particular feature, but provides Rust FFI bindings for seccomp module. These are primarily generated by using rsut-bindgen on seccomp C header file, and then manually fixed where any issues were found. More information about seccomp can be found in its <a href="https://man7.org/linux/man-pages/man2/seccomp.2.html">man page</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="youki-2"><a class="header" href="#youki-2">youki</a></h1>
<p>This is the core crate that contains the youki binary itself. This provides the user interface, as well as binds the other crates together to actually perform the work of creation and management of containers. Thus, this provides implementation of all the commands supported by youki.</p>
<p>The simple control flow of youki can be explained as :</p>
<p align="center">
<img src="developer/../assets/control_flow.drawio.svg">
</p>
<p>When given the create command, Youki will load the specification, configuration, sockets etc., and use clone syscall to create an intermediate process. This process will set the cgroups and capabilities, and then fork to the init process. Reason to create this intermediate process is that the clone syscall cannot enter into existing pid namespace that has been created for the container. Thus first we need to make a transition to that namespace in the intermediate process and fork that to the container process. After that the main youki process is requested the uid and gid mappings, and after receiving them the intermediate process sets these mapping, fork the init process and return pid of this init process to the main youki process before exiting.</p>
<p>The init process then transition completely into the new namespace setup for the container (the init process only transitions the pid namespace). It changes the root mountpoint for the process using <a href="https://man7.org/linux/man-pages/man2/pivot_root.2.html">pivot_root</a>, so that the container process can get impression that it has a complete root path access. After that the init process sets up the capabilities and seccomp, and sends the seccomp notify fd to the main youki process. When the seccomp agent running on the host system sets up the seccomp profile, it notifies the init process, after which it can execute the programto be executed inside the container. Thus the init process then sends ready notification to the main youki process, and waits for the start signal.</p>
<p>The main youki process which started creating the container, when receives the ready signals update the pid file of the container process and exits. This concludes the creation of the container.</p>
<p>To start the container, when youki start it executed along with the container id, start signal is sent to the waiting container init process, and the the youki process exists.</p>
<p>When the init process receives the start signal, it execs the program to be run in the container, and then exits.</p>
<h3 id="notes-1"><a class="header" href="#notes-1">Notes</a></h3>
<p>The main youki process will set up pipes used as message passing and synchronization mechanism with the init process. The reason youki needs to create/fork two process instead of one is due to the user and pid namespaces. In rootless container, we need to first enter user namespace, since all other namespaces requires CAP_SYSADMIN. When unshare or set_ns into pid namespace, only the children of the current process will enter into a different pid namespace. As a result, we must first fork a process to enter into user namespace, call unshare or set_ns for pid namespace, then fork again to enter into the correct pid namespace.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="e2e-tests"><a class="header" href="#e2e-tests">e2e tests</a></h1>
<p>There are various e2e tests:</p>
<ul>
<li>
<p><a href="developer/e2e/./rust_oci_test.html">rust oci tests</a></p>
<p>This is youki's original integration to verify the behavior of the low-level container runtime.</p>
</li>
<li>
<p><a href="developer/e2e/./containerd_integration_test_using_youki.html">containerd integration test</a></p>
<p>This is the method that containerd's integration test runs with youki.</p>
</li>
<li>
<p><a href="developer/e2e/./runtime_tools.html">runtime tools</a></p>
<p>This is the method to run the runtime tools that OCI manages as a test tool to verify meeting the OCI Runtime Spec</p>
</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="contest"><a class="header" href="#contest">Contest</a></h1>
<p>This is youki's original integration to verify the behavior of the low-level container runtime.</p>
<p><img src="developer/e2e/../../assets/rust_oci_tests.png" alt="Overview" /></p>
<h1 id="how-to-run"><a class="header" href="#how-to-run">How to run</a></h1>
<pre><code class="language-console">just test-contest
</code></pre>
<h1 id="how-to-write"><a class="header" href="#how-to-write">How to write</a></h1>
<p>We will not go into detail here, but will explain how to write and add a new test case based on an example test.</p>
<details>
<summary>Fully the code of the example test</summary>
<p>
<pre><code class="language-rust no_run noplayground">use anyhow::{Context, Result};
use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder};
use test_framework::{test_result, Test, TestGroup, TestResult};
use crate::utils::test_inside_container;
////////// ANCHOR: get_example_spec
fn create_spec() -&gt; Result&lt;Spec&gt; {
SpecBuilder::default()
.process(
ProcessBuilder::default()
.args(
["runtimetest", "hello_world"]
.iter()
.map(|s| s.to_string())
.collect::&lt;Vec&lt;String&gt;&gt;(),
)
.build()?,
)
.build()
.context("failed to create spec")
}
////////// ANCHOR_END: get_example_spec
////////// ANCHOR: example_test
fn example_test() -&gt; TestResult {
let spec = test_result!(create_spec());
test_inside_container(spec, &amp;|_| Ok(()))
}
////////// ANCHOR_END: example_test
////////// ANCHOR: get_example_test
pub fn get_example_test() -&gt; TestGroup {
let mut test_group = TestGroup::new("example");
let test1 = Test::new("hello world", Box::new(example_test));
test_group.add(vec![Box::new(test1)]);
test_group
}
////////// ANCHOR_END: get_example_test</code></pre>
</p>
</details>
<ol>
<li>
<p>Build the OCI Runtime Spec you want to verify</p>
<p>This testing framework automatically places <a href="developer/e2e/./runtimetest.html">runtimetest</a> in the container.
In other words, you can test the processes you want to execute within a container by writing them in runtimetest.
Therefore, it is common practice here to write an OCI Runtime Spec that executes <code>runtimetest</code>.</p>
</li>
</ol>
<pre><code class="language-rust no_run noplayground">fn create_spec() -&gt; Result&lt;Spec&gt; {
SpecBuilder::default()
.process(
ProcessBuilder::default()
.args(
["runtimetest", "hello_world"]
.iter()
.map(|s| s.to_string())
.collect::&lt;Vec&lt;String&gt;&gt;(),
)
.build()?,
)
.build()
.context("failed to create spec")
}</code></pre>
<ol start="2">
<li>Prepare a function that returns a <code>TestResult</code>, which represents the result of the test.</li>
</ol>
<pre><code class="language-rust no_run noplayground">fn example_test() -&gt; TestResult {
let spec = test_result!(create_spec());
test_inside_container(spec, &amp;|_| Ok(()))
}</code></pre>
<ol start="3">
<li>Create a <code>TestGroup</code> and register a test case you created</li>
</ol>
<pre><code class="language-rust no_run noplayground">pub fn get_example_test() -&gt; TestGroup {
let mut test_group = TestGroup::new("example");
let test1 = Test::new("hello world", Box::new(example_test));
test_group.add(vec![Box::new(test1)]);
test_group
}</code></pre>
<ol start="4">
<li>Register the <code>TestGroup</code> you created to a <code>TestManager</code></li>
</ol>
<pre><code class="language-rust no_run noplayground"> let mut tm = TestManager::new();
let example = get_example_test();
tm.add_test_group(Box::new(example));</code></pre>
<ol start="5">
<li>Write the validation you want to run within a test container</li>
</ol>
<pre><code class="language-rust no_run noplayground">fn main() {
let spec = get_spec();
let args: Vec&lt;String&gt; = env::args().collect();
let execute_test = match args.get(1) {
Some(execute_test) =&gt; execute_test.to_string(),
None =&gt; return eprintln!("error due to execute test name not found"),
};
match &amp;*execute_test {
"hello_world" =&gt; tests::hello_world(&amp;spec),</code></pre>
<pre><code class="language-rust no_run noplayground">pub fn hello_world(_spec: &amp;Spec) {
println!("Hello world");
}</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="integration_test"><a class="header" href="#integration_test">integration_test</a></h1>
<p><strong>Note</strong> that these tests resides in <code>/tests/integration_test/</code> at the time of writing.</p>
<p>This crate contains the Rust port of OCI-runtime tools integration tests, which are used to test if the runtime works as per the OCI spec or not. Initially youki used the original implementation of these test provided in the OCI repository <a href="https://github.com/opencontainers/runtime-tools/tree/master/validation">here</a>. But those tests are written in Go, which made the developers depend on two language environments Rust and Go to compile youki and test it. The Validation tests themselves also have an optional dependency on node js to parse their output, which can make it a third language dependency.</p>
<p>Other than that, those tests also showed some issues while running on some local systems, and thus running the tests would be difficult on local system. As the runtime is a complex piece of software, it becomes useful to have a set of tests that can be run with changes in code, so one can verify that change in one part of youki has not accidentally broken some other part of youki.</p>
<p>Thus we decided to port the tests to Rust, and validate them, so that we have a set of unit tests as well of integration tests to validate the working of runtime. These tests are still under development, and you can check the <a href="https://github.com/containers/youki/issues/361">tracking issue</a> for more details. More details on working of these tests can be found at <a href="https://github.com/containers/youki/tree/main/crates/integration_test">https://github.com/containers/youki/tree/main/crates/integration_test</a>.</p>
<p>As these tests are under development, these are validated on a standard runtime such as runc in the GitHub CI, so validate the tests themselves.</p>
<h2 id="notes-2"><a class="header" href="#notes-2">Notes</a></h2>
<h3 id="about-the-create-container-function"><a class="header" href="#about-the-create-container-function">About the create container function</a></h3>
<p>The test_utils provides a <code>create_container</code> function which can be used to run the <code>youki create</code> command. It returns the child process struct, which can be either <code>wait()</code> or <code>wait_with_output()</code> to wait for it finishing. Unless you know what are you doing, it is recommended to call <code>wait()</code> on it, as otherwise the process will hang. As explained in the <a href="developer/e2e/../youki.html">youki docs</a> , the <code>youki create</code> process, after starting forks, and the forked process keeps waiting for another youki process to send it the <code>start</code> signal , and after receiving it, that forked process execs the container program. If you are simply trying to create a container, such as in case of <code>test_outside_runtime</code> then calling <code>wait_with_output()</code> will cause it to hang. If you are actually going to start a container, and need output from the container process, then you must keep the <code>Child struct</code> returned by <code>create</code> function and call <code>wait_with_output()</code> on it <strong>AFTER</strong> you have called the start command on that container, which will give you the <code>stdout</code> and <code>stderr</code> of the process running inside the container.</p>
<p>To understand how this works, take a look at <a href="https://github.com/opencontainers/runc/blob/master/docs/terminals.md">handling stdio</a> of the runc, specially the <a href="https://github.com/opencontainers/runc/blob/master/docs/terminals.md#detached-pass-through">detached pass-through mode</a> section. As explained in it, we setup the stdio for the original youki process in <code>youki create</code> by setting the stdio to <code>Stdio::piped()</code> in the <code>create</code> function. Then we set the <code>terminal</code> option to <code>false</code> (which is the default anyway) in the spec, which makes it run in the pass-through mode. Then when the create process is done its work, and its forked process is waiting for the start signal, it uses the same stdio pipes. Thus calling <code>wait_with_output()</code> without starting will keep it hanged up, and after calling start, stdio of the program to be run inside the container can be obtained from the <code>youki create</code>'s process.</p>
<h3 id="how-test-inside-container-works"><a class="header" href="#how-test-inside-container-works">How test inside container works</a></h3>
<p>We use <code>test_inside_container</code> for making sure that the restrictions and constraints are uphold from inside the container process.
For that, first whichever integration test needs to use it, must define the runtimetest as the container process in the spec, and then use <code>test_inside_container</code> function. It requires a function which will do the necessary setup for the tests that are to be run inside. Then the counterpart for the test should be added to the <code>runtimetest</code> crate, which will run inside the container and if there is any error, print it to the <code>stderr</code>. The <code>test_inside_container</code> function will wait for the tests to be over and then check the <code>stderr</code> to be empty. If it is not, the test is assumed to fail.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="test_framework"><a class="header" href="#test_framework">test_framework</a></h1>
<p><strong>Note</strong> that these tests resides in /tests/test_framework at the time of writing.</p>
<p>This crate contains the testing framework specifically developed for porting the OCI integration test to rust. This contains structs to represent the individual tests, group of tests and a test manager that has responsibility to run tests. This Also exposes traits which can be used to implement custom test structs or test group structs if needed.</p>
<p>By default the test groups are run in parallel using the <a href="https://www.crates.io/crates/crossbeam">crossbeam crate</a>, and the default test_group implementation also runs individual tests parallelly.</p>
<p>Sometimes you might need to run the tests in a test group serially or in certain order, for example in case of testing container lifecycle, a container must be created and started before stopping it. In such cases, you will need to implement the respective traits on your own structs, so that you can have fine control over thr running of tests. Check the readme of the test_framework crate to see the struct and trait documentation <a href="https://github.com/containers/youki/tree/main/crates/test_framework">here</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="runtime-test"><a class="header" href="#runtime-test">Runtime Test</a></h1>
<p><strong>Note</strong> that these tests resides in /tests/runtimetest at the time of writing.</p>
<p>This crate provides a binary which is used by integration tests to verify that the restrictions and constraints applied to the container are upheld by the container process, from inside the container process. This runs the tests one-by-one, and the failing test prints the error to the stderr.</p>
<h2 id="notes-3"><a class="header" href="#notes-3">Notes</a></h2>
<p>This binary must be compiled with the option of static linking to crt0 given to the rustc. If compiled without it, it will add a linking to /lib64/ld-linux-x86-64.so . The binary compiled this way cannot be run inside the container process, as they do not have access to /lib64/... Thus the runtime test must be statically linked to crt0.</p>
<p>While developing, originally this was added to the common workspace of all crates in youki. But then it was realized that this was quite inefficient because :</p>
<ul>
<li>All packages except runtimetest will be compiled with dynamic linking</li>
<li>Runtimetest will be compiled with static linking</li>
</ul>
<p>Now runtimetest needs at least <code>oci-spec</code> and <code>nix</code> package for its operations, which are also dependencies of other packages in the workspace. Thus both of these, and recursively their dependencies must be compiled twice, each time, once for dynamic linking and once for static. The took a long time in the compilation stage, especially when developing / adding new tests. Separating runtimetest from the workspace allows it to have a separate target/ directory, where it can store the statically compiled dependencies, and the workspace can have its target/ directory, where it can store its dynamically compiled dependencies. That way only the crates which have changes need to be compiled (runtimetest or integration test), and not their dependencies.</p>
<p>In case in future this separation is not required, or some other configuration is chosen, make sure the multiple compilation issue does not arise, or the advantages of new method outweigh the time spent in double compilation.</p>
<p>To see if a binary can be run inside the container process, run</p>
<pre><code class="language-console">readelf -l path/to/binary |grep "program interpreter"
</code></pre>
<p><code>[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]</code> means that the binary is not statically linked, and cannot be run inside the container process. If the above command gives no output, that means it does not require any program interpreter and can be run inside the container.</p>
<p>Another way is to run</p>
<pre><code class="language-console">file path/to/binary
</code></pre>
<pre><code class="language-console">./youki: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=...., for GNU/Linux 3.2.0, with debug_info, not stripped`
</code></pre>
<p>This output indicates that the binary is dynamically linked, thus cannot be run inside the container process</p>
<pre><code class="language-console">./runtimetest: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=...., for GNU/Linux 3.2.0, with debug_info, not stripped
</code></pre>
<p>This output indicates that the binary is statically linked, and can be run inside the container process</p>
<p>Some links to help :</p>
<ul>
<li><a href="https://stackoverflow.com/questions/31770604/how-to-generate-statically-linked-executables">how to generate static executable</a></li>
<li><a href="https://superuser.com/questions/248512/why-do-i-get-command-not-found-when-the-binary-file-exists">understanding the error which dynamically linked library gives</a></li>
<li><a href="https://doc.rust-lang.org/cargo/reference/config.html">Rust cargo config for rustflags</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="containerd-integration-test-using-youki"><a class="header" href="#containerd-integration-test-using-youki">containerd integration test using youki</a></h1>
<h2 id="local"><a class="header" href="#local">local</a></h2>
<pre><code class="language-console">just containerd-test
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="runtime-tools"><a class="header" href="#runtime-tools">runtime tools</a></h1>
<h2 id="local-1"><a class="header" href="#local-1">local</a></h2>
<pre><code class="language-console">just test-oci
</code></pre>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js"></script>
<script src="mark.min.js"></script>
<script src="searcher.js"></script>
<script src="clipboard.min.js"></script>
<script src="highlight.js"></script>
<script src="book.js"></script>
<!-- Custom JS scripts -->
<script>
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</div>
</body>
</html>