Главная/Проверка знаний/Проверка знаний

Quizzes по разделам

По десять вопросов на раздел: пять на понимание, три на применение, два на диагностику. Занимает 10–15 минут.

Правило. Ответ формулируется до открытия разбора. Прочитанный ответ всегда кажется очевидным — это свойство чтения, а не признак понимания.

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

Как читать результат

ВерныхЧто это значитЧто делать
9–10Материал усвоенДальше
7–8Есть пробелыПеречитать уроки по темам ошибок
5–6Усвоено фрагментарноПеречитать раздел, повторить упражнения
0–4Раздел не пройденВернуться к разделу целиком

Quiz 01. Подготовка среды

  1. Что именно устанавливается при установке Docker Engine, кроме самого демона?
  2. Почему добавление пользователя в группу docker эквивалентно выдаче прав root?
  3. Чем Docker Desktop отличается от Docker Engine на Linux?
  4. Что произойдёт с запущенными container'ами при перезапуске демона?
  5. Где Docker хранит образы, тома и метаданные?
  6. Как проверить, что установка работает, не запуская стороннего образа?
  7. Как узнать версию клиента и версию демона по отдельности и зачем их различать?
  8. Как посмотреть, сколько места занимает Docker, с разбивкой по видам?
  9. docker: permission denied while trying to connect to the Docker daemon socket. Причина и два способа исправления?
  10. docker version печатает версию клиента и ошибку для демона. Что это означает?
Ответы
  1. Клиент (docker), демон (dockerd), containerd, runc, плагины (buildx, compose). Клиент — только преобразователь команд в HTTP-запросы к сокету.
  2. Через сокет создаётся container с любыми параметрами, включая монтирование корня хоста. Демон работает от root, значит и результат — права root (урок 17.1).
  3. Desktop запускает Linux в виртуальной машине и добавляет интерфейс; на Linux Engine работает с ядром хоста напрямую. Отсюда различия в производительности ввода-вывода и в доступе к устройствам.
  4. Зависит от настройки: при включённом live-restore продолжают работать, иначе останавливаются. Сам container'ом владеет shim, а не демон, поэтому продолжение работы возможно в принципе (урок 17.2).
  5. /var/lib/docker. Образы — в подкаталоге драйвера хранения, тома — в volumes, метаданные — в containers и image.
  6. docker version и docker info — оба обращаются к демону и не требуют загрузки образов.
  7. docker version --format '{{.Client.Version}}' и {{.Server.Version}}. Различать нужно, потому что клиент новее демона может использовать недоступные возможности API.
  8. docker system df -v — по образам, container'ам, томам и кэшу сборки. Логи container'ов в этот вывод не входят (урок 13.4).
  9. Нет прав на сокет. Способы: добавить пользователя в группу docker (равно правам root) или использовать rootless-режим. Второй безопаснее.
  10. Клиент установлен, демон не запущен или недоступен. Проверять — systemctl status docker и права на сокет.

Quiz 02. Основы Containerization

  1. Что такое container с точки зрения ядра Linux?
  2. Какие механизмы ядра обеспечивают изоляцию и что именно каждый изолирует?
  3. Чем container отличается от виртуальной машины в терминах ядра?
  4. Почему container не может использовать ядро другой версии, чем у хоста?
  5. Что означает «один процесс на container» и почему это правило, а не совет?
  6. Как посмотреть, в каких namespace находится процесс?
  7. Как ограничить память container'а и как проверить, что ограничение применилось?
  8. Как убедиться, что процесс внутри container'а — это обычный процесс хоста?
  9. Приложение внутри container'а видит все процессы хоста. Что не так с конфигурацией?
  10. uname -r внутри container'а с Ubuntu показывает ядро Fedora. Это ошибка?
Ответы
  1. Обычный процесс, которому ядро показывает ограниченный вид системы. Виртуализации нет; планировщик и память те же.
  2. Namespaces — видимость объектов (PID, сеть, точки монтирования, IPC, имя узла, пользователи); cgroups — объём ресурсов; capabilities — набор привилегированных операций; seccomp — доступные системные вызовы.
  3. У ВМ своё ядро и гипервизор между ней и оборудованием. У container'а ядро общее с хостом — отсюда и меньшие расходы, и меньшая изоляция (урок 19.2).
  4. Ядро одно на всех: container содержит только пользовательскую часть системы. Дистрибутив внутри может быть любым, ядро — всегда хостовое.
  5. Правило, потому что оркестратор наблюдает за PID 1: отказ второго процесса снаружи не виден. Плюс масштабирование становится невозможным — реплицируется весь набор целиком.
  6. readlink /proc/PID/ns/* — сравнивать нужно по номеру inode, а не по поведению.
  7. docker run -m 256m; проверять изнутри: cat /sys/fs/cgroup/memory.max. docker inspect показывает запрошенное, а не применённое.
  8. ps -ef | grep на хосте покажет тот же процесс с другим PID. Изнутри он PID 1, снаружи — обычный номер.
  9. Задан --pid host: PID namespace не создан, и процессы хоста видны.
  10. Нет. Внутри — файловая система Ubuntu, ядро — хоста. Это и есть главное отличие от ВМ.

Quiz 03. Работа с Images

  1. Из чего состоит образ и что содержит его манифест?
  2. Что такое слой и почему слои неизменяемы?
  3. Чем digest отличается от тега?
  4. Что происходит при записи в файл, пришедший из нижнего слоя?
  5. Почему удаление файла в новом слое не уменьшает образ?
  6. Как найти, какие слои занимают больше всего места?
  7. Как закрепить образ так, чтобы завтра запустился ровно тот же набор байтов?
  8. Как перенести образ на машину без доступа к registry?
  9. Образ пересобран, тег тот же, но container запускает старый код. Две вероятные причины?
  10. docker images показывает десятки строк <none>:<none>. Откуда они?
Ответы
  1. Манифест, конфигурация и слои. Манифест перечисляет digest'ы слоёв и конфигурации; конфигурация содержит Env, Cmd, User и историю.
  2. Слой — набор изменений файловой системы. Неизменяемы, потому что адресуются по содержимому: изменение даёт другой digest, то есть другой слой.
  3. Тег — изменяемая ссылка, digest — контрольная сумма содержимого. Один digest всегда означает один набор байтов (урок 14.4).
  4. Copy-up: файл копируется в верхний слой целиком, и дальше правится копия. Запись одного байта в файл на 128 МиБ даёт слой на 128 МиБ.
  5. Слои только добавляются. Удаление создаёт запись «файла здесь нет», содержимое остаётся ниже и извлекается (урок 12.6).
  6. docker history образ --human --format 'table {{.Size}}\t{{.CreatedBy}}'.
  7. Ссылаться по digest: образ@sha256:…. Тег для этого непригоден.
  8. docker save образ -o файл.tar, перенести, docker load -i файл.tar.
  9. Первая: docker pull выполнен, но container не пересоздан — работает старый. Вторая: тег перезаписан после pull, локальная копия устарела.
  10. Образы, потерявшие тег: сборка с тем же именем переносит тег на новый образ, старый остаётся безымянным.

Quiz 04. Containers и lifecycle

  1. Какие состояния проходит container и что вызывает переход между ними?
  2. Почему container завершается, когда завершается процесс с PID 1?
  3. Чем docker stop отличается от docker kill?
  4. Что означает код возврата 137 и что — 143?
  5. Почему PID 1 не получает сигналы по умолчанию так же, как обычный процесс?
  6. Как узнать, почему container завершился, если логи пусты?
  7. Как посмотреть иерархию процессов внутри container'а?
  8. Как задать политику перезапуска и чем always отличается от unless-stopped?
  9. docker stop занимает ровно 10 секунд. Две вероятные причины и как их различить?
  10. Container перезапускается в цикле. С чего начать расследование?
Ответы
  1. createdrunningpaused/exitedremoved. Переходы вызывают команды клиента и завершение процесса PID 1.
  2. Container — это и есть процесс PID 1 со своим окружением. Нет процесса — нечему принадлежать namespace'ам.
  3. stop посылает SIGTERM и ждёт grace period, затем SIGKILL. kill посылает SIGKILL сразу (или указанный сигнал).
  4. 137 = 128 + 9: завершён SIGKILL — либо OOM killer, либо истёк grace period у PID 1 без обработчика. 143 = 128 + 15: PID 1 умер от SIGTERM, что на практике означает --init. Правильно написанный сервис, обработавший сигнал, даёт 0 — проверено запуском (verify/FACTS.md).
  5. Ядро не применяет к PID 1 действия по умолчанию для сигналов: если обработчик не установлен, сигнал игнорируется. Обычный процесс от SIGTERM умер бы.
  6. docker inspect --format '{{.State.ExitCode}} {{.State.OOMKilled}}', затем dmesg и docker events — запись мог сделать не процесс, а ядро.
  7. docker exec container ps -eo pid,ppid,cmd. Первая строка не должна быть sh.
  8. --restart; always перезапускает и после перезагрузки демона даже остановленный вручную, unless-stopped — нет.
  9. Первая: PID 1 — оболочка (shell-форма CMD). Вторая: обработчик SIGTERM отсутствует. Различает ps внутри container'а: в первом случае PID 1 — sh.
  10. Код возврата и последние строки логов предыдущего запуска: docker logs --previous. Политика перезапуска маскирует причину.

Quiz 05. Dockerfile

  1. Что такое контекст сборки и что попадает в образ из него?
  2. Чем COPY отличается от ADD и почему предпочитают первый?
  3. Как устроен кэш сборки: что делает слой недействительным?
  4. Чем exec-форма CMD отличается от shell-формы?
  5. Что означает EXPOSE и что он не делает?
  6. Как передать секрет в сборку так, чтобы он не попал в слои?
  7. Как расположить инструкции, чтобы изменение кода не пересобирало зависимости?
  8. Как убедиться, что стадия тестов действительно выполняется?
  9. Сборка занимает три минуты после правки одной строки кода. Причина?
  10. docker run образ --help печатает справку Python вместо справки приложения. Что не так?
Ответы
  1. Каталог, отправляемый демону. В образ попадает только то, что явно скопировано инструкциями, но весь контекст передаётся и влияет на кэш — отсюда нужен .dockerignore.
  2. ADD дополнительно распаковывает архивы и загружает по URL. Неявное поведение — источник неожиданностей, поэтому для локальных файлов берут COPY.
  3. Слой недействителен, если изменилась инструкция или содержимое копируемых файлов. Один недействительный слой обесценивает все последующие.
  4. Exec-форма запускает процесс напрямую, он становится PID 1. Shell-форма разворачивается в /bin/sh -c, и PID 1 — оболочка.
  5. Документирует предполагаемый порт. Не публикует его и не создаёт ни одного правила.
  6. RUN --mount=type=secret,id=… плюс docker build --secret. ARG и ENV не годятся: остаются в метаданных и истории.
  7. Сначала копировать файл зависимостей и устанавливать, потом копировать исходники.
  8. Стадия, на которую нет ссылок, при обычной сборке не выполняется. Запускать явно: docker build --target test . — и проверить, что сломанный тест останавливает сборку.
  9. COPY . . стоит раньше установки зависимостей: любое изменение сбрасывает кэш установки.
  10. Задан только CMD. Аргументы docker run заменяют его целиком. Нужен ENTRYPOINT для команды и CMD для значений по умолчанию.

Quiz 06. Python внутри Container

  1. Почему python:3.13-slim обычно предпочтительнее python:3.13 и alpine?
  2. Зачем нужен PYTHONUNBUFFERED и что происходит без него?
  3. Чем виртуальное окружение внутри образа помогает multi-stage сборке?
  4. Почему USER задают числом, а не именем?
  5. Что должно происходить в приложении при получении SIGTERM?
  6. Как передать конфигурацию в приложение и как проверить её при старте?
  7. Как прочитать ограничение памяти изнутри container'а?
  8. Как запустить тесты внутри образа так, чтобы их провал остановил сборку?
  9. docker logs пуст, хотя приложение работает и печатает. Причина?
  10. Приложение падает с PermissionError при записи в смонтированный том. Где искать?
Ответы
  1. slim даёт малый размер без смены реализации libc. alpine использует musl: часть колёс приходится собирать из исходников, и сборка замедляется в разы.
  2. Python буферизует stdout блоками, когда он не терминал. Без переменной строки копятся и появляются пачкой — иногда только при завершении.
  3. Окружение по фиксированному пути (/opt/venv) копируется одной инструкцией COPY --from, без привязки к версии Python в путях.
  4. Имя внутри образа снаружи не разрешается. Kubernetes с runAsNonRoot: true не может убедиться, что UID ненулевой, и pod не стартует.
  5. Прекратить приём новых задач, доработать текущие, закрыть соединения, завершиться — до истечения grace period.
  6. Через переменные окружения; проверять при старте и падать сразу при неверном значении. Сервис, стартовавший с испорченной настройкой, выглядит здоровым и отвечает ошибками.
  7. cat /sys/fs/cgroup/memory.max — работает без прав root и показывает применённое, а не запрошенное.
  8. Отдельной стадией multi-stage и вызовом docker build --target test. Проверить, сломав один тест намеренно.
  9. Буферизация вывода: нет PYTHONUNBUFFERED=1.
  10. Владелец каталога внутри container'а: ls -ldn. Пустой том получает права из образа при первом подключении.

Quiz 07. Storage

  1. Чем именованный том отличается от bind mount по управлению и по переносимости?
  2. Что происходит с данными в записываемом слое при удалении container'а?
  3. Почему монтирование каталога поверх пути образа скрывает его содержимое?
  4. Откуда пустой том берёт владельца и права?
  5. Для чего нужен tmpfs и чем он отличается от тома?
  6. Как сделать резервную копию именованного тома?
  7. Как запустить container с корневой файловой системой только для чтения?
  8. Как проверить, что данные переживают пересоздание container'а?
  9. chmod 777 на томе «решил» проблему прав. Чем это плохо и что сделать вместо?
  10. После -v $(pwd):/app приложение не находит свои модули. Что произошло?
Ответы
  1. Томом управляет Docker, путь на хосте не важен; bind mount привязан к конкретному каталогу конкретной машины. Том переносим, bind mount — нет.
  2. Исчезают вместе с container'ом. Записываемый слой — часть container'а, а не образа.
  3. Монтирование накрывает точку целиком: содержимое образа по этому пути становится недоступно, хотя никуда не делось.
  4. Из образа — точнее, из каталога, поверх которого подключён, — и только при первом подключении. Дальше права сохраняются.
  5. Каталог в памяти для временных файлов; исчезает вместе с container'ом и не касается диска. Нужен, например, при read_only: true.
  6. Запустить вспомогательный container с подключённым томом и заархивировать: docker run --rm -v том:/data -v "$PWD:/backup" alpine tar czf /backup/том.tgz -C /data .
  7. --read-only плюс --tmpfs /tmp для того, что всё же требует записи.
  8. Записать, docker rm -f, создать заново с тем же томом, прочитать.
  9. Полный доступ получает любой процесс. Вместо — согласовать UID и GID процесса с владельцем каталога, задав их в образе.
  10. Каталог хоста накрыл каталог образа. Ставить зависимости вне монтируемого пути или монтировать подкаталог.

Quiz 08. Networking

  1. Что происходит с сетью при создании container'а?
  2. Чем сеть по умолчанию bridge отличается от пользовательской?
  3. Что означает публикация порта на уровне механизма?
  4. Почему 127.0.0.1 внутри container'а — не то же, что на хосте?
  5. Как container обращается к сервису на хосте?
  6. Как создать сеть, из которой нет выхода в интернет?
  7. Как проверить, слушает ли приложение нужный адрес, не устанавливая утилит?
  8. Как выяснить, в каких сетях состоит container?
  9. Порт опубликован, docker ps это подтверждает, но ответа нет. Три гипотезы?
  10. Внутри container'а не разрешаются внешние имена, а адреса пингуются. Две причины?
Ответы
  1. Создаётся network namespace, пара veth, один конец в container'е, другой в мосте. Адрес выдаётся из подсети моста.
  2. В пользовательской работает встроенное разрешение имён по имени container'а; в сети по умолчанию — только адреса.
  3. Правило трансляции адресов: трафик на порт хоста направляется на адрес container'а в сети Docker.
  4. У каждого container'а свой network namespace со своим петлевым интерфейсом. 127.0.0.1 внутри — сам container.
  5. По имени host.docker.internal (при поддержке) либо по адресу шлюза сети Docker.
  6. internal: true у сети в Compose или --internal при создании. Внутренние имена продолжают разрешаться.
  7. Изнутри: python -c "import socket; s=socket.socket(); print(s.connect_ex(('0.0.0.0', 8000)))". Ноль — слушает.
  8. docker inspect container --format '{{json .NetworkSettings.Networks}}'.
  9. Приложение слушает другой порт; приложение слушает 127.0.0.1; приложение не запустилось. Различает проверка изнутри.
  10. Сеть объявлена internal: true — и это может быть намеренно. Либо DNS хоста недоступен из сети Docker (например, 127.0.0.53).

Quiz 09. Docker Compose

  1. Какую задачу решает Compose и чего он не делает?
  2. Что создаёт Compose при up, кроме container'ов?
  3. Что именно гарантирует depends_on в форме списка?
  4. Чем condition: service_healthy отличается от service_started?
  5. Зачем нужен service_completed_successfully и для каких сервисов?
  6. Как разделить конфигурации разработки и эксплуатации?
  7. Как передать секрет сервису, не записывая его в файл конфигурации?
  8. Как убедиться, что порядок старта соблюдается воспроизводимо?
  9. docker compose up --scale web=3 не работает: «имя занято». Причина?
  10. Приложение падает при старте с Connection refused к базе. При этом имя базы разрешается. Что не так?
Ответы
  1. Описывает многокомпонентную систему на одной машине. Не занимается распределением по узлам, автоматическим восстановлением и масштабированием по нагрузке.
  2. Пользовательскую сеть проекта, объявленные тома, и присваивает имена по шаблону «проект-сервис-номер».
  3. Только запуск container'а, а не готовность процесса внутри.
  4. service_started ждёт запуска container'а; service_healthy — успешного healthcheck, то есть готовности принимать запросы.
  5. Для одноразовых задач вроде миграций: зависимый сервис не стартует, пока задача не завершится с кодом 0.
  6. Базовый файл плюс override: docker compose -f compose.yaml -f compose.dev.yaml up. Отладочные настройки не попадают в основной файл.
  7. Через secrets с внешним файлом вне репозитория; в переменные передаётся путь (*_FILE), а не значение.
  8. Запустить из чистого состояния десять раз подряд. Один успешный запуск ничего не доказывает: гонка проявляется не всегда.
  9. Задан container_name: имя фиксировано, второй экземпляр создать нельзя.
  10. Либо в строке подключения localhost вместо имени сервиса, либо depends_on без условия готовности — база ещё инициализируется.

Quiz 10. Development workflow

  1. Что должно отличаться между окружением разработки и эксплуатации, а что обязано совпадать?
  2. Зачем монтировать код внутрь container'а при разработке и чем за это платят?
  3. Почему автоперезагрузчик не используют в эксплуатации?
  4. Как отлаживать приложение внутри container'а?
  5. Почему образ для разработки и образ для эксплуатации собирают из одного Dockerfile?
  6. Как ускорить цикл «правка — проверка» без пересборки образа?
  7. Как запустить одну команду внутри уже работающего container'а?
  8. Как убедиться, что образ эксплуатации не содержит средств разработки?
  9. Изменение кода не подхватывается, хотя каталог смонтирован. Две причины?
  10. Локально работает, в CI падает. С чего начать?
Ответы
  1. Совпадать обязаны версии зависимостей и способ запуска приложения. Отличаться могут монтирование кода, уровень журналирования, публикация портов, отладочные средства.
  2. Чтобы не пересобирать образ на каждую правку. Плата — расхождение с тем, что поедет в эксплуатацию, и риск, что каталог хоста накроет содержимое образа.
  3. Он перезапускает процесс при изменении файлов: лишняя нагрузка, лишняя площадь атаки и непредсказуемое поведение при случайном изменении файла.
  4. Подключением отладчика по порту либо docker exec с интерактивной оболочкой — если она есть в образе. В образе эксплуатации её лучше не иметь.
  5. Multi-stage: общие стадии дают одинаковые зависимости, а различия сводятся к целевой стадии. Два файла разъезжаются.
  6. Монтировать только каталог с кодом, оставив зависимости в образе по пути вне монтирования.
  7. docker exec container команда. Для образов без оболочки — docker exec container python -c "…".
  8. docker run --rm образ which gcc pip pytest — ничего не должно найтись; плюс проверка размера и списка пакетов.
  9. Смонтирован не тот каталог (или не тот путь внутри); либо процесс не перезапускается, потому что перезагрузчик не включён.
  10. Сравнить окружения: версия базового образа, переменные, наличие файлов вне репозитория. Проверить сборку из чистого клона во временном каталоге.

Quiz 11. Production

  1. Какие требования отличают образ, пригодный к эксплуатации, от работающего?
  2. Зачем разделять liveness и readiness и что происходит при их слиянии?
  3. Для чего нужна startup-проба, если есть readiness?
  4. Чем requests отличается от limits по смыслу?
  5. Почему конфигурация приходит из окружения, а не из файла в образе?
  6. Как задать ограничение памяти и убедиться, что оно применилось?
  7. Как проверить, что приложение переживает SIGTERM за отведённое время?
  8. Как закрепить версию базового образа так, чтобы сборка была воспроизводимой?
  9. Отказ базы приводит к перезапуску всех экземпляров приложения. Что настроено неверно?
  10. Container помечен unhealthy, но сервис отвечает. Где искать?
Ответы
  1. Non-root с числовым UID, exec-форма точки входа, обработка SIGTERM, healthcheck, ограничения ресурсов, логи в stdout, конфигурация из окружения, отсутствие состояния внутри.
  2. Liveness отвечает «жив ли процесс», readiness — «готов ли обслуживать». При слиянии отказ зависимости вызывает перезапуск, который ничего не чинит.
  3. Чтобы медленный старт не требовал большого начального ожидания у остальных проб: пока startup не пройдена, liveness не выполняется.
  4. requests влияет на размещение (сколько зарезервировать), limits — на поведение под нагрузкой (когда тормозить или убивать).
  5. Один образ должен работать во всех средах. Конфигурация в образе означает отдельный образ на среду — то есть в эксплуатацию едет не то, что проверяли.
  6. -m 256m; проверять изнутри cat /sys/fs/cgroup/memory.max, а не docker inspect.
  7. docker run -d, затем time docker stop. Меньше секунды — обработчик работает; ровно grace period — нет.
  8. Ссылаться на базовый образ по digest: FROM python:3.13-slim@sha256:….
  9. Liveness-проба обращается к базе. Она должна проверять только сам процесс.
  10. docker inspect --format '{{json .State.Health}}': код 127 — команды нет в образе, 124 — истёк timeout, 1 — проверка вернула ошибку.

Quiz 12. Security

  1. От чего container защищает, а от чего нет?
  2. Почему root внутри container'а по умолчанию равен root на хосте?
  3. Что даёт --privileged и почему это эквивалентно правам root на хосте?
  4. Почему монтирование /var/run/docker.sock опаснее, чем кажется?
  5. Что делает профиль seccomp по умолчанию и чего он не делает?
  6. Как передать секрет в сборку, чтобы он не остался в слоях?
  7. Как проверить образ на наличие секретов в истории?
  8. Как снять все capabilities и вернуть только нужную?
  9. Секрет удалён следующей инструкцией Dockerfile, но найден в образе. Почему?
  10. Приложение работает только с --privileged. Как найти, что именно ему нужно?
Ответы
  1. Защищает хост от ошибок приложения и приложения друг от друга. Не защищает от недоверенного кода и не защищает приложение от его собственных зависимостей.
  2. Docker не создаёт user namespace: UID 0 внутри — это UID 0 снаружи. Разделяют их namespaces и capabilities, а не преобразование идентификаторов.
  3. Снимает seccomp, AppArmor, возвращает все capabilities и открывает устройства. Вместе с монтированием каталогов хоста это полный доступ.
  4. Через сокет создаётся container с любыми параметрами — включая монтирование корня хоста. Сокет выглядит «просто файлом», а даёт полный контроль над демоном, работающим от root.
  5. Запрещает несколько десятков заведомо опасных системных вызовов из более чем трёхсот. Основная часть интерфейса ядра остаётся доступной.
  6. RUN --mount=type=secret плюс docker build --secret. Либо multi-stage: слой с секретом останется в промежуточной стадии.
  7. docker history --no-trunc образ | grep -iE 'password|secret|token'; надёжнее — распаковать docker save и искать по содержимому слоёв.
  8. --cap-drop ALL --cap-add НУЖНАЯ. Искать нужную перебором от пустого набора, а не снятием от полного.
  9. Слои только добавляются. Удаление создаёт запись «файла нет», содержимое остаётся в нижнем слое.
  10. Запускать с --cap-drop ALL и добавлять по одной возможности, пока не заработает; проверить, не нужен ли вместо этого доступ к конкретному устройству через --device.

Quiz 13. Observability

  1. Что происходит с тем, что приложение пишет в stdout?
  2. Почему логи пишут в stdout, а не в файл внутри container'а?
  3. Что показывает docker inspect и чего он показать не может?
  4. Чем docker stats отличается от чтения /sys/fs/cgroup изнутри?
  5. Почему docker logs может быть пуст при работающем приложении?
  6. Как настроить ротацию логов и почему она не включена по умолчанию для всех?
  7. Как посмотреть сетевые соединения container'а без утилит в образе?
  8. Как найти, что занимает место, если docker system df показывает немного?
  9. Container исчез, логи приложения обрываются на обычной строке. Где искать причину?
  10. Сервис не отвечает, docker ps показывает Up. Что проверить в первую очередь?
Ответы
  1. Демон перехватывает поток и передаёт драйверу журналирования; при драйвере по умолчанию пишет в файл JSON на хосте.
  2. Файл внутри container'а не виден сборщику логов, исчезает вместе с container'ом и растёт в записываемом слое.
  3. Показывает намерение: что было запрошено при создании. Не показывает, применилось ли оно — это видно только по показаниям ядра.
  4. docker stats берёт те же данные снаружи и агрегирует. Чтение изнутри даёт применённые значения, включая memory.events — счётчики давления, которых в stats нет.
  5. Буферизация вывода в приложении; либо приложение пишет в файл; либо драйвер журналирования не сохраняет вывод.
  6. --log-opt max-size=10m --log-opt max-file=3 или daemon.json. По умолчанию не включена ради совместимости — и это самая частая причина заполнения диска.
  7. docker exec container cat /proc/net/tcp либо nsenter -n -t PID ss -tlnp с хоста.
  8. Логи container'ов: du -sh /var/lib/docker/containers/*/*-json.log. В system df они не входят.
  9. docker inspect --format '{{.State.ExitCode}} {{.State.OOMKilled}}', затем dmesg и docker events: запись мог сделать не процесс, а ядро.
  10. Healthcheck: Up означает «процесс жив», а не «сервис работает». Затем — слушает ли приложение нужный адрес.

