Pull to refresh
VK
Building the Internet

Жизнь после Docker: как команда VK Cloud переходила на CRI-O

Reading time7 min
Views19K


Kubernetes прекратил поддержку Docker и отказался от dockershim — прокладки между kubelet и Docker, которая позволяет последнему работать с CRI. В итоге разработчики столкнулись с необходимостью использования новых, совместимых с CRI, движков для запуска контейнеров. Из числа общеизвестных таких два — containerd и CRI-O.

Меня зовут Александр Чадин, я руководитель команды разработки в VK Cloud. Расскажу, как мы искали замену Docker для сервиса Cloud Containers, на что ориентировались при выборе нового движка, как внедряли новое решение и с какими подводными камнями при этом столкнулись. 

Подробнее о предпосылках


Docker — один из удобных и популярных инструментов для работы с контейнерами, который можно использовать как на сервере, так и на любом ПК. Он содержит набор инструментов, благодаря которым может выполнять несколько задач:

  • создавать образы контейнеров;
  • работать с репозиториями;
  • создавать, запускать и управлять контейнерами.

Фактически Docker — мультитул для работы с контейнерами.



Но это все-таки вендорское решение. У него открыт канал обратной связи, но, учитывая последние новости, путь этого решения сложно предсказать. Устранение уязвимостей, добавление новых функций, расширение совместимостей и других обновлений — все зависит от вендора.

Для регулирования экосистемы контейнерных технологий были разработаны два основных стандарта. Они помогают добиться совместимости между различными инструментами и избежать зависимости от одной компании или проекта.

  • Container Runtime Interface (CRI). CRI — API, который Kubernetes использует для управления различными Container Runtime, создающими и управляющими контейнерами. CRI упрощает работу с Container Runtime. Вместо того, чтобы включать в Kubernetes поддержку каждой из них, используется стандарт CRI. При этом задача управления контейнерами полностью ложится на Container Runtime.
  • Open Container Initiative (OCI). Определяет стандарт образов и контейнеров.

С появлением и внедрением стандартов Kubernetes стал поддерживать только те Container Runtime, которые работают с Container Runtime Interface (CRI). При этом Docker не поддерживает этот стандарт напрямую и использует для работы с Kubernetes прослойку — компонент dockershim. 



Кроме того, у Docker при обновлении иногда изменяется API. Поэтому приходится привязываться к конкретным версиям Docker, чтобы быть уверенным, что все работает как надо.

В результате от монолитного Docker, выполняющего сразу много задач, отказались — Kubernetes полностью прекратил его поддержку. Разработчики, в том числе и наша команда, столкнулись с необходимостью переезда на совместимые с CRI инструменты. Среди альтернатив — движки containerd и CRI-O, отвечающие за запуск контейнеров. 

  • сontainerd — движок Container Runtime, который выделен из проекта Docker. Реализует спецификацию CRI. Умеет скачивать образы из репозитория, управлять ими, передавать их Container Runtime нижнего уровня. containerd использует собственный плагин для поддержки CRI в Kubernetes.
  • CRI-O — движок Container Runtime, реализующий Container Runtime Interface (CRI). Реализация создана с нуля при поддержке Red Hat, IBM, Intel и SUSE как Container Runtime для Kubernetes. Это альтернатива containerd, которая также позволяет загружать образы контейнеров из репозиториев, управлять ими и запускать Container Runtime нижнего уровня для запуска процессов контейнера.



CRI-O или сontainerd: что и как мы выбрали для себя


CRI-O или сontainerd — разные реализации CRI. Ввиду стандартизации компонента они практически идентичны, в том числе и по совместимости с другими узлами Kubernetes. Фактически движки отличаются только на уровне технических параметров — именно они и являются определяющими при выборе.

При поиске движка для нашего облачного Kubernetes Cloud Containers мы учитывали:

  • скорость загрузки и остановки контейнеров;
  • безопасность;
  • структурированность компонентов;
  • поддержку со стороны сообщества.

В результате изучения уже проведенных исследований и собственных тестов мы обнаружили, что:

Containerd в связке с runc работает быстрее CRI-O. Но отличие незначительно и глобально не влияет на производительность всей системы. На графике — сравнение скорости обработки запросов (в секундах) при одинаковых параметрах системы.



CRI-O безопаснее containerd. CRI-O, в отличие от containerd, имеет ограниченный набор функций и внутренних компонентов. Это уменьшает потенциальную поверхность атаки и снижает уязвимость компонента.

У CRI-O понятная структура компонентов. CRI-O состоит из нескольких компонентов, отвечающих за хранилище, образы, сетевой интерфейс, мониторинг и безопасность. Все, что надо для работы с CRI-O, есть в открытом доступе, в том числе сценарии использования, инструкции по внедрению и настройке.

У CRI-O шире поддержка со стороны коммьюнити. У CRI-O много пользователей и активное сообщество. Получить помощь в решении возникающих проблем легче, чем с containerd.

Взвесив плюсы и минусы, мы выбрали для себя CRI-O.

Внедрение CRI-O


Внедрять CRI-O мы начали с версии Kubernetes 1.21. Выполняли его силами двух разработчиков. Алгоритм стандартный: перед настройкой отключаем swap, все операции на хосте выполняем от имени пользователя root.

Настройка операционной системы, установка предварительных зависимостей для CRI-O


1. Обновляем операционную систему:

dnf -y update

2. Настраиваем firewall и SELinux. В зависимости от окружения, можно настраивать по документации или принять сторонний firewall, изменив зону:

firewall-cmd --set-default-zone trusted
firewall-cmd --reload

