Стадия жизненного цикла модуля: Preview
Как устроена миграция на модуль registry?
Управлять путём загрузки образов в Deckhouse Platform (DP) можно двумя способами:
- предыдущая реализация — секция
registryв ModuleConfigdeckhouseс режимамиUnmanaged,Direct,ProxyиLocal; - текущая реализация — этот модуль, настраиваемый через ModuleConfig
registry.
Миграция — это передача пути загрузки от предыдущей реализации модулю. Её не нужно запускать вручную, и отдельной команды для неё нет: модуль принимает управление автоматически после обновления кластера на релиз, в котором он появился. Единственное условие — к этому моменту предыдущая реализация должна освободить путь загрузки. Обе реализации настраивают на каждом узле одно и то же — из какого хранилища образов контейнеров container runtime загружает образы и с какими учётными данными, — поэтому они никогда не управляют кластером одновременно.
Что нужно сделать для передачи управления и когда — зависит от режима, в котором работает предыдущая реализация. Режим указан в параметре settings.registry.mode ModuleConfig deckhouse:
d8 k get mc deckhouse -o jsonpath='{.spec.settings.registry.mode}'
Пустой вывод означает режим Unmanaged: настройки хранилища образов контейнеров в этом кластере никогда не задавали.
Такому кластеру — как и любому другому в Unmanaged — для миграции ничего делать не нужно: передача управления произойдёт сама.
| Режим | Что сделать | Когда |
|---|---|---|
Unmanaged |
Ничего — передача произойдёт сама | — |
Direct |
Настроить модуль: mode: Managed с primary.upstream |
До обновления |
Proxy |
Перевести кластер в Unmanaged |
До обновления |
Local |
Выполнить процедуру для изолированных кластеров | До обновления |
Подготовьте кластер до обновления: обновление на релиз с модулем заблокировано, пока кластер работает в режиме Proxy или Local, а также в режиме Direct без настроенного ModuleConfig registry. В новом релизе нет ни компонентов предыдущей реализации, ни кода, который переключает её режимы, поэтому вся подготовка выполняется на текущем релизе.
Режиму Direct переключение не требуется. Настройки модуля намеренно принимаются на релиз
раньше: записанные до обновления, они хранятся без эффекта и срабатывают на первой итерации
согласования модуля после обновления.
Кластер записывает, на какой реализации он фактически работает (как это проверить). Пока передача управления невозможна, обновление на релиз с модулем блокируется, и в сообщении об ошибке сказано, что сделать.
На какой реализации работает мой кластер?
Когда модуль принимает управление путём загрузки, он записывает это в секрет
registry-v2-switch. Проверьте, существует ли секрет:
d8 k -n d8-system get secret registry-v2-switch >/dev/null 2>&1 \
&& echo "текущая реализация" || echo "предыдущая реализация"
Если кластер всё ещё на предыдущей реализации, модуль сообщает причину на каждой итерации
согласования, а в кластере срабатывает алерт
D8RegistryMigrationPending. Посмотреть, чего он ждёт:
d8 k -n d8-system get secret registry-state -o jsonpath='{.data.state}' | base64 -d | head
Как мигрировать из режима Unmanaged?
В режиме Unmanaged предыдущая реализация не управляет путём загрузки, поэтому готовить
нечего: передача управления произойдёт сама.
-
Если кластер переводится в
Unmanagedиз другого режима, дождитесь завершения перехода. В статусе должно бытьmode: Unmanagedбез ожидающего целевого режима:d8 k -n d8-system get secret registry-state -o jsonpath='{.data.state}' | base64 -d | head -
Обновите кластер на релиз с текущей реализацией модуля (или, если он уже обновлён, просто дождитесь следующей итерации согласования). Модуль примет управление автоматически, и поведение не изменится: режим модуля по умолчанию — тоже
Unmanaged, поэтому кластер продолжит загружать образы из того же хранилища образов контейнеров, что и раньше. -
Чтобы модуль начал управлять путём загрузки, задайте
mode: Managedв ModuleConfigregistryи укажите хранилище образов контейнеров, из которого загружать образы. Готовая конфигурация для вашего кластера публикуется в секретеregistry-suggested-config; как её применить, показано в примере «Включение модуля».
Как мигрировать из режима Direct?
В режиме Direct узлы загружают образы через внутрикластерный адрес, который обслуживает
прокси предыдущей реализации. Модуль обслуживает тот же адрес, поэтому миграция — это прямая
передача адреса: переход через Unmanaged не нужен, и компоненты не перезапускаются.
-
Настройте модуль до обновления — без этой конфигурации обновление заблокировано. Возьмите значения из секции
registry.directв ModuleConfigdeckhouse:imagesRepoразделяется наhostиpath, учётные данные — тот жеlicense(илиusername/password), плюсca, если хранилище образов контейнеров его требует:apiVersion: deckhouse.io/v1alpha1 kind: ModuleConfig metadata: name: registry spec: enabled: true version: 1 settings: mode: Managed primary: upstream: scheme: HTTPS host: registry.deckhouse.io path: /deckhouse/ee auth: license: <LICENSE_KEY>Предыдущий релиз сохраняет эти настройки, но не действует по ним, поэтому до обновления в кластере ничего не меняется.
-
Обновите кластер. Передача произойдёт на следующей итерации согласования модуля, и загрузка образов всё это время работает: Service и прокси предыдущей реализации продолжают обслуживать внутрикластерный адрес, пока агент модуля не примет его на каждом узле, и только после этого контроллер их удаляет.
-
Следите за ходом передачи:
d8 k -n d8-system get secret registry-v2-switch >/dev/null 2>&1 && echo "управление передано" d8 k get registrynode -o custom-columns='NODE:.metadata.name,READY:.status.reconciled,SERVING:.status.proxyListening'Для ориентира — порядок и масштаб событий: передача фиксируется через пару минут после старта новой версии, агент появляется на узлах ещё через несколько минут, а объекты предыдущей реализации удаляются вскоре после этого. Загрузка образов работает на каждом из этих этапов.
Как мигрировать из режима Proxy?
Режим Proxy держит на каждом узле собственный прокси со своими сертификатами — состояние,
которое модуль перенять не может. Поэтому кластер сначала нужно перевести в Unmanaged, и
сделать это можно только до обновления.
-
В ModuleConfig
deckhouseзадайтеregistry.mode: Unmanaged, сохранив тот же адрес хранилища образов контейнеров и учётные данные. Готовые манифесты приведены в примерах переключения режимов. Все узлы будут перенастроены на загрузку напрямую из внешнего хранилища образов контейнеров, поэтому кеширование, которое давалProxy, пропадёт до шага 4. -
Дождитесь завершения перехода —
mode: Unmanagedбез ожидающего целевого режима. Кластер, застигнутый посреди перехода, мигрировать нельзя; в статусе видно, в какой режим он ещё переключается:d8 k -n d8-system get secret registry-state -o jsonpath='{.data.state}' | base64 -d | head -
Обновите кластер. Передача произойдёт на следующей итерации согласования модуля и не изменит поведения: в режиме
Unmanagedмодуль тоже не управляет путём загрузки. -
Чтобы вернуть кеширование внутри кластера, задайте
mode: Managedсstorage.cache: trueи тем же внешним хранилищем образов контейнеров. Учтите, что кеш модуля устроен иначе, чемProxy: одно хранилище с репликами на master-узлах вместо прокси на каждом узле. Прежде чем включать его на кластере, где на master-узлах мало свободного места, прочитайте, как кеш наполняется и очищается.
Как мигрировать изолированный кластер из режима Local?
В режиме Local у кластера нет внешнего хранилища образов контейнеров — его роль выполняет хранилище внутри самого кластера. Поэтому стандартная процедура миграции здесь не работает: она проходит через режим Unmanaged, в котором каждый узел загружает образы напрямую из внешнего хранилища образов контейнеров, а такому кластеру загружать их неоткуда.
Выход в том, чтобы подключить к кластеру внешнее хранилище образов контейнеров на время миграции. Оно запускается в том же кластере, но вне неймспейсов DP. Кластер переключается на него и обновляется, а когда хранилище модуля наполнится образами, временное хранилище образов контейнеров удаляется.
Требования к диску
Перед началом убедитесь, что на master-узлах свободно место под четыре набора образов: на пике миграции одновременно существуют три копии набора — хранилище Local, временное хранилище образов контейнеров и наполняющееся хранилище модуля, — а место под четвёртый набор нужно как запас, который узел не должен исчерпать.
Масштаб можно оценить на примере набора образов в 13 ГБ (платформа с модулями): он занимает около 21 ГБ во временном хранилище образов контейнеров (там хранятся два релиза — тот, на котором кластер работает, и тот, на который обновляется) и увеличивает хранилище с 13 до 21 ГБ, не считая кеша образов самого узла и системы. На master-узле со 100 ГБ диска пик составляет около 38 ГБ, а на узле с 50 ГБ та же миграция упирается в вытеснение подов.
Запас нужен не для перестраховки. Когда свободное место на master-узле заканчивается, kubelet начинает вытеснять поды и удалять образы, которые считает неиспользуемыми, — а в кластере, чьё хранилище образов контейнеров работает внутри него самого, удалённым может оказаться образ самого хранилища образов контейнеров. Тогда хранилище остаётся без процесса, который его обслуживает, а узел — без места, откуда этот образ можно загрузить. При тестировании эта взаимная блокировка продержалась 99 минут, и разрешить её удалось только ручной загрузкой образов на узел. Поэтому рассчитайте место заранее и следите за диском, пока идёт миграция.
Часть места экономится за счёт того, что образы, уже хранящиеся на дисках master-узлов, хранилище модуля проверяет и принимает на месте, а не скачивает заново (шаг 6). Но превратить два разных релиза в один набор такая проверка не может: в хранилище оказываются и релиз, на котором работал старый кластер, и релиз, на котором работает новый, — отсюда и размер пика.
Порядок действий
Шаги 1–4 выполняются до обновления, на релизе, в котором ещё есть предыдущая реализация.
-
Запустите временный OCI-registry в собственном неймспейсе. Реализация хранилища образов контейнеров может быть любой, но оно должно отдавать TLS с сертификатом, который кластер сможет проверить, и DP не должна им управлять: что бы ни происходило с объектами модуля, временное хранилище образов контейнеров должно продолжать работать.
Кроме того, хранилище образов контейнеров должно быть доступно по одному и тому же адресу из двух мест: на шаге 3 из него загружают образы узлы, а на шаге 6 его читает syncer модуля, работающий в поде. Сертификат должен покрывать выбранный адрес. Варианты, проверенные на одном кластере:
Адрес С узла Из пода порт hostNetworkна IP самого узла (<NODE_IP>:5000)работает работает имя Service ( <SERVICE>.<NAMESPACE>.svc:<PORT>)не разрешается работает NodePort на IP узла работает не работает: operation not permittedИспользуйте первый вариант: запустите хранилище образов контейнеров с
hostNetwork: trueна одном узле, обращайтесь к нему по IP этого узла и добавьте этот IP в SAN сертификата — тогда вся миграция пройдёт на одном адресе. Имя Service выглядит аккуратнее, но перестаёт работать на шаге 3, потому что container runtime узла не разрешает имена через кластерный DNS. -
Загрузите набор образов во временное хранилище образов контейнеров: сначала скачайте его командой
d8 mirror pullна машине, у которой есть доступ к хранилищу образов контейнеров DP, а затем отправьте во временное хранилище образов контейнеров командойd8 mirror push. Именно эта копия набора учтена выше в расчёте места на диске. -
Направьте предыдущую реализацию модуля на временное хранилище образов контейнеров и переведите её в
Unmanaged: в ModuleConfigdeckhouseзадайтеregistry.mode: Unmanagedвместе с адресом, сертификатом CA и учётными данными временного хранилища образов контейнеров. После этого узлы начнут загружать образы из него, а хранилищеLocalуйдёт с пути загрузки — но его данные останутся на месте, в каталоге/opt/deckhouse/registryна master-узлах. -
Убедитесь, что переход завершён и образы действительно загружаются из временного хранилища образов контейнеров, — на этом шаге держится вся остальная миграция. Проверки те же, что и для любого кластера в
Unmanaged:d8 k -n d8-system get secret registry-state -o jsonpath='{.data.state}' | base64 -d | head -
Обновите кластер на релиз с модулем. Передача управления произойдёт на следующей итерации согласования, и всё это время кластер продолжит загружать образы из временного хранилища образов контейнеров.
-
Включите модуль: задайте
mode: Managedсstorage.cache: true, укажите временное хранилище образов контейнеров в качествеprimary.upstream— и добавьтеstorage.sourceв том же изменении. Хранилище модуля запустится на том же пути на хосте, который использовалLocal, поэтому уже имеющиеся на дисках образы не будут скачиваться заново: наполнение проверит их и дозагрузит только недостающее.Откладывать указание
storage.sourceнельзя: без него не получится убрать внешнее хранилище образов контейнеров на следующем шаге, потому что конфигурацияManagedбезprimary.upstreamпринимается только при заданномstorage.source, — следующий шаг будет отклонён с ошибкой'storage.source' is required when 'primary.upstream' is not set.В
storage.sourceполеbundleRef— это произвольное имя набора образов, аexpectedDigests— число различных дайджестов в нём. Посчитать их можно по bundle с шага 2:for tar in <BUNDLE_DIR>/*.tar; do tar -xOf "$tar" --wildcards '*index.json'; done | jq -r '.manifests[]?.digest' | sort -u | wc -lИзменение этих настроек перезапускает процесс хранилища образов контейнеров, поэтому ресурс RegistryStorage примерно на минуту перейдёт в
Failedс ошибкой чтения собственного хранилища. Это ожидаемо — дождитесь возврата вReady. -
Дождитесь, пока хранилище сообщит, что держит весь набор (
phase: ReadyиsafeToDropUpstream: true), и уберитеprimary.upstreamиз ModuleConfigregistry. После этого кластер снова изолирован — теперь уже на модуле:d8 k get registrystorage registry -o jsonpath='{.status.phase} {.status.safeToDropUpstream}{"\n"}' -
Удалите временное хранилище образов контейнеров и освободите занятый им диск.
Что означают алерты модуля registry?
Ни один из этих алертов не означает, что кластер перестал загружать образы. Большинство сообщает о состоянии, в котором всё работает, но не так, как задано в конфигурации, — такое состояние легко не заметить.
D8RegistryMigrationPending- Кластер всё ещё работает на предыдущей реализации модуля. Ничего не сломано — миграция не завершена. Дальнейшие действия описаны в разделе «Как устроена миграция на модуль registry».
D8RegistryConfigInvalid- Конфигурация отклонена; кластер продолжает работать с прежней. Причина — в
.status.conditionsресурсаregistryconfig/registry. D8RegistryNodeNotConverged- Агент на части узлов не применил выданную ему конфигурацию. Эти узлы продолжают загружать образы по старой конфигурации, и следующие изменения до них тоже не дойдут.
D8RegistryNodeRunningFromDisk- Часть узлов не может подключиться к API-серверу и маршрутизирует загрузки по копии конфигурации на диске. Этот резервный механизм работает как задуман: загрузки на таких узлах проходят успешно, поэтому больше ничто о проблеме не сообщит, — а конфигурация узлов тем временем может сколь угодно сильно отстать от кластерной.
D8RegistryStorageIncomplete- Часть реплик кеша не держит весь ожидаемый набор образов. Пока настроено внешнее хранилище образов контейнеров, на загрузки это не влияет, но его потерю кластер бы не пережил — и именно это блокирует переход в air-gap.
D8RegistryAirGapTransitionHeld- Вы убрали внешнее хранилище образов контейнеров из конфигурации, но модуль продолжает им пользоваться, потому что кеш пока не может обслуживать кластер сам. Это безопасный исход: отключение внешнего хранилища образов контейнеров при неполном кеше оставило бы узлы без источника образов. Алерт не разрешится сам, если кеш перестал наполняться.
D8RegistryUpstreamProbeFailing- Изменение основного внешнего хранилища образов контейнеров отклонено, и кластер продолжает пользоваться последним
работавшим. Метка
outcomeразличает три проблемы:unreachable— сеть или само хранилище образов контейнеров;auth— обычно истёкший лицензионный ключ;sentinel— хранилище образов контейнеров ответило и приняло учётные данные, но не содержит образов DP (обычно неверный путь репозитория). D8RegistryUpstreamRejected- Ресурс RegistryUpstream не принят, поэтому загрузки для указанного в нём хранилища образов контейнеров не
перехватываются. Метка
reasonговорит, конфликтует он с основным хранилищем образов контейнеров или с другим ресурсом, претендующим на то же имя. D8RegistryStorageNotReclaimed- Ни одна реплика не выполняла сборку мусора неделю. Сборка — единственный механизм, который удаляет данные из хранилища, поэтому, если она остановилась, диск рано или поздно заполнится. Подробнее о сборке — в разделе «Кеш растёт. Что его чистит».
D8RegistryStaleCacheData- На узле лежат данные кеша, которые никто не использует. Как освободить место, описано в разделе «Как удалить с узла оставшиеся данные кеша».
D8RegistryStoreWritesRefused- Кеш на master-узле перестал принимать запись: хранилище заняло
storage.size(BudgetExhausted) или дошло до места, оставленного для узла (ReserveExhausted). Узлу ничего не грозит, кеш продолжает отдавать то, что в нём есть; останавливается только его наполнение. Подробнее — в разделе «Что происходит, когда хранилище кеша заполнено». D8RegistryStoreNearlyFull- Хранилище на master-узле заняло больше 85% от
storage.size. Следующий релиз может не поместиться. D8RegistryStoreUnbounded- Кеш включён, а
storage.sizeне задан, поэтому единственный предел — место, оставленное для узла.
Как удалить с узла оставшиеся данные кеша?
При выключении кеша данные в /opt/deckhouse/registry намеренно сохраняются: если включить кеш
снова, он наполнится из того, что уже хранится на диске, а не будет скачивать всё заново — на
медленном канале это экономит часы. Автоматическое удаление сделало бы выключение кеша
необратимым, поэтому решение модуль оставляет вам: агент измеряет оставшиеся данные и поднимает
алерт D8RegistryStaleCacheData.
Чтобы узнать, сколько места занимают данные, используйте команду:
d8 k get registrynodes -o custom-columns=\
NODE:.metadata.name,STALE:.status.staleStorageDataBytes
Если включать кеш обратно вы не собираетесь, удалите каталог на узле:
ssh <NODE> 'du -sh /opt/deckhouse/registry && sudo rm -rf /opt/deckhouse/registry'
Кеш растёт. Что его чистит?
Кеш очищает сборка мусора, которую по расписанию выполняют сами реплики хранилища.
Это единственный механизм, который вообще что-то удаляет из хранилища. Каждый релиз DP добавляет новые образы, поэтому без сборки хранилище кластера, работающего долгое время, рано или поздно заполнится и перестанет принимать запись, — а изолированный кластер с заполненным хранилищем нельзя обновить.
Сборка удаляет образы релизов, которые кластер уже прошёл. Сохраняются:
- развёрнутый релиз и предыдущий — чтобы откат не скачивал заново то, к чему откатывается;
- всё, что новее развёрнутого релиза, — это обновление в процессе или, в изолированном кластере, релиз, загруженный намеренно;
- все теги, которые не являются версиями: имена каналов обновлений вроде
stable, плавающие теги, всё загруженное вручную. Сборщик не может знать, что они означают, поэтому не трогает их.
Сборщик намеренно осторожен. В изолированном кластере удаление ещё нужного блоба невосстановимо
без повторного d8 mirror push, а хранение ненужного стоит только места на диске. Поэтому
проход, который не может определить, что сохранять — например, когда не найден развёрнутый
релиз, — не делает ничего вовсе.
Чтобы посмотреть состояние и расписание сборки, используйте команды:
Посмотреть время последней сборки на каждой реплике и ошибки, если они были:
d8 k get registrystorage registry -o jsonpath='{.status.replicas}' | jq \
'map({node, collectedAt, collectionError})'
Посмотреть текущее расписание сборки:
d8 k get registrystorage registry -o jsonpath='{.spec.garbageCollection}' | jq
Когда сборка запускается и почему реплика переходит в режим read-only
Сборщик хранилища образов контейнеров сначала вычисляет множество достижимых блобов, а затем удаляет остальные — поэтому блоб, загруженный между этими шагами, был бы удалён. Сборку безопасно выполнять только на хранилище, в которое никто не пишет, поэтому реплика на время сборки не принимает запись.
Все свои образы реплика при этом продолжает отдавать. Чего она не может во время сборки:
- сохранить результат промаха кеша — агент на узле обращается к внешнему хранилищу образов контейнеров, то есть загрузка становится медленнее, но не завершается ошибкой;
- принять
d8 mirror push— он завершается с явной ошибкой, и его можно повторить.
Одновременно сборку выполняет только одна реплика. Остальные работают как обычно.
По умолчанию сборка назначается на ночной час, а если у NodeGroup master задано окно
обслуживания — на его начало: этот час уже объявлен допустимым для перерывов. Чтобы задать своё
расписание, используйте параметр storage.garbageCollection:
spec:
settings:
storage:
garbageCollection:
schedule: "0 2 * * Sun"
Нечитаемое cron-выражение отклоняется, а не угадывается, так как сборка в неожиданный час хуже, чем её отсутствие.
Как выключить
Сборка мусора выключается в настройках модуля (параметр storage.garbageCollection):
spec:
settings:
storage:
garbageCollection:
enabled: false
Это имеет смысл только с диском, размера которого хватит при неограниченном росте хранилища.
Обратите внимание: алерт D8RegistryStorageNotReclaimed всё равно сработает
через неделю после последней сборки — снаружи «выключено» и «молча перестало работать» выглядят
одинаково.
Что происходит, когда хранилище кеша заполнено?
У хранилища на каждом master-узле два предела, и registry проверяет оба при каждой записи:
- бюджет —
storage.sizeв ModuleConfigregistry, сколько хранилище может занимать; - резерв — сколько места на файловой системе хранилища должно оставаться свободным для узла:
мягкий порог выселения kubelet (10% файловой системы, не больше 40 ГиБ) плюс запас (10%, не
больше 20 ГиБ). Резерв действует при любом
storage.size, в том числе незаданном.
Когда достигнут любой из пределов, хранилище отклоняет новые записи и продолжает отдавать всё, что в нём есть:
- наполнение и репликация останавливаются, причину реплика пишет в
.status.replicas[].error; d8 mirror pushполучает507 Insufficient Storageс причиной и способом исправить;- образ, который хранилище не может сохранить, отдаётся потоком из upstream, пока он настроен, и узлы этого не замечают; в изолированном кластере релиз, которого нет в хранилище, установить нельзя.
Ради места ничего не удаляется. Место освобождает сборка мусора, либо его добавляют, увеличив
storage.size или диск под хранилищем.
Посмотреть состояние каждой реплики:
d8 k get registrystorage registry -o json | jq '.status.replicas[] | {node, store}'
writable: false с reason: BudgetExhausted значит, что достигнут storage.size: увеличьте его,
если на диске есть место, или дождитесь, пока сборка мусора освободит то, что не нужно
развёрнутым релизам. reason: ReserveExhausted значит, что закончился диск: освободите место на
узле или перенесите хранилище на отдельный диск.
На узле не загружается образ. Куда смотреть?
Начните с агента: он стоит на пути каждой загрузки на узле. Агент разворачивается в виде статического пода, поэтому доступен, даже когда кластер — нет.
Чтобы посмотреть логи агента, используйте команду:
d8 k -n kube-system logs -l component=registry-agent --tail=100
Чтобы посмотреть, какую конфигурацию агент получил и согласен ли он с кластером, используйте команду:
d8 k get registrynode <NODE> -o jsonpath='{.status}' | jq
Метрики агента — его собственный взгляд на проходящие через него загрузки. Они читаются напрямую с узла, а не через Prometheus: агент работает статическим подом именно потому, что должен работать при недоступном API-сервере, а kube-rbac-proxy рядом с ним аутентифицировался бы в том самом API-сервере:
ssh <NODE> 'curl -s http://127.0.0.1:4286/metrics | grep d8_registry_agent'
Конфигурация, выданная container runtime. Это один файл независимо от того, сколько registry настроено:
ssh <NODE> 'cat /etc/containerd/registry.d/_default/hosts.toml'
Если этого файла нет, агент ещё не применил конфигурацию: на узле не загрузится ничего, а причина — в логе агента. Если файл есть, а загрузки всё равно завершаются ошибкой, отказ находится за агентом — метрики выше называют, какая цель отказала и почему.
Модули из других ModuleSource: что хранит кеш и что сделать перед air-gap
Кеш хранит все модули, которые запущены в кластере, из какого бы ModuleSource они ни были установлены, и учитывает их в полноте: кластер не переходит в air-gap, пока в хранилище не хватает хотя бы одного из них.
- Модуль, чей ModuleSource находится в реестре upstream, заливается из upstream, как и сама платформа.
- Модуль, чей ModuleSource находится в отдельном реестре, заливается из этого реестра с учётными данными и центром сертификации, указанными в ModuleSource.
Собственные модули платформы (ModuleSource deckhouse) хранятся в system/deckhouse/modules.
У каждого другого ModuleSource во внутрикластерном реестре свой путь,
system/deckhouse/module-sources/<ModuleSource>: модули — в <путь>/<модуль>, список модулей — тегами
самого <путь>. Путь отдельный, потому что путь платформы, пока настроен upstream, отражает upstream,
и модуль другого источника с тем же именем, что у модуля платформы, был бы там заменён сборкой из
upstream.
Только из этого пути узел сможет загрузить модули источника, когда upstream отключён. Но поды модуля продолжают ссылаться на реестр своего ModuleSource, пока источник не изменён. Поэтому перед отключением upstream направьте каждый такой ModuleSource на его путь, с настройками реестра собственного источника платформы:
NAME=<ModuleSource>
REGISTRY=$(d8 k get modulesource deckhouse -o json | jq -c --arg name "$NAME" \
'.spec.registry | .repo = "registry.d8-system.svc:5001/system/deckhouse/module-sources/" + $name')
d8 k patch modulesource "$NAME" --type merge -p "{\"spec\":{\"registry\":$REGISTRY}}"
После этого образы модуля рендерятся с внутрикластерным адресом и отдаются из кеша.
Алерты доступности образов от extended-monitoring для внутрикластерного реестра
Когда реестром управляет модуль, все ссылки на образы в кластере указывают на
registry.d8-system.svc:5001. Этот адрес обслуживается только на узлах: containerd передаёт pull
агенту узла на loopback-адресе. Из пода адрес не резолвится, поэтому экспортер доступности образов
модуля extended-monitoring, который проверяет образы из пода, сообщает о каждом таком образе
алертом …ImageAvailabilityUnknownError (no such host, позже
x509: certificate signed by unknown authority).
С самими образами всё в порядке: то, что обнаружил бы экспортер, покрывают алерты этого модуля —
D8RegistryStorageIncomplete, D8RegistryUpstreamProbeFailing, D8RegistryNodeNotConverged.
Исключите внутрикластерный адрес из проверок экспортера:
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
name: extended-monitoring
spec:
version: 2
settings:
imageAvailability:
ignoredImages:
- 'registry\.d8-system\.svc:5001/.*'
Каждый элемент — регулярное выражение. Образы из других реестров по-прежнему проверяются.
Как посмотреть состояние внутрикластерного кеша?
Состояние кеша публикуется в статусе ресурса RegistryStorage. Для его просмотра используйте команду:
d8 k get registrystorage registry -o jsonpath='{.status}' | jq
Описание полей ответа:
replicas— единственное место, где сообщается информация о полноте кеша; каждая запись — отчёт реплики о самой себе. Реплика сfull: trueрядом сerrorполной не считается:fullговорит о том, что она держит, а ошибка — о том, завершился ли её последний проход.leader— реплика, которая наполняется из внешнего хранилища образов контейнеров и служит источником репликации для остальных. Выборы намеренно несимметричны: когда у какой-то реплики появляется полный набор, лидер, который не может собрать его сам, уступает ей. Лидер не может собрать набор сам, если нет внешнего хранилища образов, если его последний проход завершился ошибкой или если его хранилище отказывает в записи. Именно это не даёт изолированному кластеру застрять в ситуации, когда лидер пуст, а полный набор есть только у другой реплики. Лидер, который ещё собирает набор из внешнего хранилища, остаётся лидером: последователь копирует данные с него и может закончить раньше, и передача лидерства в этот момент ничего не даёт.
Предыдущая реализация
Информация ниже относится к кластеру, который всё ещё работает на реализации модуля, настраиваемой через ModuleConfig deckhouse.
Как мигрировать на модуль registry?
Во время миграции, для containerd v1 будет выполнен переход на новую схему конфигурации хранилища образов контейнеров. containerd v2 использует новую схему по умолчанию. Подробнее можно ознакомиться в разделе с описанием способов конфигурации
Для containerd v2
-
Выполните переключение на использование модуля
registry. Для этого, укажите в ModuleConfigdeckhouseпараметры режимаUnmanaged. Если используется хранилище образов контейнеров, отличное отregistry.deckhouse.ru, ознакомьтесь с конфигурацией модуля deckhouse для корректной настройки.Посмотреть текущие настройки хранилища образов контейнеров можно с помощью команды:
d8 k -n d8-system exec -it svc/deckhouse-leader -c deckhouse -- deckhouse-controller global values | yq e '.modulesImages.registry' -Данные настройки укажите при конфигурации
Unmanagedрежима:apiVersion: deckhouse.io/v1alpha1 kind: ModuleConfig metadata: name: deckhouse spec: version: 1 enabled: true settings: registry: mode: Unmanaged unmanaged: imagesRepo: registry.deckhouse.ru/deckhouse/ee scheme: HTTPS license: <LICENSE_KEY> # Замените на ваш лицензионный ключ -
Дождитесь завершения переключения. Пример статуса переключения:
conditions: # ... - lastTransitionTime: "..." message: "" reason: "" status: "True" type: Ready hash: .. mode: Unmanaged target_mode: Unmanaged
Для containerd v1
- Во время переключения containerd v1 сервис будет перезапущен.
- Во время переключения containerd v1 будет переведен на новую схему конфигурации хранилища образов контейнеров.
- Во время переключения, пользовательские конфигурации registry для containerd v1 будут временно недоступны.
-
Убедитесь, что на узлах с containerd v1 отсутствуют пользовательские конфигурации хранилища образов контейнеров, расположенные в директории
/etc/containerd/conf.d. -
Если конфигурации присутствуют, необходимо выполнить миграцию на новый формат конфигурации хранилища образов контейнеров в containerd. Для этого, необходимо добавить новые конфигурации в директорию
/etc/containerd/registry.d. Данные конфигурации вступят в силу после переключения на модульregistry. Для добавления конфигураций подготовьте NodeGroupConfiguration, подробнее в разделе с описанием способов конфигурации. Пример:apiVersion: deckhouse.io/v1alpha1 kind: NodeGroupConfiguration metadata: name: containerd-additional-config-auth.sh spec: # Шаг может быть любой, так как не требуется перезапуск сервиса containerd. weight: 0 bundles: - '*' nodeGroups: - "*" content: | # Copyright 2023 Flant JSC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. REGISTRY_URL=private.registry.example mkdir -p "/etc/containerd/registry.d/${REGISTRY_URL}" bb-sync-file "/etc/containerd/registry.d/${REGISTRY_URL}/hosts.toml" - << EOF [host] [host."https://${REGISTRY_URL}"] capabilities = ["pull", "resolve"] [host."https://${REGISTRY_URL}".auth] username = "username" password = "password" EOF -
Примените NodeGroupConfiguration. Дождитесь появления конфигурационных файлов в директории
/etc/containerd/registry.dна всех узлах. -
Проверьте корректность работы конфигураций. Для этого воспользуйтесь командой:
# Для https: ctr -n k8s.io images pull --hosts-dir=/etc/containerd/registry.d/ private.registry.example/registry/path:tag # Для http: ctr -n k8s.io images pull --hosts-dir=/etc/containerd/registry.d/ --plain-http private.registry.example/registry/path:tag -
Выполните переключение на использование модуля
registry. Для этого, укажите в ModuleConfigdeckhouseпараметры режимаUnmanaged. Если используется хранилище образов контейнеров, отличное отregistry.deckhouse.ru, ознакомьтесь с конфигурацией модуля deckhouse для корректной настройки.Посмотреть текущие настройки хранилища образов контейнеров можно с помощью команды:
d8 k -n d8-system exec -it svc/deckhouse-leader -c deckhouse -- deckhouse-controller global values | yq e '.modulesImages.registry' -Данные настройки укажите при конфигурации
Unmanagedрежима:apiVersion: deckhouse.io/v1alpha1 kind: ModuleConfig metadata: name: deckhouse spec: version: 1 enabled: true settings: registry: mode: Unmanaged unmanaged: imagesRepo: registry.deckhouse.ru/deckhouse/ee scheme: HTTPS license: <LICENSE_KEY> # Замените на ваш лицензионный ключ -
После применения, дождитесь в статусе переключения сообщение:
Пример вывода:
conditions: # ... - lastTransitionTime: "2025-08-13T15:22:34Z" message: | Check current nodes configuration 2/2 node(s) Unready: - master-0: has custom toml merge containerd configuration - worker-5e389be0-578df-s5sm5: has custom toml merge containerd configuration reason: Processing status: "False" type: ContainerdConfigPreflightReadyДанное сообщение означает, что на узлах имеются старые конфигурации хранилища образов контейнеров, расположенные в директории
/etc/containerd/conf.d. И в данный момент переключение на новую конфигурацию containerd заблокировано. Для того чтобы разрешить переключение, необходимо удалить старые конфигурационные файлы. -
Удалите старые конфигурационные файлы, чтобы разрешить переключение на модуль
registry. Для этого создайте NodeGroupConfiguration. Пример манифеста NodeGroupConfiguration:apiVersion: deckhouse.io/v1alpha1 kind: NodeGroupConfiguration metadata: name: containerd-additional-config-auth-delete.sh spec: # Шаг должен выполниться до '032_configure_containerd.sh' weight: 0 bundles: - '*' nodeGroups: - "*" content: | # Copyright 2023 Flant JSC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. file="/etc/containerd/conf.d/old-config.toml" [ -f "$file" ] && rm -f "$file" -
После удаления старых конфигураций, убедитесь, что переключение продолжило выполняться. Пример статуса переключения:
conditions: # ... - lastTransitionTime: "2025-08-13T16:42:09Z" message: "" reason: "" status: "True" type: ContainerdConfigPreflightReady -
Дождитесь завершения переключения. Пример статуса переключения:
conditions: # ... - lastTransitionTime: "..." message: "" reason: "" status: "True" type: Ready hash: .. mode: Unmanaged target_mode: Unmanaged -
Удалите NodeGroupConfiguration, созданный на шаге удаления старых конфигурационных файлов:
d8 k delete nodegroupconfiguration containerd-additional-config-auth-delete.shЧтобы убедиться, что NodeGroupConfiguration удалён, используйте команду:
d8 k get nodegroupconfigurationВ списке не должно быть NodeGroupConfiguration, подлежащего удалению (в этом примере —
containerd-additional-config-auth-delete.sh).
Как мигрировать обратно с модуля registry?
- Это устаревший (deprecated) формат управления хранилищем образов контейнеров.
- Во время переключения containerd v1 будет перезапущен.
- Во время переключения containerd v1 будет переведен на старую схему конфигурации хранилища образов контейнеров.
- Во время переключения, пользовательские конфигурации registry для containerd v1 будут временно недоступны.
-
Переведите хранилище образов контейнеров в режим
Unmanaged. Если используется хранилище образов контейнеров, отличное отregistry.deckhouse.ru, ознакомьтесь с конфигурацией модуля deckhouse для корректной настройки.Пример конфигурации:
apiVersion: deckhouse.io/v1alpha1 kind: ModuleConfig metadata: name: deckhouse spec: version: 1 enabled: true settings: registry: mode: Unmanaged unmanaged: imagesRepo: registry.deckhouse.ru/deckhouse/ee scheme: HTTPS license: <LICENSE_KEY> # Замените на ваш лицензионный ключ -
Проверьте статус переключения, используя инструкцию. Пример вывода:
conditions: # ... - lastTransitionTime: "..." message: "" reason: "" status: "True" type: Ready hash: .. mode: Unmanaged target_mode: Unmanaged -
Переведите хранилище образов контейнеров в неконфигурируемый режим
Unmanaged. Пример конфигурации:apiVersion: deckhouse.io/v1alpha1 kind: ModuleConfig metadata: name: deckhouse spec: version: 1 enabled: true settings: registry: mode: Unmanaged -
Проверьте статус переключения, используя инструкцию. Пример вывода:
conditions: # ... - lastTransitionTime: "..." message: "" reason: "" status: "True" type: Ready hash: .. mode: Unmanaged target_mode: Unmanaged -
Если используется containerd v1, и в кластере применены пользовательские конфигурации registry, их необходимо заменить на старый формат. Для этого, подготовьте конфигурации хранилища образов контейнеров старого формата. Данные конфигурации на данном этапе применять не нужно. Пример конфигурации:
apiVersion: deckhouse.io/v1alpha1 kind: NodeGroupConfiguration metadata: name: containerd-additional-config-auth.sh spec: # Для добавления файла перед шагом '032_configure_containerd.sh' weight: 31 bundles: - '*' nodeGroups: - "*" content: | # Copyright 2023 Flant JSC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. REGISTRY_URL=private.registry.example mkdir -p /etc/containerd/conf.d bb-sync-file /etc/containerd/conf.d/additional_registry.toml - << EOF [plugins] [plugins."io.containerd.grpc.v1.cri"] [plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."${REGISTRY_URL}"] endpoint = ["https://${REGISTRY_URL}"] [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."${REGISTRY_URL}".auth] username = "username" password = "password" # OR auth = "dXNlcm5hbWU6cGFzc3dvcmQ=" EOF -
Удалите секрет
registry-bashible-config. Во время удаления, containerd v1 переключится на старый формат конфигурации containerd:d8 k -n d8-system delete secret registry-bashible-config -
После удаления дождитесь завершения переключения. Для отслеживания используйте инструкцию. Пример вывода:
conditions: # ... - lastTransitionTime: "..." message: "" reason: "" status: "True" type: Ready hash: .. mode: Unmanaged target_mode: Unmanaged -
Если используется containerd v1, примените заготовленные этапом ранее NodeGroupConfiguration с пользовательскими конфигурациями хранилища образов контейнеров.
-
Отключите модуль
registry. Пример:apiVersion: deckhouse.io/v1alpha1 kind: ModuleConfig metadata: name: registry spec: enabled: false settings: {} version: 1
Как посмотреть статус переключения режима registry?
Статус переключения режима хранилища образов контейнеров можно получить с помощью следующей команды:
d8 k -n d8-system -o yaml get secret registry-state | yq -C -P '.data | del .state | map_values(@base64d) | .conditions = (.conditions | from_yaml)'
Пример вывода:
conditions:
- lastTransitionTime: "2025-07-15T12:52:46Z"
message: 'registry.deckhouse.ru: all 157 items are checked'
reason: Ready
status: "True"
type: RegistryContainsRequiredImages
- lastTransitionTime: "2025-07-11T11:59:03Z"
message: ""
reason: ""
status: "True"
type: ContainerdConfigPreflightReady
- lastTransitionTime: "2025-07-15T12:47:47Z"
message: ""
reason: ""
status: "True"
type: TransitionContainerdConfigReady
- lastTransitionTime: "2025-07-15T12:52:48Z"
message: ""
reason: ""
status: "True"
type: InClusterProxyReady
- lastTransitionTime: "2025-07-15T12:54:53Z"
message: ""
reason: ""
status: "True"
type: DeckhouseRegistrySwitchReady
- lastTransitionTime: "2025-07-15T12:55:48Z"
message: ""
reason: ""
status: "True"
type: FinalContainerdConfigReady
- lastTransitionTime: "2025-07-15T12:55:48Z"
message: ""
reason: ""
status: "True"
type: Ready
mode: Direct
target_mode: Direct
Вывод отображает состояние процесса переключения. Каждое условие может находиться в статусе True или False, а также содержать поле message с пояснением.
Описание условий:
| Условие | Описание |
|---|---|
ContainerdConfigPreflightReady |
Состояние проверки конфигурации containerd. Проверяется, что на узлах отсутствуют пользовательские auth конфигурации containerd. |
TransitionContainerdConfigReady |
Состояние подготовки конфигурации containerd в новый режим. Проверяется, что конфигурация containerd успешно подготовлена и содержит одновременно конфигурации нового и старого режима. |
FinalContainerdConfigReady |
Состояние завершения переключения containerd в новый режим. Проверяется, что конфигурация containerd успешно применена и содержит конфигурацию нового режима. |
DeckhouseRegistrySwitchReady |
Состояние переключения Deckhouse и его компонентов на использование нового хранилища образов контейнеров. Значение True указывает, что Deckhouse успешно переключился на сконфигурированное хранилище образов контейнеров и готов к работе. |
InClusterProxyReady |
Состояние готовности In-Cluster Proxy. Проверяется, что In-Cluster Proxy успешно запущен и работает. |
CleanupInClusterProxy |
Состояние очистки In-Cluster Proxy, если прокси не нужен для работы желаемого режима. Проверяется, что все ресурсы, связанные с In-Cluster Proxy, успешно удалены. |
NodeServicesReady |
Состояние готовности Node Services Manager и Static-Pod хранилища образов контейнеров. Проверяется, что Node Services Manager успешно запущен и работает, и что Static-Pod хранилища образов контейнеров был успешно развёрнут с помощью Node Services Manager. |
CleanupNodeServices |
Состояние очистки Node Services Manager и Static-Pod хранилища образов контейнеров, если компоненты не нужны для работы желаемого режима. Проверяется, что все ресурсы, связанные с Node Services Manager и Static-Pod хранилища образов контейнеров, успешно удалены. |
RegistryContainsRequiredImages |
Состояние проверки хранилища образов контейнеров на наличие необходимых образов. |
Ready |
Общее состояние готовности хранилища образов контейнеров к работе в указанном режиме. Проверяется, что все предыдущие условия выполнены и модуль готов к работе. |