cri#
The Kubernetes Container Runtime Interface (CRI, runtime.v1) in pure
OCaml: IO-free protobuf types and gRPC method definitions, a runtime
backend boundary, an Eio server and daemon, an Eio client and CLI, and a
body-aware gateway. The same definitions back the client, the server, and
the proxy.
It is the gRPC/protobuf sibling of ocaml-docker: where Docker exposes a
REST/JSON Engine API, CRI is gRPC over protobuf. The wire layer is built
on nox-protobuf, nox-grpc and nox-grpc-eio; no protoc or external
toolchain is needed.
Installation#
Install with opam:
$ opam install cri
If opam cannot find the package, it may not yet be released in the public
opam-repository. Add the overlay repository, then install it:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install cri
Packages#
| Package | What it is |
|---|---|
cri-api |
IO-free protobuf message types, nox-grpc method registry, and Cri.Runner backend interface/simulator for the full RuntimeService and ImageService (41 RPCs). No IO dependency. |
cri-server |
Server.Make (B : Cri.Runner.BACKEND) over nox-grpc-eio and the Server.Proxy body-aware gateway. |
cri-client |
An Eio gRPC client: one typed function per RPC. |
cri |
The cri CLI: serve runs the in-memory simulator server, proxy runs the gateway, and the remaining subcommands are a crictl-shaped client (version, status, pods, ps, images, pull, runp, rmp). |
The binary is named cri so it never shadows the upstream cri-tools
crictl on PATH.
Library#
The cri library names every CRI message as a nox-protobuf codec and
every RPC as a nox-grpc method:
# let path = Grpc.Method.path Cri.Service.run_pod_sandbox ;;
val path : string = "/runtime.v1.RuntimeService/RunPodSandbox"
Cri_server.Make builds a server over any backend; Make (Cri.Runner.Memory)
is an in-memory CRI that speaks real gRPC:
module Server = Cri_server.Make (Cri.Runner.Memory)
let server = Server.v (Cri.Runner.Memory.v ())
(* Server.run ~sw ~net ~addr:(`Unix "/run/crid.sock") server *)
The proxy#
Cri_server.Proxy is a body-aware CRI gateway between a kubelet and an
upstream runtime. Because every request is typed, policies inspect the
body, not just the method path:
allow_all-- forward everything;read_only-- deny mutating RPCs;no_privileged-- deny privileged sandboxes and containers;image_allowlist names-- deny images outside an allowlist.
crict-proxy --listen /run/crict-proxy.sock \
--upstream /run/containerd/containerd.sock \
--policy no_privileged --audit
CLI#
crictd --socket /tmp/crid.sock # run the simulator daemon
crict -r /tmp/crid.sock version # query it
crict -r /tmp/crid.sock runp web # run a pod sandbox
crict -r /tmp/crid.sock pods # list pod sandboxes
Testing#
dune test # codec round-trips, wire format, in-process e2e
CRI_LIVE=1 dune build @critest # cri-tools `critest` conformance (needs critest)
The critest harness runs the official conformance suite against crictd
(an API-shape smoke) or, with UPSTREAM=<sock>, through crict-proxy in
front of a real runtime -- putting our codecs and proxy in the path of the
authoritative suite.
Status#
The in-memory backend is an honest simulator: the pod/container/image
lifecycle is bookkeeping, stats are zero, and exec/attach return a URL
but stream no bytes -- no process is ever run. A real runc/VMM backend
behind the same BACKEND boundary is the natural next step.