Прозрачно кешируем несколько Container Registry в CRI-O и Podman

Возможно, вы уже активно используете CRI-O и Podman, а может только смотрите на альтернативы Docker с осторожностью. Но, как бы там ни было, альтернативные решения создают конкуренцию монополисту Docker и предлагают новые и востребованные улучшения. Одна из таких особенностей, это исключение Docker Hub как корневого и основного источника образов контейнеров. Таким образом, снимается привязка к поставщику и появляются новые возможности, а одной из таких мы и поговорим.

Это инструкция по выбору решения и настройке прозрачного кеширования множества реестров контейнеров для CRI-O, Podman, Buildah, Skopeo и прочих инструментов, работающих с образами контейнеров OCI и использующих общую конфигурацию containers/common.

803c922dbeaf4ca5f3e23e3f0f964c46.jpg

Для чего нужен прокси?

Если у вас есть несколько экземпляров CRI-O или Podman, работающих в вашей инфраструктуре, например: кластер Kubernetes, сборочные агенты, физические или виртуальные машины на которых работают контейнеризированные приложения. Каждый такой узел при запуске контейнера обращается к реестру контейнеров и пытается скачать образ контейнера, если он остутствует локально в кеше или найдена новая версия. Мы развернем и настроим кеширующий прокси сервер, для получения образов контейнеров из быстрого локального источника, тем самым:

  • Обойдем ограничения на скачивание;

  • Увеличим скорость запуска контейнеров, единожды запрошеный контейнер осядет в кеш и все последующие запросы будут отработаны гораздо быстрее, особенно актуально для узких интернет каналов;

  • Увеличим доступность и стабильность. Аптайма 100% не существует, и в самый ответственный момент публичный реестр контейнеров может оказаться недоступным, по разным причинам, имя кеш мы с большей вероятностью, по-прежнему сможем запускать контейнеры, а в случае недоступности кеша контейнер будет получен на прямую;

  • Сэкономи трафик (если это еще кому-то актуально);

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

А самое главное, это будет прозрачный кеш, т.е. людям работающим в этих окружениях не нужно будет даже знать или как-то дополнительно заботится о том, что есть прокси сервер и его нужно использовать. Это просто будет работать и даже в закрытом контуре.

image-loader.svg

Проксирование будем настраивать для семи популярных реестров контейнеров:

  • docker.io

  • quay.io

  • gcr.io

  • k8s.gcr.io

  • ghcr.io

  • mcr.microsoft.com

  • registry.gitlab.com

Этот список может быть расширен любыми другими реестрами контейнеров, к примеру вы можете добавить ваш личный реестр или запроксировать только определенную группу (namespace, организацию).

image-loader.svg

Отличие использования зеркал

Docker — позволяет указать список registry-mirrors только для основного зеркала (librarry), это означает, что только Docker Hub будет проксироваться прозрачно, для прочих реестров контейнеров будет необходимо использовать измененный адрес контейнера proxy.host.tld/registry.host.tld/namespace/image:tag. Есть конечно обходной путь с использованием HTTPS_PROXY, но этот метод также не лишен недостатков.

CRI-O и Podman — предоставляет возможность настроить перечень реестров контейнеров и зеркал для каждого реестра персонально, чем мы непременно воспользуемся.

Конфигурация registries.conf

‎Все настройки мы будем выполнять в конфигурационных файлах из проекта containers/image. Здесь вы можете найти документацию к файлу registries.conf.

А еще, для большего удобства, вы можете хранить конфигурацию в отдельных файлах в каталоге /etc/containers/registries.conf.d/ или использовать персональные настройки в пользовательском каталоге $HOME/.config/containers/registries.conf

А это пример конфигурации реестров и их зеркал:

unqualified-search-registries = ["my-registry.tld"]

[[registry]]
prefix = "my-registry.tld/namespace"
location = "internal-registry.tld/project-one"

[[registry.mirror]]
location = "container-mirror.local/namespace"

[[registry.mirror]]
location = "container-mirror-2.local/mirrors/namespace"
insecure = true

Пример того как будет обработан запрос скачивания образа  namespace/image:tag

  1. Сначала будет попытка найти алиас [aliases] из registries.conf для образа namespace/image, пример имеющихся алиасов вы найдете в файле
    /etc/containers/registries.conf.d/000-shortnames.conf

  2. Далее будет попытка найти образ на предложенном из списка unqualified-search-registries узле, так как указан всего один узел, поиск произойдет автоматически на my-registry.tld

  3. Первая попытка получения образа произойдет по адресу
    container-mirror.local/namespace/image:tag

  4. Иначе, будет запрошен адрес
    container-mirror-2.local/mirrors/namespace/image:tag
    игнорируя проверку сертификата

  5. Иначе, обратимся в оригинальный источник
    internal-registry.tld/project-one/image:tag

