Перейти к содержимому
TelegramGitHub

Что такое контейнер

Это урок-теория. Здесь мало команд. Но он нужен: дальше вся работа с Docker строится на трёх словах — образ, контейнер, реестр.

Вы написали Python-скрипт. На вашем ноутбуке он работает. Коллега запускает его и получает ошибку. Причины бывают такие:

  • У коллеги Python 3.9, а у вас 3.12.
  • У него другая версия библиотеки pandas.
  • Не хватает системной библиотеки.
  • Другая операционная система.

Это называют «у меня работает». Docker решает проблему так: вы упаковываете скрипт вместе со всем окружением в образ. Образ запускается одинаково везде.

Контейнер — это изолированный процесс на вашем компьютере. У него есть:

  • своя файловая система;
  • свои процессы (он не видит процессы хоста);
  • своя сеть;
  • лимиты памяти и процессора, если их задать.

Внутри контейнера программа думает, что она одна в системе. Но на деле это обычный процесс Linux. Контейнеры используют общее ядро операционной системы. Поэтому они лёгкие и запускаются за секунды.

Изоляцию делает само ядро Linux. Две главные функции:

  • namespaces — ограничивают, что процесс видит (файлы, сеть, другие процессы);
  • cgroups — ограничивают, сколько процесс использует (память, процессор).

Docker не придумал эти функции. Он сделал их простыми в использовании.

Виртуальная машина (VM) — это целый компьютер внутри компьютера. У неё своя операционная система. Контейнер делит ядро с хостом и несёт только программу и её библиотеки.

Сравнение виртуальных машин и контейнеров: у каждой VM своя гостевая ОС, а контейнеры делят одно ядро хоста

Виртуальная машинаКонтейнер
Что внутриСвоя ОС целикомПрограмма и библиотеки
РазмерГигабайтыМегабайты (десятки или сотни)
ЗапускМинутыСекунды
ИзоляцияСильнаяХорошая, но слабее: общее ядро
Сколько на одном сервереЕдиницыДесятки и сотни

Контейнеры не заменяют виртуальные машины. Часто контейнеры работают внутри VM. Облако так и устроено.

Почему на macOS и Windows контейнеры видят Linux?

Контейнеры Linux нуждаются в ядре Linux. На macOS и Windows его нет. Поэтому Docker Desktop запускает одну маленькую виртуальную машину с Linux. Все ваши контейнеры работают внутри неё. Это видно по команде docker run --rm alpine:3.22 uname -sr. Она покажет Linux даже на Mac.

Три главных слова:

СловоЧто этоКак связано
Образ (image)Файл с программой и всем, что ей нужно. Только для чтения. НеизменяемыйШаблон для контейнеров
Контейнер (container)Запущенная копия образаИз одного образа можно запустить много контейнеров
Реестр (registry)Хранилище образов. Например, Docker HubОткуда образы скачивают и куда загружают

Ещё одно слово: Dockerfile — текстовый файл с рецептом. В нём написано, как собрать образ. Мы напишем его в уроке Свой образ.

Путь от Dockerfile к образу и контейнерам: build собирает образ, push и pull работают с реестром, run запускает контейнеры

Образ и контейнер — разные вещи. Это важно:

  • Образ — это файл на диске. Он не «работает».
  • Контейнер — это процесс. Он работает, останавливается, удаляется.
  • Из образа postgres:17 можно запустить три контейнера Postgres. Образ при этом один.

Если вы знаете Python: образ похож на класс, а контейнер — на объект этого класса.

Образ состоит из слоёв. Команды RUN, COPY и ADD в Dockerfile создают слои с файлами: базовая система, Python, ваши библиотеки, ваш код.

  • Слои только для чтения.
  • Слои общие. Если у двух образов одинаковая база, она хранится на диске один раз.
  • Docker скачивает только те слои, которых у вас ещё нет.

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

Из этого следует важное правило: когда контейнер удаляют, слой записи удаляется вместе с ним. Данные пропадают. Как сохранить данные, мы разберём в уроке Тома.

Когда вы пишете docker run, работают две части:

  • CLI (docker) — программа, которую вы запускаете. Она только отправляет запросы.
  • Engine (демон dockerd) — программа в фоне. Она скачивает образы и запускает контейнеры.

