Главная/Registry/Практика

Раздел 14. Практические задания

Задания раздела опираются на локальный registry: он позволяет проверить публикацию, получение и удаление, не расходуя лимиты и не затрагивая чужие репозитории.

Формулировка «показать» означает выполнить команду и привести её вывод. Формулировка «объяснить» требует механизма, а не пересказа наблюдения.

Как проверять себя

ФормулировкаЧто требуется
«показать, что…»Команда и её фактический вывод
«измерить»Число, полученное командой
«объяснить»Механизм, а не описание симптома
«спроектировать»Решение плюс обоснование каждого выбора

Задание 1. Локальный registry: публикация и получение

Тип: обязательное
Материал: 14.3

Запустить локальный registry, опубликовать образ и получить его на «чистой» стороне.

Требования:

  1. Запустить registry с сохранением данных в volume.
  2. Проверить доступность API двумя запросами.
  3. Опубликовать образ и увидеть его в каталоге.
  4. Удалить образ локально и получить его заново.
  5. Показать, что данные переживают пересоздание container'а registry.
  6. Показать для сравнения, что без volume данные теряются.

Проверка

bash
curl -s http://localhost:5000/v2/
curl -s http://localhost:5000/v2/_catalog | python3 -m json.tool
docker rm -f registry && docker run -d --name registry -p 5000:5000 -v reg-data:/var/lib/registry registry:2
curl -s http://localhost:5000/v2/_catalog | python3 -m json.tool

Критерий

Каталог после пересоздания содержит опубликованный репозиторий. Контрольный экземпляр без volume после пересоздания пуст — сравнение выполнено, а не заявлено.


Задание 2. Digest до и после

Тип: обязательное
Материал: 14.1

Сравнить digest до публикации и после получения, объяснить результат.

Требования:

  1. Зафиксировать Id и RepoDigests образа до публикации.
  2. Опубликовать, удалить локально, получить заново.
  3. Сравнить оба значения.
  4. Объяснить, почему RepoDigests до первой публикации пуст.
  5. Объяснить, чем Id отличается от RepoDigests и почему в docker pull передаётся второе.
  6. Показать третий digest — манифеста конкретной платформы.

Проверка

bash
docker inspect ОБРАЗ --format '{{.Id}}'
docker inspect ОБРАЗ --format '{{if .RepoDigests}}{{index .RepoDigests 0}}{{end}}'
docker manifest inspect ОБРАЗ | python3 -m json.tool | head -20

Критерий

Id совпадает до и после. Объяснено, что RepoDigests появляется только после публикации или получения, потому что это digest манифеста в конкретном репозитории.

Ориентир для самопроверки

Собранный локально образ не имеет RepoDigests: манифест ещё нигде не опубликован, и digest'а в registry у него нет. Id есть всегда — это хеш конфигурации, вычисляемый при сборке.

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


Задание 3. Три тега, один набор слоёв

Тип: обязательное
Материал: 14.4

Присвоить одному образу три тега и показать, что слои не дублируются.

Требования:

  1. Присвоить образу три тега и опубликовать каждый.
  2. Измерить размер хранилища registry после первого тега и после третьего.
  3. Показать, что digest всех трёх тегов совпадает.
  4. Показать в выводе push, что при втором и третьем теге слои не передавались.
  5. Объяснить, что именно передаётся при публикации дополнительного тега.
  6. Показать, что удаление одного тега из трёх не освобождает место.

Проверка

bash
docker exec registry du -sk /var/lib/registry
docker push localhost:5000/app:ТЕГ 2>&1 | grep -c 'Layer already exists'
curl -sI -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
    http://localhost:5000/v2/app/manifests/ТЕГ | grep -i docker-content-digest

Критерий

Размер после трёх тегов отличается от размера после одного на килобайты, а не в разы. Совпадение digest показано. Объяснено, что передаётся только манифест.


Задание 4. Развёртывание по digest

Тип: обязательное
Материал: 14.4

Развернуть образ по digest и объяснить, чем это надёжнее развёртывания по тегу.

