Docker для начинающих: что это такое и как пользоваться

vaspvort

Ночной дозор
Команда форума
Модератор
ПРОВЕРЕННЫЙ ПРОДАВЕЦ
Private Club
Старожил
Migalki Club
Меценат💎
Регистрация
10/4/18
Сообщения
6.984
Репутация
12.129
Реакции
18.854
USDT
0
Сделок через гаранта
18
Статей про Docker много не бывает.

В этом материале мы разберём базу: что такое Docker, как он работает и зачем нужен, а затем пошагово пройдём путь от установки до запуска первого контейнера.

Что такое Docker простыми словами​

При разработке приложений часто возникает одна и та же проблема: у каждого разработчика своё окружение. Разные версии библиотек, интерпретаторов, системных зависимостей. В результате код, который стабильно работает на одном компьютере, может не запуститься на другом.

Docker решает эту проблему — он создаёт единый и воспроизводимый способ запускать приложения.

Основная идея: Docker — это платформа контейнеризации. Она позволяет упаковать приложение вместе со всеми зависимостями в изолированную среду — контейнер.

Такой контейнер можно запустить где угодно: на локальной машине, сервере или в облаке, и результат всегда будет одинаковым.

Чем отличается виртуализация от контейнеризации​

До появления Docker основным способом изоляции приложений были виртуальные машины.

Каждая виртуальная машина имитировала полноценный компьютер: со своей ОС, драйверами и файловой системой. Для управления ими использовался гипервизор — программный слой, который распределяет ресурсы хоста между несколькими виртуальными машинами.

Подход надёжный, но тяжёлый. Каждая ВМ занимала сотни мегабайт или гигабайты памяти, запускалась медленно и требовала отдельного обслуживания.

Docker работает иначе. Он использует подход OS-level virtualization — контейнеризацию на уровне операционной системы. Вместо того чтобы поднимать отдельную ОС под каждое приложение, Docker создаёт изолированные контейнеры, которые делят ядро хостовой системы, но имеют собственное пространство процессов, файлов и сетей.

Если упрощённо:

  • виртуальная машина изолирует железо и поднимает целую операционную систему;
  • контейнер изолирует процессы в рамках одной ОС.
Как это выглядит на схеме:

Источник

Источник

5 причин, зачем нужен Docker​

1. Стабильное окружение. Docker гарантирует, что приложение будет работать одинаково везде — на ноутбуке разработчика, тестовом сервере или в продакшене. Всё, что нужно для запуска, уже собрано в контейнер, поэтому код не зависит от различий в системах и настройках.

2. Лёгкость и скорость. Контейнеры используют общее ядро операционной системы и не создают отдельную копию ОС. За счёт этого они занимают меньше места и запускаются за секунды. Один сервер может без труда обслуживать десятки контейнеров.

3. Масштабирование. При росте нагрузки можно просто запустить дополнительные контейнеры — система быстро увеличит мощность и перераспределит ресурсы без изменения кода.

4. Изоляция процессов. Каждый контейнер работает независимо от других. Если в одном произойдёт сбой или утечка памяти, это не затронет другие контейнеры и систему в целом.

5. Удобная интеграция в CI/CD. Контейнеры уже стали стандартом в современных пайплайнах. Приложение можно собрать, протестировать и запустить в одинаковой среде — от локальной машины до продакшена. Благодаря этому меньше неожиданных багов, а релизы проходят быстрее и спокойнее.

Архитектура Docker: как он устроен и как работает​

В основе Docker лежит Docker Engine — система, которая управляет контейнерами, образами, сетями и томами. Она состоит из трёх ключевых компонентов:

  1. CLI (Command Line Interface) — интерфейс командной строки.
  2. Docker Daemon (dockerd) — фоновый процесс, который выполняет все реальные действия: создаёт, запускает и удаляет контейнеры.
  3. 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-образа:

Схема показывает, как образ собирается из Dockerfile, сохраняется локально, загружается в реестр (push), скачивается обратно (pull) и запускается как контейнер. Источник

Схема показывает, как образ собирается из Dockerfile, сохраняется локально, загружается в реестр (push), скачивается обратно (pull) и запускается как контейнер. Источник
Каждый образ в реестре состоит из манифеста и набора слоёв.