Выбор решения

Существует несколько решений, позволяющих развернуть локально кеширующий прокси сервер для образов контейнеров, рассмотрим некоторые популярные решения:

  • Собственная реализация — можно реализовать что-то своё взяв библиотеку distribution или сконфигурировать Nginx, Squid или другой подходящий прокси. Но мы не первопроходцы в этом деле, и можно обратить внимание на проект Docker Registry Proxy реализованный на Nginx или Docker Registry Cache основанный на Squid. Это самое гибкое и легковесное решение позволяющее проксировать любое количество репозиториев, а при помощи Lua или njs возможно реализовать почти любые идеи, но это потребует дополнительных компетенций и добавит сложности в обслуживании. Врят ли это то, что вы искали.

  • Distribution — стандартное решение для запуска реестра контейнеров с открытым исходным кодом, совместимо со спецификацией OCI, библиотека используется в реализации таких проектов как: Docker Hub, GitHub Container Registry, GitLab Container Registry, VMware Harbor Registry и прочих. По мимо хранения ваших контейнеров также поддерживает проксирование одного репозитория, для проксирования нескольких репозиториев, придется запустить по экземпляру приложения на каждый репозиторий.

  • Harbor — зрелое решение для запуска реестра контейнеров от VMware с открытым исходным кодом. Имеет web-интерфейс, ролевой контроль доступа, интеграцию c AD и OIDC, аудит, сканирование уязвимостей и прочие возможности. Позволяет создать прокси-репозиторий на каждый реестр контейнеров. GitHub.

  • Quay и Project Quay — коммерческий и свободно распространяемый реестр контейнеров от Red Hat. По функциональности схож с Harbor, но имеет более гибкие возможности по разграничению прав доступа, основным отличием для нас является зеркалирование удаленного реестра контейнеров. Здесь это именно зеркалирование в отличии от прозрачного кеширования, что может оказаться избыточным и не подходящим при решении нашей задачи. ProjectQuay GitHub.

  • Sonatype Nexus — коммерческий и свободно распространяемый реестр артефактов от Sonatype. Это настоящий комбайн для хранения и проксирования артефактов для огромного множества поддерживаемых репозиториев, таких как: Nuget, NPM, Maven, APT, YUM, Conan, Docker и прочих. Как рестр образов контейнеров, Nexus уступает Quay и Harbor по обилию возможностей, но вполне успешно справляется с проксированием нескольких реестров и позволяет объединить их в группы. Прекрасно подойдет вам, если в вашем окружении выполняются сборочные пайплайны, вы сомжете собрать весь необходимый кеш артефактов через единый сервис. Nexus OSS GitHub.

  • JFrog Artifactory — коммерческое решение схожее по функциональности с Sonatype Nexus. Решения довольно похожи, можно посмотреть сравнение решений от Sonatype, и от JFrog.

image-loader.svg

Реализация класса «Стяжки и синяя изолента»

Изобретать свой велосипед мы с вами не будем, а пожалуй воспользуемся уже готовым Docker Registry Proxy, для демонстрации возможностей этого решения будет достаточно. Тем не менее, изучив образ контейнера Docker Registry Proxy, вы сможете реализовать своё решение под ваши нужды.

Запустим контейнер:

mkdir -p ./containers-registry-proxy/cache
mkdir -p ./containers-registry-proxy/certs

podman run --rm --detach --name containers-registry-proxy \
  --publish 0.0.0.0:3128:3128 \
  --env ENABLE_MANIFEST_CACHE=true \
  --env REGISTRIES="quay.io gcr.io k8s.gcr.io ghcr.io mcr.microsoft.com registry.gitlab.com" \
  --volume "$(pwd)/containers-registry-proxy/cache":/docker_mirror_cache \
  --volume "$(pwd)/containers-registry-proxy/certs":/ca \
  rpardini/docker-registry-proxy:0.6.4

Вот и всё, прокси запущен, в отличии от прочих решений, данный пример работает как HTTP прокси, и требует указания адреса прокси сервера в переменных HTTP_PROXY и HTTPS_PROXY. Для более тонкой настройки обратитесь к документации проекта.

⚠️ Будьте готовы встретить проблемы и необходимость искать ошибки и дорабатывать решение под свои потребности.

Оценка решения: