Образы и слои
Образ vs контейнер, слои + copy-on-write, тэги и digest, почему latest — ловушка, что такое registry.
Из чего состоит образ
Со стороны nginx:1.27 выглядит одним файлом. На самом деле это
манифест + список слоёв. Манифест — небольшой JSON, описывающий, что
лежит внутри. Слои — архивы с дельтами файловой системы.
Когда ты делаешь docker pull nginx:1.27, Docker:
- Тянет манифест.
- Параллельно качает только те слои, которых у тебя ещё нет.
- Складывает их в локальный store, по хешам.
Если у тебя уже лежит nginx:1.26 и большая часть слоёв общая, Docker их
переиспользует. Поэтому первое скачивание долгое, а последующие
быстрые.
Слои образа
Инструкции RUN, COPY и ADD создают новый слой, то есть архив с тем,
что инструкция добавила или изменила. FROM слой не создаёт, а задаёт стартовую
стопку слоёв базового образа. Слои read-only, идентифицируются по хешу
содержимого и шарятся между образами.
Когда ты запускаешь контейнер, поверх стопки read-only слоёв ложится один writable-слой. Все изменения внутри контейнера пишутся туда. Файловая система контейнера — это слепка снизу вверх через OverlayFS.
Это и есть copy-on-write. Пока файл никто не трогал, он один на все контейнеры из этого образа. Как только кто-то его поменял, копия уезжает наверх, в writable-слой только этого контейнера. Соседние контейнеры всё ещё видят оригинал.
Что из этого следует:
- Запускать 10 контейнеров из одного образа дёшево по диску: базовые слои не дублируются.
- Менять файлы внутри контейнера можно, но это локально для него. После перезапуска без volume изменений нет (см. урок про volumes).
- Раздувать образ копированием тяжёлых файлов в
RUNнельзя: они останутся в слое, даже если в следующей строке их удалили.
Тэги
Тэг — метка на манифесте. nginx:1.27.0, nginx:1.27, nginx:latest -
могут указывать на один и тот же образ, а могут на разные. Это
просто строки, и владелец репозитория может их двигать как хочет.
Обычные практики:
| Тэг | Что значит |
|---|---|
1.27.0 | конкретный патч-релиз, обычно не двигается |
1.27 | минорная серия, двигается на новые патчи |
1 | мажорная серия, двигается на новые миноры |
latest | «последний стабильный», но кто такой стабильный, решает мейнтейнер |
alpine, slim | варианты на минимальной базе |
latest — ловушка
nginx:latest сегодня указывает на один конкретный образ. Завтра
мейнтейнер выпустит новый релиз и сдвинет тэг на него. Твой
docker pull подтянет уже другое, хотя строка в Dockerfile не
изменилась.
Что ломается:
- Сборка в CI: проходила год, в один прекрасный день падает на новой major-версии базового образа.
- Прод: ноды выкачивают разные «latest» в разное время, на одной 1.27, на другой 1.28, поведение расходится.
- Безопасность: думаешь, что зафиксировал версию, а тэг подвижный - кто-то мог перезаписать тэг с уязвимым контентом.
Простое правило: в проде ставь конкретный тэг (1.27.3) или digest. В
dev тоже желательно конкретный, но допустимо 1.27 или 1.
Digest: иммутабельная ссылка
Тэг можно двигать, а digest прибит намертво: это SHA256-хеш самого манифеста. Поменялся манифест → поменялся digest.
docker pull nginx@sha256:abc123… # заглушка; реальный digest — это полный sha256 из 64 символов
Подсмотреть digest можно так:
docker inspect nginx:1.27 --format '{{index .RepoDigests 0}}'
В проде, где нужна абсолютная воспроизводимость, ссылаются именно по digest. В Kubernetes-манифестах для критичных сервисов делают так же.
Полезные команды по образам
docker pull nginx:1.27 # скачать
docker images # список локальных
docker image ls --digests # с digest'ами
docker history nginx:1.27 # из чего собран образ, какие слои
docker rmi nginx:1.27 # удалить
docker image prune # удалить безымянные dangling-образы
docker image prune -a # удалить все, на которые нет контейнеров
docker history показывает каждый слой и какая инструкция его создала -
полезно, когда непонятно, откуда в образе раздулся размер.
Registry: где живут образы
Registry — это сервер, который хранит образы. Их много:
- Docker Hub (
docker.io) — дефолтный публичный, на нёмnginx,postgres,node, и тысячи других. Без логина дёргать его можно, но с лимитами по rate. - GitLab Container Registry встроен в каждый GitLab-репо. Сюда
пушит CI этого проекта (см.
.gitlab-ci.yml). - GitHub Container Registry (
ghcr.io) — аналог для GitHub. - Yandex Container Registry живёт в России, удобно, если деплой в YC.
- Selectel Container Registry, VK Cloud Container Registry — то же самое у других облаков.
Логин:
docker login registry.gitlab.com
docker pull registry.gitlab.com/myorg/myrepo:1.0
docker push registry.gitlab.com/myorg/myrepo:1.0
Полное имя образа — это <registry>/<namespace>/<name>:<tag>. Если
<registry> опущен, подставляется docker.io.
Что важно запомнить
Образ — стопка read-only слоёв. Контейнер — writable-слой поверх неё. Copy-on-write: файлы копируются в writable-слой только при изменении.
Тэги двигаются, digest — нет. В проде используй конкретные тэги или
digest, а не latest.
Registry — просто HTTP-сервер с образами. Docker Hub подставляется по умолчанию, остальные подключаются через полное имя образа.
В следующем уроке соберём собственный образ через Dockerfile.
Дрилл · вопросы как на собесе
Проверь себя без подсказок
Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.
Что такое OverlayFS и copy-on-write? Как хранятся образы?
DOC-017Что такое слои в Docker?
DOC-002Какие инструкции Dockerfile создают слои?
DOC-009Что такое `<none>` образы в выводе `docker images`?
DOC-011В чём риск использования тега `:latest`?
DOC-010Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс