Как вы, возможно, заметили, на этой неделе Docker Hub резко изменил политику и фактически уведомил о выселении за 30 дней почти все образы, управляемые сообществом.
Теперь они принесли извинения, чтобы «уточнить» несколько деталей, и услужливо убрали некоторые острые углы, но это все еще подчеркивает большую проблему. К счастью, есть решения.
Как было описано изначально, это было бы катастрофой: Docker Hub использовался в качестве хоста по умолчанию для инструментов, учебных пособий, демонстраций, сообщений в блогах, сценариев, определений развертывания, сборок CI и многого другого в течение многих лет, и все эти ссылки собирались break — самостоятельный левый пэд для экосистемы Docker. В их обновленной политике, похоже, теперь они не будут удалять какие-либо существующие изображения, но проекты, которые не заплатят, не смогут публиковать какие-либо изображения. новый изображения, поэтому они фактически потеряли контроль над пространством имен, которое они использовали для развертывания в своих сообществах, если они не приобретут полную групповую подписку. Многие этого не сделают.
Это интересный вызов. Даже если существующие образы не будут удалены, направление движения Docker Hub теперь ясно: они больше не хотят размещать ядро сообщества Docker, больше никаких халявы, платить или идти куда-то еще (небезосновательно, но что-то вроде ковра после целого десятилетия противоположного).
Если вы являетесь небольшим издателем изображений с открытым исходным кодом, сообществом или любителем, или если вы зависите от Docker Hub для бесплатной публикации изображений в любом качестве, у вас теперь есть проблема. Они не хотят, чтобы ты был там. Вы явно не их аудитория, и правила, скорее всего, будут ужесточены. Это небезосновательно — это их услуга, а хостинг платный — но это стоит рассмотреть и отреагировать соответствующим образом. Если вы не являетесь платным клиентом Docker Hub, пришло время покинуть Docker Hub.
Трудная часть заключается в том, что делать вместо этого.
Самостоятельное размещение реестра не бесплатно, и это больше работы, чем кажется: это надлежащая часть инфраструктуры и включает все вытекающие обязательства, от мониторинга до быстрого применения обновлений безопасности до управления загрузкой и дисковым пространством. Никто (не говоря уже о таких крошечных проектах) не хочет эту работу.
В качестве альтернативы, существует множество других бесплатных размещенных реестров Docker, а также платных сервисов, но прямой переход на один из них очень похож на то, что вы просто столкнетесь с той же проблемой через 6 месяцев, и вам придется везде менять ссылки на изображения. все сначала.
Что, если бы вы могли использовать свой собственный фиксированный URL-адрес реестра, в своем собственном домене и полностью под вашим контролем, но без необходимости самостоятельного размещения навсегда или даже фиксации в каком-либо конкретном реестре или покрытия всех расходов на пропускную способность и хранение?
Мы ищем способ:
- Ссылайтесь на свои изображения с адреса, которым вы полностью управляете (
docker pull docker.my-org.example.com/org/my-image
) - Сделайте это, сохраняя при этом возможность использовать любой реестр, размещенный в другом месте, или самостоятельно размещенный.
- Избегайте хранения, загрузки или предоставления контента отдельно. По крайней мере, на данный момент есть довольно много других реестров, которые с радостью сделают это для общедоступных изображений бесплатно, и даже если бы их не было, мы хотели бы избежать дополнительных задержек или платы за вход и выход из-за проксирования этого трафика.
- Возможность изменить резервный реестр, который мы будем использовать в будущем, без повторного изменения каких-либо адресов образов..
Что, если я скажу вам, что это на самом деле очень просто?
Изучение возможностей
Давайте поговорим о том, как это мог работать, а потом мы будем копаться в том, что docker pull
на самом деле делает, и составить быстрое решение (если вы просто хотите знать, как это сделать немедленно, сейчас самое время перейти к концу).
API-интерфейс реестра Docker работает на довольно простом HTTP, а API-интерфейсы HTTP имеют несколько различных решений, доступных для подобных ситуаций.
Классическое решение «хост в собственном домене» — использовать CNAME на уровне DNS. Это означает настройку DNS-записи в вашем домене, которая указывает на домен в другом месте, эффективно определяя псевдоним. Когда клиент пытается подключиться, он будет искать your-registry.example.com
найдите запись, ссылающуюся на вспомогательный реестр (registry.hub.docker.com
), и тогда все запросы будут отправляться туда.
Если бы это работало здесь, это было бы здорово! Нулевой хостинг не требуется, просто обработайте его на уровне DNS.
К сожалению, для этого требуется, чтобы целевой сервер правильно обрабатывал HTTP-запросы с вашим сторонним доменным именем в Host
заголовок, зная, что они должны обрабатываться как запросы к реальному сервису. По крайней мере, для Docker Hub это невозможно (конечно, не бесплатно — хотя, как и многие другие сервисы, это может предлагаться в качестве платного дополнения). Запросы, отправленные в Docker Hub с неправильным именем хоста, просто терпят неудачу:
> curl -I
HTTP/1.1 200 OK
...
> curl -I -H'Host: example.com'
HTTP/1.0 503 Service Unavailable
...
Я подозреваю, что это относится и ко многим другим реестрам, поэтому перенаправление только на уровне DNS отсутствует.
Следующий план: можем ли мы сделать это с помощью перенаправления и/или проксирования на уровне HTTP? В самом HTTP есть множество стандартных инструментов и подходов для этого, а также целая экосистема обратных прокси. Однако, к сожалению, не гарантируется, будут ли клиенты API обрабатывать перенаправления так, как нам хотелось бы, а проксирование, не сталкиваясь с другими проблемами, нетривиально.
Чтобы выяснить, сработает ли это, нам нужно покопаться в трафике Docker напрямую.
Как работает Docker Pull
Во-первых, давайте посмотрим, что на самом деле делает Docker pull под капотом.
Когда вы бежите docker pull
или сделать что-нибудь еще с Docker (например, создать образ), который запускает получение образа в пути, есть несколько запросов, которые должны произойти, чтобы загрузить полный образ, который вы ищете.
Чтобы разобраться в этом трафике, проще всего использовать инструмент отладки HTTP (например, HTTP-инструментарий), чтобы просмотреть необработанные взаимодействия, и настроить Docker для использования его в качестве HTTP-прокси (документы здесь) и доверять сертификату ЦС (здесь).
Если вы не очень увлечены этим, вы все можете пропустить это — я проделал за вас тяжелую работу. Вот что происходит, когда вы бежите docker pull nginx
:
Что мы имеем здесь:
- инициал
/v2/
запрос на проверку статуса API (документы здесь). В Docker Hub это обычно возвращает 401 с заголовками, перенаправляющими клиента для аутентификации. - Запрос аутентификации на
auth.docker.io
который возвращает JWT. - Запрос HEAD к URL-адресу базового изображения (
/v2/library/nginx/manifests/latest
), который возвращает ответ сdocker-content-digest
заголовок, содержащий хэш sha256:
-
Два запроса GET для определенных манифестов, оба получили 200:
/v2/library/nginx/manifests/sha256:aa0a...
(хэш из предыдущего заголовка ответа), который возвращает список манифестов, помеченных платформой:
/v2/library/nginx/manifests/sha256:942a...
(хэш платформы linux из предыдущего запроса), который возвращает хэши списка манифеста для отдельных слоев изображения.
-
Набор параллельных запросов для определенных хэшей больших двоичных объектов, все в формате
/v2/library/nginx/blobs/sha256:$HASH
.Каждый из них делает нет вернуть контент – они возвращают 307 редиректов на контент! В случае с Docker Hub они возвращают перенаправления на CDN с поддержкой Cloudflare:
- Чередующийся набор параллельных запросов к хосту реального изображения (
production.cloudflare.docker.com
), чтобы фактически получить содержимое конфигурации и слоев изображения.
После того, как клиент извлечет все слои и конфигурацию образа, они снова объединятся в образ Docker, который вы можете использовать непосредственно локально.
Прозрачная обертка реестра Docker
Все это очень интересно и дает нам хорошее представление о том, что происходит на сетевом уровне, так что мы можем приступить к тестированию, чтобы построить то, что я называю «фасадом реестра» (служба, которая находится впереди, но так же, как и оболочка, а не прокси).
Удобно, что в трафике выше мы видим, что редиректы уже есть, и работают! Это означает, что все клиенты Docker должен поддержка редиректов по крайней мере для /blobs/
запросы (иначе Docker Hub был бы непригоден для использования) и поэтому, вероятно, поддерживает их для всех запросов.
Итак, учитывая это, что произойдет, если мы просто сделаем то же самое непосредственно сами, создав правило для возврата 307 HTTP-перенаправлений со всех $OUR_HOST/*
URL-адреса соответствующих $OUR_REGISTRY/*
на любой запрос?
Бинго.
Это работает достаточно хорошо! Мы добавляем немного накладных расходов с дополнительным ответом перенаправления 307 на каждом шаге (каждый запрос с красным значком представляет собой введенное перенаправление), но они очень быстрые, все здесь отправляется успешно и отлично работает в каждом сценарии. Я тестировал. Определенно достаточно хорошо для начала (и, поскольку все это будет под нашим собственным контролем, мы можем итерировать, чтобы улучшить это решение в будущем).
Я протестировал это с помощью правила быстрой хакерской перезаписи в HTTP Toolkit — как вы делаете это в рабочей среде?
Оказывается, это тоже довольно просто: я создал крошечный контейнер Docker на основе Caddy (мне понравилась ирония публикации этого в Docker Hub), который вы можете развернуть непосредственно на любой платформе хостинга Docker, чтобы сделать это в кратчайшие сроки, или если вы у вас уже есть CDN или хостинговая платформа (например, Netlify), которая позволяет вам определять простые правила, такие как «перенаправить все запросы для X на один и тот же путь на хосте Y», тогда вы тоже можете использовать это.
В моем случае я использую Bunny CDN, у которого есть хорошая система правил, которая может сделать это очень легко, например:
В производственной среде вы, возможно, захотите ограничить эту функциональность только изображениями вашей собственной организации, чтобы она не использовалась в качестве фасада общего назначения для всех изображений и т. п., чтобы вы знали, что все запросы к вашему домену всегда будут получать ваш изображений. Контейнер на основе Caddy выше поддерживает это, устанавливая REGISTRY_ORG
переменная, например, httptoolkit
в этом случае будут доступны только эти изображения, а все остальные получат ошибку 403.
Если вы хотите ограничить подобные запросы самостоятельно с помощью других инструментов, вам просто нужно убедиться, что запросы ко всем путям URL, начинающимся с /v2/$YOUR_ORG/
перенаправляются вместе с конкретными /v2/
конечная точка – без этой последней аутентификация конечной точки не будет работать.
Как только это на месте, у вас все хорошо. В моем случае я развернул это как docker.httptoolkit.tech
так что теперь вы можете получить мои образы Docker с этого имени хоста, даже если они в настоящее время все еще размещены в Docker Hub, например:
> docker pull docker.httptoolkit.tech/httptoolkit/docker-socks-tunnel
В будущем я буду переносить свои образы в другое место, но я могу начать использовать этот адрес изображения немедленно, зная, что он всегда будет работать, подкрепленный любым реестром, который мне нравится, пока я контролирую этот домен.
Уклонение от следующего Dockerpocalypse
Если вы участвуете в проекте, затронутом этой проблемой, это то, что вы можете настроить прямо сейчас в качестве быстрой оболочки, прежде чем даже начать миграцию из Docker Hub, и вы можете начать переносить все свои документы и сценарии, чтобы сразу ссылаться на этот новый URL-адрес без каких-либо недостатков.
Что еще более важно, в любом случае это гарантирует, что какой бы реестр вы ни перенесли, это не повлияет на переключение в будущем, когда ваш новый выбранный реестр неизбежно также разорится / потеряет все ваши данные / изменит свои правила с уведомлением всего за 30 дней.
На данный момент этого достаточно (мне нужно вернуться к фактическому выполнению полной миграции для всех существующих изображений HTTP Toolkit), но я надеюсь, что это поможет другим в том же беспорядке. Если у вас есть комментарии, свяжитесь с Mastodon, Твиттерили отправить сообщение напрямую.