Что такое Docker

Ноутбук с терминалом и схема из одинаковых блоков на доске за ним Программное обеспечение

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

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

Возьмём учебный проект: фронтенд на React, API на Python и база PostgreSQL. Чтобы его запустить, нужно установить Node.js, Python и PostgreSQL нужных версий. У коллеги стоит другой Python, на сервере третий, и приложение, которое «у меня работает», у него падает с ошибкой.

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

Исходный код Docker открыли в марте 2013 года. Начинался он как внутренний проект облачной компании и первое время работал поверх LXC, а через год, в версии 0.9, перешёл на собственную библиотеку libcontainer. Написан Docker на языке Go, а Docker Engine распространяется под лицензией Apache 2.0.

Зачем нужен Docker и где его используют

Контейнеры используют на каждом этапе работы с кодом:

  • в среде разработки: база, очередь и кеш поднимаются без установки в систему, версии зафиксированы в файлах проекта;
  • для тестирования: система CI/CD собирает образ, автоматически запускает в контейнере тесты и удаляет его, каждый прогон идёт в чистом окружении;
  • при развертывании: на сервер уходит тот же образ, что прошёл тесты, и в продакшене работает ровно та сборка, которую проверяли;
  • в микросервисной архитектуре: каждый сервис живёт в своём контейнере, и его можно обновлять и масштабировать отдельно.

Готовые сервисы вроде Redis или Memcached из официальных образов поднимаются за секунды. Благодаря общему ядру ОС контейнеры расходуют меньше ресурсов, чем виртуальные машины, и на том же железе помещается больше приложений.

Разработчик за двумя мониторами с окнами терминала

Образ и контейнер: в чём разница

Образ (image) хранит неизменяемый набор файлов: файловая система с приложением, библиотеками и настройками плюс команда, которую нужно выполнить при запуске. Он служит шаблоном для создания контейнеров. Образ создаётся по инструкциям из Dockerfile, обычно поверх другого, базового образа: к нему добавляют дополнительные слои с зависимостями и кодом.

Изменить готовый образ нельзя, можно только собрать новый поверх него. Образ состоит из слоёв, и каждый слой хранит изменения файлов. Например, один слой добавляет базовую систему, второй Python, третий зависимости, четвёртый код приложения. Если слоёв мало, образ получается совсем маленьким: hello-world, который содержит одну программу, весит около 10 КБ, учебный образ docker/welcome-to-docker с веб-сервером занимает 29,7 МБ в распакованном виде.

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

Программисту проще всего сравнить их с классом и объектом. Образ это класс: описание того, что будет внутри. Контейнер соответствует объекту, созданному по этому описанию. У каждого объекта своё состояние, а класс один.

Для хранения образов используют реестр (registry). По умолчанию используется Docker Hub, публичный реестр, где больше 100 тысяч образов, но можно поднять и свой, приватный. Полное имя образа выглядит так: [хост[:порт]/]путь[:тег]. Если хост не указан, подставляется docker.io, для официальных образов путь начинается с library/, а без тега подставляется latest. Так что nginx и docker.io/library/nginx:latest обозначают один и тот же образ.

Тег latest просто подставляется по умолчанию и с выходом новой версии образа может перейти на неё. Поэтому в Dockerfile указывают версию: python:3.14 не перескочит на 3.15. Но теги изменяемые, и 3.14 всегда указывает на последний патч этой ветки, так что пересборка через три месяца может взять другую патч-сборку. Для полностью одинаковых сборок фиксируют патч (python:3.14.8) или digest образа.

Стопка прозрачных пластин разного цвета на столе: слои образа

Чем Docker отличается от виртуальной машины

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

Виртуальная машина Контейнер Docker
Что внутри Полная ОС: ядро, драйверы, программы Процесс приложения и его файлы
Ядро Своё у каждой машины Общее с хостом
Изоляция Гипервизор Механизмы Linux: namespaces и cgroups
Другая ОС Можно запустить Windows на Linux Linux-контейнеру нужно ядро Linux

Linux-контейнеру нужен Linux, поэтому Docker Desktop на Windows и macOS запускает виртуальную машину с Linux (на Windows по умолчанию через WSL 2), а уже в ней контейнеры. На Linux Docker Engine работает прямо на ядре системы, без виртуальной машины, а Docker Desktop for Linux тоже поднимает виртуальную машину. Для Windows-контейнеров нужна Windows 10 или 11 в редакции Pro или Enterprise.

Контейнеры и виртуальные машины часто работают вместе: в облаке арендуют виртуальную машину и запускают на ней несколько контейнеров. Если же нужна другая операционная система, например Windows на Linux-сервере, Docker её не заменит, тут нужна виртуалка.