Также можно выключить firewall:

 systemctl disable --now firewalld

SELinux выключаем или переводим в режим permissive:

setenforce 0

sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

3. Загружаем модули ядра и пакеты. Настраиваем автоматическую загрузку модуля br_netfilter при запуске системы:

modprobe overlay
modprobe br_netfilter
echo "br_netfilter" >> /etc/modules-load.d/br_netfilter.conf
dnf -y install iproute-tc

4. Активируем форвардинг пакетов и настраиваем корректную обработку трафика:

cat > /etc/sysctl.d/99-kubernetes-cri.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

Применяем внесенные настройки:

sysctl --system

5. Задаем нужную версию CRI-O. Она совпадает с версией Kubernetes:

export REQUIRED_VERSION=1.21

6. Добавляем нужные репозитории:

dnf -y install 'dnf-command(copr)'
dnf -y copr enable rhcontainerbot/container-selinux
curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/CentOS_8/devel:kubic:libcontainers:stable.repo
curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o:$REQUIRED_VERSION.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$REQUIRED_VERSION/CentOS_8/devel:kubic:libcontainers:stable:cri-o:$REQUIRED_VERSION.repo

7.Устанавливаем CRI-O:

dnf -y install cri-o

8. Активируем и запускаем образ CRI-O:

systemctl enable --now crio

9. Проверяем статус CRI-O:

systemctl status crio

Установка и активация Kubernetes


1. Добавляем репозиторий:

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

2. Устанавливаем Kubernetes версии 1.21:

dnf install -y kubelet-1.21* kubeadm-1.21* kubectl-1.21* --disableexcludes=kubernetes

3. Для использования демона CRI-O, до запуска и инициализации Kubernetes настраиваем конфигурационный файл /var/lib/kubelet/config.yaml и предварительно создаем нужный каталог:

mkdir /var/lib/kubelet
cat <<EOF > /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF

4. Добавляем в файл аргументы драйвера cgroup:

cat /dev/null > /etc/sysconfig/kubelet
cat <<EOF > /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS=--container-runtime=remote --cgroup-driver=systemd 
--container-runtime-endpoint='unix:///var/run/crio/crio.sock'
EOF

5. Активируем kubelet:

sudo systemctl enable --now kubelet

Дополнительно мы указали insecure_registries, в который внесли перечень приватных клиентских реестров, для которых пропускается TLS-проверка сертификатов.

Для наших клиентов переход на CRI-O получился прозрачным и бесшовным — он выполняется при апгрейде кластера: если у клиента был кластер на 1.20 с Docker, то при апгрейде на 1.21 он получает CRI-O.

Подводные камни, или Что следует учесть при переходе на CRI-O


CRI-O — стандартизованный компонент с исходной совместимостью с Kubernetes. Благодаря этому его можно внедрить на замену Docker без особых проблем. Но на практике мы убедились, что подводные камни, которые нужно учитывать, все же есть.

Важно проверить, как работают контейнеры, которые ранее взаимодействовали с Docker. Для этого мы проводили CNCF-тесты: создавали кластер с тремя мастер-нодами и тремя воркерами и запускали тесты командой 

./sonobuoy run --mode certified-conformance --plugin-env=e2e.E2E_EXTRA_ARGS="--allowed-not-ready-nodes=3"

Следует проверять наличие метрик из kubelet и их лейблов. Для этого можно с любой ноды выполнить запрос: 

curl --cacert <path-to-certificate-authority.crt> \
--cert <path-to-kubelet.crt>\
--key <path-to-kubelet.key>\
https://<node_ip>:10250/metrics/cadvisor

В полученном запросе у контейнерных метрик должны быть заполнены лейблы. Например:

container_cpu_cfs_throttled_periods_total{container="metrics-server",id="/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod8af71958_7609_48db_afe4_dc772d845654.slice/crio-f56572cefcf697840ad7a11cdaf0a6b4f6b074216a863447dc61d0287b1cd3c7.scope",name="k8s_metrics-server_metrics-server-6ffbbb5859-hch2c_kube-system_8af71958-7609-48db-afe4-dc772d845654_0",namespace="kube-system",pod="metrics-server-6ffbbb5859-hch2c"} 1788 1660899417380

Нужно учитывать, что cadvisor — утилита для сбора метрик с контейнеров и нод — имеет захардкоженный сокет crio. Из-за этого смена дефолтного сокета ведет к пустым лейблам в метриках.

Новая архитектура решения: что получили мы и конечные пользователи


После внедрения CRI-O мы получили следующую схему связей Kubernetes c runc и низкоуровневыми библиотеками.


Схема запуска контейнера в среде с CRI-O

Мы как провайдер Kubernetes as a Service получили более легковесное и управляемое решение (по сравнению с Docker). Также с внедрением CRI-O повысилась отказоустойчивость нашего Kubernetes — при необходимости компонент можно легко заменить аналогичным или совместимым с CRI, не допуская остановки всей системы. 

Для пользователей Cloud Containers переход на CRI-O прошел практически незаметно — изменился только формат сбора логов с контейнеров: Docker позволял собирать логи в json формате, а CRI-O собирает только plain-логи. При этом всегда можно настроить fluentd или fluentbit для преобразования логов в json-формат.

Вы прямо сейчас можете попробовать Kubernetes от VK Cloud. Для тестирования мы начисляем новым пользователям 3 000 бонусных рублей и будем рады вашей обратной связи.
Tags:
Hubs:
Total votes 38: ↑38 and ↓0+38
Comments9

Articles

Information

Website
vk.com
Registered
Founded
Employees
5,001–10,000 employees
Location
Россия
Representative
Миша Берггрен