$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогDocker для разработчика: запускаем приложение в контейнере с нуля

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

$ sudo teach IT
·17 июля 2026 г.·8 мин·70
Docker для разработчика: запускаем приложение в контейнере с нуля

Пошаговый гид по Docker без лишней теории: пишем Dockerfile, поднимаем сервис, разбираемся с volumes и сетями. После прочтения вы сможете контейнеризировать любой свой проект.

Содержание
Что такое контейнер и почему это не виртуальная машинаУстановка и первый запускАнатомия DockerfileПочему порядок инструкций важенСборка и запускVolumes: как не потерять данныеТипы монтированияСети в Docker: как контейнеры общаются между собойDocker Compose: оркестрация для разработкиТипичные ошибки и подводные камниЗапуск от root внутри контейнераХранение секретов в образеИгнорирование .dockerignoreИспользование тега latestОдин процесс — один контейнерМногоэтапная сборка (multi-stage build)Что дальшеВывод

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

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


Что такое контейнер и почему это не виртуальная машина

Прежде чем писать первый Dockerfile, стоит понять, с чем именно мы работаем.

Виртуальная машина эмулирует целый компьютер: у неё есть своё ядро ОС, драйверы, системные процессы. Это мощно, но тяжело — VM весит гигабайты и стартует минутами.

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

Docker — это не единственный инструмент для работы с контейнерами (есть Podman, containerd, LXC), но именно он стал стандартом де-факто для разработки благодаря удобному CLI и экосистеме Docker Hub.


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

На macOS и Windows проще всего установить Docker Desktop — он включает Docker Engine, CLI и графический интерфейс. На Linux устанавливаем через пакетный менеджер:

code
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
sudo apt-get install docker-ce docker-ce-cli containerd.io

# Добавляем пользователя в группу docker, чтобы не использовать sudo
sudo usermod -aG docker $USER

После установки проверяем:

code
docker --version
# Docker version 25.0.3, build 4debf41

docker run hello-world

Команда docker run hello-world скачает образ с Docker Hub и запустит контейнер. Если вы видите сообщение «Hello from Docker!» — всё работает.


Анатомия Dockerfile

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

Допустим, у нас есть простое веб-приложение на Python/Flask:

code
my-app/
├── app.py
├── requirements.txt
└── Dockerfile

app.py:

code
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return {"status": "ok", "message": "Hello from Docker!"}

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

requirements.txt:

code
flask==3.0.0

Dockerfile:

code
# Базовый образ: официальный Python 3.12 на slim-варианте Debian
FROM python:3.12-slim

# Устанавливаем рабочую директорию внутри контейнера
WORKDIR /app

# Сначала копируем только файл зависимостей — для кэширования слоёв
COPY requirements.txt .

# Устанавливаем зависимости
RUN pip install --no-cache-dir -r requirements.txt

# Копируем остальной код
COPY . .

# Указываем порт, который будет слушать приложение
EXPOSE 5000

# Команда запуска
CMD ["python", "app.py"]

Почему порядок инструкций важен

Docker строит образ послойно. Каждая инструкция (FROM, RUN, COPY) создаёт новый слой. Если слой не изменился — Docker берёт его из кэша. Именно поэтому мы сначала копируем requirements.txt и устанавливаем зависимости, а потом копируем код. Зависимости меняются редко, код — часто. Такой порядок экономит время при пересборке: при изменении app.py Docker не будет заново устанавливать все пакеты.

Сборка и запуск

code
# Собираем образ и даём ему тег
docker build -t my-flask-app:1.0 .

# Запускаем контейнер
# -d — в фоне (detached mode)
# -p 8080:5000 — пробрасываем порт хоста 8080 на порт контейнера 5000
docker run -d -p 8080:5000 --name flask-app my-flask-app:1.0

Открываем http://localhost:8080 — приложение работает в контейнере.

Полезные команды для работы с запущенными контейнерами:

code
docker ps                    # список запущенных контейнеров
docker logs flask-app        # логи контейнера
docker exec -it flask-app sh # зайти внутрь контейнера
docker stop flask-app        # остановить
docker rm flask-app          # удалить

Volumes: как не потерять данные

Контейнер по природе своей эфемерен. Когда вы его удаляете, все данные внутри исчезают. Для приложений с состоянием — баз данных, файловых хранилищ — это критично. Здесь на помощь приходят volumes.

Типы монтирования

Bind mount — монтируем директорию с хоста в контейнер. Удобно при разработке: изменения в коде сразу видны внутри контейнера.

code
docker run -d \
  -p 8080:5000 \
  -v $(pwd):/app \
  --name flask-dev \
  my-flask-app:1.0

Named volume — Docker сам управляет хранилищем. Подходит для баз данных: данные переживают удаление контейнера.

