Что есть OCI Runtime
Подсистема которая берёт OCI bundle ([[oci-spec|spec]]:
config.json + rootfs/) и запускает контейнер. Что именно
делает, её решение, главное чтобы соответствовало OCI runtime spec.
Три популярных варианта в 2026:
| Runtime | Подход | Trade-off |
|---|---|---|
| runc | namespaces + cgroups + seccomp в host kernel | максимум performance, минимум изоляции |
| runsc (gVisor) | userspace-kernel перехватывает syscall'ы | ~30% slower, гораздо больше isolation |
| kata-containers | каждый контейнер в lightweight VM | ~5% overhead, VM-grade isolation |
| crun | альтернатива runc на C, faster startup | как runc по isolation |
| youki | runc-compatible на Rust | как runc |
runc, референс
Создан Docker/OCI как минимальный эталон. Открытый код, в каждом дистро. Под капотом всех общих контейнерных стеков (Docker, containerd, CRI-O, podman), runc или его replacement (crun).
Что делает runc при runc run myctr:
- Читает
config.json - Создаёт [[namespaces|namespaces]] (PID, NET, MNT, IPC, UTS, USER)
- Настраивает [[cgroups|cgroups]] (memory, cpu)
- Применяет capabilities dropping (CAP_DROP)
- Применяет seccomp profile
- Применяет AppArmor/SELinux profile если задан
chrootвrootfsexecуказанной в config команды
Всё это, в host kernel. Контейнер видит host'овое ядро, использует тот же VFS, тот же scheduler. Изоляция, namespaces.
Прямой запуск без Docker:
# Подготовить bundle
mkdir -p mycontainer/rootfs
cd mycontainer
docker export $(docker create alpine) | tar -C rootfs -xf -
runc spec # создаст config.json
# отредактировать config.json под нужды
# Запустить
sudo runc run mycontainer-id
# Управление
runc list
runc kill mycontainer-id KILL
runc delete mycontainer-id
Это уровень "ниже Docker". Используют когда нужно понять, что именно происходит, или для embedded-сценариев.
runc, где он в Docker stack'е
docker / podman
│
▼
containerd (или CRI-O)
│
▼
containerd-shim (один на контейнер, переживает рестарт containerd)
│
▼
runc (запускает init-процесс, потом exit'ит)
│
▼
init процесс контейнера (PID 1 в pid-namespace)
shim нужен чтобы переживать рестарт higher-level менеджеров. runc, короткоживущий, отрабатывает и умирает.
crun, альтернатива на C
Тот же контракт, что runc, но:
- Написан на C (runc, Go), startup faster
- Меньше memory footprint
- Default в podman / RHEL 8+
Полностью drop-in replacement: containerd конфигом можно переключить с runc на crun, всё работает.
Используй когда стартуете много короткоживущих контейнеров (CI, k8s job's, function-as-a-service).
runsc / gVisor, userspace kernel
Концепция: между application syscall и host kernel поставить userspace-kernel (gVisor's "Sentry"), который перехватывает большинство syscall'ов и реализует их сам.
app (внутри контейнера)
│ syscall
▼
Sentry (gVisor userspace-kernel)
│ ограниченное подмножество host-syscall'ов
▼
host kernel
Плюсы:
- Не привязан к host kernel для большинства syscall'ов эксплойт kernel CVE сложнее
- Меньше attack surface: ~50 host syscalls вместо ~400
- Без VM, startup быстрый (доли секунды)
Минусы:
- Performance hit, 10-50% в зависимости от нагрузки
- Не все syscalls работают, пограничные сетевые/file features
могут не поддерживаться (
io_uring, например, частично) - Не все workload подходят, БД с iouring или AIO будут страдать
Запуск:
# Установка
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor ...
apt install runsc
# Регистрация в Docker
cat /etc/docker/daemon.json
{ "runtimes": { "runsc": { "path": "/usr/bin/runsc" }}
}
systemctl restart docker
# Использовать
docker run --runtime=runsc -it alpine
Где применяется:
- Google App Engine / Cloud Run, внутри
- Untrusted code execution (online code playgrounds)
- Multi-tenant CI, где шаренный кластер запускает чужой код
kata-containers, VM-based
Каждый контейнер запускается в lightweight VM (через qemu/cloud-hypervisor/firecracker). Плюс:
- Hardware-grade isolation, VM boundary, не namespace boundary
- Совместимость почти 100%, внутри VM настоящее Linux-ядро
- Поддержка GPU passthrough, custom kernels
Минус:
- Overhead в RAM (~50-200 MB на контейнер для VM)
- Startup slower, 1-2 сек вместо < 100ms
- Nested virtualization в облаках бывает запрещена
# k8s через crio, runtimeClass
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata
handler: kata
---
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
runtimeClassName: kata
containers:
- name: app
image: myapp:v1
Используется в:
- AWS Lambda + Firecracker, не Kata, но ту же идею
- Kata on AKS / Azure Container Instances
- Confidential containers (CoCo), Kata + AMD SEV / Intel TDX для unencrypted-memory-protection
Сравнение
| Признак | runc | runsc / gVisor | kata-containers |
|---|---|---|---|
| Изоляция | namespaces | userspace kernel | VM |
| Performance | 100% (baseline) | ~70-90% | ~95% |
| Memory overhead | ~few MB | ~30 MB на Sentry | ~50-200 MB на VM |
| Startup | ~100 ms | ~150 ms | ~1-2 sec |
| Compatibility | 100% | ~85% | ~99% |
| Use case | default everywhere | untrusted code | multi-tenant secure |
| Где default | Docker, containerd, CRI-O, k8s | Google Cloud Run | OCI confidential |
RuntimeClass в k8s
k8s allows multiple runtimes side-by-side:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
---
apiVersion: v1
kind: Pod
spec:
runtimeClassName: gvisor # этот pod через gVisor
containers: [...]
Default, пусто (== runc). Опционально можно отдельные namespaces / labels форсить на untrusted-runtime.
Когда что-то пошло не так
exec format error, multi-arch image, runtime запускает binary не своей архитектуры. Pull правильный platform.OCI runtime exec failed: exec failed, entrypoint не существует или не executable в rootfs.chmod +xили проверь путь.- runsc workload падает с unsupported syscall,
runsc --straceилиdmesgот gVisor покажет какой; иногда--platform=ptracefallback (медленнее, шире совместимость). - Kata медленно стартует, обычно cold-start cloud-hypervisor.
enable_template = trueв configuration.toml для prebooted-VM. - runc-update не работает на cgroup'ы, на cgroupv1 vs v2 разные пути. Современные runc оба умеют, но containerd может не передавать новый формат.
Unknown runtimeв Docker, не зарегистрирован в/etc/docker/daemon.jsonлибоsystemctl restart dockerне сделан.
Альтернативы и связанные
- firecracker, VMM, не runtime, но Kata может его использовать
- bubblewrap (bwrap), как runc для Flatpak; не OCI-compatible
- lxc/lxd, старая, не OCI; больше "system contains" вместо "application contains"
- systemd-nspawn, встроенная контейнеризация systemd; тоже не OCI