Ноутбук рядом с небольшим сервером: виртуальная машина и контейнеры

Как работает Docker: архитектура и принцип работы

Docker построен по схеме «клиент-сервер». Когда вы пишете docker run, команда docker (клиент) ничего не запускает сама. Она отправляет запрос через REST API демону dockerd (Docker daemon), а тот выполняет команду: скачивает образы, создаёт контейнеры, сети и тома. Демон работает в фоне как служба. Клиент и демон взаимодействуют через UNIX-сокет или по сети, так что клиент можно подключить и к демону на другом сервере.

Изоляцию обеспечивают механизмы ядра Linux. Для каждого контейнера Docker создаёт набор пространств имён (namespaces): у контейнера свой список процессов, своя сеть, свои точки монтирования. Ограничения по ресурсам, памяти и процессору, задают cgroups. Как смотреть процессы в Linux, мы разбирали в статье про список процессов Linux.

Основные компоненты:

  • Docker Engine: демон dockerd, его API и клиент командной строки;
  • Docker Desktop: приложение с графическим интерфейсом, поддерживает Windows, macOS и Linux и включает демон, клиент, Docker Compose, Kubernetes и другие инструменты;
  • реестр: Docker Hub или свой приватный, туда отправляют образы командой docker push и оттуда скачивают командой docker pull.

Серверная стойка с сетевыми кабелями и индикаторами

Как установить Docker

На Windows и macOS ставят Docker Desktop. Требования для Windows с WSL 2 на октябрь 2026 года:

  • Windows 10 64-bit версии 22H2 (сборка 19045) или Windows 11 64-bit версии 23H2 (сборка 22631) и новее, редакции Pro, Enterprise или Education;
  • WSL версии 2.1.5 или новее;
  • 8 ГБ оперативной памяти и включённая аппаратная виртуализация в BIOS/UEFI.

Windows Home в этом списке нет, а на Home и Education можно запускать только Linux-контейнеры.

На Mac нужно от 4 ГБ памяти и одна из трёх последних версий macOS. Платная подписка на Docker Desktop нужна для коммерческого использования в крупных организациях, у которых больше 250 сотрудников или больше 10 млн долларов годовой выручки: достаточно одного из двух условий.

На Linux можно поставить Docker Desktop, который работает внутри виртуальной машины, или только Docker Engine, например на Ubuntu из apt-репозитория Docker: из того же репозитория его потом и обновляют. Установочный скрипт с сайта Docker рекомендуют только для тестовых сред и сред разработки. После установки проверьте работу командой sudo docker run hello-world.

При обычной установке без sudo команды Docker на Linux не пойдут, пока пользователя нет в группе docker. Добавить его можно с помощью usermod, после чего нужно выйти из системы и зайти снова (или выполнить newgrp docker):

sudo usermod -aG docker $USER

С точки зрения безопасности группа docker даёт пользователю права уровня root. На личном ноутбуке это нормально, на общем сервере мы бы так не делали.

Ноутбук с открытым терминалом на столе рядом с чашкой кофе

Первые команды Docker

Учиться лучше в командной строке, а не кнопками Docker Desktop: те же команды работают на сервере и в CI, где графического интерфейса нет. Первая проверка после установки:

docker run hello-world

Если образа нет, Docker его скачает, создаст контейнер, и приложение внутри напечатает:

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Сообщение пересказывает ту же архитектуру: клиент обратился к демону, демон скачал образ из Docker Hub, создал контейнер и передал его вывод в терминал. В строке в скобках стоит архитектура процессора, на Mac с Apple Silicon она будет другой. Напечатав текст, приложение завершилось, и контейнер остановился вместе с ним.

Рука на клавиатуре перед монитором с окном терминала

Теперь контейнер, который работает в фоне. Учебный образ docker/welcome-to-docker запускает веб-сервер на порту 80:

docker run -d -p 8080:80 docker/welcome-to-docker

Флаг -d запускает контейнер в фоне, и команда печатает только полный ID контейнера. Флаг -p 8080:80 публикует порт: запросы на 8080 вашего компьютера уходят на 80 внутри контейнера. Первым идёт порт хоста, вторым контейнера. После запуска страница открывается в браузере по адресу http://localhost:8080. Список запущенных контейнеров показывает docker ps:

 CONTAINER ID   IMAGE                      COMMAND                  CREATED          STATUS          PORTS                      NAMES
 a1f7a4bb3a27   docker/welcome-to-docker   "/docker-entrypoint.…"   11 seconds ago   Up 11 seconds   0.0.0.0:8080->80/tcp       gracious_keldysh

