Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке Docker image (образ)?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Docker image** це незмінний read-only шаблон, що пакує застосунок з усім потрібним для запуску: код, рантайм, системні бібліотеки, конфіг. Збираєш раз, запускаєш як завгодно багато container. ```bash $ docker pull nginx:1.27-alpine # завантажує шари + manifest + config; image тепер у локальному кеші $ docker images REPOSITORY TAG IMAGE ID SIZE nginx 1.27-alpine 4f06b3e2c0c1 54.9MB ``` **Головне:** image це креслення, ідентифіковане tag (мінливе ім'я як `nginx:1.27`) або digest (незмінний SHA256 hash). Container це екземпляри image; сам image ніколи не змінюється.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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 читають той самий формат. ### Швидкий приклад ```bash # Тягнемо конкретну версію 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.9MB ``` Image тепер на твоєму диску. Поки нічого не робить. Щоб використати, стартуєш container з нього: `docker run nginx:1.27-alpine`. ### Що насправді всередині image Три частини, всі адресовані по hash контенту: 1. **Шари** (layers) - вміст файлової системи, розділений на впорядковані tarball'и. Кожна Dockerfile-інструкція зазвичай дає один шар. Шари дедуплікуються між image: якщо `node:22-alpine` і `python:3.13-alpine` обидва на тій самій Alpine-основі, ця базова частина лежить на диску один раз. 2. **Config blob** - JSON, що описує як запускати container з цього image: working directory, дефолтна команда, environment-змінні, експоновані порти, користувач, entrypoint. 3. **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...` завжди означає рівно ті самі байти, назавжди. ```bash # Відтворювано: 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, свій власний). ```bash $ docker pull postgres:16-alpine ``` **Build** - зібрати свій з Dockerfile, шар за шаром. ```bash $ 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, namespace `library`. - `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** ```bash $ 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` у проді** ```yaml # НЕПРАВИЛЬНО: це може помінятися за ніч image: nginx:latest # ПРАВИЛЬНО: пін на конкретну версію image: nginx:1.27.4 # КРАЩЕ: пін на digest image: nginx@sha256:4f06b3e2c0c1... ``` Новий `:latest` може принести breaking change між двома деплоями того самого коду. Digest ніколи не бреше. **Слати весь репо як build context** ```dockerfile # Без .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 ```bash $ 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 # Dockerfile FROM alpine:3.21 RUN apk add --no-cache curl CMD ["curl", "--version"] ``` ```bash $ 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-тулзів.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.