Раздел 14. Практические задания
Задания раздела опираются на локальный registry: он позволяет проверить публикацию, получение и удаление, не расходуя лимиты и не затрагивая чужие репозитории.
Формулировка «показать» означает выполнить команду и привести её вывод. Формулировка «объяснить» требует механизма, а не пересказа наблюдения.
Как проверять себя
| Формулировка | Что требуется |
|---|---|
| «показать, что…» | Команда и её фактический вывод |
| «измерить» | Число, полученное командой |
| «объяснить» | Механизм, а не описание симптома |
| «спроектировать» | Решение плюс обоснование каждого выбора |
Задание 1. Локальный registry: публикация и получение
Тип: обязательное
Материал: 14.3
Запустить локальный registry, опубликовать образ и получить его на «чистой» стороне.
Требования:
- Запустить registry с сохранением данных в volume.
- Проверить доступность API двумя запросами.
- Опубликовать образ и увидеть его в каталоге.
- Удалить образ локально и получить его заново.
- Показать, что данные переживают пересоздание container'а registry.
- Показать для сравнения, что без volume данные теряются.
Проверка
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 до публикации и после получения, объяснить результат.
Требования:
- Зафиксировать
IdиRepoDigestsобраза до публикации. - Опубликовать, удалить локально, получить заново.
- Сравнить оба значения.
- Объяснить, почему
RepoDigestsдо первой публикации пуст. - Объяснить, чем
Idотличается отRepoDigestsи почему вdocker pullпередаётся второе. - Показать третий digest — манифеста конкретной платформы.
Проверка
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
Присвоить одному образу три тега и показать, что слои не дублируются.
Требования:
- Присвоить образу три тега и опубликовать каждый.
- Измерить размер хранилища registry после первого тега и после третьего.
- Показать, что digest всех трёх тегов совпадает.
- Показать в выводе
push, что при втором и третьем теге слои не передавались. - Объяснить, что именно передаётся при публикации дополнительного тега.
- Показать, что удаление одного тега из трёх не освобождает место.
Проверка
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 и объяснить, чем это надёжнее развёртывания по тегу.
Требования:
- Опубликовать образ с тегом и зафиксировать его digest.
- Переназначить тег на другой образ — воспроизвести ошибку выпуска.
- Получить образ по тегу и по digest; показать, что содержимое разное.
- Объяснить, кто выбирает манифест в каждом случае.
- Показать, что закрепление по digest работает и при отсутствии TLS.
- Предложить схему, в которой используются оба: тег для чтения, digest для развёртывания.
Проверка
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 и проверить, что именно проверяет клиент.
Требования:
- Выпустить сертификат с корректным SAN, включающим имя и адрес.
- Запустить registry с TLS.
- Показать три случая: без доверия к корню, с корнем и верным именем, с корнем и именем вне SAN.
- Настроить доверие в Docker через
/etc/docker/certs.d/. - Объяснить, почему имя каталога должно включать порт.
- Опубликовать образ по HTTPS и подтвердить.
Проверка
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 и проверить, что пароль не попал в историю.
Требования:
- Поднять registry с аутентификацией.
- Показать разницу ответов с учётными данными и без.
- Выполнить вход через
--password-stdin. - Проверить командой, что пароль отсутствует в истории оболочки и в списке процессов.
- Показать, что
config.jsonсодержит обратимое кодирование, не печатая сам секрет. - Выполнить
docker logoutи показать, что запись исчезла.
Проверка
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-ветками.
Требования:
- Для каждого типа ветки определить: неизменяемые теги, подвижные указатели, срок хранения.
- Обосновать каждый тег: кто им пользуется и зачем.
- Обработать имена веток с недопустимыми в теге символами.
- Определить, какой тег попадает в развёртывание, и обосновать.
- Составить cleanup policy с исключениями, проверяемыми до правил.
- Показать случай, где правило удалило бы развёрнутый образ, а исключение сохранило.
- Реализовать схему как программу и проверить её на пяти веток.
Проверка
Программа из пункта 7 должна:
- давать неизменяемый тег для любой ветки, включая нераспознанную;
- приводить имя ветки к допустимым символам;
- отдельно возвращать тег для развёртывания.
Критерий
У каждой ветки есть якорь. Cleanup policy содержит хотя бы один случай, где исключение переопределяет правило. Обоснование каждого тега отвечает на вопрос «кто им пользуется».
Ориентир: минимальный набор
| Ветка | Неизменяемые | Подвижные | Хранение |
|---|---|---|---|
main | git-<sha>, <дата>-<sha> | main, latest | последние 30 |
release/X.Y | git-<sha>, X.Y.Z | X.Y, X, stable | бессрочно |
feature/* | git-<sha> | branch-<имя> | 14 дней |
| прочее | git-<sha> | other-<имя> | 7 дней |
Признак хорошей схемы: на вопрос «какой код работает в эксплуатации» ответ даётся одной командой, а на вопрос «можно ли откатиться на предыдущую версию» — «да, тег такой-то».
Задание 8. Диагностика: образ в эксплуатации отличается от собранного
Тип: диагностическое
Материал: весь раздел
Образ в production отличается от собранного в CI при одинаковом теге. Объяснить, как это возможно.
Требования:
- Назвать четыре механизма, приводящих к такому расхождению.
- Для каждого предложить проверку, подтверждающую или опровергающую его.
- Воспроизвести хотя бы один механизм на локальном registry.
- Предложить меру, закрывающую все четыре сразу.
- Показать, что предложенная мера действительно работает.
- Объяснить, почему проверка «тег совпадает» недостаточна.
Проверка
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 |
| 2 | Digest до и после | обяз. | 14.1 |
| 3 | Три тега, один набор слоёв | обяз. | 14.4 |
| 4 | Развёртывание по digest | обяз. | 14.4 |
| 5 | TLS для registry | доп. | 14.3 |
| 6 | Аутентификация без следов | доп. | 14.2 |
| 7 | Схема тегов для проекта | ★ | 14.4 |
| 8 | Образ отличается от собранного | диаг. | весь раздел |
Что должно получиться
После заданий 1–4 у вас есть практическое подтверждение четырёх утверждений раздела:
- Registry без volume теряет данные при пересоздании container'а.
IdиRepoDigests— разные значения; вdocker pullпередаётся второе.- Несколько тегов на один образ занимают место один раз.
- Тег фиксирует имя, digest фиксирует содержимое — и это разные гарантии.
Задания 5–6 закрывают эксплуатационную часть: защита канала и учётных данных.
Задание 7 — проектное: его результат применим к вашему проекту напрямую. Задание 8 проверяет понимание того, зачем всё предыдущее нужно.
Навигация
← Предыдущий материал
Вернуться к разделу
Следующий раздел: Testing →
Главное оглавление