Що таке Docker image (образ)?
Docker image це незмінний read-only шаблон, що пакує застосунок разом з усім потрібним для запуску: код, рантайм, системні бібліотеки, environment-змінні і дефолтну конфігурацію.
Теорія
TL;DR
- Image це креслення, а не щось запущене. Container це запущені екземпляри image.
- Складається з трьох частин: шарів (файлова система), manifest (які шари і config використати) і config blob (env, команда, working dir, тощо).
- Ідентифікується через tag на кшталт
nginx:1.27-alpine(мінливий, може бути перенесений) або digest на кшталтsha256:4f06b3e2...(незмінний, content-addressed). - Ти або тягнеш з registry (
docker pull), або збираєш свій з Dockerfile (docker build). - Стандартизовано OCI Image Spec, тому Docker, Podman, containerd, Kubernetes читають той самий формат.
Швидкий приклад
# Тягнемо конкретну версію nginx
$ docker pull nginx:1.27-alpine
1.27-alpine: Pulling from library/nginx
9824c27679d3: Pull complete
8e1015e74a85: Pull complete
Digest: sha256:4f06b3e2c0c1e8e6a9d2c8f3e8d7a6b5c4...
Status: Downloaded newer image for nginx:1.27-alpine
# Бачимо у локальному кеші
$ docker images
REPOSITORY TAG IMAGE ID SIZE
nginx 1.27-alpine 4f06b3e2c0c1 54.9MBImage тепер на твоєму диску. Поки нічого не робить. Щоб використати, стартуєш container з нього: docker run nginx:1.27-alpine.
Що насправді всередині image
Три частини, всі адресовані по hash контенту:
- Шари (layers) - вміст файлової системи, розділений на впорядковані tarball'и. Кожна Dockerfile-інструкція зазвичай дає один шар. Шари дедуплікуються між image: якщо
node:22-alpineіpython:3.13-alpineобидва на тій самій Alpine-основі, ця базова частина лежить на диску один раз. - Config blob - JSON, що описує як запускати container з цього image: working directory, дефолтна команда, environment-змінні, експоновані порти, користувач, entrypoint.
- Manifest - JSON, що зв'язує вищезгадане: список digest'ів шарів, digest config'у, платформа (linux/amd64, linux/arm64). Сам manifest має digest, і цей digest це справжня ідентичність твого image.
Коли ти робиш pull, daemon тягне спочатку manifest, потім ті шари і config, яких ще немає локально.
Tag vs digest
Це підставляє майже всіх з першого разу.
- Tag це ім'я, що вказує на manifest. Воно мінливе. Сьогодні
nginx:latestвказує на manifest A; завтра Docker Inc пушить новий latest, іnginx:latestвже вказує на manifest B. Те саме ім'я, інший image. - Digest це SHA256 hash контенту manifest'у. За визначенням незмінний.
nginx@sha256:4f06b3e2...завжди означає рівно ті самі байти, назавжди.
# Відтворювано: pull по digest
$ docker pull nginx@sha256:4f06b3e2c0c1e8e6...
# Не відтворювано: tag міг помінятися з моменту тестування
$ docker pull nginx:latestДля проду пінься на digest або хоча б на конкретну версію типу nginx:1.27.4. :latest тебе колись здивує.
Build vs pull
Два шляхи отримати image у локальний кеш:
Pull - завантажити готовий image з registry (Docker Hub, ECR, GHCR, свій власний).
$ docker pull postgres:16-alpineBuild - зібрати свій з Dockerfile, шар за шаром.
$ docker build -t myapp:0.1 .У реальному workflow твій CI збирає image з Dockerfile, тегує його і пушить у registry. Прод-хости потім тягнуть.
Іменування image
Повна форма: [REGISTRY[:PORT]/]NAMESPACE/REPOSITORY[:TAG|@DIGEST]. Кілька прикладів:
nginx:1.27-alpine- shorthand. Дефолтний registry Docker Hub, namespacelibrary.myorg/myapp:v2.3- приватне репо на Docker Hub.ghcr.io/myorg/myapp:v2.3- GitHub Container Registry.123456789.dkr.ecr.eu-west-1.amazonaws.com/myapp:v2.3- AWS ECR.
Якщо tag не вказати, Docker припускає :latest. Зручність, що кусає у проді.
Типові помилки
Плутати image з container
$ docker images # список IMAGES (шаблони)
$ docker ps -a # список CONTAINERS (екземпляри)
$ docker rmi <id> # видалити image
$ docker rm <id> # видалити containerЯкщо docker rmi nginx скаржиться «image is being used by stopped container», image це шаблон, і якийсь container ще навколо і його використовує. Спочатку видали container, потім image.
Довіряти :latest у проді
# НЕПРАВИЛЬНО: це може помінятися за ніч
image: nginx:latest
# ПРАВИЛЬНО: пін на конкретну версію
image: nginx:1.27.4
# КРАЩЕ: пін на digest
image: nginx@sha256:4f06b3e2c0c1...Новий :latest може принести breaking change між двома деплоями того самого коду. Digest ніколи не бреше.
Слати весь репо як build context
# Без .dockerignore це шле все на daemon:
COPY . /app# .dockerignore - тримай build context маленьким
node_modules
.git
*.log
dist/Docker заливає build context (твою поточну директорію) на daemon перед збіркою. Без .dockerignore ти шиппиш node_modules, .git і гігабайти dev-артефактів на daemon на кожен білд.
Мутувати image після збірки
Не можна. Якщо ти заходиш у container і apt-get install щось, image не змінився, ці пакети живуть у writable-шарі того одного container і зникають при рестарті. Щоб запекти, редагуй Dockerfile і пересобирай.
Реальне застосування
- Docker Hub - публічний registry, що хостить
nginx,postgres,redis,nodeі мільйони community-image. Дефолтний registry, коли робишdocker pullбез вказівки. - AWS ECR / Google Artifact Registry / GitHub Container Registry - приватні registry, що використовують більшість команд, які шиппять прод. Той самий формат image, інший контроль доступу.
- Multi-architecture image - один tag типу
nginx:1.27насправді вказує на manifest list («image index»), що містить manifest'и дляlinux/amd64,linux/arm64,linux/arm/v7. Твій клієнт сам обирає правильний для твого CPU. - Cosign / Sigstore - криптографічне підписування digest'ів image. Використовується у supply-chain-обережних сетапах, щоб прод деплоїв тільки image, підписані довіреним CI.
Питання для поглиблення
Q: Де image реально лежать на моїй машині?
A: У зоні зберігання Docker, типово /var/lib/docker/overlay2/ на Linux. Кожен шар це окрема директорія. Шлях приватний для daemon, ти не взаємодієш з ним напряму, ти використовуєш docker images і docker rmi.
Q: Docker image і OCI image це одне й те саме?
A: По суті так. OCI Image Specification було витягнуто з формату Docker і тепер є відкритим стандартом. Docker, Podman, containerd і Kubernetes читають OCI image. «Docker image» і «OCI image» на практиці взаємозамінні.
Q: Чому два image з однаковим контентом іноді мають різні digest?
A: Бо метадані у config blob (build timestamps, build args) міняють байти, навіть коли шари файлової системи однакові. Щоб отримати reproducible digest, збирай з --build-arg SOURCE_DATE_EPOCH=... та іншими reproducibility-флагами, і уникай вшитих timestamp'ів.
Q: Яка різниця між image і manifest list?
A: Image manifest описує один image для однієї платформи (скажімо linux/amd64). Manifest list (OCI кличе це «image index») вказує на кілька manifest'ів для різних платформ. Коли ти робиш docker pull nginx:1.27, daemon тягне manifest list, обирає manifest для твого CPU, потім тягне ті шари. Той самий tag, інші байти на різних платформах.
Q: (Senior) Як гарантувати, що image, протестований у CI, це рівно той image, що задеплоєний у проді?
A: Пінься на digest, не на tag. Твій CI ловить digest після збірки (docker buildx imagetools inspect або вивід docker push), і твій deployment manifest посилається на image@sha256:.... Це повністю обходить проблему мінливості tag. Для supply-chain гарантії підписуй digest через Cosign і верифікуй підпис на etapi admission. Tag-based деплої зручні, але такої гарантії дати не можуть.
Приклади
Інспекція image
$ docker inspect nginx:1.27-alpine | head -30
[
{
"Id": "sha256:4f06b3e2c0c1...",
"RepoTags": ["nginx:1.27-alpine"],
"RepoDigests": ["nginx@sha256:abcd1234..."],
"Architecture": "amd64",
"Os": "linux",
"Size": 54923456,
"Config": {
"Cmd": ["nginx", "-g", "daemon off;"],
"ExposedPorts": { "80/tcp": {} },
"WorkingDir": "",
"Env": ["PATH=/usr/local/sbin:/usr/local/bin..."]
}
}
]Це виплюне manifest, config і метадані image. Секція Config це те, що визначає дефолти container: коли ти робиш docker run nginx:1.27-alpine без overrides, ти отримуєш рівно ці значення.
Збірка крихітного image
# Dockerfile
FROM alpine:3.21
RUN apk add --no-cache curl
CMD ["curl", "--version"]$ docker build -t curl-tool:0.1 .
[+] Building 4.2s (6/6) FINISHED
=> [1/2] FROM alpine:3.21
=> [2/2] RUN apk add --no-cache curl
=> exporting to image
$ docker run --rm curl-tool:0.1
curl 8.10.1 (x86_64-alpine-linux-musl)Три шари видно: Alpine-основа, apk add, метадані image. Загальний розмір під 10 MB. Той самий image, без install, без залишків dev-тулзів.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів