Runtime
External Link:
В 2007 году Google сделали проект Let Me Contain That For You (LMCTFY), в 2008 году появился Linux Containers (LXC). Для управления LXC в 2013 году появился инструмент Docker. Далее в 2015, команда Docker разработали проект libcontainer на языке Go. Также, в 2015 вышел Kubernetes 1.0. В 2015 собрали Open Container Initiative (OCI), которые стали разрабатывать стандарты на метаданные (манифесты-спецификации), образы контейнеров, методы управления ими. В том числе, в рамках OCI создали инструмент запуска и работы с контейнерами runc.
runc
sudo apt install runc
runc spec
cat config.json
В спецификации от runc можно увидеть всё необходимое для создания и запуска контейнера: environment variables, user + group IDs, mount points, Linux namespaces. Не хватает только файловой системы (rootfs), базового образа контейнера:
sudo apt install skopeo, umoci # Ubuntu 2404+
skopeo copy docker://opensuse/tumbleweed:latest oci:tumbleweed:latest
sudo umoci unpack --image tumbleweed:latest bundle
В распакованном образе можно найти готовую Runtime Specification:
sudo chown -R $(id -u) bundle
cat bundle/config.json
В ней можно увидеть обычные поля из runc, а доп заполненные annotations:
"annotations": {
"org.opencontainers.image.title": "openSUSE Tumbleweed Base Container",
"org.opencontainers.image.url": "https://www.opensuse.org/",
"org.opencontainers.image.vendor": "openSUSE Project",
"org.opencontainers.image.version": "20190517.6.190",
Чтобы создать контейнер с runc, нужно его зацепить на терминал ввода команд TTY:
sudo runc create -b bundle container
ERRO[0000] runc create failed: cannot allocate tty if runc will detach without setting console socket
На существующий TTY зацепить контейнер нельзя (потому что окно удалённого xTerm не поддерживает такое), нужно создать новый виртуальный TTY и указать его сокет. Для этого надо установить Golang, скачать приложение rectty, создать с его помощью виртуальный терминал, после чего В ДРУГОМ ОКНЕ терминала создать контейнер и зацепить его на создвнный TTY:
sudo apt install wget
wget https://go.dev/dl/go1.23.3.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.23.3.linux-amd64.tar.gz
/usr/local/go/bin/go install github.com/opencontainers/runc/contrib/cmd/recvtty@latest
rectty tty.sock
В ДРУГОМ ОКНЕ терминала создать контейнер и зацепить его на создвнный TTY:
sudo runc create -b bundle --console-socket $(pwd)/tty.sock container
sudo runc list # контейнер в статуса created, не запущен
sudo runc ps container # посмотрим что внутри него
UID PID PPID C STIME TTY TIME CMD
root 29772 1 0 10:35 ? 00:00:00 runc init
runc init создаёт новую среду со всеми namespaces. /bin/bash ещё не запущен в контейнере, но уже можно запускать в нём свои процессы, полезно чтоб настроить сеть:
sudo runc exec -t container echo "Hello, world!"
Hello, world!
Для запуска контейнера выполним:
sudo runc start container
sudo runc list
sudo runc ps container
UID PID PPID C STIME TTY TIME CMD
root 6521 6511 0 14:25 pts/0 00:00:00 /bin/bash
Исходный runc init пропал, теперь только /bin/bash существует в контейнере. На ПЕРВОМ ОКНЕ терминала появилась консоль контейнера:
$ ps aux
ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 5156 4504 pts/0 Ss 10:28 0:00 /bin/bash
root 29 0.0 0.0 6528 3372 pts/0 R+ 10:32 0:00 ps aux
Можно проверить управление: заморозим контейнер. Во ВТОРОМ ОКНЕ терминала выполним:
sudo runc pause container
# в первом окне ввод команд прервётся
sudo runc resume container
# Рассмотрим события контейнера:
sudo runc events container
#{...}
Для остановки контейнера достаточно выйти из rectty-сессии, после чего удалить контейнер. Остановленный контейнер нельзя перезапустить, можно лишь пересоздать в новом состоянии:
> sudo runc list
ID PID STATUS BUNDLE CREATED OWNER
container 0 stopped /bundle 2019-05-21T10:28:32.765888075Z root
> sudo runc delete container
> sudo runc list
ID PID STATUS BUNDLE CREATED OWNER
Можно модифицировать спецификацию в контейнере (bundle/config.json):
> sudo apt install moreutils, jq # инструмент jq для работы с JSON
> cd bundle
> jq '.process.args = ["echo", "Hello, world!"]' config.json | sponge config.json
> sudo runc run container
> Hello, world!
Можно удалить разделение PID namespace процессов в контейнере с хостом:
> jq '.process.args = ["ps", "a"] | del(.linux.namespaces[0])' config.json | sponge config.json
> sudo runc run container
16583 ? S+ 0:00 sudo runc run container
16584 ? Sl+ 0:00 runc run container
16594 pts/0 Rs+ 0:00 ps a
[output truncated]
- runc очень низкоуровневый и позволяет серьёзно нарушить работу и безопасность контейнеров.
- Поэтому сделаны надстройки обеспечения ИБ уровня ОС: seccomp, SELinux и AppArmor
- Однако, их намного удобнее использовать на уровне управления выше
- Для защиты также можно запускать контейнеры в режиме rootless из runc
- С помощью runc нужно руками настраивать сетевые интерфейсы, очень трудоемко
CRI-O
Инструмент CRI-O разработан в 2016 при участии OCI в рамках проекта Kubernetes. Философия UNIX, максимально лёгкий аналог Docker/containerd. Он НЕ предназначен как инструмент для приёма команд от разработчиков. Задача - принимать команды от K8s. Внутри себя CRI-O использует runc как backend, и принимает команды по gRPC API как frontend.
Попробуем CRI-O с помощью спец-контейнера с crictl:
sudo apt install podman
sudo vim /etc/containers/registries.conf
# нужно задать репозиторий для скачивания:
# unqualified-search-registries=["docker.io"]
sudo podman run --privileged -h crio-playground -it saschagrunert/crio-playground
Внутри лежит файл sandbox.yml:
---
metadata:
name: sandbox
namespace: default
dns_config:
servers:
- 8.8.8.8
Из него можно создать Pod:
$ crictl runp sandbox.yml
5f2b94f74b28c092021ad8eeae4903ada4b1ef306adf5eaa0e985672363d6336