Docker для начинающих: что это такое и как пользоваться
Статей про Docker много не бывает.
В этом материале мы разберём базу: что такое Docker, как он работает и зачем нужен, а затем пошагово пройдём путь от установки до запуска первого контейнера.
С подготовкой статьи помог:
Андрей Хомутов
Эксперт-разработчик в Ростелеком ИТ, дипломный руководитель в Нетологии (Python, JS), эксперт в ИТ–школе Ростелекома (DevOps)
Для навигации:
Что такое Docker простыми словами
При разработке приложений часто возникает одна и та же проблема: у каждого разработчика своё окружение. Разные версии библиотек, интерпретаторов, системных зависимостей. В результате код, который стабильно работает на одном компьютере, может не запуститься на другом.
Docker решает эту проблему — он создаёт единый и воспроизводимый способ запускать приложения.
Основная идея: Docker — это платформа контейнеризации. Она позволяет упаковать приложение вместе со всеми зависимостями в изолированную среду — контейнер.
Такой контейнер можно запустить где угодно: на локальной машине, сервере или в облаке, и результат всегда будет одинаковым.
Чем отличается виртуализация от контейнеризации
До появления Docker основным способом изоляции приложений были виртуальные машины.
Каждая виртуальная машина имитировала полноценный компьютер: со своей ОС, драйверами и файловой системой. Для управления ими использовался гипервизор — программный слой, который распределяет ресурсы хоста между несколькими виртуальными машинами.
Подход надёжный, но тяжёлый. Каждая ВМ занимала сотни мегабайт или гигабайты памяти, запускалась медленно и требовала отдельного обслуживания.
Docker работает иначе. Он использует подход OS-level virtualization — контейнеризацию на уровне операционной системы. Вместо того чтобы поднимать отдельную ОС под каждое приложение, Docker создаёт изолированные контейнеры, которые делят ядро хостовой системы, но имеют собственное пространство процессов, файлов и сетей.
Если упрощённо:
виртуальная машина изолирует железо и поднимает целую операционную систему;
контейнер изолирует процессы в рамках одной ОС.
Как это выглядит на схеме:
5 причин, зачем нужен Docker
1. Стабильное окружение. Docker гарантирует, что приложение будет работать одинаково везде — на ноутбуке разработчика, тестовом сервере или в продакшене. Всё, что нужно для запуска, уже собрано в контейнер, поэтому код не зависит от различий в системах и настройках.
2. Лёгкость и скорость. Контейнеры используют общее ядро операционной системы и не создают отдельную копию ОС. За счёт этого они занимают меньше места и запускаются за секунды. Один сервер может без труда обслуживать десятки контейнеров.
3. Масштабирование. При росте нагрузки можно просто запустить дополнительные контейнеры — система быстро увеличит мощность и перераспределит ресурсы без изменения кода.
4. Изоляция процессов. Каждый контейнер работает независимо от других. Если в одном произойдёт сбой или утечка памяти, это не затронет другие контейнеры и систему в целом.
5. Удобная интеграция в CI/CD. Контейнеры уже стали стандартом в современных пайплайнах. Приложение можно собрать, протестировать и запустить в одинаковой среде — от локальной машины до продакшена. Благодаря этому меньше неожиданных багов, а релизы проходят быстрее и спокойнее.
Архитектура Docker: как он устроен и как работает
В основе Docker лежит Docker Engine — система, которая управляет контейнерами, образами, сетями и томами. Она состоит из трёх ключевых компонентов:
CLI (Command Line Interface) — интерфейс командной строки.
Docker Daemon (dockerd) — фоновый процесс, который выполняет все реальные действия: создаёт, запускает и удаляет контейнеры.
REST API — интерфейс, который предоставляет Docker Daemon для управления. В локальной установке CLI общается с демоном напрямую через сокет, но API следует REST-архитектуре.
Когда вы вводите команду вроде docker run
, клиент CLI отправляет запрос в dockerd
через сокет. Фоновый процесс получает эту команду, проверяет наличие нужного образа, готовит окружение, настраивает сеть, монтирует тома и запускает контейнер. Вся работа по созданию и управлению контейнерами происходит именно внутри демона (Docker Daemon) — он оркестрирует остальные части системы.
Внутри dockerd
используются вспомогательные среды:
containerd отвечает за жизненный цикл контейнеров — создание, запуск, остановку и удаление
runc делает сам запуск контейнера: изолирует процессы и использует механизмы ядра Linux которые отвечают за изоляцию и контроль ресурсов (
namespaces
,cgroups
и другие). О них поговорим дальше.
Обе следуют стандартам OCI (Open Container Initiative) — это набор правил, которые определяют формат контейнеров и способы их запуска. Благодаря этому Docker совместим с другими инструментами контейнеризации и предсказуемо работает в разных средах.
При этом работающие контейнеры не зависят от dockerd
: если демон перезапустить, они продолжат работу — за них отвечает containerd
.
Вся эта цепочка выглядит так: CLI → Docker Daemon (dockerd
) → containerd → runc → процессы контейнера.
Docker Desktop
Архитектура Docker отличается в зависимости от операционной системы.
На Linux Docker Engine устанавливается напрямую и работает как системная служба (systemd
). Об этом писали выше.
На Windows и macOS контейнеризация не встроена в ядро, поэтому Docker запускается через Docker Desktop.
Это настольное приложение, которое устанавливает Docker Engine внутри лёгкой виртуальной машины (в Windows — через WSL 2, в macOS — через HyperKit или Apple Virtualization Framework) и предоставляет удобный интерфейс для управления контейнерами, образами и настройками.
Docker Desktop объединяет всё в единую среду:
внутри работает Linux с установленным Docker Engine;
снаружи доступны привычный CLI и графический интерфейс;
настройки сети, проброс портов, управление томами и обновления выполняются автоматически.
Для новичков это самый удобный способ начать работу — установить Docker Desktop и сразу перейти к практике, без ручной настройки окружения.
Как Docker работает с образами
Контейнеры запускаются из образов (image) — шаблонов, в которых уже собраны все зависимости, библиотеки и настройки, нужные приложению.
Образ состоит из слоёв, и каждый слой — это отдельный шаг сборки: установка пакета, копирование файлов, настройка окружения.
Как устроены слои и почему это эффективно
При сборке Docker складывает слои в определённом порядке и объединяет их в единую файловую систему с помощью драйвера overlay2. Он накладывает слои друг на друга так, что контейнер видит их как один диск, хотя физически это отдельные части.
Главное преимущество такого подхода — повторное использование слоёв. Если два образа используют один и тот же базовый слой, Docker хранит его на диске только один раз.
Например, слой ubuntu:20.04
— это минимальная версия Ubuntu, собранная специально для Docker. В нём нет лишних программ, только базовая файловая система Linux. Его используют сотни тысяч других образов, но на диске у разработчика этот слой лежит в единственном экземпляре, и Docker просто ссылается на него.
Это экономит место и ускоряет работу: если слой уже есть, Docker не скачивает его повторно.
Как работает кэширование слоёв при сборке образа
Когда Docker собирает образ из Dockerfile, он проходит инструкции сверху вниз и создаёт слой для каждой из них.
Если какая-то инструкция уже выполнялась раньше и её входные данные не изменились, Docker берёт слой из кэша — это быстрее, чем пересобирать его заново.
Из-за этого порядок инструкций в Dockerfile критически важен:
то, что меняется редко (установка системных пакетов, базовые настройки), лучше размещать выше;
то, что меняется часто (исходный код приложения), — ниже.
Например:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
Если изменился только код, Docker пересоберёт только последний слой, а установку зависимостей возьмёт из кэша.
Как Docker использует слои при запуске контейнера
Docker монтирует все слои образа в режиме «Только чтение» и добавляет поверх них записываемый слой. В нём появляются временные файлы, кэш, логи и любые изменения, которые происходят во время работы приложения.
Когда контейнер удаляют, этот слой также исчезает, а образ остаётся неизменным — именно поэтому один и тот же образ можно запускать многократно и получать одинаковый результат.
Чтобы данные не пропадали между перезапусками, Docker хранит их вне контейнера, но с доступом к ним изнутри. Делается это двумя способами:
С помощью томов (volumes) — Docker создаёт отдельные области на диске и управляет ими. Контейнер может читать данные из тома и записывать их в него, а при удалении контейнера том остаётся.
С помощью монтирований (bind mounts) — к контейнеру подключается папка на хостовой системе. Например, можно смонтировать локальный каталог с кодом, чтобы контейнер работал с ним напрямую.
Хранилища образов: Docker Hub и Registry
Docker хранит образы в реестрах (registries) — это серверы, которые принимают, хранят и отдают образы по запросу.
Работают они по простому принципу: при сборке образа команда docker build
сохраняет его локально, а при публикации (docker push
) отправляет копию в выбранный реестр (как если бы вы выкладывали проект в GitHub). Позже этот же образ можно скачать (docker pull) на другой машине и запустить как контейнер.
Самый известный и используемый реестр — Docker Hub. В нём размещаются миллионы готовых образов: как официальные (от Docker и разработчиков языков или фреймворков), так и пользовательские. Например, образы nginx, Redis, Python, PostgreSQL — все они загружаются именно оттуда.
Hub также поддерживает приватные репозитории, если образ должен оставаться внутри команды.
Для корпоративных сценариев Docker предлагает установить собственный Docker Registry. Он разворачивается локально или в облаке, работает по тому же API, что и Hub, и поддерживает аутентификацию, разграничение прав и контроль версий образов. Это полезно, когда образы содержат закрытый код или конфигурации, которые нельзя выкладывать в публичный доступ.
Жизненный цикл Docker-образа:
Каждый образ в реестре состоит из манифеста и набора слоёв.
Манифест — это файл-описание, в котором указано, какие слои входят в образ и как их собрать. Сами слои хранятся отдельно — как архивы, каждый со своим уникальным идентификатором (sha256
).
При загрузке или скачивании Docker проверяет, не были ли повреждены файлы образа.
Каждый слой имеет свою контрольную сумму (sha256
), и система сверяет их перед установкой. Если хотя бы один байт отличается от ожидаемого, хэш не совпадает — и такой образ не принимается. Так Docker гарантирует, что загруженные данные точно совпадают с оригиналом.
У каждого образа есть тег — вроде latest
, v1.0
или dev
. Тег — это просто метка, которая указывает на конкретную версию образа. Но за этой меткой всегда стоит digest — уникальный хэш (sha256
), по которому Docker определяет точный набор слоёв.
Даже если тег изменится, digest остаётся прежним — и Docker понимает, какой именно образ нужно использовать.
Сеть контейнеров
При запуске Docker автоматически подключает контейнер к внутренней виртуальной сети. По умолчанию используется bridge-сеть — изолированная среда, где каждый контейнер получает свой IP-адрес и может обмениваться данными с другими участниками этой сети.
С точки зрения контейнера это выглядит как отдельная машина с полноценным сетевым интерфейсом, маршрутизацией и DNS.
Bridge-сеть создаётся на стороне хоста: Docker поднимает виртуальный интерфейс, назначает подсеть и настраивает NAT, чтобы контейнеры могли выходить в интернет.
Как контейнеры находят друг друга внутри сети
В пользовательских bridge-сетях Docker поднимает встроенный DNS-сервер. Благодаря этому контейнеры могут обращаться друг к другу по имени, а не по IP-адресу.
Например, если в одной сети запущены два контейнера — web
и db
, — то приложение внутри web
может подключиться к базе по адресу db:5432
. Docker сам сопоставит имя контейнера с его текущим IP-адресом, так что вручную настраивать адреса не нужно.
Доступ к контейнеру снаружи
Если приложению нужно принимать запросы извне, при запуске указывают флаг -p
, чтобы пробросить порт хоста внутрь контейнера (например, -p 8080:80
делает порт 80 контейнера доступным по адресу 8080 на хосте).
Кроме стандартной сети есть и другие режимы:
host — контейнер работает в сетевом пространстве хоста и использует его IP-адрес и интерфейсы. Режим без изоляции, зато с максимальной производительностью. Подходит, если сервису нужен прямой доступ к сети.
none — сеть полностью отключена. Контейнер не получает IP, не выходит в интернет и не принимает входящие запросы. Подходит для задач, которые работают изолированно и не используют сеть.
overlay — объединяет контейнеры, запущенные на разных машинах, в одну виртуальную сеть. Такой тип используется, когда приложение развёрнуто на нескольких серверах и между ними нужно наладить связь.
macvlan — каждому контейнеру назначается собственный MAC-адрес, и в локальной сети он виден как отдельное устройство. Это удобно для тестирования сетевых сервисов или если контейнер должен работать наравне с другими машинами.
Сетью управляет Docker Daemon: он создаёт интерфейсы, подключает контейнеры и автоматически настраивает сетевые правила.Благодаря этому контейнеры остаются изолированными друг от друга, но при этом могут безопасно взаимодействовать через чётко заданные правила.
Логи и события
Все процессы внутри контейнера работают так же, как в обычной системе Linux. Всё, что они выводят в стандартные потоки (stdout
и stderr
), Docker перенаправляет в драйвер логирования — систему, которая отвечает за сбор и хранение логов.
По умолчанию используется драйвер json-file: логи записываются на хосте в файл в формате JSON. Это удобно для локальной отладки и просмотра через команду docker logs
.
В продакшене часто выбирают другие драйверы:
journald — чтобы писать логи в системный журнал Linux;
syslog — для отправки логов на внешний сервер;
fluentd — для сбора и анализа данных в распределённых системах.
Для каждого контейнера можно задать свои параметры: выбрать драйвер, ограничить размер файлов, включить ротацию (автоматическую замену старых логов новыми).
Помимо логов Docker фиксирует события — всё, что происходит в системе: запуск или остановка контейнера, создание или удаление, загрузка образа, подключение тома. Эти данные доступны через API или команду docker events
.
На практике события используют для мониторинга и интеграций: например, CI/CD-система может автоматически запускать тесты при создании контейнера или отправлять уведомления о сбоях.
Благодаря этой системе Docker остаётся прозрачным: всегда можно узнать, что происходит с каждым контейнером и когда именно, не заходя внутрь вручную.
Безопасность
Docker использует несколько уровней защиты, чтобы контейнеры оставались изолированными и не мешали друг другу. Основу этой модели составляют механизмы ядра Linux — namespaces и cgroups.
Namespaces
Отвечают за изоляцию. Они создают для каждого контейнера отдельное пространство процессов, пользователей, сети и файловой системы. Контейнер видит только свои процессы и каталоги и не может напрямую взаимодействовать с хостом.
Например, процесс с PID 1 внутри контейнера не имеет ничего общего с системным PID 1 на хосте.
То же и с сетью: у контейнера свой IP и таблицы маршрутов, и он не видит соседей, пока их явно не объединят в одну сеть.
Cgroups (control groups)
Отвечают за контроль ресурсов. Они задают, сколько CPU, оперативной памяти и дисковых операций контейнер может использовать.
Если приложение начнёт потреблять слишком много, cgroups ограничат его, не давая перегрузить систему или повлиять на другие контейнеры.
Помимо этого, Docker использует дополнительные механизмы безопасности:
Capabilities — система тонкой настройки прав. Она ограничивает возможности root-пользователя внутри контейнера. Например, процесс может записывать файлы, но не может менять сетевые настройки.
AppArmor и SELinux — инструменты контроля доступа. Они определяют, какие файлы и операции разрешены процессам контейнера.
seccomp — фильтр системных вызовов, который блокирует потенциально опасные обращения к ядру, например попытки загрузить модули или изменить сетевые интерфейсы.
Все эти уровни работают вместе и создают прочную изоляцию. Даже если приложение внутри контейнера окажется уязвимым, оно не сможет повредить хост или получить доступ к другим контейнерам.
Для дополнительной защиты Docker можно запустить в rootless-режиме — тогда контейнеры работают не от имени администратора, а под обычным пользователем. Функциональности чуть меньше, зато риск для хоста минимален.
На чём написан Docker
Docker написан на Go — простом и быстром языке, удобном для создания системных инструментов. На нём легко работать с потоками и собирать программы, которые запускаются без внешних библиотек.
На Go построены все основные части Docker:
dockerd — демон, который принимает команды и управляет контейнерами;
containerd — служба, отвечающая за запуск и работу контейнеров;
runc — утилита, которая создаёт изолированные процессы в Linux;
BuildKit — инструмент для сборки образов.
Docker использует стандарты OCI (Open Container Initiative). Они описывают, из чего состоит контейнер и как его запускать. Поэтому образы, собранные в Docker, можно использовать и в других системах — например, в Podman или CRI-O.
Исходный код Docker открыт и распространяется по лицензии Apache 2.0. Она разрешает использовать код в любых целях — в личных, учебных или коммерческих проектах. Можно изменять исходники, собирать собственные версии Docker и включать его части в другие продукты. Единственное требование — сохранять уведомление об авторских правах, текст лицензии и указывать, если вносились изменения.
Имя и логотип Docker использовать без разрешения нельзя.
Основные понятия Docker: краткий справочник
Мы уже разобрались, как устроен Docker и из чего он состоит.
Теперь соберём ключевые термины в одном месте, чтобы было проще ориентироваться, когда перейдём к практике.
Образ (Image)
Это шаблон, из которого запускаются контейнеры. В нём уже есть всё, что нужно приложению: системные библиотеки, зависимости, код и настройки.
Образ не меняется — Docker использует его как основу и добавляет поверх слой, куда попадают изменения, сделанные во время работы. Так один и тот же образ можно использовать многократно, не затрагивая исходное содержимое.
Контейнер (Container)
Это изолированная среда, в которой работает приложение. Контейнер можно запускать, останавливать и удалять, не затрагивая исходный образ.
Когда он удаляется, его данные исчезают — поэтому для постоянного хранения используется следующий механизм.
Том (Volume)
Это место для данных, которые должны сохраняться между перезапусками. Тома подключаются к контейнерам как внешние диски: всё, что записано в них, остаётся даже после удаления контейнера.
Так обычно хранят базы данных, логи и другие важные файлы.
Сеть (Network)
Отвечает за взаимодействие контейнеров между собой и с внешним миром. Docker автоматически создаёт bridge-сеть, где каждый контейнер получает свой IP-адрес.
Можно объединять контейнеры в собственные сети или, наоборот, изолировать их, если требуется полная автономность.
Реестр (Registry)
Реестр — это место, где хранятся Docker-образы.
Публичный вариант — Docker Hub, в нём размещены миллионы готовых сборок, включая официальные (nginx, Redis, Python). Если образы должны оставаться внутри компании, разворачивают собственный Docker Registry — тот же сервер хранения, но с ограниченным доступом.
Практика: установка и первые команды
Чтобы начать работать с Docker, нужно установить Docker Engine. В него входят фоновый процесс dockerd
, интерфейс командной строки и набор инструментов для работы с образами и сетями.
На macOS и Windows установка выполняется через приложение Docker Desktop. Скачайте его с официального сайта.
На Linux Docker устанавливается из официальных репозиториев пакетов.
Для Debian и Ubuntu команда выглядит так:
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
Подробные инструкции для других дистрибутивов есть в документации Docker.
Проверка установки
После установки убедитесь, что Docker работает корректно. Для этого выполните команду:
docker version
Docker выведет информацию о клиенте и сервере. Пример вывода может выглядеть так:
Client:
Version: 28.4.0
API version: 1.51
Go version: go1.25.1
Git commit: d8eb465f86
OS/Arch: linux/amd64
Server:
Engine:
Version: 28.4.0
API version: 1.51 (minimum version 1.24)
Go version: go1.25.1
containerd:
Version: v2.1.4
runc:
Version: 1.3.1
Главное, чтобы в выводе присутствовали обе части: Client и Server, что означает, что dockerd
запущен и Docker Engine работает.
Первый тест
Для проверки можно запустить тестовый контейнер, который выводит приветственное сообщение. Используйте команду:
docker run hello-world
Docker загрузит с Hub тестовый образ и запустит контейнер, который выведет сообщение Hello from Docker! — это значит, что система установлена правильно и демон отвечает на запросы.
Запуск первого контейнера
Когда Docker установлен, можно сразу попробовать запустить приложение. Например, веб-сервер Nginx — лёгкий, стабильный и идеально подходит для первого теста.
Выполните команду:
docker run -d -p 8080:80 nginx
Что здесь происходит:
docker run
— создаёт контейнер из указанного образа (в данном случаеnginx
);если образа нет локально, Docker автоматически скачает его с Docker Hub;
флаг
-d
запускает контейнер в фоновом режиме (detached);-p 8080:80
пробрасывает порт 80 внутри контейнера на порт 8080 вашей машины.
После запуска Docker создаст контейнер и выведет его идентификатор. Теперь можно открыть http://localhost:8080 и увидеть стандартную стартовую страницу Nginx.
Чтобы убедиться, что контейнер работает, выполните:
docker ps
В списке будет строка с именем nginx
и статусом Up
, который означает, что сервер запущен.
Интерактивный запуск контейнера
Иногда нужно не просто запустить контейнер, а зайти внутрь него ― посмотреть файлы, проверить окружение, выполнить команды. Для этого используют флаг -it
.
Например:
docker run -it ubuntu bash
Что делает эта команда:
-i
— оставляет стандартный ввод открытым,-t
— выделяет псевдотерминал,ubuntu
— образ, который нужно запустить,bash
— команда, которая будет выполнена внутри контейнера.
После запуска вы попадёте в оболочку контейнера и сможете выполнять обычные Linux-команды.
Чтобы выйти, используйте:
exit
Важно: если нужно зайти в уже работающий контейнер, используется другая команда:
docker exec -it bash
Как следить за контейнерами
Когда контейнер запущен, Docker продолжает отслеживать его состояние. В любой момент можно посмотреть, что происходит, не прерывая работу приложения.
Для этого используем уже знакомую команду:
docker ps
Она покажет список активных контейнеров: их идентификаторы, образы, статус и открытые порты. Если добавить флаг -a
, отобразятся и завершённые контейнеры — это помогает найти старые тестовые сборки или понять, почему что-то остановилось.
Чтобы узнать подробнее, как запущен контейнер и какие у него настройки, выполните:
docker inspect
Она выводит полное описание: от сетевых параметров до переменных окружения и точной команды запуска.
Если нужно быстро понять, что происходит внутри контейнера, не заходя в него вручную (например, просмотреть вывод приложения), используйте команду:
docker logs
Но если всё-таки нужно заглянуть внутрь, например, проверить файлы или конфигурацию, — можно открыть интерактивную консоль:
docker exec -it /bin/bash
Если в образе нет Bash (например, в Alpine), вместо него используют:
docker exec -it /bin/sh
Так вы попадёте в среду контейнера, где доступны обычные Linux-команды.
Управление контейнерами
Контейнеры можно запускать, останавливать и удалять — так же просто, как обычные процессы в системе.
Если контейнер нужно приостановить, используйте:
docker stop
Docker отправит сигнал завершения процессу внутри контейнера и аккуратно его остановит.
Контейнер останется в списке и при желании его можно снова запустить командой:
docker start
Когда контейнер больше не нужен, его можно удалить:
docker rm
Важно: если попытаться удалить запущенный контейнер, Docker выдаст ошибку, чтобы не потерять данные.
Для принудительного удаления (остановка и удаление одной командой) используйте флаг -f
:
docker rm -f
Помимо контейнеров, система хранит образы, из которых они создаются. Посмотреть список локальных образов можно так:
docker images
А удалить ненужные с помощью команды:
docker rmi
Чтобы быстро очистить всё, что не используется (остановленные контейнеры, старые образы, неактивные сети), есть команда:
docker system prune
Она освободит место на диске и оставит только то, что нужно для работы.
Создание своего образа: работа с Dockerfile
Готовые образы покрывают большинство задач. Но в реальных проектах почти всегда нужно что-то своё: добавить зависимости, конфигурации или сам код приложения. Для этого в Docker используется Dockerfile — текстовый файл, в котором по шагам описано, как собрать образ.
Пример простого Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
Что здесь происходит:
FROM
— задаёт базовый образ, от которого наследуется ваш;WORKDIR
— создаёт рабочую директорию внутри контейнера;COPY
— копирует файлы проекта с компьютера в образ;RUN
— выполняет команды при сборке (например, установка библиотек);CMD
— указывает, что делать при запуске контейнера.
Важно: когда контейнер запускается, процессы внутри него по умолчанию работают с правами root.
Для обучения или экспериментов этого вполне достаточно. Но в продакшене такое поведение создаёт лишние риски: если приложение внутри контейнера уязвимо, злоумышленник получает те же привилегии, что и root-процесс.
Чтобы снизить эти риски, в Dockerfile часто добавляют собственного пользователя и запускают приложение уже от его имени. Сделать это можно буквально в пару строк:
RUN useradd -m appuser
USER appuser
ENTRYPOINT: второй способ задать команду запуска
В Dockerfile есть две инструкции, которые определяют, что выполняется при старте контейнера — CMD и ENTRYPOINT.
Чем отличаются:
CMD — это команда по умолчанию. Если при запуске контейнера (
docker run
) вы передаёте свои аргументы, Docker заменяет CMD на то, что вы указали.ENTRYPOINT — это фиксированная основа команды. Она всегда выполняется, а аргументы из
docker run
просто добавляются к ней.
Пример:
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8000"]
ENTRYPOINT задаёт программу, которую контейнер запускает всегда —
в нашем случае это python app.py
. CMD добавляет аргументы по умолчанию.
То есть Docker склеивает их и при обычном запуске выполнит:
python app.py --port 8000
Если при запуске указать свои параметры:
docker run myapp --port 9000
Docker возьмёт ENTRYPOINT и добавит ваши аргументы, заменив CMD:
python app.py --port 9000
ENTRYPOINT используют, когда контейнер всегда должен запускать одно и то же приложение — веб-сервер, скрипт, обработчик. Пользователь может менять только параметры.
CMD используют как набор аргументов по умолчанию. Если при запуске передать свои параметры, CMD полностью заменится.
Разобрались с основами, а теперь перейдём к практике.
Практический пример
Разберём, как создать и запустить свой первый контейнер.
Сначала в папке проекта должны лежать три файла:
myapp/
├─ app.py
├─ requirements.txt
└─ Dockerfile
В app.py
напишем простейшее приложение на Python. Оно будет показывать строку "Hello from Docker!", когда вы откроете страницу в браузере:
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(b"Hello from Docker!")
if __name__ == "__main__":
HTTPServer(("0.0.0.0", 8000), Handler).serve_forever()
Файл requirements.txt
нужен, чтобы перечислить зависимости проекта. Если их нет — можно оставить его пустым.
Собираем образ
Теперь откройте терминал, перейдите в папку myapp
и выполните:
docker build -t myapp .
Флаг -t
задаёт имя образа, а точка в конце указывает путь к контексту сборки — папке, где лежит Dockerfile и файлы проекта. После выполнения команды Docker по шагам прочитает Dockerfile
и соберёт новый образ.
Запускаем контейнер
Мы уже пробовали запускать контейнер с Nginx. Теперь сделаем то же самое, но с собственным образом:
docker run -d -p 8000:8000 myapp
Docker создаст контейнер и запустит внутри него ваш app.py
.
Откройте http://localhost:8000, и вы увидите сообщение: "Hello from Docker!".
Вы только что собрали собственный образ, запустили контейнер и проверили, что приложение работает. В реальных проектах таких образов может быть несколько — для тестов, продакшена или разных версий кода.
Чтобы их различать, Docker использует теги — о них поговорим дальше.
Зачем нужны теги
Тег — это просто метка версии. Если его не указать, Docker присвоит latest
, но лучше явно обозначать версии:
docker build -t myapp:1.0 .
Так проще поддерживать несколько вариантов образа — например, стабильный (1.0
) и тестовый (dev
).
Передача и хранение образов
После сборки Docker сохраняет образ локально — он доступен только на вашем компьютере. Если нужно поделиться им с коллегами или перенести на другой компьютер, проще всего воспользоваться Docker Hub.
1. Создайте репозиторий на Docker Hub
Зайдите в свой аккаунт на https://hub.docker.com и вручную создайте новый репозиторий — так же, как создают новый проект на GitHub.
Название репозитория должно совпадать с тем, что вы будете использовать в команде docker push
.
Это важный шаг: Docker не создаёт репозитории автоматически.
2. Переименуйте образ
Перед загрузкой образ нужно оформить в правильном формате:
username/repository:tag
где:
username
— ваш логин на Docker Hub,repository
— имя проекта,tag
— версия образа (например,1.0
).
Пример:
docker tag myapp:1.0 username/myapp:1.0
3. Войдите в Docker Hub
Авторизуйтесь через команду:
docker login
Docker попросит ввести логин и пароль от вашего аккаунта. После успешного входа появится сообщение вроде Login Succeeded
.
4. Отправьте образ в репозиторий
Выполните команду:
docker push username/myapp:1.0
Docker начнёт загружать образ в ваш репозиторий на Docker Hub.
Когда загрузка закончится, образ появится в вашем профиле и станет доступен другим (если репозиторий публичный).
5. Загрузите образ на другом компьютере
Чтобы использовать тот же образ на другой машине, выполните:
docker pull username/myapp:1.0
Docker скачает только нужные слои — если часть уже есть локально, она не будет загружаться заново.
После этого образ можно запустить как обычно:
docker run -d -p 8000:8000 username/myapp:1.0
Docker Compose: что это и зачем нужно
Когда проект растёт, одного контейнера уже не хватает. Помимо основного приложения появляются база данных, кэш, очередь сообщений. Все они работают в отдельных контейнерах, и запускать их вручную неудобно.
Docker Compose позволяет описать все нужные контейнеры и их настройки в одном файле — docker-compose.yml
. После этого весь проект можно запустить одной командой.
Пример простого docker-compose.yml
:
services:
web:
build: .
ports:
- "8000:8000"
redis:
image: redis:alpine
Что здесь происходит:
services
— список контейнеров, которые нужно запустить;web
— наш основной сервис, собирается из текущей директории (build: .
) и открывает порт 8000;redis
— дополнительный контейнер, разворачивается из готового образаredis:alpine
.
Как это работает
Сохраните файл и выполните команду:
docker compose up
Docker соберёт образы, создаст контейнеры, подключит сеть и запустит всё вместе.
Чтобы остановить проект, выполните команду:
docker compose down
Что изучать дальше
Следующие шаги зависят от того, что вы хотите делать дальше:
Для разработки — попробуйте написать собственный
Dockerfile
для своего проекта и оптимизировать его размер. Изучите многоэтапную сборку (multi-stage build) и работу с кэшем.Для деплоя — посмотрите, как запускать контейнеры на сервере: настройка автозапуска через
systemd
, передача переменных окружения, управление логами.Для продакшена — изучите оркестраторы: Docker Swarm или Kubernetes. Они позволяют управлять десятками контейнеров и масштабировать сервисы.
Для безопасности — разберитесь с rootless-режимом, настройкой прав доступа и сканированием образов на уязвимости (например, с помощью
docker scan
).Для автоматизации — посмотрите, как Docker используется в CI/CD-сценариях: тестирование, сборка и публикация образов через GitHub Actions или GitLab CI.
Ну, вот и всё. Теперь у вас уже есть рабочая база Docker: вы понимаете, как устроены образы и контейнеры, умеете собирать собственные образы и запускать сервисы через Compose. Этого достаточно, чтобы уверенно продолжать и постепенно подключать более сложные вещи.
Чтобы расти, нужно выйти из привычной зоны и сделать шаг к переменам. Можно изучить новое, начав с бесплатных занятий:
записи вебинара «Секретный рецепт DevSecOps, или почему его используют 90% компаний» совместно с НИУ ВШЭ;
мастер-класса «Искусственный интеллект в разработке»;
вводного курса магистратуры МФТИ «Разработка IT-продукта».
Или можно стать востребованным сотрудником и открыть открыть бóльшие перспективы в карьере с профессиональным обучением:
на программе «Системный администратор»;
на курсе «DevOps-инженер» для действующих специалистов;
на расширенном курсе «Python-разработчик» с изучением ИИ.