code
# Создаём volume
docker volume create postgres-data

# Запускаем PostgreSQL с volume
docker run -d \
  --name postgres-db \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=myapp \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:16

Теперь даже если удалить контейнер postgres-db и создать новый с тем же volume — данные сохранятся.

code
docker volume ls       # список volumes
docker volume inspect postgres-data  # детали
docker volume rm postgres-data       # удалить (только если не используется)

Сети в Docker: как контейнеры общаются между собой

По умолчанию Docker создаёт три сети: bridge, host и none. Для большинства задач разработки используется bridge — изолированная виртуальная сеть, в которой контейнеры могут общаться по имени.

Создадим пользовательскую сеть и запустим в ней несколько контейнеров:

code
# Создаём сеть
docker network create app-network

# Запускаем PostgreSQL в этой сети
docker run -d \
  --name postgres-db \
  --network app-network \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=myapp \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:16

# Запускаем Flask-приложение в той же сети
docker run -d \
  --name flask-app \
  --network app-network \
  -p 8080:5000 \
  my-flask-app:1.0

Внутри app-network контейнер flask-app может обращаться к базе данных по имени postgres-db — Docker автоматически резолвит имена контейнеров в IP-адреса. В коде приложения строка подключения будет выглядеть так:

code
DATABASE_URL = "postgresql://postgres:secret@postgres-db:5432/myapp"

Docker Compose: оркестрация для разработки

Управлять несколькими контейнерами через CLI быстро становится неудобным. docker-compose.yml — это декларативное описание всего стека приложения.

Вот полноценный пример для Flask + PostgreSQL + Redis:

code
# docker-compose.yml
version: "3.9"

services:
  web:
    build: .
    ports:
      - "8080:5000"
    environment:
      - DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
      - REDIS_URL=redis://cache:6379
    volumes:
      - .:/app
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    networks:
      - app-network

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  cache:
    image: redis:7-alpine
    networks:
      - app-network

volumes:
  postgres-data:

networks:
  app-network:
    driver: bridge

Запуск всего стека одной командой:

code
docker compose up -d        # поднять в фоне
docker compose logs -f web  # следить за логами сервиса web
docker compose down         # остановить и удалить контейнеры
docker compose down -v      # то же + удалить volumes

Обратите внимание на healthcheck для базы данных и depends_on с условием service_healthy. Без этого Flask может попытаться подключиться к PostgreSQL раньше, чем та успеет запуститься — типичная ошибка при первом знакомстве с Compose.

Тем, кто хочет разобраться в Docker системно — не только в синтаксисе, но и в архитектурных решениях, CI/CD-интеграции и работе с реестрами образов — стоит посмотреть на структурированные курсы. Один из вариантов — курс по Docker от «sudo teach IT», где практика идёт параллельно с теорией.


Типичные ошибки и подводные камни

Запуск от root внутри контейнера

По умолчанию процессы в контейнере работают от пользователя root. Это удобно при разработке, но небезопасно в продакшене. Добавьте в Dockerfile:

code
# Создаём непривилегированного пользователя
RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser

# Переключаемся на него
USER appuser

Хранение секретов в образе

Никогда не кладите пароли, токены и ключи API прямо в Dockerfile или в docker-compose.yml в открытом виде. Используйте переменные окружения через .env-файл (который добавлен в .gitignore) или Docker Secrets для продакшена.

code
# .env файл
POSTGRES_PASSWORD=supersecret
API_KEY=abc123

# docker-compose.yml читает его автоматически
# или явно:
docker compose --env-file .env up

Игнорирование .dockerignore

Без .dockerignore команда COPY . . затащит в образ node_modules, .git, виртуальное окружение Python и прочий мусор. Это раздувает образ и замедляет сборку.

code
# .dockerignore
.git
.gitignore
__pycache__
*.pyc
.env
node_modules
venv
.venv
*.log

Использование тега latest

FROM python:latest — плохая практика. latest — это движущаяся цель: сегодня это Python 3.12, завтра — 3.13 с breaking changes. Всегда фиксируйте версию: FROM python:3.12-slim.

Один процесс — один контейнер

Не запускайте в одном контейнере nginx, приложение и базу данных. Это нарушает принцип единственной ответственности и усложняет масштабирование, логирование и обновление компонентов.


Многоэтапная сборка (multi-stage build)

Для компилируемых языков (Go, Rust, Java) или фронтенда с этапом сборки используют многоэтапные Dockerfile. Это позволяет получить минимальный финальный образ без инструментов сборки.

Пример для Go-приложения:

code
# Этап сборки
FROM golang:1.22-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server

# Финальный образ
FROM alpine:3.19

WORKDIR /app
# Копируем только скомпилированный бинарник из этапа builder
COPY --from=builder /app/server .