Quiz 14. Registry

  1. Что происходит при docker push и что при этом передаётся?
  2. Почему повторная выгрузка того же образа в другой репозиторий передаёт слои заново?
  3. Сколько разных digest'ов у одного образа и какой годится для закрепления?
  4. Чем неизменяемый тег отличается от перезаписываемого на практике?
  5. Что защищает TLS у registry и чего он не защищает?
  6. Как выгрузить образ в приватный registry с аутентификацией?
  7. Как узнать digest опубликованного образа, не загружая его?
  8. Как поднять локальный registry для проверки процесса доставки?
  9. В эксплуатации работает не тот код, который ожидали. Три места, где могло разойтись?
  10. Место на диске кончается из-за образов, хотя каждый небольшой. Причина?
Ответы
  1. Передаются слои, которых в registry ещё нет, затем конфигурация и манифест. Уже имеющиеся слои не передаются.
  2. Дедупликация действует в пределах репозитория. Другой репозиторий — другое хранилище ссылок, слои загружаются заново.
  3. Три: у index (списка манифестов), у манифеста конкретной платформы и у конфигурации. Для закрепления годится тот, что показывает RepoDigests.
  4. Неизменяемый позволяет откатиться: предыдущий образ по-прежнему доступен под своим именем. Перезаписываемый этого не даёт — под именем уже другой образ.
  5. Защищает содержимое при передаче. Не защищает от подмены манифеста по тегу: тег изменяем, и подмена законна с точки зрения протокола.
  6. docker login registry с токеном, затем docker tag и docker push. Пароль передавать через --password-stdin, а не аргументом.
  7. docker buildx imagetools inspect образ:тег — обращается к registry без загрузки слоёв.
  8. docker run -d -p 5000:5000 registry:2; для проверки процесса этого достаточно, для эксплуатации — нет.
  9. pull выполнен, но container не пересоздан; тег перезаписан между pull и запуском; опубликовано не то, что собрано.
  10. Перезаписываемые теги оставляют безымянные образы: каждая сборка добавляет ещё один, и они не удаляются.