У вас ID и имя будут другими: имя gracious_keldysh Docker придумал сам, потому что мы не задали его с помощью --name. Остановить контейнер можно по имени или по началу ID, достаточно стольких символов, чтобы ID не путался с другими: docker stop a1f.

Порт по умолчанию публикуется на всех сетевых интерфейсах, то есть приложение доступно любому, кто достучится до вашего компьютера по сети. Для базы данных на ноутбуке в кафе это плохая идея. Чтобы доступ был только с вашей машины, укажите адрес: -p 127.0.0.1:8080:80.

Команды, которые понадобятся в первую неделю:

Команда Что делает
docker search nginx Ищет образы в Docker Hub
docker pull nginx Скачивает образ из реестра
docker images Показывает скачанные образы
docker run --name web -d -p 8080:80 nginx Создаёт и запускает контейнер с именем web
docker ps, docker ps -a Запущенные контейнеры; с -a ещё и остановленные
docker logs web Выводит то, что приложение в контейнере напечатало
docker exec -it web sh Открывает оболочку внутри работающего контейнера
docker stop web Останавливает контейнер
docker start web Снова запускает остановленный контейнер
docker rm web Удаляет остановленный контейнер; -f удалит и запущенный
docker rmi nginx Удаляет образ
docker container prune Удаляет все остановленные контейнеры

docker ps без флагов показывает только запущенные контейнеры. Если вы запустили hello-world и не нашли его в списке, он никуда не делся: команда docker ps -a покажет его среди остановленных. Чтобы такие одноразовые контейнеры не копились, используйте флаг --rm, и Docker удалит контейнер сам, когда приложение завершится.

Окно терминала со списком контейнеров на экране монитора

Свой образ: Dockerfile для Python-приложения

Образ собирают по Dockerfile, текстовому файлу с инструкциями. Соберём образ для маленького веб-сервера на Python. Зависимостей у него нет, только стандартная библиотека, и пример получается коротким. Код проверен на Python 3.14.3. Файл app.py:

import os
from http.server import BaseHTTPRequestHandler, HTTPServer

HOST = os.environ.get("HOST", "0.0.0.0")
PORT = int(os.environ.get("PORT", "8000"))


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        body = "Привет из контейнера!\n".encode("utf-8")
        self.send_response(200)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)


if __name__ == "__main__":
    print(f"Сервер слушает {HOST}:{PORT}", flush=True)
    HTTPServer((HOST, PORT), Handler).serve_forever()

Сначала запустим его без Docker, командой python app.py. Сервер печатает:

Сервер слушает 0.0.0.0:8000

Запрос curl -i http://localhost:8000/ из второго окна консоли возвращает ответ (заголовки Date и Server убраны, они меняются от запуска к запуску):

HTTP/1.0 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 40

Привет из контейнера!

Content-Length равен 40 при 22 символах: кириллица в UTF-8 занимает два байта на букву. Рядом с app.py кладём файл Dockerfile, без расширения:

FROM python:3.14
WORKDIR /app
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]

Что делает каждая строка:

  1. FROM python:3.14 задаёт базовый образ: Debian с Python 3.14. Для учёбы берите полный образ: в python:3.14-slim нет многих системных пакетов, и сборка может упасть на установке зависимостей. Slim нужен, когда не хватает места.
  2. WORKDIR /app задаёт рабочую папку внутри образа: туда копируются файлы, там выполняются команды.
  3. COPY app.py . копирует файл с вашего компьютера в образ.
  4. EXPOSE 8000 только помечает, что приложение слушает порт 8000. Публикует порт флаг -p при запуске.
  5. CMD задаёт команду, которую контейнер выполнит при старте.

Собираем образ и запускаем контейнер:

docker build -t hello-docker .
docker run -d -p 8000:8000 --name hello hello-docker
curl http://localhost:8000/
docker logs hello

Точка в конце docker build задаёт контекст сборки, то есть папку, где Docker ищет Dockerfile и файлы для COPY. Флаг -t даёт образу имя, без него останется только длинный ID. Ответ на curl должен совпасть с телом из локального запуска выше, а в docker logs hello должна быть строка Сервер слушает 0.0.0.0:8000: её код печатает при старте, как и при запуске без Docker.

Разработчик пишет Dockerfile в редакторе кода на ноутбуке

У контейнера свой сетевой стек и свой адрес 127.0.0.1, отдельный от вашего. Поэтому в app.py переменная HOST по умолчанию равна 0.0.0.0: сервер слушает все интерфейсы контейнера. Если задать 127.0.0.1, сервер не примет запросы, которые приходят с хоста через -p: контейнер работает, docker ps показывает проброшенный порт, а браузер страницу не открывает. Воспроизвести это можно вторым контейнером, передав адрес через переменную окружения: docker run -d -p 8001:8000 -e HOST=127.0.0.1 --name hello-local hello-docker, и сравнить curl http://localhost:8001/ с ответом первого контейнера.

