open atlas
↑ К треку
Docker: контейнеры как система DOCK · 06 · 01

Модель Compose: проект, сеть и сервисы, находящие друг друга по имени

Compose превращает топологию из нескольких контейнеров в декларативный YAML: namespace проекта, сеть по умолчанию с DNS по имени сервиса, именованные тома и модель сходимости к желаемому состоянию. Файл — это локальное окружение, а не вики из команд run.

DOCK Senior ◷ 16 min
Уровень
ОсновыJuniorMiddleSenior

Документ для онбординга назывался «Как поднять стек локально» и содержал 41 нумерованный шаг: создай сеть руками, запусти Postgres с правильным портом и паролем, запусти Redis, собери образ API, запусти его с одиннадцатью флагами окружения, указывающими на двух других по IP, потом воркер, потом фронтенд. Каждый новичок терял на этом день, а страница плыла — шаг 17 всё ещё ссылался на имя контейнера, переименованное в шаге 9. Staff-инженер удалил всю страницу и заменил её одним файлом compose.yaml и одной командой. IP исчезли, потому что сервисы теперь находили друг друга по имени через сеть проекта, которую инструмент создавал сам. Одиннадцать флагов схлопнулись в блок environment. То, что было племенным знанием, размазанным по вики, стало диффом в системе контроля версий, который ревьюер мог прочитать сверху донизу. Выигрыш был не в меньшем числе нажатий клавиш — а в том, что окружение перестало быть последовательностью команд, которую кто-то должен выполнить правильно, и стало описанием желаемого состояния, к которому инструмент сходится.

Проект — это namespace

Файл Compose описывает один проект, и имя проекта (по умолчанию имя каталога или -p) — это префикс на всём, что Compose создаёт: контейнерах, сетях, томах, метках. Этот namespace и делает compose up и compose down идемпотентными — Compose ищет ресурсы по метке com.docker.compose.project, а не угадывает, поэтому повторный up сверяется с существующим проектом, а не плодит дубли. Две копии одного репозитория в двух каталогах дают два независимых проекта с двумя сетями и двумя томами БД, без коллизий. Модель — это сходимость к желаемому состоянию, а не скрипт: ты объявляешь набор сервисов, которые хочешь видеть запущенными, а up сравнивает это с тем, что есть, и делает минимум изменений — стартует недостающее, пересоздаёт изменённое, оставляет совпадающее. Поэтому правка образа одного сервиса и повторный up пересоздают именно этот контейнер, а не весь стек.

# compose.yaml — всё локальное окружение как данные
services:
  api:
    build: .
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/app   # "db" — DNS-имя
      REDIS_URL: redis://cache:6379
    ports:
      - "8080:8080"
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data   # именованный том переживает `down`
  cache:
    image: redis:7

volumes:
  pgdata:

Сервисы находят друг друга по имени, а не по IP

Когда ты запускаешь compose up, Compose создаёт для проекта сеть по умолчанию (с именем <project>_default) и подключает к ней каждый сервис. В этой сети Docker поднимает встроенный DNS-сервер, и каждый сервис разрешается по имени сервисаapi достаёт Postgres по хосту db, порт 5432, без единого IP в конфиге. Это главная причина, по которой вики из команд run исчезает: хрупкой частью ручной оркестрации всегда была сшивка контейнеров по адресам, а адреса меняются при каждом перезапуске. DNS по имени сервиса делает связи стабильными и читаемыми. Два момента кусаются на senior-уровне: DNS-имя — это ключ сервиса, и container_name его не меняет — переопредели имя контейнера, и DNS-запись всё равно db. И опубликованный маппинг ports: ("8080:8080") нужен, чтобы хост достучался до контейнера; трафик между сервисами идёт прямо на порт контейнера через сеть проекта и вовсе не требует записи ports:. Частая ошибка — публиковать порт БД на хост, «чтобы API мог достучаться» — API достучится по имени через сеть в любом случае; публикация лишь открывает БД твоему ноутбуку.

Викторина

В файле выше сервис api подключается к Postgres по хосту db. Откуда берётся это имя и что его разрешает?

Почему это работает

Почему сходимость лучше стартового скрипта? Скрипт кодирует путь — сделай это, потом то, — а путь можно выполнить наполовину: он умирает на шаге 17 и оставляет окружение в состоянии, которое никто не проектировал. Сходимость кодирует цель: up всегда ведёт к объявленному набору, поэтому повтор прерванного up доводит дело до конца, а не множит хаос, а down с последующим up — чистая пересборка, ведь желаемое состояние — единственный источник истины. Файл говорит, что должно быть правдой, а задача инструмента — привести реальность в соответствие — та же ментальная модель, что у манифестов Kubernetes или Terraform, освоенная дёшево на ноутбуке.

Именованные тома — это долговечный слой

Контейнеры одноразовы; данные под ними — обычно нет. Именованный том (pgdata выше) — это управляемый Compose, привязанный к проекту объект хранения, переживающий пересоздание контейнера: compose up после смены образа пересоздаёт контейнер db, но заново монтирует тот же pgdata, так что локальная база сохраняет строки. Острые края — это разрушающие команды: обычный compose down удаляет контейнеры и сеть по умолчанию, но сохраняет именованные тома, а compose down -v их удаляет — этот флаг и есть разница между «перезапустить стек» и «стереть локальную базу». Анонимные тома (голый путь без имени) — ловушка: они создаются заново при каждом пересоздании и осиротевают, поэтому сервис, который должен хранить данные, но использует анонимный том, тихо их теряет на следующем up. Дай тому имя, смонтируй его в путь данных — и down/up можно гонять весь день безопасно.

Викторина

Разработчик делает compose down, затем compose up после смены тега образа Postgres и удивлён, что его засеянные локальные данные пропали. Какой один факт объясняет наиболее вероятную причину?

Вспомните перед уходом
  1. 01
    Объясни, как один сервис Compose достигает другого без единого IP в конфигурации, и одну тонкость, которая удивляет.
  2. 02
    Сопоставь стартовый скрипт с моделью сходимости Compose и объясни, как именованные тома взаимодействуют с down и down -v.
Итог

Compose схлопывает локальное окружение из нескольких контейнеров с вики из команд run в один декларативный файл, описывающий один проект. Имя проекта даёт namespace всему, что Compose создаёт, и делает up и down идемпотентными: up сравнивает объявленное желаемое состояние с тем, что есть, и делает минимум изменений, поэтому правка одного сервиса пересоздаёт один контейнер, а не стек. Сервисы находят друг друга через сеть проекта по умолчанию, где встроенный DNS Docker разрешает каждый сервис по его ключу — api достаёт Postgres по хосту db, без единого IP, и container_name это DNS-имя не меняет. Опубликованные порты — только для хоста; трафик между сервисами идёт на порт контейнера через сеть проекта, так что публикация порта БД — это открытие, а не требование. Именованные тома — долговечный слой, переживающий пересоздание контейнера; обычный compose down их сохраняет, а compose down -v удаляет, тогда как анонимные тома пересоздаются пустыми на каждом up и тихо теряют данные. Выигрыш концептуальный, а не косметический: окружение становится доступным для ревью, версионируемым описанием желаемого состояния — той же ментальной моделью, что у манифестов Kubernetes, освоенной на ноутбуке. Теперь, когда встретишь у коллеги документ «запусти эти двенадцать команд», ты знаешь, чем его заменить — и что именно перестанет ломаться после следующего переименования контейнера.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.