Docker без страшных слов: Углубленное погружение.
Вы уже знаете классику: "У меня на машине все работает!". Давайте копнем глубже, почему так происходит?
- Разные версии библиотек: У вас `openssl` версии 3.x, а на сервере 1.1.x. Ваша программа, использующая новые функции шифрования, падает.
- Разные системные пути: Ваша программа ищет конфиг в `/home/user/app/config.ini`, а на сервере ее запускают из-под `www-data` и конфиг лежит в `/var/www/config.ini`.
- Разные "скрытые" зависимости: Ваша программа использует утилиту `imagemagick` для обработки картинок. Вы ее себе поставили и забыли. На сервере ее нет — программа падает с невнятной ошибкой.
Проблема: Приложение — это не только ваш код. Это "код + зависимости + конфиги + файловая структура". Этот клубок называется Окружение. Docker "запаковывает" весь этот клубок в один стандартный ящик.
Оба решают проблему изоляции. Но цена решения — разная.
Аналогия: Дом vs. Квартира (Pro)
🏠 Виртуальная Машина (Дом)
Вы строите полноценный дом с нуля. Вам нужен свой фундамент (эмуляция железа), свои трубы, свой генератор, свои стены (целая Гостевая ОС — Windows Server, Ubuntu и т.д.). Вы тратите кучу ресурсов (диск, память) на то, чтобы просто "стоял дом", еще до того, как "завезли мебель" (ваше приложение).
📦 Контейнер (Квартира)
Вы арендуете квартиру в готовом здании. Здание — это ваш компьютер (Хост-система). Фундамент и коммуникации (вода, свет, канализация) — общие. В роли "коммуникаций" выступает Ядро Linux.
Контейнер — это просто изолированная комната (процесс), которая пользуется общим ядром. Docker — это "домоуправ", который следит, чтобы жилец из квартиры 1 не ходил в квартиру 2 и не видел, что там происходит.
| Параметр |
Виртуальная Машина (VM) |
Контейнер (Docker) |
| Что изолирует? |
Полностью Аппаратное Обеспечение |
Процессы, Файлы, Сеть (на уровне ОС) |
| Что запускает? |
Целую Гостевую ОС (со своим ядром) |
Только Приложение (использует ядро Хоста) |
| Размер |
Гигабайты (ГБ) |
Мегабайты (МБ) |
| Время запуска |
Минуты |
Секунды (или доли секунд) |
| Накладные расходы |
Высокие (Память/CPU на целую ОС) |
Очень низкие |
Повторим "трех китов" (рецепт, торт, текст), но детальнее.
-
1. Dockerfile — Текст Рецепта
Это файл с именем `Dockerfile` (без расширения). Он — ваш план по сборке "коробки".
# Базовый "слой". Берем готовый рецепт Python
# Нам не нужно ставить Ubuntu, потом Python...
# Берем готовый образ из Docker Hub
FROM python:3.10-slim
# Устанавливаем "рабочую папку" внутри контейнера
WORKDIR /app
# Копируем файл с зависимостями
# (снаружи из . в /app внутри)
COPY requirements.txt .
# Устанавливаем зависимости ВНУТРИ контейнера
RUN pip install -r requirements.txt
# Копируем ВЕСЬ наш код (снаружи из . в /app внутри)
COPY . .
# Сообщаем Docker, что приложение будет "слушать" порт 5000
EXPOSE 5000
# Команда, которая запустится, когда "включат" контейнер
CMD ["python", "app.py"]
-
2. Image (Образ) — Рецепт
Когда вы запускаете команду `docker build -t my-app .`, Docker читает `Dockerfile` и создает Образ.
Образ — это неизменяемый (read-only), упакованный шаблон. Он как `*.iso` файл или слепок системы. Он лежит у вас на диске и ничего не делает, пока его не попросят.
-
3. Container (Контейнер) — Торт
Когда вы запускаете `docker run my-app`, Docker берет неизменяемый Образ `my-app`, создает поверх него тонкий "записываемый" слой и запускает процесс (вашу команду `CMD`).
Все, что ваше приложение будет писать на диск (логи, временные файлы), попадет в этот верхний "записываемый" слой. Сам Образ (`my-app`) остается нетронутым.
Это самое важное, что нужно понять о Docker. Почему он такой быстрый и эффективный?
Каждая команда в `Dockerfile` (FROM, RUN, COPY) создает новый слой (layer) в Образе.
Представьте, что Образ — это стопка прозрачных пленок:
- Слой 1 (FROM): Пленка с файлами ОС Ubuntu.
- Слой 2 (RUN apt install): Пленка, где *добавлен* Python.
- Слой 3 (COPY .): Пленка, где *добавлен* ваш `app.py`.
Все эти слои неизменяемы (read-only). Когда вы смотрите на "собранный" Образ, вы видите их все, как будто они — единое целое.
В чем магия? (Кэширование)
Допустим, вы собрали Образ. Теперь вы поменяли одну строчку в `app.py` и запускаете `docker build` снова.
Docker смотрит на `Dockerfile`:
- Слой 1 (FROM): "Команда `FROM python:3.10` не менялась? Беру готовый слой из кэша."
- Слой 2 (RUN pip install): "Команда `RUN pip install...` не менялась? И `requirements.txt` не менялся? Беру готовый слой из кэша." (Установка пакетов пропускается!)
- Слой 3 (COPY . .): "Ага! Файл `app.py` изменился. Этот слой нужно пересобрать."
Результат: Вместо полной пересборки (10 минут), сборка занимает 2 секунды (только копирование вашего кода). Это и есть кэширование слоев.
Команды, которые вы будете использовать 90% времени.
-
Собрать Образ (из `Dockerfile`):
# -t my-app (tag, "дать имя" my-app)
# . (точка в конце) "искать Dockerfile в этой папке"
docker build -t my-app .
-
Запустить веб-сервер (Самая важная команда):
docker run -d -p 8080:80 --name my-nginx nginx
Разбор флагов: -d -p --name
-d (Detached): Запустить в фоне (не занимать терминал).
-p 8080:80 (Port): "Пробросить" порт. Соединить порт 8080 на моем компьютере (Хосте) с портом 80 внутри контейнера (где Nginx слушает).
--name my-nginx: Дать контейнеру "человеческое" имя, чтобы не запоминать ID.
nginx: Имя Образа, который нужно запустить (Docker сам скачает его из Docker Hub).
После этой команды откройте в браузере `http://localhost:8080` и увидите "Welcome to Nginx!".
-
Посмотреть логи (что происходит внутри `-d` контейнера):
-
Посмотреть запущенные контейнеры:
-
Остановить и Удалить контейнер:
docker stop my-nginx
docker rm my-nginx
-
Удалить Образ (чтобы освободить место):
-
"Прибраться" (Удалить все остановленные контейнеры):
Я сказал, что контейнеры "используют Ядро Хоста". Но что, если у вас Windows или macOS? У них же нет ядра Linux!
Подвох: Docker для Windows и Mac все равно запускает Linux.
При установке Docker Desktop он ставит сверх-легкую, оптимизированную виртуальную машину Linux (используя WSL2 в Windows или HyperKit в Mac). Она запускается быстро и "прячется" от вас.
Все ваши контейнеры на самом деле запускаются внутри этой скрытой VM. Docker Desktop просто "пробрасывает" команды и порты из вашей Windows/Mac в эту VM.
Поэтому Docker "роднее" всего чувствует себя именно на Linux-серверах, где ему не нужна прослойка в виде VM. Но для разработки на Windows/Mac это почти незаметно.