Docker-образ (image)— это статический слепок (шаблон), включающий все необходимые файлы, библиотеки, настройки и инструкции для запуска контейнера. Внутри каждый Docker-образ построен из слоёв (layers). Эти слои делаются из команд, которые надстраиваются поверх базовой основы.
- Создаем образ на базе Alpine Linux: пошаговая инструкция
- Оптимизация образа и советы
- Управление Docker-образами
Что такое слои?
- Слои — это неизменяемые части образа, каждый слой — результат выполнения одной команды Dockerfile (например, установка пакета, копирование файла).
- Кэширование — Docker использует кэш слоёв. Если слой не изменился, он берётся из кеша, что ускоряет сборку.
- Многослойная структура — это подобие конструкторов, где каждый слой добавляет что-то новое. Финальный образ — это сумма всех слоёв.
Почему это важно?
- Можно повторно использовать слои при создании новых образов, что ускоряет процесс.
- Размер образа можно уменьшить, минимизируя количество и размер слоёв.
- Образы можно легко обновлять — просто добавляя новые слои поверх старых.
Создаем образ на базе Alpine Linux: пошаговая инструкция
Начинаем с минимального образа Alpine Linux — он очень легкий и быстрый, подходит для обучения и простых приложений. Если вы не знакомы с базовыми командами для работы с контейнерами, вы можете найти ответы в отдельной статье.
Шаг 1. Начальный образ (alpine)
Скачаем образ alpine – это базовый, минимальный образ. Он содержит минимальную установку Linux, с очень ограниченным набором команд и программ. Данный образ послужит нам заготовкой.
Скачиваем образ и проверяем список образов:
[root@waky ~]# docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
2d35ebdb57d9: Pull complete
Digest: sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest
[root@waky ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest 706db57fb206 2 weeks ago 8.32MB
[root@waky ~]#
Образ успешно скачан, теперь он доступен локально.
Создадим из этого образа контейнер и запустим его в фоновом режиме.
[root@waky ~]# docker run -td --name alpine_0 alpine /bin/sh
06611f5fc0e48a03176ec65635c7245ae3b19a79ce8db24ef9c87cb77789e44c
[root@waky ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
06611f5fc0e4 alpine "/bin/sh" 6 seconds ago Up 5 seconds alpine_0
[root@waky ~]#
Зайдем в консоль этого контейнера:
[root@waky ~]# docker exec -it alpine_0 sh
/ #
Мы попали в условно отдельный маленький Linux, можете попробовать исполнить базовые команды (ls, cd и прочие)
Вы можете обнаружить, что нет многих привычных команд и утилит, например такого полезного инструмента как curl.
Давайте установим его:
/ # curl -I https://www.google.com
sh: curl: not found
/ # apk add curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/community/x86_64/APKINDEX.tar.gz
(1/9) Installing brotli-libs (1.1.0-r2)
(2/9) Installing c-ares (1.34.5-r0)
(3/9) Installing libunistring (1.3-r0)
(4/9) Installing libidn2 (2.3.7-r0)
(5/9) Installing nghttp2-libs (1.65.0-r0)
(6/9) Installing libpsl (0.21.5-r3)
(7/9) Installing zstd-libs (1.5.7-r0)
(8/9) Installing libcurl (8.14.1-r2)
(9/9) Installing curl (8.14.1-r2)
Executing busybox-1.37.0-r19.trigger
OK: 12 MiB in 25 packages
/ # curl -I https://www.google.com
HTTP/2 200
content-type: text/html; charset=ISO-8859-1
…
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
/ #
Мы успешно установили и проверили работу утилиты. Но что будет, если пересоздать контейнер?
Выйдем из консоли контейнера, удалим и создадим его по новой:
/ # exit
[root@waky ~]# docker stop alpine_0
alpine_0
[root@waky ~]# docker rm alpine_0
alpine_0
[root@waky ~]# docker run -td --name alpine_0 alpine /bin/sh
fc92bbca682977a8eb85e2bd84a1bc4238db260eaaf10a3ddaae37b6c8179a22
[root@waky ~]# docker exec -it alpine_0 sh
/ # curl -I https://www.google.com
sh: curl: not found
/ # exit
[root@waky ~]#
Очевидно, в новом контейнере снова отсутствует curl. Контейнер каждый раз создается из базового образа, в котором нет данной программы. Чтобы не устанавливать ее каждый раз вручную мы можем создать свой образ.
Шаг 2. Создание своего образа
Два способа кастомизировать базовый образ – это модифицировать контейнер и сделать с него слепок, использовать специальный файл (Dockerfile) с набором команд необходимых к выполнению поверх базового образа.
2.1. Создание образа из контейнера
У нас остался контейнер развернутый из базового образа, повторим установку curl:
[root@waky ~]# docker exec -it alpine_0 sh
/ # apk add curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/community/x86_64/APKINDEX.tar.gz
(1/9) Installing brotli-libs (1.1.0-r2)
…
(9/9) Installing curl (8.14.1-r2)
Executing busybox-1.37.0-r19.trigger
OK: 12 MiB in 25 packages
/ # exit
[root@waky ~]#
Создадим образ из нашего контейнера с помощью команды commit, первым мы указываем имя или ID контейнера (не образа из которого сделан контейнер), а затем имя создаваемого образа:
[root@waky ~]# docker commit alpine_0 alpine_curl
sha256:346dc939b1b6ddebab2b1c879b985cf1c011c83fca0a44b65eef75235d881e0d
[root@waky ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine_curl latest 346dc939b1b6 7 seconds ago 16.1MB
alpine latest 706db57fb206 2 weeks ago 8.32MB
[root@waky ~]#
Создадим новый контейнер из образа alpine_curl:
[root@waky ~]# docker run -td --name alpine_1 alpine_curl /bin/sh
de5f3dea4245cc0be6453f003eb61d73c333a0acc0c3c3fe35ec4c5d05ae1c7f
[root@waky ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
de5f3dea4245 alpine_curl "/bin/sh" 9 seconds ago Up 8 seconds alpine_1
fc92bbca6829 alpine "/bin/sh" 46 minutes ago Up 46 minutes alpine_0
[root@waky ~]#
Проверим наличие curl в новом контейнере:
[root@waky ~]# docker exec -it alpine_1 sh
/ # curl -I https://www.google.com
HTTP/2 200
content-type: text/html; charset=ISO-8859-1
…
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
/ # exit
[root@waky ~]#
Отлично, утилита доступна сразу после создания контейнера.
Такой способ создания образа вполне рабочий, и может использоваться для небольших задач. Однако для более комплексных случаев лучше использовать Dockerfile.
2.2. Создание образа из Dockerfile
Работая даже с небольшими системами будет полезно придерживаться принципов IaC (Infrastructure-as-Code инфраструктура как код). Это подход к управлению инфраструктурой с помощью программного кода вместо ручных настроек.
Использование Dockerfile отличный тому пример, вместо ручной настройки мы используем файл с описанием шагов и компонентов.
Создадим отдельную директорию, а в ней пустой Dockerfile:
[root@waky ~]# mkdir my-app
[root@waky ~]# cd my-app/
[root@waky my-app]# touch Dockerfile
[root@waky my-app]#
Используйте любой текстовый редактор, чтобы добавить в Dockerfile следующий код:
FROM alpine:latest
RUN apk add curl
FROM указывает базовый образ взятый за основу, а с помощью RUN мы перечисляем команды которые нужно выполнить поверх базовой сборки. Результат выполнения RUN ляжет на базу новыми слоями.
Используем команду build с указанием имени нового образа (alpine_df) и директории из которой проводится сборка (. – текущая директория)
[root@waky my-app]# docker build -t alpine_df .
[+] Building 7.9s (6/6) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.2s
=> => transferring dockerfile: 134B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [1/2] FROM docker.io/library/alpine:latest 0.1s
=> [2/2] RUN apk add curl 6.8s
=> exporting to image 0.4s
=> => exporting layers 0.3s
=> => writing image sha256:08bb728a3bb62325065e63732cbe47b1f420550928aa444ab0a108fdacdd8331 0.0s
=> => naming to docker.io/library/alpine_df 0.0s
[root@waky my-app]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine_df latest 08bb728a3bb6 16 seconds ago 16.1MB
alpine_curl latest 346dc939b1b6 About an hour ago 16.1MB
alpine latest 706db57fb206 2 weeks ago 8.32MB
[root@waky my-app]#
Как можете видеть по выдаче, процесс сборки образа содержит шаги описанные в Dockerfile.
Создадим из нового образа контейнер и проверим, что curl работает:
[root@waky my-app]# docker run -td --name alpine_2 alpine_df /bin/sh
603024d41b234f880bf254ea121df500b199d83bb275d8792fca6fe79bb0b1d5
[root@waky my-app]# docker exec -it alpine_2 sh
/ # curl -I https://www.google.com
HTTP/2 200
content-type: text/html; charset=ISO-8859-1
…
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
/ # exit
[root@waky my-app]#
Оптимизация образа и советы
Мы рассмотрели тривиальный пример, когда Dockerfile содержит минимальное количество строк. В реальных условиях он может содержать десятки строк.
Основным правилом оптимизации образа является уменьшение количества строк RUN. Уменьшая их количество, вы тем самым уменьшения количества слоёв накладываемых поверх базового образа и ускоряете весь процесс создания.
Используйте минимальное необходимое количество команд, не оставляйте неиспользуемые элементы, например:
RUN apk add curl wget
вместо
RUN apk add curl
RUN apk add wget
Объединяйте несколько команд в одну используя &&, например:
RUN apk update && apk add curl wget
вместо
RUN apk update
RUN apk add curl wget
Не пытайтесь объединить все RUN в одну строку, это сильно усложнит читаемость файла, а как результат и вашу работу с ним.
Удаляйте временные файлы после установки пакетов.
Используйте минимальные базовые образы, например Alpine, чтобы снизить размер.
Управление Docker-образами
Основные операции с образами простые и интуитивно понятны.
Просмотр существующих образов
Чтобы увидеть все локальные образы, выполните команду images:
[root@waky my-app]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine_df latest 08bb728a3bb6 41 minutes ago 16.1MB
alpine_curl latest 346dc939b1b6 2 hours ago 16.1MB
alpine latest 706db57fb206 2 weeks ago 8.32MB
[root@waky my-app]#
Переименование образа
Чтобы переименовать образ, используйте команду tag с указанием старого и нового имени:
[root@waky my-app]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine_df latest 08bb728a3bb6 42 minutes ago 16.1MB
alpine_new latest 08bb728a3bb6 42 minutes ago 16.1MB
alpine_curl latest 346dc939b1b6 2 hours ago 16.1MB
alpine latest 706db57fb206 2 weeks ago 8.32MB
[root@waky my-app]#
При этом Docker создает запись с новым именем, но с тем же ID
Удаление образa
Если образ больше не нужен, его можно удалить, используя rmi:
[root@waky my-app]# docker rmi alpine_df
Untagged: alpine_df:latest
[root@waky my-app]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine_new latest 08bb728a3bb6 44 minutes ago 16.1MB
alpine_curl latest 346dc939b1b6 2 hours ago 16.1MB
alpine latest 706db57fb206 2 weeks ago 8.32MB
[root@waky my-app]#
Мы удалили дублирующий образ. Попробуем удалить и другой:
[root@waky my-app]# docker rmi alpine_new
Error response from daemon: conflict: unable to remove repository reference "alpine_new" (must force) - container 603024d41b23 is using its referenced image 08bb728a3bb6
[root@waky my-app]#
Нельзя удалить образ, если есть контейнер, созданный из этого образа. Сначала нужно удалить контейнер:
[root@waky my-app]# docker stop 603024d41b23
603024d41b23
[root@waky my-app]# docker rm 603024d41b23
603024d41b23
[root@waky my-app]# docker rmi alpine_new
Untagged: alpine_new:latest
Deleted: sha256:08bb728a3bb62325065e63732cbe47b1f420550928aa444ab0a108fdacdd8331
[root@waky my-app]#
В этот раз удалена не только привязка к имени, но и сам образ. Удалять образы можно как по имени, так и по ID.
Заключение
Образы Docker — это гибкая структура, основанная на слоях, которые позволяют легко управлять, обновлять и оптимизировать ваши контейнеры. Почти все операции начинаются с Dockerfile — описания шагов сборки.
Использование минимальных образов вроде Alpine позволяет создавать легкие, быстрые и надежные контейнеры, подходящие для разнообразных задач.