Quiz 15. Testing

  1. Что проверяют unit-тесты, что интеграционные, а что — проверки образа?
  2. Почему тесты запускают внутри образа, а не только локально?
  3. Почему стадия тестов может не выполниться при обычной сборке?
  4. Чем проверка образа отличается от проверки исходников?
  5. Что даёт Testcontainers по сравнению с Compose для интеграционных тестов?
  6. Как сделать так, чтобы провал теста останавливал сборку?
  7. Как убедиться, что интеграционные тесты действительно работали с базой?
  8. Как проверить, что образ запускается не от root?
  9. Тесты «прошли», но в отчёте 33 пропущенных. Что это означает для конвейера?
  10. Тесты проходят локально и падают в CI. Первые три вещи для проверки?
Ответы
  1. Unit — логику без зависимостей; интеграционные — взаимодействие с настоящими базой и очередью; проверки образа — то, что появилось между исходниками и образом благодаря Dockerfile.
  2. Локально проверяются исходники рядом с тестами. В образе установлен пакет, другие пути и другой пользователь — расхождения находятся только там.
  3. Стадия, на которую нет ссылок, при сборке целевого образа не нужна и пропускается. Сборка при этом успешна.
  4. Между ними лежит Dockerfile: лишний пакет, секрет в слое, забытый USER, неверная форма точки входа исходниками не ловятся.
  5. Testcontainers поднимает зависимости из кода теста и убирает их гарантированно (в том числе после kill -9). Compose требует внешнего управления жизненным циклом.
  6. Прогонять их в стадии сборки (RUN pytest) либо отдельным шагом с ненулевым кодом возврата. Проверить, сломав один тест намеренно.
  7. Ввести переключатель, превращающий пропуск в отказ при отсутствии базы. Иначе «пропущено» неотличимо от «прошло».
  8. docker image inspect --format '{{.Config.User}}' — значение должно быть числовым и ненулевым.
  9. Что проверено меньше, чем кажется. В конвейере пропуск должен быть отказом: зелёная сборка не должна означать «мы перестали проверять».
  10. Версия базового образа; переменные окружения; файлы вне репозитория, попадающие в сборку локально и отсутствующие в CI.

