9 releases (breaking)

0.7.1 Mar 19, 2024
0.7.0 Mar 14, 2024
0.6.0 Oct 30, 2023
0.5.0 Aug 24, 2023
0.1.1 Dec 10, 2021

#39 in Asynchronous

Download history 380/week @ 2024-01-03 642/week @ 2024-01-10 782/week @ 2024-01-17 911/week @ 2024-01-24 1234/week @ 2024-01-31 801/week @ 2024-02-07 773/week @ 2024-02-14 1034/week @ 2024-02-21 1500/week @ 2024-02-28 1180/week @ 2024-03-06 1588/week @ 2024-03-13 1387/week @ 2024-03-20 1169/week @ 2024-03-27 1044/week @ 2024-04-03 1010/week @ 2024-04-10 995/week @ 2024-04-17

4,350 downloads per month
Used in 2 crates

Apache-2.0

200KB
4K SLoC

Shim extension for containerd

Crates.io docs.rs Crates.io CI

Rust crate to ease runtime v2 shim implementation.

It replicates same shim.Run API offered by containerd's shim v2 runtime implementation written in Go.

Runtime

Runtime v2 introduces a first class shim API for runtime authors to integrate with containerd. The shim API is minimal and scoped to the execution lifecycle of a container.

This crate simplifies shim v2 runtime development for containerd. It handles common tasks such as command line parsing, setting up shim's TTRPC server, logging, events, etc.

Clients are expected to implement [Shim] and [Task] traits with task handling routines. This generally replicates same API as in Go version.

Once implemented, shim's bootstrap code is as easy as:

shim::run::<Service>("io.containerd.empty.v1")

Look and feel

The API is very similar to the one offered by Go version:

use std::sync::Arc;

use async_trait::async_trait;
use containerd_shim::{
    asynchronous::{run, spawn, ExitSignal, Shim},
    publisher::RemotePublisher,
    Config, Error, Flags, StartOpts, TtrpcResult,
};
use containerd_shim_protos::{
    api, api::DeleteResponse, shim_async::Task, ttrpc::r#async::TtrpcContext,
};
use log::info;

#[derive(Clone)]
struct Service {
    exit: Arc<ExitSignal>,
}

#[async_trait]
impl Shim for Service {
    type T = Service;

    async fn new(_runtime_id: &str, _args: &Flags, _config: &mut Config) -> Self {
        Service {
            exit: Arc::new(ExitSignal::default()),
        }
    }

    async fn start_shim(&mut self, opts: StartOpts) -> Result<String, Error> {
        let grouping = opts.id.clone();
        let address = spawn(opts, &grouping, Vec::new()).await?;
        Ok(address)
    }

    async fn delete_shim(&mut self) -> Result<DeleteResponse, Error> {
        Ok(DeleteResponse::new())
    }

    async fn wait(&mut self) {
        self.exit.wait().await;
    }

    async fn create_task_service(&self, _publisher: RemotePublisher) -> Self::T {
        self.clone()
    }
}

#[async_trait]
impl Task for Service {
    async fn connect(
        &self,
        _ctx: &TtrpcContext,
        _req: api::ConnectRequest,
    ) -> TtrpcResult<api::ConnectResponse> {
        info!("Connect request");
        Ok(api::ConnectResponse {
            version: String::from("example"),
            ..Default::default()
        })
    }

    async fn shutdown(
        &self,
        _ctx: &TtrpcContext,
        _req: api::ShutdownRequest,
    ) -> TtrpcResult<api::Empty> {
        info!("Shutdown request");
        self.exit.signal();
        Ok(api::Empty::default())
    }
}

#[tokio::main]
async fn main() {
    run::<Service>("io.containerd.empty.v1", None).await;
}

How to use with containerd

Note: All operations are in the root directory of rust-extensions.

With shim v2 runtime:

$ cargo build --example skeleton
$ sudo cp ./target/debug/examples/skeleton /usr/local/bin/containerd-shim-skeleton-v1
$ sudo ctr run --rm --runtime io.containerd.skeleton.v1 -t docker.io/library/hello-world:latest hello

Or if on 1.6+

$ cargo build --example skeleton
$ sudo ctr run --rm --runtime ./target/debug/examples/skeleton docker.io/library/hello-world:latest hello

Or manually:

$ touch log

# Run containerd in background
$ sudo TTRPC_ADDRESS="/var/run/containerd/containerd.sock.ttrpc" \
    cargo run --example skeleton -- \
    -namespace default \
    -id 1234 \
    -address /var/run/containerd/containerd.sock \
    -publish-binary ./bin/containerd \
    start
unix:///var/run/containerd/eb8e7d1c48c2a1ec.sock

$ cargo build --example shim-proto-connect
$ sudo ./target/debug/examples/shim-proto-connect unix:///var/run/containerd/eb8e7d1c48c2a1ec.sock
Connecting to unix:///var/run/containerd/eb8e7d1c48c2a1ec.sock...
Sending `Connect` request...
Connect response: version: "example"
Sending `Shutdown` request...
Shutdown response: ""

$ cat log
[INFO] server listen started
[INFO] server started
[INFO] Shim successfully started, waiting for exit signal...
[INFO] Connect request
[INFO] Shutdown request
[INFO] Shutting down shim instance
[INFO] close monitor
[INFO] listener shutdown for quit flag
[INFO] ttrpc server listener stopped
[INFO] listener thread stopped
[INFO] begin to shutdown connection
[INFO] connections closed
[INFO] reaper thread exited
[INFO] reaper thread stopped

Running on Windows

# Run containerd in background
$env:TTRPC_ADDRESS="\\.\pipe\containerd-containerd.ttrpc"

$ cargo run --example skeleton -- -namespace default -id 1234 -address "\\.\pipe\containerd-containerd" start
\\.\pipe\containerd-shim-17630016127144989388-pipe

# (Optional) Run the log collector in a separate command window
# note: log reader won't work if containerd is connected to the named pipe, this works when running manually to help debug locally
$ cargo run --example windows-log-reader \\.\pipe\containerd-shim-default-1234-log
Reading logs from: \\.\pipe\containerd-shim-default-1234-log
<logs will appear after next command>

$ cargo run --example shim-proto-connect \\.\pipe\containerd-shim-17630016127144989388-pipe
Connecting to \\.\pipe\containerd-shim-17630016127144989388-pipe...
Sending `Connect` request...
Connect response: version: "example"
Sending `Shutdown` request...
Shutdown response: ""

Supported Platforms

Currently, following OSs and hardware architectures are supported, and more efforts are needed to enable and validate other OSs and architectures.

  • Linux
  • Mac OS
  • Windows

Dependencies

~8–22MB
~305K SLoC