Kubernetes объявил Docker устаревшим и планирует прекратить его использование примерно через год, в версии 1.22 или 1.23. Эта новость вызвала много вопросов и непонимания. В блоге Kubernetes появилось целых две статьи, разъясняющих смысл записи в Changelog (раз и два). Если все обобщить, то для разработчиков (те, которые Dev) ничего не меняется они все так же могут продолжать использовать docker build для сборки своих контейнеров, а вот для инженеров, ответственных за эксплуатацию кластера (Ops), пришла пора разобраться и освоить несколько новых инструментов.
Но для начала давайте вернемся в 2016 год, когда Kubernetes представил Container Runtime Interface (CRI). До версии Kubernetes 1.3 kubelet умел работать только с Docker, а в 1.3 анонсировали поддержку rkt Container Engine (этот проект уже прекратил свое существование). При этом код, отвечающий за взаимодействие с Docker и rkt, был глубоко и широко интегрирован в исходники kubelet. Процесс добавления поддержки rkt показал, что для добавления новых container runtime необходимо хорошо разбираться в устройстве и алгоритмах работы kubelet, а результат получился очень сложным для поддержки и развития.
Поэтому решили все упростить, и добавили новую абстракцию Container Runtime Interface. Весь код, реализующий высокоуровневый интерфейс работы с Docker, был убран из kubelet, а вместо него kubelet стал использовать CRI. Но тут возникла небольшая проблемка Docker не умеет в CRI. Чтобы ее решить, в Kubernetes написали программу-прокладку между kubelet и Docker. С одной стороны она принимает запросы от kubelet через CRI, а с другой транслирует их в docker-демон.
И назвали эту программу dockershim, и вот как раз ее и хотят убрать к версии 1.23.
Возвращаемся назад, в конец 2020 года, и смотрим чего у нас новенького по сравнению с 2016 в плане развития Container Runtime Interface.
-
Проект rkt закрыт.
-
RedHat создали и всячески продвигают свой собственный движок для запуска контейнеров CRI-O.
-
Монолитный Docker разделили на containerd, dockerd и docker-cli.
В итоге у нас сейчас есть два движка для запуска контейнеров, которые умеют в CRI это containerd и cri-o. Естественно, все эти события произошли не прямо в 2020 году, но массовый интерес к ним возник именно после объявления Kubernetes о прекращении поддержки Docker.
Сборка
Давайте же разберемся, чем нам это грозит. Начнем с наиболее частого вопроса: Как же собирать образ контейнера с помощью containerd или cri-o?
И ответ очень простой: Никак. Потому что containerd и cri-o предназначены только для запуска контейнеров из готовых образов. Плюс они умеют еще несколько вещей, необходимых для эксплуатации:
-
загрузка образов из интернета;
-
просмотр информации о запущенных подах и контейнерах;
-
удаление образов, подов и контейнеров;
-
подключение внутрь контейнера (запуск внутри контейнера процесса с bash).
Но они не умеют ни собирать новый образ, ни пушить его в какой-либо registry. И как теперь быть ?
Начнем с того, что в 2015 году Docker и остальные лидеры индустрии контейнеризации основали Open Container Initiative (OCI) и опубликовали в ее рамках спецификацию формата хранения образов контейнеров. Эту спецификацию поддерживают Docker, containerd и cri-o, так что образ, собранный с помощью docker build на машине разработчика, должен без проблем запустится с помощью containerd или cri-o. И так и происходит в подавляющем большинстве случаев, но время от времени попадаются нюансы реализации. Эти нюансы, конечно, исправляются, но иногда не так быстро, как хотелось бы.
Если вы за стабильность и минимальные изменения запускайте новые кластера с containerd в качестве движка контейнеризации. А на машинах разработчиков и CI runners сборки смело оставляйте Docker.
Если вы выбрали безопасность из коробки, кровавый enterpise и платную поддержку, то наверняка используете Openshift (платформа на базе Kubernetes), в котором для запуска контейнеров применяется cri-o. А для сборки образов RedHat создала отдельную утилиту, которую назвала buildah. В отличие от Docker, этой утилите для сборки образов не нужен запущенный движок контейнеризации.
Из альтернатив можно еще посоветовать kaniko утилиту для сборки образов от Google. Она поставляется в виде образа контейнера, так что для запуска ей нужен движок контейнеризации, но для сборки уже нет.
Эксплуатация
С разработкой разобрались, теперь пора перейти к самому
интересному: что делать инженеру, который зашел на узел, чтобы
разобраться, почему статус узла стал NotReady, набрал привычную
команду docker ps
, а в ответ получил docker:
command not found
.
Фундаментальная проблема тут в том, что Docker изначально ориентировался на взаимодействие с человеком, и к последним версиям у него появился очень классный и удобный консольный интерфейс, а вот компоненты, реализующие CRI они by design ориентированы на взаимодействие с программой-оркестратором, а не с человеком.
Но внезапно оказалось, что человек тоже хочет взаимодействовать с CRI, и для этого была написана отдельная утилита crictl, которая представляет собой CLI-консоль к CRI-движку. То есть одна и та же утилита crictl позволяет посмотреть список контейнеров запущенных с помощью containerd или cri-o.
Очень часто встречается информация, что можно просто заменить
docker на crictl и вместо docker ps
набирать
crictl ps
. Сказка заканчивается, когда вы попробуете
запустить контейнер с помощью crictl run
, и внезапно
оказывается, что сначала надо создать манифест для PodSandbox, а
уже потом написать манифест, описывающий контейнер, который будет
запущен в этом поде.
С другой стороны надо помнить, что CRI был придуман командой разработки Kubernetes и для Kubernetes, и поэтому, на мой взгляд, правильнее было бы его назвать Pod Runtime Interface.
И crictl оптимизирован для работы с контейнерами, созданными kubelet. Например, в выводе не показываются служебные PODSandbox контейнеры, а имена контейнеров показываются в более понятном виде, чем в Docker. Да и возможности по работе с CLI постоянно улучшаются.
Еще одна вещь, с которой часто возникают проблемы понимания.
Многие путают docker
(бинарник из пакета docker-cli) и
dockerd
(демон, управляющий контейнерами). Пытаются
сделать crictl image save/load
и внезапно оказывается,
что в crictl таких команд нет. А на созданный issue авторы устало
отвечают, что crictl консоль для CRI runtime, который должен только
запускать контейнеры, ну еще образ скачать из registry. А про
манипуляции с образами в спецификации ничего нет.
Но выход есть! Для манипуляции с образами контейнеров можно воспользоваться дополнительной утилитой skopeo, если вы используете cri-o, или утилитой ctr из комплекта containerd.
И в завершение дам ответ на идею, которая наверняка возникнет после прочтения этой статьи:
Подождите, но ведь есть containerd, с которым умеет работать
kubelet и dockerd! Давайте поставим Docker (три пакета docker-cli,
docker, containerd), kubelet настроим на работу с containerd
напрямую, и у нас при этом останется старая добрая команда
docker
.
Вот только docker ps
не покажет контейнеров,
запущенных kubelet через CRI. Произойдет это из-за того, что
containerd умеет работать в многопользовательском режиме, и docker
с kubelet запускают контейнеры в различных
containerd namespaces moby
и k8s.io
соответственно (не надо путать их с
kubernetes namespaces). Посмотреть на контейнеры в разных
неймспейсах можно с помощью утилиты ctr -n <ns_name>
container ls
.