EXPOSE 8080
CMD ["./server"]

Образ на этапе сборки весит ~350 МБ (Go toolchain), финальный — около 15 МБ. Разница принципиальная, особенно если образы хранятся в реестре и деплоятся по сети.


Что дальше

После того как вы освоили базовый цикл — Dockerfile, docker compose, volumes и сети — открывается следующий уровень:

  • Docker Registry: публикация образов на Docker Hub или в приватный реестр (GitLab Registry, AWS ECR, GitHub Container Registry)
  • CI/CD интеграция: автоматическая сборка и публикация образов при пуше в репозиторий
  • Kubernetes: оркестрация контейнеров в продакшене, когда docker compose уже недостаточно
  • Оптимизация образов: инструменты вроде dive для анализа слоёв, дистроless-образы
  • Безопасность: сканирование образов на уязвимости через Trivy или Snyk

Вывод

Docker стал стандартом не потому что это модно, а потому что решает реальную проблему воспроизводимости окружения. Написав Dockerfile один раз, вы получаете гарантию: приложение запустится одинаково у любого члена команды, в CI-пайплайне и на продакшен-сервере.

Ключевые вещи, которые стоит запомнить: порядок слоёв в Dockerfile влияет на скорость сборки; volumes — единственный надёжный способ хранить данные; пользовательские сети дают контейнерам возможность общаться по имени; docker compose — ваш главный инструмент для локальной разработки многокомпонентных приложений.

Если хотите пройти этот путь более системно — с разбором архитектурных решений, практикой деплоя и интеграцией в реальные пайплайны — можно посмотреть на курс по Docker от «sudo teach IT»: там практика выстроена вокруг реальных сценариев, а не синтетических примеров.

Контейнеризация — это навык, который окупается быстро. Один раз разобравшись с Docker, вы перестанете тратить время на «а у меня не запускается» и начнёте тратить его на то, что действительно важно.

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Простыми словами о защищенном соединении

Простыми словами о защищенном соединении

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

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Сервис, который не падает - как построить безопасную зашифрованную сеть

Сервис, который не падает - как построить безопасную зашифрованную сеть

Практический курс по безопасности сети: как собрать собственный сервис защищённого доступа - от пустого сервера до подписки с автовыбором - и, что важнее, как потом не уронить его своими руками.

Перейти к курсу

Другие статьи

Деплой без сюрпризов: как подготовить Docker-образ, переменные окружения и healthcheck
деплой

Деплой без сюрпризов: как подготовить Docker-образ, переменные окружения и healthcheck

Разберём практический чек-лист подготовки контейнера к продакшену: как собрать воспроизводимый образ, как правильно прокидывать конфиги/секреты через окружение и как настроить healthcheck, чтобы деплой не “успевал” раньше готовности сервиса.

18 сентября 2026 г.
1030
Пишем CLI для разработчика на Python: команды, конфиги и корректные exit codes по стандарту проекта
пишем

Пишем CLI для разработчика на Python: команды, конфиги и корректные exit codes по стандарту проекта

Построим удобный интерфейс командной утилиты: структура подкоманд, чтение конфигов, валидация входных параметров и единая логика ошибок с понятными exit code. Сфокусируемся на UX для тех, кто запускает CLI в CI и в скриптах.

15 сентября 2026 г.
1030
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1431
Как выбрать, куда идти в IT: веб, боты, данные — без хаоса
выбрать

Как выбрать, куда идти в IT: веб, боты, данные — без хаоса

Разберём самые понятные направления для новичка (веб, боты, аналитика/данные) и чем они отличаются по шагам обучения. В конце вы получите простой план, как проверить, что вам подходит, и какой стартовый путь выбрать.

5 октября 2026 г.
580
Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть
путь

Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть

Разберём структуру репозитория, как упаковать проект в понятную историю (проблема → решение → запуск → метрики качества) и какие артефакты повышают доверие: скринкасты, архитектурная схема, чек-лист продакшн-готовности.

17 сентября 2026 г.
1090
Как понять, какой язык программирования учить первым: простой тест и выбор
понять

Как понять, какой язык программирования учить первым: простой тест и выбор

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

2 октября 2026 г.
630

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Что такое контейнер и почему это не виртуальная машинаУстановка и первый запускАнатомия DockerfileПочему порядок инструкций важенСборка и запускVolumes: как не потерять данныеТипы монтированияСети в Docker: как контейнеры общаются между собойDocker Compose: оркестрация для разработкиТипичные ошибки и подводные камниЗапуск от root внутри контейнераХранение секретов в образеИгнорирование .dockerignoreИспользование тега latestОдин процесс — один контейнерМногоэтапная сборка (multi-stage build)Что дальшеВывод