Docker основы
Docker — платформа для упаковки, запуска и распространения приложений в изолированных контейнерах. Контейнер содержит приложение, его зависимости и настройки, но использует ядро операционной системы хоста.
Docker используют для локальной разработки, тестирования, сборки воспроизводимых окружений, запуска микросервисов и автоматизации CI/CD.
Основные понятия:
- образ — неизменяемый шаблон файловой системы;
- контейнер — запущенный экземпляр образа;
- Dockerfile — набор инструкций для сборки образа;
- registry — хранилище образов, например Docker Hub;
- volume — постоянное хранилище, управляемое Docker;
- network — виртуальная сеть для связи контейнеров.
Содержание
- Контейнеры и виртуальные машины
- Образы и слои
- Dockerfile и инструкции
- docker build и docker run
- docker ps, logs и exec
- Volumes и bind mounts
- Docker networks
- Docker Compose базово
- Практический пример
- Рекомендации
Контейнеры и виртуальные машины
Контейнеры и виртуальные машины изолируют приложения, но работают на разных уровнях.
Виртуальная машина
Виртуальная машина запускает полноценную гостевую операционную систему с собственным ядром:
Физический компьютер
└── Гипервизор
├── Виртуальная машина
│ ├── Гостевая ОС
│ └── Приложение
└── Виртуальная машина
├── Гостевая ОС
└── ПриложениеКонтейнер
Контейнер изолирует процессы, файловую систему, сеть и ресурсы, но использует ядро операционной системы хоста:
Физический компьютер
└── Операционная система
└── Docker Engine
├── Контейнер
│ └── Приложение
└── Контейнер
└── ПриложениеСравнение
| Характеристика | Контейнер | Виртуальная машина |
|---|---|---|
| Уровень изоляции | Процессы и ресурсы ОС | Полноценная гостевая ОС |
| Ядро | Использует ядро хоста | Собственное ядро |
| Размер | Обычно меньше | Обычно больше |
| Запуск | Очень быстрый | Медленнее |
| Потребление ресурсов | Ниже | Выше |
| Типичное применение | Сервисы, CI/CD, разработка | Полная изоляция ОС |
ВМ удобнее, когда нужна отдельная операционная система или собственное ядро. Docker удобнее для быстрого запуска большого количества сервисов и воспроизводимых окружений.
Образы и слои
Образ — неизменяемый шаблон, из которого создаются контейнеры. Один образ можно использовать для запуска нескольких контейнеров:
docker run -d --name web-1 nginx:alpine
docker run -d --name web-2 nginx:alpineТеги образов
Обычно образ записывают как имя:тег:
nginx:1.27-alpine
python:3.12-slimЕсли тег не указан, используется latest. Это не обязательно самая новая версия, а только имя тега. Для воспроизводимости лучше указывать конкретную версию.
docker pull nginx:alpine
docker image lsСлои
Образ состоит из последовательности слоёв:
Слой 3: файлы приложения
Слой 2: установленные зависимости
Слой 1: базовый образ
----------------------
ОбразСлои кэшируются и переиспользуются. Если базовый образ и зависимости не изменились, Docker может не выполнять соответствующие шаги повторно.
При запуске контейнер получает тонкий записываемый слой поверх слоёв образа. Это механизм copy-on-write. Изменение файла из образа приводит к его копированию в слой контейнера.
Записываемый слой контейнера не следует использовать как постоянное хранилище: после удаления контейнера данные могут исчезнуть. Для данных применяют volumes или bind mounts.
Полезные команды:
docker image inspect nginx:alpine
docker history nginx:alpine
docker image rm nginx:alpine
docker system prunedocker system prune удаляет неиспользуемые объекты, поэтому перед выполнением нужно проверить, какие данные больше не нужны.
Dockerfile и инструкции
Dockerfile — текстовый файл с инструкциями для сборки образа.
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]FROM
Задаёт базовый образ:
FROM node:22-alpinealpine и slim могут уменьшить образ, но иногда требуют дополнительной настройки системных зависимостей.
WORKDIR
Устанавливает рабочую директорию:
WORKDIR /appЕсли каталога нет, Docker создаст его. Обычно это лучше, чем использовать RUN cd /app.
COPY и ADD
COPY копирует файлы из контекста сборки:
COPY package.json package-lock.json ./
COPY src ./srcADD умеет больше, например распаковывать локальные архивы. Для обычного копирования предпочтительнее COPY, поскольку его поведение проще.
RUN
Выполняет команду во время сборки:
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*Установку и очистку часто объединяют в одной инструкции, чтобы временные файлы не оставались в отдельном слое.
ENV и ARG
ENV задаёт переменную окружения для образа и контейнера:
ENV APP_ENV=production
ENV PYTHONDONTWRITEBYTECODE=1ARG доступен во время сборки:
ARG APP_VERSION=dev
RUN echo "Building ${APP_VERSION}"Передача значения:
docker build --build-arg APP_VERSION=1.2.0 -t my-app:1.2.0 .Пароли и токены нельзя хранить в ENV, ARG и Dockerfile: они могут попасть в метаданные или слои образа.
EXPOSE
Документирует порт приложения внутри контейнера:
EXPOSE 8000EXPOSE не публикует порт на хосте. Для публикации используют docker run -p или Compose.
CMD и ENTRYPOINT
CMD задаёт команду по умолчанию:
CMD ["python", "app.py"]Её можно переопределить:
docker run --rm my-app python healthcheck.pyENTRYPOINT задаёт основной исполняемый файл:
ENTRYPOINT ["python"]
CMD ["app.py"]В этом случае docker run my-app other.py запустит python other.py.
Для приложений обычно используют exec-формат в виде JSON-массива:
CMD ["node", "server.js"]USER
Запускает приложение не от root:
RUN useradd --create-home --shell /usr/sbin/nologin appuser
USER appuserHEALTHCHECK
Описывает проверку работоспособности:
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8000/health || exit 1Multi-stage build
Позволяет не включать инструменты разработки в финальный образ:
FROM node:22-alpine AS build
WORKDIR /src
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /src/dist /usr/share/nginx/html.dockerignore
.git
node_modules
__pycache__
*.log
.env
Dockerfile
README.md.dockerignore уменьшает контекст, ускоряет сборку и помогает не передавать секреты.
docker build и docker run
docker build
Сборка образа:
docker build -t my-app:1.0.0 .-tзадаёт имя и тег;.— текущая директория как контекст сборки.
Другой Dockerfile:
docker build -f Dockerfile.dev -t my-app:dev .Без кэша:
docker build --no-cache -t my-app:clean .Контекстом сборки является последний аргумент команды. Не следует указывать слишком большую родительскую директорию.
docker run
docker run nginx:alpine
docker run -d --name web nginx:alpineПубликация порта выполняется в формате порт_хоста:порт_контейнера:
docker run -d \
--name web \
-p 8080:80 \
nginx:alpineПорт 80 внутри контейнера доступен на порту 8080 хоста. Ограничить публикацию localhost можно так:
docker run -p 127.0.0.1:8080:80 nginx:alpineПеременные окружения:
docker run -d \
--name api \
-e APP_ENV=production \
-e LOG_LEVEL=info \
my-api:1.0.0Из файла:
docker run --env-file .env my-api:1.0.0Автоматический перезапуск:
docker run -d \
--restart unless-stopped \
--name api \
my-api:1.0.0Основные политики:
| Политика | Поведение |
|---|---|
no |
Не перезапускать автоматически |
always |
Перезапускать всегда |
on-failure |
Перезапускать после ошибки процесса |
unless-stopped |
Перезапускать, если контейнер не остановлен вручную |
Интерактивный режим:
docker run --rm -it alpine sh-iоставляет стандартный ввод открытым;-tвыделяет псевдотерминал;--rmудаляет контейнер после завершения.
Ограничение ресурсов:
docker run -d --memory=512m --cpus=1.0 --name api my-api:1.0.0docker ps, stop, start и rm
Запущенные контейнеры:
docker psВсе контейнеры, включая остановленные:
docker ps -aОстановка и запуск:
docker stop web
docker start web
docker restart webПринудительная остановка:
docker kill webУдаление остановленного контейнера:
docker rm webПринудительное удаление:
docker rm -f webВременный контейнер можно запускать с --rm:
docker run --rm alpine echo "temporary container"Информация о контейнере и использование ресурсов:
docker inspect web
docker stats
docker stats webdocker inspect возвращает настройки, состояние, сети, монтирования и переменные окружения контейнера.
docker logs
Рекомендуется писать логи приложения в stdout и stderr. Тогда Docker сможет их собирать и показывать.
docker logs web
docker logs -f web
docker logs --tail 100 web
docker logs -t webЛоги за период:
docker logs --since 10m web
docker logs --since 2026-01-01T10:00:00 webdocker logs показывает только стандартный вывод процесса. Если приложение пишет в собственный файл внутри контейнера, эта команда его не покажет. Для production-систем нужно отдельно настроить ротацию и централизованное хранение логов.
docker exec
docker exec запускает дополнительный процесс внутри работающего контейнера.
docker exec -it web sh
docker exec -it web bash
docker exec web ls -la /usr/share/nginx/html
docker exec -u root -it web sh
docker exec -e DEBUG=1 web printenv DEBUGКоманда работает, пока основной процесс контейнера запущен. Ручные изменения внутри контейнера не становятся частью образа и исчезнут после его удаления. Воспроизводимые изменения нужно вносить в Dockerfile или конфигурацию запуска.
Volumes и bind mounts
Контейнеры обычно пересоздаются, поэтому важные данные нельзя хранить только в записываемом слое контейнера. Docker предоставляет named volumes и bind mounts.
Named volumes
docker volume create app-data
docker run -d \
--name database \
-v app-data:/var/lib/postgresql/data \
postgres:16
docker volume ls
docker volume inspect app-data
docker volume rm app-dataФормат — имя_тома:путь_в_контейнере. Удаление тома может привести к потере данных, поэтому перед удалением нужно проверить, какие контейнеры его используют.
Bind mounts
Bind mount связывает путь хоста с путём в контейнере:
docker run --rm \
-v "$PWD":/app \
-w /app \
node:22-alpine npm testЯвная форма через --mount:
docker run --rm \
--mount type=bind,source="$PWD",target=/app \
-w /app \
node:22-alpine npm testТолько для чтения:
docker run --rm \
-v "$PWD/config":/app/config:ro \
my-app:devСравнение
| Свойство | Named volume | Bind mount |
|---|---|---|
| Где хранится | В каталоге Docker | В указанном пути хоста |
| Управление | Docker | Пользователь и ОС хоста |
| Типичное применение | Базы данных, постоянные данные | Исходники и конфигурация разработки |
| Переносимость | Выше внутри Docker | Зависит от структуры хоста |
| Права доступа | Docker и ОС | Напрямую зависят от хоста |
Для базы данных обычно используют named volume. Для разработки, когда изменения исходников должны сразу попадать в контейнер, удобен bind mount.
Docker networks
Сети Docker позволяют контейнерам обмениваться данными. В одной пользовательской сети контейнеры могут обращаться друг к другу по имени.
docker network ls
docker network inspect bridge
docker network create app-netЗапуск сервисов в одной сети:
docker run -d --name db --network app-net postgres:16
docker run -d --name api --network app-net my-api:1.0.0API обращается к базе по имени db:
postgresql://db:5432/appIP-адрес контейнера использовать не следует: после пересоздания он может измениться.
Подключение и отключение контейнера:
docker network connect app-net existing-container
docker network disconnect app-net existing-containerОсновные драйверы:
| Драйвер | Назначение |
|---|---|
bridge |
Сеть контейнеров на одном Docker-хосте |
host |
Использование сетевого стека хоста |
none |
Отключение сетевого доступа |
overlay |
Сеть между узлами в кластерных сценариях |
Порт может быть доступен внутри сети, но не опубликован наружу. Публикация выполняется через -p:
docker run -d --name api --network app-net -p 8080:8000 my-api:1.0.0Базу данных обычно не публикуют на внешний интерфейс, если к ней обращается только API-контейнер.
Docker Compose базово
Docker Compose описывает приложение из нескольких контейнеров в YAML-файле. Современная команда — docker compose.
Минимальный пример
services:
web:
image: nginx:alpine
ports:
- "8080:80"docker compose up -d
docker compose ps
docker compose logs -f web
docker compose downПриложение и база данных
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
environment:
APP_ENV: production
DATABASE_URL: postgresql://app:app_password@db:5432/app
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: app_password
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:Внутри Compose-сети api обращается к PostgreSQL по имени db, а не по localhost. localhost внутри контейнера указывает на этот же контейнер.
Команды Compose
docker compose up -d --build
docker compose build api
docker compose exec api sh
docker compose logs -f api
docker compose stop
docker compose downУдаление вместе с томами:
docker compose down -vdown -v следует использовать осторожно: команда может удалить данные баз данных в Compose volumes.
Bind mount
services:
api:
build: .
volumes:
- .:/app
- /app/node_modulesТакой вариант типичен для разработки. В production обычно используют неизменяемый образ и не монтируют весь исходный код хоста.
.env
POSTGRES_DB=app
POSTGRES_USER=app
POSTGRES_PASSWORD=change-meИспользование:
services:
db:
image: postgres:16
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}Файл с реальными секретами не следует коммитить в репозиторий.
Healthcheck
Запуск контейнера не означает, что сервис готов принимать запросы. Можно добавить проверку:
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: app_password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
api:
build: .
depends_on:
db:
condition: service_healthyДаже при healthcheck приложение должно уметь повторять подключение к зависимостям, потому что сеть или база могут временно быть недоступны.
Практический пример
Структура проекта:
my-api/
├── app.py
├── Dockerfile
├── .dockerignore
└── compose.yamlФайл app.py:
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = b"ok" if self.path == "/health" else (
f"Hello from {os.getenv('APP_ENV', 'development')}\n".encode()
)
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)
server = HTTPServer(("0.0.0.0", 8000), Handler)
print("Server started on port 8000", flush=True)
server.serve_forever()Файл Dockerfile:
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
COPY app.py .
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"
CMD ["python", "app.py"]Файл .dockerignore:
.git
.env
__pycache__
*.pyc
*.logСборка, запуск и проверка:
docker build -t my-api:1.0 .
docker run -d --name my-api -p 8000:8000 -e APP_ENV=production my-api:1.0
curl http://localhost:8000/
curl http://localhost:8000/health
docker ps
docker logs my-apiCompose-файл:
services:
api:
build: .
ports:
- "8000:8000"
environment:
APP_ENV: developmentdocker compose up --build
docker compose downРекомендации
Делайте образы небольшими
Используйте подходящий базовый образ, .dockerignore и multi-stage build. Не включайте в production-образ кэши, исходники и инструменты, которые не нужны во время запуска.
Фиксируйте версии
FROM python:3.12.5-slimПри необходимости образ можно зафиксировать по digest. Обновлять базовые образы следует осознанно, чтобы получать исправления безопасности.
Не храните секреты в образе
Пароли, токены и ключи не должны находиться в Dockerfile, ENV, ARG или исходном коде. Используйте переменные окружения, secrets-механизмы платформы или внешнее хранилище.
Разделяйте код, конфигурацию и данные
- код и системные зависимости — в образе;
- настройки — в переменных окружения или конфигурационных файлах;
- пользовательские данные и базы — в volumes или внешнем хранилище.
Пишите логи в стандартный вывод
Так их можно читать через docker logs и передавать в централизованную систему логирования.
Не исправляйте контейнер вручную
Контейнер обычно пересоздают из образа. Изменения, выполненные через docker exec, исчезнут после удаления контейнера. Воспроизводимая настройка должна быть описана в Dockerfile или Compose.
Используйте healthcheck
Проверка должна показывать реальную готовность сервиса и не создавать лишнюю нагрузку.
Краткая шпаргалка
# Образы
docker pull nginx:alpine
docker build -t my-app:1.0 .
docker image ls
docker image inspect my-app:1.0
# Контейнеры
docker run -d --name app -p 8080:8000 my-app:1.0
docker ps
docker ps -a
docker stop app
docker start app
docker rm app
# Диагностика
docker logs -f app
docker exec -it app sh
docker inspect app
docker stats app
# Томы
docker volume create app-data
docker volume ls
docker volume inspect app-data
# Сети
docker network create app-net
docker network ls
docker network inspect app-net
# Compose
docker compose up -d --build
docker compose ps
docker compose logs -f
docker compose exec api sh
docker compose downDocker позволяет описывать окружение приложения как код: Dockerfile определяет образ, Compose — взаимодействие сервисов, volumes — хранение данных, а networks — связь между контейнерами. Это делает сборку и запуск приложения более воспроизводимыми.