Quiz 16. CI/CD

  1. По какому признаку упорядочивают этапы конвейера?
  2. Почему логику выносят в скрипты, а не пишут в шагах YAML?
  3. Зачем конвейеру третий код возврата, кроме «успех» и «провал»?
  4. Почему сканирование выполняют после сборки, а не до?
  5. Что экспортирует cache-to и чего он не экспортирует?
  6. Как перенести кэш сборки между запусками конвейера?
  7. Как назначить образу тег, по которому можно найти коммит?
  8. Как проверить, что конвейер падает быстро на частой ошибке?
  9. Сканер не установился, конвейер зелёный. Чем это опасно?
  10. «Кэш настроен, а сборка медленная». Самая частая причина?
Ответы
  1. По отношению «время выполнения / вероятность отказа»: вперёд то, что падает часто и проверяется быстро.
  2. Тогда конвейер отлаживается локально за секунды, перенос на другую платформу становится механическим, и те же скрипты годятся для проверки образа в тестах.
  3. Чтобы «не проверено» отличалось от «проверено и чисто». Иначе отсутствие инструмента делает конвейер зелёным ровно тогда, когда проверять перестали.
  4. Проверяется образ, а не исходники: между ними Dockerfile, и его вклад исходниками не проверяется.
  5. Экспортирует слои образа. Содержимое RUN --mount=type=cache не экспортирует — это разные механизмы.
  6. --cache-from и --cache-to с хранением в registry, обязательно с mode=max.
  7. Тег из короткого SHA коммита плюс признак незафиксированных изменений. Проверять чистоту дерева нужно git status --porcelain: git diff не видит неотслеживаемых файлов.
  8. Сломать самую дешёвую проверку и замерить время до падения. Минуты вместо секунд означают неверный порядок.
  9. Зелёный цвет начинает означать «не проверяли», и узнать об этом неоткуда. Публикация должна требовать кода 0, а не «не провалено».
  10. mode=min — значение по умолчанию: экспортируется только последняя стадия, и слой с зависимостями в кэш не попадает.