Требования:

  1. Опубликовать образ с тегом и зафиксировать его digest.
  2. Переназначить тег на другой образ — воспроизвести ошибку выпуска.
  3. Получить образ по тегу и по digest; показать, что содержимое разное.
  4. Объяснить, кто выбирает манифест в каждом случае.
  5. Показать, что закрепление по digest работает и при отсутствии TLS.
  6. Предложить схему, в которой используются оба: тег для чтения, digest для развёртывания.

Проверка

bash
DIGEST=$(docker inspect ОБРАЗ --format '{{index .RepoDigests 0}}')
docker tag ДРУГОЙ_ОБРАЗ localhost:5000/app:ТОТ_ЖЕ_ТЕГ && docker push localhost:5000/app:ТОТ_ЖЕ_ТЕГ
docker pull localhost:5000/app:ТОТ_ЖЕ_ТЕГ && docker run --rm localhost:5000/app:ТОТ_ЖЕ_ТЕГ
docker pull "$DIGEST" && docker run --rm "$DIGEST"

Критерий

Два запуска дают разное содержимое при одном имени тега. Объяснено: манифест по тегу выбирает сервер, по digest — клиент.


Задание 5. TLS для локального registry

Тип: дополнительное
Материал: 14.3

Настроить TLS и проверить, что именно проверяет клиент.

Требования:

  1. Выпустить сертификат с корректным SAN, включающим имя и адрес.
  2. Запустить registry с TLS.
  3. Показать три случая: без доверия к корню, с корнем и верным именем, с корнем и именем вне SAN.
  4. Настроить доверие в Docker через /etc/docker/certs.d/.
  5. Объяснить, почему имя каталога должно включать порт.
  6. Опубликовать образ по HTTPS и подтвердить.

Проверка

bash
openssl x509 -in cert.pem -noout -ext subjectAltName
curl -s --cacert cert.pem -o /dev/null -w '%{http_code}\n' https://registry.local:5443/v2/
ls -l /etc/docker/certs.d/registry.local:5443/

Критерий

Три случая проверки дают три разных результата. Объяснено, что имя каталога должно точно совпадать с тем, что пишется в docker pull, включая порт.


Задание 6. Аутентификация без следов

Тип: дополнительное
Материал: 14.2

Аутентифицироваться через --password-stdin и проверить, что пароль не попал в историю.

Требования:

  1. Поднять registry с аутентификацией.
  2. Показать разницу ответов с учётными данными и без.
  3. Выполнить вход через --password-stdin.
  4. Проверить командой, что пароль отсутствует в истории оболочки и в списке процессов.
  5. Показать, что config.json содержит обратимое кодирование, не печатая сам секрет.
  6. Выполнить docker logout и показать, что запись исчезла.

Проверка

bash
echo "$TOKEN" | docker login localhost:5000 -u user --password-stdin
grep -c "$TOKEN" ~/.bash_history 2>/dev/null || echo 0
ps -eo args | grep -c "$TOKEN"
python3 -c "import json; c=json.load(open('$HOME/.docker/config.json')); print(len(c.get('auths',{})))"

Критерий

Проверки из пункта 4 выполнены командами и дали ноль. Пункт 5 показывает длину секрета, но не его значение.

Ориентир для самопроверки

Проверка «пароля нет в истории» должна быть выполнимой и в обратную сторону: если повторить вход через -p, та же команда должна найти пароль. Без этого неизвестно, работает ли проверка вообще.


Задание 7. Схема тегов для проекта ★

Тип: итоговое
Материал: 14.4