Если Engine не запущен, CLI пишет Cannot connect to the Docker daemon. Мы видели это в уроке Установка.

Docker — не единственный инструмент. Формат образов и контейнеров стандартный (OCI). Образ, который вы собрали в Docker, запустят Podman, containerd и Kubernetes.

Работа с данными — это много разных инструментов. У каждого свои зависимости и своя версия Python или Java. Контейнеры дают каждому инструменту отдельную изолированную среду.

ИнструментКак работает в контейнерах
AirflowНесколько контейнеров: планировщик, веб-сервер, воркеры, база метаданных
dbtОбраз с нужной версией dbt. Запуск моделей как одноразовая команда
BI (Metabase, Superset)Один контейнер для BI и один для базы. Запуск за минуту
Postgres, MySQL, ClickHouseЛокальная база для разработки. Удаляется и создаётся заново за секунды
KafkaЛокальный брокер для проверки стриминга без кластера
SparkОбраз с Spark и нужными JAR-файлами. Одинаковая среда для разработки и кластера
JupyterНоутбук с нужными библиотеками. Не засоряет систему
ML (MLflow, модели)Модель и её зависимости в одном образе. Запуск сервиса предсказаний
Проверки качества данных (Great Expectations, Soda)Запуск проверок как одноразовая задача после загрузки
ETL-скрипт на PythonОбраз со скриптом. Запуск по расписанию в облаке (ECS, Cloud Run, Airflow)
Тесты и CIТестовая база создаётся на время теста и удаляется
Стек командыgit clone и docker compose up. Новый коллега работает в первый же день

Общая идея: контейнер — одноразовая и повторяемая среда. Сломали — удалили и создали заново.

Подробные примеры с командами — в уроке Open source за минуты.

  • Одноразовый запрос в DuckDB на вашем компьютере. Достаточно duckdb.
  • Продакшен-база с важными данными. Postgres в контейнере хорош для разработки. В продакшене чаще берут управляемую базу (Amazon RDS, Cloud SQL), чтобы не отвечать за бэкапы самому.
  • Программы с окном и мышью. Контейнеры лучше всего работают с серверными программами.

Для заданий нужен запущенный Docker. Первые команды docker run мы разберём подробно в следующем уроке. Сейчас просто выполните их.

Запустите Python 3.12 в контейнере и узнайте его версию. Затем узнайте версию Python на своём компьютере (python3 --version). Совпадают ли они?

Проверка: контейнер печатает Python 3.12.<число>. Версия на вашем компьютере может быть другой, или Python может не быть совсем. Контейнеру это всё равно.

Решение
Окно терминала
docker run --rm python:3.12-slim python --version
python3 --version

Узнайте, какая операционная система и какое ядро внутри контейнера Alpine. Затем сравните с вашей системой (uname -sr).

Проверка: контейнер отвечает Linux <версия>. Первое слово — всегда Linux, даже если у вас Mac.

Решение
Окно терминала
docker run --rm alpine:3.22 uname -sr
uname -sr

На macOS второй ответ начнётся с Darwin. Ядро контейнера — Linux из виртуальной машины Docker Desktop.

Файл в контейнере исчезает вместе с ним. Проверьте это. Создайте файл /tmp/f внутри контейнера и покажите содержимое /tmp. Затем запустите новый контейнер из того же образа и снова покажите /tmp.

Проверка: первый контейнер показывает f. Второй не показывает ничего, папка пустая.

Решение
Окно терминала
docker run --rm alpine:3.22 sh -c 'echo test > /tmp/f && ls /tmp'
docker run --rm alpine:3.22 ls /tmp

Каждый контейнер — новая копия образа со своим слоем записи. Файл из первого контейнера пропал вместе с ним.

Ответьте без команд. Это образ или контейнер?

  1. postgres:17 на Docker Hub.
  2. Процесс Postgres, который сейчас слушает порт 5432.
  3. Файл на диске, который Docker скачал командой pull.
  4. То, что показывает docker ps.

Проверка: образ, контейнер, образ, контейнер.

Объяснение

Образ — это шаблон, он лежит на диске и не работает. Контейнер — запущенный процесс. docker ps показывает только контейнеры. Образы показывает docker images.

Дальше: Первые контейнеры.