Адрес решает, дойдёт ли запрос до сервера, а flush=True в print решает, увидите ли вы строку о старте. Без терминала Python буферизует stdout блоками, а Docker без флага -t терминал контейнеру не выделяет. Строка «Сервер слушает…» застрянет в буфере и в docker logs не появится, хотя журнал запросов http.server пишет в stderr и будет виден. Мы проверили это локально: тот же сервер без flush=True с выводом в файл за полторы секунды работы не записал в файл ни байта. Для всего приложения разом можно задать в Dockerfile ENV PYTHONUNBUFFERED=1.

Когда у приложения появятся зависимости, порядок строк в Dockerfile начнёт влиять на скорость сборки. Docker кеширует каждый слой, а изменение файлов, скопированных через COPY, сбрасывает кеш этого слоя и всех следующих. Сначала копируют requirements.txt и ставят пакеты, а код копируют последним:

FROM python:3.14
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

При правке кода слой с установленными пакетами берётся из кеша, pip install не запускается заново, и сборка идёт быстрее. Чтобы COPY . . не унёс в образ .venv и .git, перечислите их в файле .dockerignore в корне контекста сборки, синтаксис похож на .gitignore. Если внутри контейнера всё же падает ModuleNotFoundError, первым делом проверьте, есть ли пакет в requirements.txt: пакеты с вашего компьютера в образ не попадают.

Где хранить данные: тома

Всё, что приложение записало внутри контейнера, лежит в его верхнем слое и пропадает при удалении контейнера. Для базы данных это означает пустую базу после каждого пересоздания. Чтобы данные пережили контейнер, используют тома (volumes): Docker хранит их отдельно от контейнера и подключает внутрь по указанному пути.

docker run --name=db -e POSTGRES_PASSWORD=secret -d -v postgres_data:/var/lib/postgresql postgres:18

Флаг -e задаёт переменные окружения (здесь пароль базы), -v postgres_data:/var/lib/postgresql подключает том postgres_data в папку, где PostgreSQL хранит файлы. Если тома нет, Docker создаст его сам. Том позволяет пересоздавать контейнер без потери данных: удалите контейнер, запустите новый с тем же томом, и данные останутся на месте. Список томов показывает docker volume ls, удаляет неиспользуемые docker volume prune.

Команда разработчиков обсуждает схему сервисов у доски

Docker Compose, Docker Swarm и Kubernetes

Когда контейнеров становится три-четыре, длинные команды docker run с портами, сетями и томами неудобно запускать руками. Docker Compose позволяет описать все контейнеры проекта и их настройки в одном YAML-файле и поднять их командой docker compose up. Dockerfile отвечает за сборку образа, а файл Compose за то, какие контейнеры запустить и как их связать. Файл кладут в репозиторий проекта, и любой, кто его склонирует, поднимет все сервисы одной командой.

Docker Swarm встроен в Docker Engine как режим для управления кластером из нескольких серверов с Docker. Kubernetes решает похожую задачу шире и ставится отдельно: платформа с открытым исходным кодом управляет контейнерами на многих машинах. Для учебных проектов и одного сервера подходит связка Docker и Compose. Как делить систему на сервисы, мы писали в статье про архитектурные паттерны.

Частые вопросы о Docker

Чем Docker отличается от Kubernetes?

Docker собирает образы и запускает контейнеры на сервере, где работает его демон. Kubernetes управляет контейнерами на кластере из многих серверов: распределяет их по машинам так, чтобы каждому хватило памяти и процессора, перезапускает упавшие, отвечает за масштабирование приложения. Проект открыли в 2014 году. Docker Desktop умеет поднимать локальный Kubernetes для проб на своём компьютере.

Docker бесплатный?

Docker Engine распространяется с открытым исходным кодом под лицензией Apache 2.0. Платная подписка на Docker Desktop нужна для коммерческого использования в организациях, где больше 250 сотрудников или больше 10 млн долларов годовой выручки.

Можно ли запустить в Docker программу для Windows на Linux?

Нет. Контейнер использует ядро хоста, и на Linux работают только Linux-контейнеры. Для Windows-программ нужен Windows-хост с поддержкой Windows-контейнеров или виртуальная машина.

Почему контейнер сразу останавливается?

Контейнер работает, пока работает его главная команда. hello-world печатает текст и завершается, поэтому контейнер останавливается сразу. Если так ведёт себя ваш сервер, значит, его программа завершилась или упала при старте. Причину ищите в docker logs <имя>, а сам контейнер в docker ps -a.

Оцените статью
bestprogrammer.ru
Добавить комментарий