Спроектировать схему тегов для проекта с ветками main, release/* и feature-ветками.

Требования:

  1. Для каждого типа ветки определить: неизменяемые теги, подвижные указатели, срок хранения.
  2. Обосновать каждый тег: кто им пользуется и зачем.
  3. Обработать имена веток с недопустимыми в теге символами.
  4. Определить, какой тег попадает в развёртывание, и обосновать.
  5. Составить cleanup policy с исключениями, проверяемыми до правил.
  6. Показать случай, где правило удалило бы развёрнутый образ, а исключение сохранило.
  7. Реализовать схему как программу и проверить её на пяти веток.

Проверка

Программа из пункта 7 должна:

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

Критерий

У каждой ветки есть якорь. Cleanup policy содержит хотя бы один случай, где исключение переопределяет правило. Обоснование каждого тега отвечает на вопрос «кто им пользуется».

Ориентир: минимальный набор
ВеткаНеизменяемыеПодвижныеХранение
maingit-<sha>, <дата>-<sha>main, latestпоследние 30
release/X.Ygit-<sha>, X.Y.ZX.Y, X, stableбессрочно
feature/*git-<sha>branch-<имя>14 дней
прочееgit-<sha>other-<имя>7 дней

Признак хорошей схемы: на вопрос «какой код работает в эксплуатации» ответ даётся одной командой, а на вопрос «можно ли откатиться на предыдущую версию» — «да, тег такой-то».


Задание 8. Диагностика: образ в эксплуатации отличается от собранного

Тип: диагностическое
Материал: весь раздел

Образ в production отличается от собранного в CI при одинаковом теге. Объяснить, как это возможно.

Требования:

  1. Назвать четыре механизма, приводящих к такому расхождению.
  2. Для каждого предложить проверку, подтверждающую или опровергающую его.
  3. Воспроизвести хотя бы один механизм на локальном registry.
  4. Предложить меру, закрывающую все четыре сразу.
  5. Показать, что предложенная мера действительно работает.
  6. Объяснить, почему проверка «тег совпадает» недостаточна.

Проверка

bash
docker inspect ОБРАЗ --format '{{index .RepoDigests 0}}'
curl -sI -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
    http://REGISTRY/v2/ИМЯ/manifests/ТЕГ | grep -i docker-content-digest
docker manifest inspect ОБРАЗ | python3 -m json.tool | grep architecture

Критерий

Названы четыре механизма с проверкой для каждого. Один воспроизведён. Предложенная мера — развёртывание по digest — проверена, а не заявлена.

Ориентир: четыре механизма
МеханизмПроверка
Тег переназначен после сборки: повторный запуск задачи CI, ручная публикацияСравнить digest тега с записанным при выпуске
Разные платформы: CI собирал на amd64, эксплуатация на arm64 — index отдаёт разные манифестыdocker manifest inspect, сравнить architecture
Кэш на узле: старый образ с тем же тегом уже есть локально, pull его не обновилimagePullPolicy, docker pull без кэша, сравнить Id
Другой registry: зеркало или прокси отдало устаревшую копиюСравнить digest, полученный от каждого registry

Мера, закрывающая все четыре: развёртывание по digest. Digest фиксирует конкретный манифест, а не имя; переназначение тега на него не влияет, платформа выбирается явно, кэш проверяется по содержимому, зеркало не может подменить.

Почему «тег совпадает» недостаточно: тег — это имя. Совпадение имён не означает совпадения содержимого — именно это и есть суть задания.


Сводная таблица

ЗаданиеТипОсновной материал
1Локальный registryобяз.14.3
2Digest до и послеобяз.14.1
3Три тега, один набор слоёвобяз.14.4
4Развёртывание по digestобяз.14.4
5TLS для registryдоп.14.3
6Аутентификация без следовдоп.14.2
7Схема тегов для проекта14.4
8Образ отличается от собранногодиаг.весь раздел

Что должно получиться

После заданий 1–4 у вас есть практическое подтверждение четырёх утверждений раздела:

  1. Registry без volume теряет данные при пересоздании container'а.
  2. Id и RepoDigests — разные значения; в docker pull передаётся второе.
  3. Несколько тегов на один образ занимают место один раз.
  4. Тег фиксирует имя, digest фиксирует содержимое — и это разные гарантии.

Задания 5–6 закрывают эксплуатационную часть: защита канала и учётных данных.

Задание 7 — проектное: его результат применим к вашему проекту напрямую. Задание 8 проверяет понимание того, зачем всё предыдущее нужно.

Навигация

← Предыдущий материал
Вернуться к разделу
Следующий раздел: Testing →
Главное оглавление

Markdown на GitHub ↗