0 added
0 removed
Original
2026-01-01
Modified
2026-02-19
1
Kubernetes объявил Docker устаревшим и планирует прекратить его использование примерно через год, в версии 1.22 или 1.23. Эта новость вызвала много вопросов и непонимания. В блоге Kubernetes появилось целых две статьи, разъясняющих смысл <a>записи </a>в Changelog (<a>раз</a> и <a>два</a>). Если все обобщить, то для разработчиков (те, которые <strong>Dev</strong>) ничего не меняется - они все так же могут продолжать использовать docker build для сборки своих контейнеров, а вот для инженеров, ответственных за эксплуатацию кластера (<strong>Ops</strong>), пришла пора разобраться и освоить несколько новых инструментов.<p>Но для начала давайте вернемся в 2016 год, когда Kubernetes<a>представил</a> Container Runtime Interface (CRI). До версии Kubernetes 1.3 kubelet умел работать только с Docker, а в 1.3 анонсировали поддержку <a>rkt Container Engine</a> (этот проект уже прекратил свое существование). При этом код, отвечающий за взаимодействие с Docker и rkt, был глубоко и широко интегрирован в исходники kubelet. Процесс добавления поддержки rkt показал, что для добавления новых container runtime необходимо хорошо разбираться в устройстве и алгоритмах работы kubelet, а результат получился очень сложным для поддержки и развития. </p>
1
Kubernetes объявил Docker устаревшим и планирует прекратить его использование примерно через год, в версии 1.22 или 1.23. Эта новость вызвала много вопросов и непонимания. В блоге Kubernetes появилось целых две статьи, разъясняющих смысл <a>записи </a>в Changelog (<a>раз</a> и <a>два</a>). Если все обобщить, то для разработчиков (те, которые <strong>Dev</strong>) ничего не меняется - они все так же могут продолжать использовать docker build для сборки своих контейнеров, а вот для инженеров, ответственных за эксплуатацию кластера (<strong>Ops</strong>), пришла пора разобраться и освоить несколько новых инструментов.<p>Но для начала давайте вернемся в 2016 год, когда Kubernetes<a>представил</a> Container Runtime Interface (CRI). До версии Kubernetes 1.3 kubelet умел работать только с Docker, а в 1.3 анонсировали поддержку <a>rkt Container Engine</a> (этот проект уже прекратил свое существование). При этом код, отвечающий за взаимодействие с Docker и rkt, был глубоко и широко интегрирован в исходники kubelet. Процесс добавления поддержки rkt показал, что для добавления новых container runtime необходимо хорошо разбираться в устройстве и алгоритмах работы kubelet, а результат получился очень сложным для поддержки и развития. </p>
2
<p>Поэтому решили все упростить, и добавили новую абстракцию - <a>Container Runtime Interface</a>. Весь код, реализующий высокоуровневый интерфейс работы с Docker, был убран из kubelet, а вместо него kubelet стал использовать CRI. Но тут возникла небольшая проблемка - Docker не умеет в CRI. Чтобы ее решить, в Kubernetes написали программу-прокладку между kubelet и Docker. С одной стороны она принимает запросы от kubelet через CRI, а с другой - транслирует их в docker-демон.</p>
2
<p>Поэтому решили все упростить, и добавили новую абстракцию - <a>Container Runtime Interface</a>. Весь код, реализующий высокоуровневый интерфейс работы с Docker, был убран из kubelet, а вместо него kubelet стал использовать CRI. Но тут возникла небольшая проблемка - Docker не умеет в CRI. Чтобы ее решить, в Kubernetes написали программу-прокладку между kubelet и Docker. С одной стороны она принимает запросы от kubelet через CRI, а с другой - транслирует их в docker-демон.</p>
3
<p>И назвали эту программу <a>dockershim</a>, и вот как раз ее и хотят убрать к версии 1.23.Возвращаемся назад, в конец 2020 года, и смотрим чего у нас новенького по сравнению с 2016 в плане развития Container Runtime Interface.</p>
3
<p>И назвали эту программу <a>dockershim</a>, и вот как раз ее и хотят убрать к версии 1.23.Возвращаемся назад, в конец 2020 года, и смотрим чего у нас новенького по сравнению с 2016 в плане развития Container Runtime Interface.</p>
4
<ul><li>Проект rkt закрыт.</li>
4
<ul><li>Проект rkt закрыт.</li>
5
<li>RedHat создали и всячески продвигают свой собственный движок для запуска контейнеров CRI-O.</li>
5
<li>RedHat создали и всячески продвигают свой собственный движок для запуска контейнеров CRI-O.</li>
6
<li>Монолитный Docker разделили на containerd, dockerd и docker-cli.</li>
6
<li>Монолитный Docker разделили на containerd, dockerd и docker-cli.</li>
7
</ul>В итоге у нас сейчас есть два движка для запуска контейнеров, которые умеют в CRI - это <strong>containerd </strong>и <strong>cri-o</strong>. Естественно, все эти события произошли не прямо в 2020 году, но массовый интерес к ним возник именно после объявления Kubernetes о прекращении поддержки Docker.<h2>Сборка</h2>
7
</ul>В итоге у нас сейчас есть два движка для запуска контейнеров, которые умеют в CRI - это <strong>containerd </strong>и <strong>cri-o</strong>. Естественно, все эти события произошли не прямо в 2020 году, но массовый интерес к ним возник именно после объявления Kubernetes о прекращении поддержки Docker.<h2>Сборка</h2>
8
Давайте же разберемся, чем нам это грозит. Начнем с наиболее частого вопроса: "Как же собирать образ контейнера с помощью containerd или cri-o?"И ответ очень простой: "Никак". Потому что containerd и cri-o предназначены только для <strong>запуска</strong> контейнеров из готовых образов. Плюс они умеют еще несколько вещей, необходимых для эксплуатации:<ul><li>загрузка образов из интернета;</li>
8
Давайте же разберемся, чем нам это грозит. Начнем с наиболее частого вопроса: "Как же собирать образ контейнера с помощью containerd или cri-o?"И ответ очень простой: "Никак". Потому что containerd и cri-o предназначены только для <strong>запуска</strong> контейнеров из готовых образов. Плюс они умеют еще несколько вещей, необходимых для эксплуатации:<ul><li>загрузка образов из интернета;</li>
9
<li>просмотр информации о запущенных подах и контейнерах;</li>
9
<li>просмотр информации о запущенных подах и контейнерах;</li>
10
<li>удаление образов, подов и контейнеров;</li>
10
<li>удаление образов, подов и контейнеров;</li>
11
<li>подключение внутрь контейнера (запуск внутри контейнера процесса с bash).</li>
11
<li>подключение внутрь контейнера (запуск внутри контейнера процесса с bash).</li>
12
</ul>Но они не умеют ни собирать новый образ, ни пушить его в какой-либо registry. И как теперь быть?<p>Начнем с того, что в 2015 году "Docker и остальные лидеры индустрии контейнеризации" основали <a>Open Container Initiative (OCI)</a> и опубликовали в ее рамках <a>спецификацию</a> формата хранения образов контейнеров. Эту спецификацию поддерживают Docker, containerd и cri-o, так что образ, собранный с помощью docker build на машине разработчика, должен без проблем запуститься с помощью containerd или cri-o. И так и происходит в подавляющем большинстве случаев, но время от времени попадаются "нюансы реализации". Эти нюансы, конечно, исправляются, но иногда не так быстро, как хотелось бы. </p>
12
</ul>Но они не умеют ни собирать новый образ, ни пушить его в какой-либо registry. И как теперь быть?<p>Начнем с того, что в 2015 году "Docker и остальные лидеры индустрии контейнеризации" основали <a>Open Container Initiative (OCI)</a> и опубликовали в ее рамках <a>спецификацию</a> формата хранения образов контейнеров. Эту спецификацию поддерживают Docker, containerd и cri-o, так что образ, собранный с помощью docker build на машине разработчика, должен без проблем запуститься с помощью containerd или cri-o. И так и происходит в подавляющем большинстве случаев, но время от времени попадаются "нюансы реализации". Эти нюансы, конечно, исправляются, но иногда не так быстро, как хотелось бы. </p>
13
<p>Если вы за стабильность и минимальные изменения - запускайте новые кластеры с containerd в качестве движка контейнеризации. А на машинах разработчиков и CI runners сборки смело оставляйте Docker.</p>
13
<p>Если вы за стабильность и минимальные изменения - запускайте новые кластеры с containerd в качестве движка контейнеризации. А на машинах разработчиков и CI runners сборки смело оставляйте Docker.</p>
14
<p>Если вы выбрали безопасность из коробки, кровавый enterpise и платную поддержку, то наверняка используете <a>Openshift </a>(платформа на базе Kubernetes), в котором для запуска контейнеров применяется cri-o. А для сборки образов RedHat создала отдельную утилиту, которую назвала <a>buildah</a>. В отличие от Docker, этой утилите для сборки образов не нужен запущенный движок контейнеризации.</p>
14
<p>Если вы выбрали безопасность из коробки, кровавый enterpise и платную поддержку, то наверняка используете <a>Openshift </a>(платформа на базе Kubernetes), в котором для запуска контейнеров применяется cri-o. А для сборки образов RedHat создала отдельную утилиту, которую назвала <a>buildah</a>. В отличие от Docker, этой утилите для сборки образов не нужен запущенный движок контейнеризации.</p>
15
<p>Из альтернатив можно еще посоветовать <a>kaniko </a>- утилиту для сборки образов от Google. Она поставляется в виде образа контейнера, так что для запуска ей нужен движок контейнеризации, но для сборки - уже нет.</p>
15
<p>Из альтернатив можно еще посоветовать <a>kaniko </a>- утилиту для сборки образов от Google. Она поставляется в виде образа контейнера, так что для запуска ей нужен движок контейнеризации, но для сборки - уже нет.</p>
16
<h2>Эксплуатация</h2>
16
<h2>Эксплуатация</h2>
17
С разработкой разобрались, теперь пора перейти к самому интересному: что делать инженеру, который зашел на узел, чтобы разобраться, почему статус узла стал NotReady, набрал привычную команду docker ps, а в ответ получил docker: command not found.<p>Фундаментальная проблема тут в том, что Docker изначально ориентировался на взаимодействие с человеком, и к последним версиям у него появился очень классный и удобный консольный интерфейс, а вот компоненты, реализующие CRI - они <em>by design </em>ориентированы на взаимодействие с программой-оркестратором, а не с человеком.</p>
17
С разработкой разобрались, теперь пора перейти к самому интересному: что делать инженеру, который зашел на узел, чтобы разобраться, почему статус узла стал NotReady, набрал привычную команду docker ps, а в ответ получил docker: command not found.<p>Фундаментальная проблема тут в том, что Docker изначально ориентировался на взаимодействие с человеком, и к последним версиям у него появился очень классный и удобный консольный интерфейс, а вот компоненты, реализующие CRI - они <em>by design </em>ориентированы на взаимодействие с программой-оркестратором, а не с человеком.</p>
18
<p>Но внезапно оказалось, что человек тоже хочет взаимодействовать с CRI, и для этого была написана отдельная утилита <a>crictl</a>, которая представляет собой CLI-консоль к CRI-движку. То есть одна и та же утилита crictl позволяет посмотреть список контейнеров запущенных с помощью containerd или cri-o.</p>
18
<p>Но внезапно оказалось, что человек тоже хочет взаимодействовать с CRI, и для этого была написана отдельная утилита <a>crictl</a>, которая представляет собой CLI-консоль к CRI-движку. То есть одна и та же утилита crictl позволяет посмотреть список контейнеров запущенных с помощью containerd или cri-o.</p>
19
<p>Очень часто встречается информация, что можно просто заменить docker на crictl и вместо docker ps набирать crictl ps. Сказка заканчивается, когда вы попробуете запустить контейнер с помощью crictl run , и внезапно оказывается, что сначала надо создать манифест для PodSandbox, а уже потом написать манифест, описывающий контейнер, который будет запущен в этом поде.С другой стороны надо помнить, что CRI был придуман командой разработки Kubernetes и для Kubernetes, и поэтому, на мой взгляд, правильнее было бы его назвать Pod Runtime Interface.</p>
19
<p>Очень часто встречается информация, что можно просто заменить docker на crictl и вместо docker ps набирать crictl ps. Сказка заканчивается, когда вы попробуете запустить контейнер с помощью crictl run , и внезапно оказывается, что сначала надо создать манифест для PodSandbox, а уже потом написать манифест, описывающий контейнер, который будет запущен в этом поде.С другой стороны надо помнить, что CRI был придуман командой разработки Kubernetes и для Kubernetes, и поэтому, на мой взгляд, правильнее было бы его назвать Pod Runtime Interface.</p>
20
<p>И crictl оптимизирован для работы с контейнерами, созданными kubelet. Например, в выводе не показываются служебные PODSandbox контейнеры, а имена контейнеров показываются в более понятном виде, чем в Docker. Да и возможности по работе с CLI постоянно улучшаются.</p>
20
<p>И crictl оптимизирован для работы с контейнерами, созданными kubelet. Например, в выводе не показываются служебные PODSandbox контейнеры, а имена контейнеров показываются в более понятном виде, чем в Docker. Да и возможности по работе с CLI постоянно улучшаются.</p>
21
<p>Еще одна вещь, с которой часто возникают проблемы понимания. Многие путают docker (бинарник из пакета docker-cli) и dockerd (демон, управляющий контейнерами). Пытаются сделать crictl image save/load и внезапно оказывается, что в crictl таких команд нет. А на созданный issue авторы устало отвечают, что crictl - консоль для CRI runtime, который должен только запускать контейнеры, ну еще образ скачать из registry. А про манипуляции с образами в спецификации ничего нет.</p>
21
<p>Еще одна вещь, с которой часто возникают проблемы понимания. Многие путают docker (бинарник из пакета docker-cli) и dockerd (демон, управляющий контейнерами). Пытаются сделать crictl image save/load и внезапно оказывается, что в crictl таких команд нет. А на созданный issue авторы устало отвечают, что crictl - консоль для CRI runtime, который должен только запускать контейнеры, ну еще образ скачать из registry. А про манипуляции с образами в спецификации ничего нет.</p>
22
<p>Но выход есть! Для манипуляции с образами контейнеров можно воспользоваться дополнительной утилитой <a>skopeo</a>, если вы используете cri-o, или утилитой <a>ctr</a> из комплекта containerd.</p>
22
<p>Но выход есть! Для манипуляции с образами контейнеров можно воспользоваться дополнительной утилитой <a>skopeo</a>, если вы используете cri-o, или утилитой <a>ctr</a> из комплекта containerd.</p>
23
<p>И в завершение дам ответ на идею, которая наверняка возникнет после прочтения этой статьи:</p>
23
<p>И в завершение дам ответ на идею, которая наверняка возникнет после прочтения этой статьи:</p>
24
<p>"Подождите, но ведь есть containerd, с которым умеет работать kubelet и dockerd! Давайте поставим Docker (три пакета docker-cli, docker, containerd), kubelet настроим на работу с containerd напрямую, и у нас при этом останется старая добрая команда docker".</p>
24
<p>"Подождите, но ведь есть containerd, с которым умеет работать kubelet и dockerd! Давайте поставим Docker (три пакета docker-cli, docker, containerd), kubelet настроим на работу с containerd напрямую, и у нас при этом останется старая добрая команда docker".</p>
25
<p>Вот только docker ps не покажет контейнеров, запущенных kubelet через CRI. Произойдет это из-за того, что containerd умеет работать в многопользовательском режиме, и docker с kubelet запускают контейнеры в различных <a>containerd namespaces</a> - moby и k8s.io соответственно (не надо путать их с <a>kubernetes namespaces</a>). Посмотреть на контейнеры в разных неймспейсах можно с помощью утилиты ctr -n <ns_name> container ls.</p>
25
<p>Вот только docker ps не покажет контейнеров, запущенных kubelet через CRI. Произойдет это из-за того, что containerd умеет работать в многопользовательском режиме, и docker с kubelet запускают контейнеры в различных <a>containerd namespaces</a> - moby и k8s.io соответственно (не надо путать их с <a>kubernetes namespaces</a>). Посмотреть на контейнеры в разных неймспейсах можно с помощью утилиты ctr -n <ns_name> container ls.</p>