Quiz 17. Docker internals

  1. Что делает клиент docker и что делает демон?
  2. Из скольких запросов API состоит docker run и почему?
  3. Какова цепочка от демона до процесса приложения?
  4. Почему runc не виден в списке процессов работающего container'а?
  5. Сколько namespace'ов создаёт Docker и какие не создаёт?
  6. Как сравнить namespace'ы двух процессов надёжно?
  7. Как прочитать применённые ограничения ресурсов изнутри container'а?
  8. Как разобрать мультиплексированный поток логов из API?
  9. docker inspect показывает лимит памяти, но приложение его не соблюдает. Как проверить?
  10. Один байт записан в файл образа, слой вырос на 128 МиБ. Почему?
Ответы
  1. Клиент преобразует аргументы в HTTP-запросы к сокету. Всю работу выполняет демон; без клиента можно обойтись, обращаясь к API напрямую.
  2. Из двух: POST /containers/create и POST /containers/ID/start. Между ними container существует, но не запущен.
  3. dockerdcontainerd → shim → runc → процесс. Shim остаётся родителем процесса и держит его стандартные потоки и код возврата.
  4. runc выполняет настройку и завершается. Родителем процесса остаётся shim, поэтому container переживает перезапуск демона.
  5. Шесть из восьми. Не создаёт user (отсюда root внутри равен root снаружи) и time.
  6. По номеру inode: readlink /proc/PID/ns/*. Сравнение по поведению обманчиво.
  7. Читать /sys/fs/cgroup/memory.max, cpu.max, pids.max — доступно без прав root.
  8. Каждый фрагмент предваряется восемью байтами: байт 0 — поток (1 stdout, 2 stderr), байты 4–7 — длина. Распаковка: struct.unpack(">BxxxI", data[:8]).
  9. Прочитать memory.max изнутри. Расхождение с inspect означает, что запрос не был выполнен: отключён контроллер cgroup или несовместимо ядро.
  10. Copy-up: при первой записи файл копируется из нижнего слоя в верхний целиком.

Quiz 18. Docker и Kubernetes

  1. Какую задачу решает Docker и какую — Kubernetes?
  2. Почему отказ Kubernetes от dockershim не потребовал пересборки образов?
  3. Чем pod отличается от container'а?
  4. Почему depends_on не имеет аналога в Kubernetes?
  5. В чём разница между requests и limits и что даёт каждый?
  6. Что нужно добавить в приложение для трёх проб?
  7. Какие конструкции Compose переносятся напрямую, а какие требуют переосмысления?
  8. Как определить, нужен ли сервису StatefulSet?
  9. После конвертации всё работает, но при масштабировании базы данные повреждаются. Причина?
  10. NetworkPolicy создана, изоляции нет. Гипотеза?
Ответы
  1. Docker собирает образ и запускает container на машине. Kubernetes поддерживает заданное состояние на множестве машин. Это разные уровни, а не конкуренты.
  2. Образы соответствуют стандарту OCI, а dockershim был лишь одним звеном между kubelet и runtime. Заменили звено, стандарт не менялся.
  3. Pod — группа container'ов, разделяющих сетевой и часть других namespace'ов. Минимальная единица планирования — pod, а не container.
  4. Pod'ы запускаются параллельно по устройству системы; гарантировать порядок значило бы отказаться от этого. Правильная замена — повторы подключения в приложении.
  5. requests учитывается при размещении, limits — при работе. От их соотношения зависит класс качества обслуживания и очередь вытеснения.
  6. Три раздельных эндпоинта: /startupz, /healthz (без проверки зависимостей) и /readyz (с проверкой). Это работа в коде, а не в манифесте.
  7. Напрямую — образ, команда, окружение, секреты, лимиты, пользователь, ограничения безопасности. Переосмысления требуют ports, тома, healthcheck и ресурсы; не переносятся depends_on, networks, profiles.
  8. По наличию состояния, которое нельзя терять: именованный том с данными. Deployment с одним PVC работает, пока реплика одна.
  9. База развёрнута как Deployment. Два pod'а обращаются к одному тому — либо Multi-Attach error, либо два процесса пишут в один файл.
  10. Плагин сети не поддерживает NetworkPolicy. Правило создано и не действует, и это никак не видно — проверять нужно фактическую доступность.

Quiz 19. Ограничения Docker

  1. Где контейнеризация добавляет расходы, а где их практически нет?
  2. Почему каждый способ ускорить container ослабляет изоляцию?
  3. Почему образ с GPU перестаёт быть переносимым?
  4. Что systemd даёт из того же, что Docker, и чего он не даёт?
  5. Почему одних container'ов недостаточно для недоверенного кода?
  6. Как измерить площадь атаки на ядро своей машины?
  7. Какой вопрос отбирает случаи, где Docker не нужен?
  8. Как проверить, что линтер конфигураций не отмечает всё подряд?
  9. Отладка стала дороже после контейнеризации. Что именно добавилось?
  10. Линтер даёт ноль находок. Что это говорит о конфигурации, а что — нет?
Ответы
  1. Расходов почти нет на вычислениях и памяти: это обычный процесс ядра. Есть — на системных вызовах (seccomp), файловой системе (OverlayFS), сети (veth, NAT) и публикации портов.
  2. --network host снимает сетевую изоляцию, seccomp=unconfined — фильтр вызовов, bind mount — независимость от хоста. Каждый приём убирает ровно то, ради чего container взят.
  3. Устройство пробрасывается, а не виртуализируется: пользовательская часть в образе должна быть совместима с драйвером хоста. Образ привязывается к машине.
  4. Даёт ограничения ресурсов, изоляцию файловой системы и сети, снятие capabilities, фильтр вызовов — на тех же механизмах ядра. Не даёт упаковки зависимостей и одинаковости окружения между машинами.
  5. Ядро общее: любая его уязвимость доступна изнутри. Container — граница между вашими сервисами, а не между вами и злоумышленником.
  6. Число системных вызовов из заголовка ядра (grep -c '^#define __NR_') и число capabilities из CapBnd в /proc/self/status.
  7. «Что перестанет работать, если убрать Docker?» Если ответ — «ничего, кроме привычки», случай попадает в список.
  8. Прогнать его на заведомо чистой конфигурации и убедиться, что находок нет. Инструмент, отмечающий всё, неотличим от сломанного.
  9. Уровни между кодом и системой: PID namespace, cgroup, OverlayFS, veth, мост, NAT. Одно сообщение Connection refused начинает означать вдвое больше вещей.
  10. Говорит, что механически проверяемых нарушений нет. Не говорит, знает ли кто-нибудь ещё, как собрать образ, обновлялся ли базовый образ и читает ли кто-нибудь отчёты сканирования.

Что делать с ошибками

Записывайте не «ошибся в вопросе 7», а тему. Три ошибки в одном разделе почти всегда лежат в одной теме, и перечитывать нужно её, а не раздел целиком.

Отдельно стоит отметить вопросы, где ответ был угадан. Угаданный верный ответ хуже честной ошибки: он не попадает в список тем для повторения.


Навигация

Вернуться к системе проверки
Checkpoints
Debugging challenges
Итоговый теоретический тест
Главное оглавление

Markdown на GitHub ↗