Манифест — это файл-описание, в котором указано, какие слои входят в образ и как их собрать. Сами слои хранятся отдельно — как архивы, каждый со своим уникальным идентификатором (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
Объяснить код с
sourcecraft-light.bf8f84c1..svg


Docker загрузит с Hub тестовый образ и запустит контейнер, который выведет сообщение Hello from Docker! — это значит, что система установлена правильно и демон отвечает на запросы.

Запуск первого контейнера​

Когда Docker установлен, можно сразу попробовать запустить приложение. Например, веб-сервер Nginx — лёгкий, стабильный и идеально подходит для первого теста.

Выполните команду:

docker run -d -p 8080:80 nginx


Что здесь происходит:

  1. docker run — создаёт контейнер из указанного образа (в данном случае nginx);
  2. если образа нет локально, Docker автоматически скачает его с Docker Hub;
  3. флаг -d запускает контейнер в фоновом режиме (detached);
  4. -p 8080:80 пробрасывает порт 80 внутри контейнера на порт 8080 вашей машины.
После запуска Docker создаст контейнер и выведет его идентификатор. Теперь можно открыть и увидеть стандартную стартовую страницу Nginx.

Чтобы убедиться, что контейнер работает, выполните:

docker ps


В списке будет строка с именем nginx и статусом Up, который означает, что сервер запущен.

Интерактивный запуск контейнера

Иногда нужно не просто запустить контейнер, а зайти внутрь него ― посмотреть файлы, проверить окружение, выполнить команды. Для этого используют флаг -it.

Например:

docker run -it ubuntu bash


Что делает эта команда:

  • -i — оставляет стандартный ввод открытым,
  • -t — выделяет псевдотерминал,
  • ubuntu — образ, который нужно запустить,
  • bash — команда, которая будет выполнена внутри контейнера.
После запуска вы попадёте в оболочку контейнера и сможете выполнять обычные Linux-команды.

Чтобы выйти, используйте:

exit


Важно: если нужно зайти в уже работающий контейнер, используется другая команда:

docker exec -it <id> bash


Как следить за контейнерами​

Когда контейнер запущен, Docker продолжает отслеживать его состояние. В любой момент можно посмотреть, что происходит, не прерывая работу приложения.

Для этого используем уже знакомую команду:

docker ps


Она покажет список активных контейнеров: их идентификаторы, образы, статус и открытые порты. Если добавить флаг -a, отобразятся и завершённые контейнеры — это помогает найти старые тестовые сборки или понять, почему что-то остановилось.

Чтобы узнать подробнее, как запущен контейнер и какие у него настройки, выполните:

docker inspect <id>


Она выводит полное описание: от сетевых параметров до переменных окружения и точной команды запуска.

Если нужно быстро понять, что происходит внутри контейнера, не заходя в него вручную (например, просмотреть вывод приложения), используйте команду:

docker logs <id>


Но если всё-таки нужно заглянуть внутрь, например, проверить файлы или конфигурацию, — можно открыть интерактивную консоль:

docker exec -it <id> /bin/bash


Если в образе нет Bash (например, в Alpine), вместо него используют:

docker exec -it <id> /bin/sh


Так вы попадёте в среду контейнера, где доступны обычные Linux-команды.

Управление контейнерами​

Контейнеры можно запускать, останавливать и удалять — так же просто, как обычные процессы в системе.

Если контейнер нужно приостановить, используйте:

docker stop <id>


Docker отправит сигнал завершения процессу внутри контейнера и аккуратно его остановит.

Контейнер останется в списке и при желании его можно снова запустить командой:

docker start <id>


Когда контейнер больше не нужен, его можно удалить:

docker rm <id>


Важно: если попытаться удалить запущенный контейнер, Docker выдаст ошибку, чтобы не потерять данные.

Для принудительного удаления (остановка и удаление одной командой) используйте флаг -f:

docker rm -f <id>


Помимо контейнеров, система хранит образы, из которых они создаются. Посмотреть список локальных образов можно так:

docker images


А удалить ненужные с помощью команды:

docker rmi <image>


Чтобы быстро очистить всё, что не используется (остановленные контейнеры, старые образы, неактивные сети), есть команда:

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.

Откройте , и вы увидите сообщение: "Hello from Docker!".

Вы только что собрали собственный образ, запустили контейнер и проверили, что приложение работает. В реальных проектах таких образов может быть несколько — для тестов, продакшена или разных версий кода.

Чтобы их различать, Docker использует теги — о них поговорим дальше.

Зачем нужны теги​

Тег — это просто метка версии. Если его не указать, Docker присвоит latest, но лучше явно обозначать версии:

docker build -t myapp:1.0 .


Так проще поддерживать несколько вариантов образа — например, стабильный (1.0) и тестовый (dev).

Передача и хранение образов​

После сборки Docker сохраняет образ локально — он доступен только на вашем компьютере. Если нужно поделиться им с коллегами или перенести на другой компьютер, проще всего воспользоваться Docker Hub.

1. Создайте репозиторий на Docker Hub​

Зайдите в свой аккаунт на и вручную создайте новый репозиторий — так же, как создают новый проект на 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. Этого достаточно, чтобы уверенно продолжать и постепенно подключать более сложные вещи.

Источник
 
  • Теги
    docker docker desktop docker-compose
  • Назад
    Сверху Снизу