Колізії імен проєктів Docker Compose: зупиніть перезапис стеків один одним

Було корисно?

Все починається як нешкідливий «швидкий тест». Хтось запускає docker compose up -d у новому каталозі на хості, де вже працюють ще три інші Compose-стеки. Через п’ять хвилин дзвонить пейджер. Контейнер «пересоздали», ім’я мережі повторно використали, а том підмонтувався до невірного сервісу. Продакшен не просто відхилився — його пограбувала конвенція імен.

Compose дружній, поки ним не скористаються неправильно. Режим відмови тонкий: нічого не «падає» миттєво; натомість стеки тихо перекриваються, контейнери «усиновлюються», а down видаляє не те, що потрібно, із впевненістю малюка з ножицями.

Що таке «ім’я проєкту» в Compose насправді (і чому це боляче)

У Compose ім’я проєкту — це межа простору імен. Саме за ним Compose вирішує, які контейнери, мережі та томи належать «одному стеку». Якщо ви думаєте: «проект — це назва каталогу», ви наполовину праві. Та саме та половина і створює проблеми.

Compose використовує ім’я проєкту для генерації імен ресурсів і для додавання лейблів, якими потім користується для пошуку та управління стеком. Це означає:

  • Імена контейнерів часто виглядають як <project>_<service>_1 (або з дефісами, залежно від версії Compose і налаштувань).
  • Стандартні імена мереж часто виглядають як <project>_default.
  • Іменовані томи часто стають <project>_<volume>, якщо ви явно не задали інше ім’я.
  • До об’єктів додаються лейбли, як-от com.docker.compose.project та com.docker.compose.service, які Compose використовує згодом під час up, down, ps та очистки.

Колізія імен проєкту — це ситуація, коли два запуски Compose використовують одне й те саме ім’я проєкту (умисно або ні) на одному й тому самому Docker-демоні. Compose тоді трактує їх як один стек. Воно охоче «узгоджує» стан: пересоздає контейнери, приєднує мережі, повторно використовує томи або видаляє ресурси, потрібні іншому стеку.

Те, що дивує людей: колізіям не потрібні ідентичні Compose-файли. Достатньо однакового імені проєкту та перекриття імен/лейблів ресурсів. Compose — не шедулер кластера. Це механізм узгодження стану, який вважає, що ви маєте на увазі те, що ввели.

Жарт №1: Імена проєктів Docker Compose як наклейки в офісній кухні: якщо двоє напишуть «Lunch», хтось отримає порцію суму.

Звідки береться ім’я проєкту (пріоритет має значення)

Compose обирає ім’я проєкту за порядком пріоритетів. Якщо ви навмисно його не вказали, ви дозволяєте середовищу робити вибір за вас — оптимістичний підхід для продакшену.

Поширені джерела, приблизно від «найявнішого» до «найменш явного»:

  • docker compose -p <name>
  • COMPOSE_PROJECT_NAME змінна середовища
  • Верхній рівень name: у Compose-файлі (підтримується сучасним Compose v2)
  • Базова назва каталогу проєкту (зазвичай поточний каталог)

Якщо в організації кілька репозиторіїв з назвою app, кілька каталогів з назвою docker або звичка клонувати в /srv без унікальних імен папок, ви вже створили машину для колізій. Додайте CI-ранери, що використовують однаковий шлях для checkout, — і ви зможете спричиняти відмови на замовлення.

Чому відбуваються колізії: три рівні іменування

Щоб зупинити колізії, треба розуміти, що саме колідується. Стеки Compose перетинаються трьома основними способами:

1) Простір імен проєкту Compose (лейбли та пошук)

Compose знаходить «свої» ресурси за лейблами. Головний з них — com.docker.compose.project. Якщо два стеки мають одне й те саме ім’я проєкту, Compose обере ресурси з обох під час операцій, як-от down, restart і інколи up з видаленням сиріт.

Саме тут відбувається «перезаписування» стеків. Compose не перезаписує файли; воно узгоджує визначення контейнера з набором об’єктів, які мають лейбли одного проєкту.

2) Імена об’єктів Docker (контейнери, мережі, томи)

Навіть якщо лейбли в порядку, імена об’єктів Docker можуть колідувати, якщо ви їх жорстко задали:

  • Жорстко задані імена контейнерів через container_name: глобальні на демоні. Два стеки не можуть одночасно мати container_name: postgres. Один з них зазнає помилки або, гірше, ви вручну вирішите це й забудете, хто власник.
  • Жорстко задані імена мереж через networks: default: name: foo можуть зробити так, що окремі проєкти поділяють мережу. Це може бути свідомо, а може стати витоком даних.
  • Жорстко задані імена томів можуть змусити кілька проєктів спільно використовувати дані. Знову ж: інколи це свідомо, але частіше це «чому в staging дані виглядають як production?» тип свідомості.

3) Прив’язки портів та ресурси хоста

Навіть якщо іменування ідеальне, хост лишається один на всіх:

  • Два стеки, що прив’язують 0.0.0.0:80, будуть конфліктувати. Compose виведе помилку (найкращий варіант) або трафік піде не туди, куди треба (гірший сценарій: фронт-проксі вказує не на той бекенд).
  • Два стеки, що монтують той самий шлях хоста (наприклад /var/lib/app), можуть перезаписувати стан один одного.
  • Два стеки, що використовують ті самі шляхи секретів/конфігів на хості, можуть спричинити «працює на моїй машині» з продакшен-наслідком.

Найхитріші колізії — ті, де Docker дозволяє шаринг (томи, мережі), а Compose за замовчуванням це заохочує, доки ви не скажете інакше.

Цікаві факти та історичний контекст

Трохи контексту допомагає, бо поведінка Compose не виникла з повітря. Ось конкретні факти, що пояснюють, чому ім’я проєкту й потужне, і небезпечне:

  1. Compose починався як «Fig» (era 2013–2014). Його раннє завдання було: «підняти кілька контейнерів з YAML», а не «керувати багатокористувацькими середовищами на одному демоні». Просторове іменування було прагматичним, а не параноїдальним.
  2. Compose v1 довгий час був інструментом на Python. Сучасний docker compose (v2) — це плагін CLI Docker, і деякі дефолти та формат імен змінилися під час переходу.
  3. Імена проєктів задумувалися як похідні від каталогів, бо це спрощувало локальну розробку: скопіював репо, запусти Compose, отримаєш ізольовані ресурси автоматично. На спільних хостах «автоматично» стає «випадково».
  4. Лейбли стали основним механізмом володіння, коли Docker еволюціонував. Compose багато покладається на лейбли, як-от com.docker.compose.project, бо імена контейнерів самі по собі ненадійні, коли користувачі їх налаштовують.
  5. container_name: навмисно не рекомендують у багатьох продакшен-патернах, бо це ламає масштабування і підвищує ризик глобальних колізій імен. Compose не зможе створити кілька реплік сервісу з фіксованим ім’ям контейнера.
  6. Стандартна мережа на проєкт була великим UX-плюсом: вона знизила потребу в ручних зв’язках і дала сервіс-дискавері в межах проєкту. Але також зробила ім’я проєкту простором імен мережі.
  7. Очищення «сиріт»-контейнерів еволюціонувало з часом. Опції на кшталт --remove-orphans корисні, але коли ідентичність проєкту неправильна, вони стають бензопилою.
  8. Семантика down у Compose навмисно руйнівна: воно видаляє ресурси, які вважає створеними ним. Це коректна поведінка — якщо межа проєкту правильна.
  9. Swarm-стеки та проєкти Compose — різні поняття, але люди їх плутають, бо обидва використовують YAML і назву «stack». Ця плутанина спричиняє оперативні спрощення та помилки в іменуванні.

Три корпоративні міні-історії з колізійних окопів

Міні-історія 1: Інцидент через хибне припущення

У середньому SaaS-компанії команда провела «тимчасове» нагрузкове тестування на тому ж Docker-хості, де працювало staging-середовище. Вони були обережні: різні каталоги, різні Compose-файли, різні імена сервісів. Що могло перетнутися?

Припущення було: «Різні каталоги — різні стеки». Вони не передавали -p, не встановлювали COMPOSE_PROJECT_NAME, а робочий каталог для обох запусків випадково виявився /srv/app, бо скрипт автоматизації беззастережно робив cd туди.

У файлі load-test був сервіс Redis і воркер. У staging теж були Redis і воркери. Одне й те саме ім’я проєкту змусило Compose вважати, що це один проєкт. Коли почався тест навантаження, Compose «дружньо» пересоздав воркер-контейнер з образом тесту, бо ім’я сервісу співпадало під тим самим лейблом проєкту.

Нічого не впало одразу. Воркер стартував, але був не тим воркером — підключився до staging Redis та черги. Користувачі staging помітили, що завдання виконуються поза чергою, а деякі повідомлення «зникали» через несумісність формату. Інцидент не був крахом; він був корупцією поведінки. Саме такі інциденти крадуть у вас вихідні.

Впровадили нудне, але правильне: явні імена проєктів для кожного середовища і автоматизація, яка відмовляється запускати Compose без імені проєкту. Ключовий висновок постмортему: «Назви каталогів не дають ізоляції».

Міні-історія 2: Оптимізація, що обернулася проти

Платформна команда хотіла прискорити деплої. Вони помітили, що пересоздання Compose займає час на завантаженому хості, тож оптимізували, зафіксувавши імена об’єктів: явні container_name, явні імена мереж і томів. Ідея була — полегшити діагностику й зменшити «чейсинг» змін.

Механізм працював місяць. Потім дві внутрішні команди задеплоїли різні сервіси, які обидві використовували спільний «base» Compose-фрагмент. У фрагменті стояв container_name: api, бо хтось вважав це «чистим». Тепер два стеки намагалися створити контейнер з ім’ям api на тім самому демоні.

Compose відмовився стартувати другий стек, і команда «виправила» це, вручну видаливши перший контейнер і перезапустивши. Другий стек запрацював… і тихо вбив перший сервіс. Аутейдж не спричинив Docker; його спричинили люди, що боролися з рішенням щодо імен, яке позбавило Compose можливості безпечно керувати кількома інстансами.

Фінальний поворот: явне ім’я мережі спричинило крос-ток. Дебаг-контейнери з одного стеку могли резолвити й звертатися до сервісів іншого, бо були в тій самій мережі. Ніхто цього не планував. Просто сталося, бо «оптимізація» замінила ізоляцію зручністю.

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

Міні-історія 3: Нудна, але правильна практика, що врятувала

Регульована корпорація запускала Compose на флоті «унікальних» ВМ (так, буває). У них була звичка, яка здалася надмірною: кожен стек мав унікальне ім’я проєкту, ім’я включало середовище та власника, і це ім’я задавали в двох місцях: CI передавав -p, а в репозиторії був .env з COMPOSE_PROJECT_NAME для локальної роботи.

Одного дня інженер мав терміново зафіксувати проблему в staging і випадково запустив Compose з неправильного каталогу на хості. Compose-файл був правильним, але робочий каталог лежав в іншому репо. Без запобіжників це б повторно використало інше ім’я проєкту і почався би танець перекриттів.

Замість цього їхній wrapper для деплою вивів розв’язане ім’я проєкту і відмовився працювати, якщо воно не відповідало дозволеному списку для того хоста. Було просто: розпарсити docker compose config, перевірити ім’я, перевірити очікувані лейбли, потім запускати up. Інженер бурмотів, виправив команду і працював далі.

Через кілька тижнів, під час ретроспективи інциденту, вони зрозуміли, що wrapper запобіг колізії, яка б загострила відмову. Ніхто не писав героїчних постів у Slack. Ось у чому суть. Нудні контролі — те, що рятує від власних постмортемів.

Практичні завдання: 12+ команд, що виявляють колізії (і що робити далі)

Це не «приємно знати». Це те, що ви запускаєте, коли хтось каже «Compose перезаписав мій стек», і вам потрібні докази, а не відчуття. Кожне завдання містить: команду, що означає вивід, і рішення, яке ви приймаєте.

Завдання 1: Подивіться, яке ім’я проєкту Compose використає до того, як торкнеться чогось

cr0x@server:~$ cd /srv/staging/app
cr0x@server:~$ docker compose config --format json | head
{
  "name": "app",
  "services": {
...

Що це означає: Розв’язане ім’я проєкту — app. Якщо ви очікували staging-app, ви вже в зоні колізій.

Рішення: Якщо ім’я надто загальне, зупиніться і перезапустіть з -p або встановіть name:/COMPOSE_PROJECT_NAME. Не запускайте up.

Завдання 2: Підтвердіть ім’я проєкту, яке Compose використовує для вже запущеного стеку

cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}' | head
NAMES              COM.DOCKER.COMPOSE.PROJECT   COM.DOCKER.COMPOSE.SERVICE
app-web-1          app                          web
app-db-1           app                          db
payments-web-1     payments                     web

Що це означає: Перші два контейнери належать проєкту app. Третій — до payments. Якщо під одним лейблом проєкту ви бачите контейнери, які не очікувалися, маєте колізію.

Рішення: Знайдіть, який репо/автоматизація також використовує це ім’я проєкту. Карантинізуйте, обравши нове ім’я проєкту для одного з них.

Завдання 3: Перекличка проєктів Compose на демоні (швидка інвентаризація)

cr0x@server:~$ docker compose ls
NAME       STATUS              CONFIG FILES
app        running(2)          /srv/staging/app/docker-compose.yml
payments   running(1)          /srv/payments/docker-compose.yml

Що це означає: Compose думає, що є проєкт з назвою app і асоціює його з певним шляхом до файлу конфігурації. Якщо кілька розгортань використовують те саме ім’я проєкту, цей список вводить в оману або буде перемикатися між шляхами.

Рішення: Якщо проєкт показує несподіваний шлях конфіга, вважайте, що маєте колізію, і проведіть аудит останніх деплоїв.

Завдання 4: Перевірити лейбли підозрілого контейнера, щоб знайти власника

cr0x@server:~$ docker inspect app-web-1 --format '{{json .Config.Labels}}' | head
{"com.docker.compose.config-hash":"b1b4...","com.docker.compose.container-number":"1","com.docker.compose.oneoff":"False","com.docker.compose.project":"app","com.docker.compose.project.config_files":"docker-compose.yml","com.docker.compose.project.working_dir":"/srv/staging/app","com.docker.compose.service":"web"}

Що це означає: Контейнер каже, що його створили з /srv/staging/app. Якщо ви знайдете контейнери з тим самим лейблом проєкту, але різними working_dir, це означає два джерела, що призводять до одного проєкту.

Рішення: Оберіть одне джерело правди. Перейменуйте один проєкт і задеплойте чисто. Не дозволяйте репозиторіям «ділитися» тим самим лейблом проєкту.

Завдання 5: Виявити мережі, що є спільними (іноді ненавмисно)

cr0x@server:~$ docker network ls --format 'table {{.Name}}\t{{.Driver}}' | grep -E 'app|payments'
NAME              DRIVER
app_default       bridge
payments_default  bridge
shared_frontend   bridge

Що це означає: app_default — стандартна мережа для проєкту app. Якщо ви бачите два проєкти, приєднані до однієї мережі, це може бути умисно або витік.

Рішення: Якщо ви навмисно не створювали спільну мережу, не приєднуйте до неї кілька проєктів. Використовуйте за замовчуванням окремі мережі для кожного проєкту.

Завдання 6: Перевірити, які контейнери приєднані до мережі

cr0x@server:~$ docker network inspect app_default --format '{{json .Containers}}' | head
{"3c1f...":{"Name":"app-web-1","IPv4Address":"172.20.0.2/16"},"a77b...":{"Name":"app-db-1","IPv4Address":"172.20.0.3/16"}}

Що це означає: Лише app-web-1 і app-db-1 в app_default. Якби тут був payments-web-1, це був би крос-проєкт приєднання.

Рішення: Крос-проєкт приєднання вимагає розмови про безпеку й налагодження. Якщо це випадково, від’єднайте й задеплойте з виправленими мережевими налаштуваннями.

Завдання 7: Знайти томи, що виглядають як проєктні, але такими не є

cr0x@server:~$ docker volume ls --format 'table {{.Name}}\t{{.Driver}}' | grep -E '^app_|^payments_'
NAME          DRIVER
app_dbdata    local
payments_pg   local

Що це означає: Це іменовані томи, ймовірно створені Compose. Якщо два стеки використовують одне й те саме ім’я тома через явне name:, вони будуть ділити дані.

Рішення: Якщо шаринг томів не задокументовано й не намірений, перейменуйте томи й мігруйте дані. Ставтеся до випадкового шарингу томів як до інциденту з даними, поки не доведено інше.

Завдання 8: Підтвердити, які контейнери монтують заданий том

cr0x@server:~$ docker ps -a --filter volume=app_dbdata --format 'table {{.Names}}\t{{.Status}}'
NAMES      STATUS
app-db-1   Up 3 hours

Що це означає: Лише app-db-1 використовує app_dbdata. Якщо ви бачите контейнери з різних стеків, що використовують один том, маєте спільний стан.

Рішення: Зупиніться й оцініть цілісність даних. Розділіть томи перед будь-якою «очисткою».

Завдання 9: Виявити сирітські контейнери, які Compose може видалити

cr0x@server:~$ docker compose -p app ps
NAME       IMAGE          COMMAND                  SERVICE   CREATED        STATUS        PORTS
app-web-1  nginx:1.25     "/docker-entrypoint.…"   web       3 hours ago    Up 3 hours    0.0.0.0:8080->80/tcp
app-db-1   postgres:16    "docker-entrypoint.s…"   db        3 hours ago    Up 3 hours

Що це означає: Compose показує те, що вважає належним -p app. Якщо ви нещодавно змінили сервіси в YAML і тепер бачите несподівані контейнери з тим самим лейблом проєкту, --remove-orphans може видалити те, що вам ще потрібно.

Рішення: Не запускайте --remove-orphans, поки не підтвердите межі проєкту.

Завдання 10: Попередній перегляд того, що буде створено/пересоздано при зміні Compose

cr0x@server:~$ docker compose -p app up -d --no-start
[+] Running 2/2
 ✔ Network app_default  Created
 ✔ Container app-web-1  Created

Що це означає: --no-start все ще створює ресурси, але не запускає їх. Це безпечніший спосіб побачити, чи Compose збирається створювати об’єкти під невірним ім’ям проєкту.

Рішення: Якщо ви бачите створення ресурсів, що вже існують і належать іншому стеку, зупиніться й виправте ім’я проєкту перш ніж продовжувати.

Завдання 11: Доказати колізію, порівнявши хеші конфігів між контейнерами

cr0x@server:~$ docker inspect app-web-1 --format '{{.Config.Labels.com.docker.compose.config-hash}}'
b1b4c7f2d4a0...
cr0x@server:~$ docker inspect app-db-1 --format '{{.Config.Labels.com.docker.compose.config-hash}}'
b1b4c7f2d4a0...

Що це означає: Співпадіння хешів підказує, що контейнери створені з одного й того самого розв’язного Compose-конфіга. Якщо в межах одного «проєкту» ви бачите різні хеші для однакового покоління стеку, можливо, два різні Compose-конфи керують одним ім’ям проєкту в різний час.

Рішення: Заморозьте зміни. Вирішіть, який конфіг є авторитетним, і перевідкрийте деплой під новим ім’ям проєкту, якщо потрібно відокремити стан безпечно.

Завдання 12: Перевірити жорстко задані глобальні імена, що колідуватимуть

cr0x@server:~$ grep -R --line-number -E 'container_name:|name:' /srv/staging/app/docker-compose.yml
14:    container_name: postgres
33:  default:
34:    name: shared_frontend

Що це означає: container_name: postgres — глобальне ім’я контейнера. Мережа shared_frontend явно спільна між проєктами (або буде такою, якщо інший стек використає те саме ім’я).

Рішення: Видаліть container_name, якщо у вас немає дуже конкретної операційної потреби. Розглядайте явні імена мереж як рішення про безпеку, а не зручність.

Завдання 13: Підтвердити, яке ім’я проєкту Compose думає, що воно має, коли є .env

cr0x@server:~$ cat /srv/staging/app/.env
COMPOSE_PROJECT_NAME=app
cr0x@server:~$ docker compose config --format json | head
{
  "name": "app",
  "services": {
...

Що це означає: Ім’я проєкту форсується через .env. Люди забувають про ці файли, особливо під час реагування на інциденти.

Рішення: Якщо значення надто загальне, змініть його й перевідкрийте деплой. Також перевірте, чи інші репозиторії не скопіювали той самий шаблон .env.

Завдання 14: Показати події під час «перезапису», щоб зафіксувати пересоздання й видалення

cr0x@server:~$ docker events --since 10m --filter type=container --filter event=destroy
2026-01-03T10:11:22.123456789Z container destroy 3c1f... (name=app-web-1, image=nginx:1.25)

Що це означає: Маєте доказ, що контейнер був знищений, коли і під яким іменем. Поєднайте це з логами деплою, щоб корелювати відповідальний запуск Compose.

Рішення: Якщо видалення сталося під час деплою з іншого репозиторію/шляху, у вас є колізія імен проєкту або оператор запустив down не в тому місці.

Завдання 15: Підтвердити, чи «down» націлений на правильний проєкт перед запуском

cr0x@server:~$ docker compose -p app ps --all
NAME       IMAGE          SERVICE   STATUS
app-web-1  nginx:1.25     web       Up 3 hours
app-db-1   postgres:16    db        Up 3 hours

Що це означає: Це ваш передпольовий чек. Якщо список містить контейнери, якими ви не планували керувати, down їх видалить.

Рішення: Якщо щось незнайоме, не запускайте down. Виправте іменування проєкту або використайте цільові операції над контейнерами.

Жарт №2: Запускати docker compose down з неправильним ім’ям проєкту — як звільнити стажера через непрацюючий сканер бейджів.

Швидка діагностика — план дій

Коли один стек «перезаписує» інший, часу на філософствування мало. Потрібен найкоротший шлях до: що змінилося, хто власник і як зупинити кровотечу.

Перше: встановіть простір імен проєкту, що колідує

  1. Запустіть docker compose ls і знайдіть підозрілі загальні імена (як-от app, backend, prod).
  2. Запустіть docker ps з виводом лейблів і підтвердьте, які контейнери ділять com.docker.compose.project.
  3. Проінспектуйте принаймні один контейнер на наявність com.docker.compose.project.working_dir. Це покаже, звідки його створено.

Друге: визначте, що саме спільне (мережі, томи, порти)

  1. Перелічіть мережі й інспектуйте мережу на наявність несподіваних контейнерів.
  2. Перелічіть томи і знайдіть, які контейнери їх монтують.
  3. Перевірте прив’язки портів у «перезаписаного» сервісу: якщо порт перемістився, трафік перемістився.

Третє: зупиніть автоматизацію і не дозволяйте подальшому узгодженню

  1. Призупиніть CI/CD джоби та заплановані скрипти деплою, що можуть знову запустити Compose.
  2. Не запускайте down, поки не доведете, що межа проєкту правильна.
  3. Якщо треба швидко стабілізувати сервіс, віддайте перевагу docker stop/docker start для конкретних контейнерів, поки розплутуєте власність.

Четверте: відновлення з чистим явним іменем проєкту

  1. Оберіть нове унікальне ім’я проєкту для стеку, який відновлюєте (стиль team-env-app).
  2. Розгорніть його поряд з іншим стеком з іншими портами (або через перемикання проксі) та ізольованими томами, якщо потрібна цілісність даних.
  3. Після відновлення акуратно очистіть ресурси колізійного проєкту.

Одна цитата, яку варто мати на стіні кожної опер-команди, бо вона пояснює, чому стандартизують нудні речі, як-от іменування: інциденти виникають через нормальну роботу, що взаємодіє несподіваними способами (парафраз ідеї Джона Оллспо).

Як запобігти колізіям імен проєктів (жорсткі правила, не відчуття)

Якщо ви запускаєте кілька Compose-стеків на одному демоні, вам потрібні явні, примусово застосовані імена. Не «не забувайте вказати -p». Примусово. Люди забувають. Автоматизація повторює.

Правило 1: Завжди явно задавайте унікальне ім’я проєкту

Оберіть один метод і стандартизуйтесь:

  • Рекомендовано для автоматизації: завжди використовуйте docker compose -p з обчисленим ім’ям (наприклад payments-staging).
  • Рекомендовано для ноутбуків розробника: верхній рівень name: у Compose-файлі або персональний .env, який включає ім’я користувача в імені проєкту.

Чого слід уникати: покладатися на назву каталогу, особливо на спільних хостах і в CI-ранерах.

Правило 2: Заборонити container_name:, якщо немає письмового виправдання

Воно ламає масштабування, ускладнює повторне використання між стеками й заохочує ручні docker-команди, що відводять стан від Compose.

Якщо потрібна стабільна DNS-адреса всередині мережі, у вас вже є сервісні імена на мережі проєкту. Якщо потрібна стабільна зовнішня інтеграція — використовуйте реверс-проксі, стабільну доменну адресу або мережевий псевдонім, а не глобальне ім’я контейнера.

Правило 3: Розглядайте явні імена мереж і томів як спільну інфраструктуру

Якщо ви задаєте name: для мережі або тому, ви виходите за межі простору імен проєкту Compose. Це може бути правильно. Але також може стати випадковим містком між проектами.

Зробіть політику: явні імена повинні включати середовище і власника, наприклад shared_frontend_prod або payments_pg_prod. «shared» має означати «підлягає рев’ю», а не «хтось колись набрав це».

Правило 4: Зробіть «передпольовий» чек обов’язковим у скриптах деплою

Ваш wrapper має виводити розв’язане ім’я проєкту і фейлити швидко, якщо воно не те, що очікуєте. Це можна зробити без побудови платформи:

  • Розв’язати конфіг (docker compose config)
  • Перевірити отримане ім’я
  • Перевірити, що docker compose -p NAME ps відповідає очікуваним сервісам (або порожній для першого деплою)
  • Потім виконати up -d

Правило 5: Розділяйте середовища за демонами, коли можете

Так, ви можете запускати продакшен і staging на одному хості. Але також можна їхати велосипедом через мийку для авто. Якщо ви мусите співмешкати, дисципліна іменування стає безальтернативною, і треба ізолювати порти, томи й мережі з уявною метою.

Типові помилки: симптом → корінь → виправлення

Це розділ, який ви пошкодуєте, якщо не прочитаєте до інциденту. Кожен пункт конкретний і показує важіль для виправлення.

1) Симптом: docker compose up «пересоздав» контейнер з іншого стеку

Корінь: те саме ім’я проєкту, те саме ім’я сервісу. Compose узгодив і замінив визначення контейнера.

Виправлення: Використовуйте унікальні імена проєктів через -p, COMPOSE_PROJECT_NAME або верхній name:. Перезапустіть перезаписаний стек під новим іменем.

2) Симптом: запуск docker compose down видалив контейнери, які ви не планували

Корінь: ви виконали down з ім’ям проєкту, що співпадає з кількома розгортаннями; Compose видалив усе, що мало цей лейбл.

Виправлення: Перед down завжди робіть docker compose -p NAME ps --all. Якщо список неправильний, зупиніться й виправте ім’я проєкту.

3) Симптом: сервіс доступний з іншого «непов’язаного» стеку

Корінь: спільна користувацька мережа через явне ім’я мережі або ручні docker network connect.

Виправлення: Приберіть явні спільні мережі, якщо це не умисно; проведіть аудит через docker network inspect для приєднань; перевідкрийте з ізольованими мережами.

4) Симптом: у staging БД дані виглядають як production

Корінь: спільний іменований том через явне name: або однакове ім’я проєкту, що призвело до однакового згенерованого імені тому.

Виправлення: Розділяйте томи для кожного середовища. Якщо треба клонувати дані — робіть це через бекап/відновлення, а не випадковий шаринг томів.

5) Симптом: Compose відмовляється стартувати з «Conflict. The container name is already in use»

Корінь: container_name: примушує глобальне ім’я. Інший контейнер уже його використовує.

Виправлення: Приберіть container_name:. Якщо вам потрібна стабільна адреса, використовуйте сервісні DNS-імена або проксі.

6) Симптом: деплой вчора працював, сьогодні падає на тому ж хості

Корінь: інша команда ввела колізію імен проєкту або імен мереж; ваш стек тепер ділить простір імен чи порти.

Виправлення: Проведіть інвентаризацію з docker compose ls, перевірте лейбли та впровадьте конвенцію імен. Додайте запобіжники в CI.

7) Симптом: --remove-orphans видалив щось «важливе»

Корінь: межа проєкту неправильна; Compose вважав «важливий» контейнер сиротою для цього проєкту.

Виправлення: Не використовуйте --remove-orphans на спільних хостах, поки іменування проєктів не впроваджене й не перевірено. Краще робіть явну очистку після валідації.

Чеклисти / покроковий план

Чеклист A: Безпечний стандарт для кожного розгортання Compose

  1. Оберіть схему іменування проєкту: <team>-<env>-<app>. Тримайте її короткою, унікальною і зрозумілою людям.
  2. Примусуйте її в автоматизації: скрипти деплою завжди викликають docker compose -p "$NAME".
  3. Примусуйте її в репо: додайте .env.example з COMPOSE_PROJECT_NAME, який задає безпечний шаблон для локальної розробки (включайте ім’я користувача).
  4. Забороніть container_name: політикою, якщо не пройшло рев’ю.
  5. Аудит явних імен мереж/томів: будь-який name: має розглядатися як інфраструктура.
  6. Передпольовий чек перед змінами: docker compose -p NAME config та docker compose -p NAME ps мають виглядати нормально.

Чеклист B: Покрокове відновлення після колізії

  1. Зупиніть дериват: призупиніть пайплайни деплою та планові завдання, що можуть знову запускати Compose.
  2. Ідентифікуйте ім’я проєкту, що колідує: використовуйте лейбли, щоб підтвердити, які контейнери ділять com.docker.compose.project.
  3. Зробіть знімок стану, якщо дані задіяні: якщо томи спільні, зробіть бекап перед зміною.
  4. Підніміть чистий стек під новим ім’ям проєкту: використовуйте ізольовані мережі й томи, якщо не плануєте повторно використовувати дані.
  5. Перенаправте трафік: оновіть конфіг реверс-проксі або прив’язки портів, щоб спрямувати на відновлений стек.
  6. Обережно очистіть колізійний стек: видаляйте лише те, що ви можете довести невірним; уникайте сліпого down, якщо володіння змішане.
  7. Додайте запобіжник: wrapper деплою, що виводить розв’язане ім’я проєкту і відмовляється, якщо воно не відповідає очікуванням для хоста.

Чеклист C: Гігієна хоста для спільних Docker-демонів

  1. Інвентаризація проєктів щотижня: docker compose ls і перевірка загальних імен.
  2. Інвентаризація завислих мереж/томів: підтвердьте, що явні імена мають власників.
  3. Документуйте спільні мережі: якщо вам дійсно потрібна комунікація між стеками, трактуйте спільну мережу як контрольований інтерфейс.
  4. Тримайте середовища роздільно: якщо можливо, розділяйте демони/хости для проду й staging.

FAQ

1) Що саме «перезаписується» під час колізії імен проєкту?

Compose не перезаписує YAML. Воно узгоджує Docker-об’єкти під одним лейблом проєкту. Це може означати пересоздання контейнерів з іншими образами/змінними середовища, спільні мережі та повторне використання томів.

2) Якщо я використовую різні Compose-файли, чи можу я все одно колізити?

Так. Достатньо однакового імені проєкту. Різні конфіги можуть генерувати ресурси в тому самому просторові імен проєкту, особливо коли імена сервісів співпадають.

3) Чи завжди назва каталогу є дефолтним іменем проєкту?

Часто так, але не завжди. .env, COMPOSE_PROJECT_NAME або верхній name: можуть переоприділити його. Саме тому перед деплоєм треба перевіряти docker compose config.

4) Де краще задавати ім’я проєкту — у .env чи передавати -p?

Для CI/CD та продакшену: віддавайте перевагу -p, щоб система деплою контролювала це. Для розробників: .env підходить, але додайте ім’я користувача, щоб уникнути колізій на спільних дев-хостах.

5) Чому container_name вважають шкідливим?

Воно примушує глобальне ім’я на демоні, ламає масштабування і знижує портативність стеків. Також спонукає до ручного втручання — саме там «тимчасові виправлення» перетворюються на тривалі відмови.

6) Чи можна умисно ділити мережу або том між проєктами?

Можна, і інколи це потрібно (наприклад, для спільної мережі реверс-проксі). Але ставте це як контракт інтерфейсу: явно назвіть, документуйте й переглядайте приєднання. Не робіть цього «бо так працювало».

7) Як визначити, яке репо створило контейнер?

Інспектуйте лейбли. Шукайте com.docker.compose.project.working_dir і com.docker.compose.project.config_files. Зазвичай це прямо вкаже шлях, використаний при створенні.

8) Чи безпечний docker compose down?

Воно безпечне, коли межа проєкту правильна. На спільних хостах із нестриманим іменуванням — це інструмент руйнування. Завжди робіть docker compose -p NAME ps --all перед запуском.

9) Чи зникне ця проблема, якщо перейти на Kubernetes/Swarm?

Ви просто поміняєте цей конкретний режим відмови на інші. Неймспейси в Kubernetes допомагають, але все одно можна зіткнутися з колізіями через спільні томи, кластерні ресурси та неправильно налаштовані селектори. Дисципліна нікуди не дівається.

Висновок: наступні кроки, які можна відправити в реліз сьогодні

Колізії імен проєктів Compose — це не дивний випадок. Це передбачуваний результат, коли дефолти вирішують за вас межі ізоляції. Виправлення просте, але його треба впровадити, а не просто пропонувати.

Зробіть наступне:

  1. Оберіть конвенцію імен проєктів, яка кодує команда + середовище + додаток.
  2. Оновіть кожну команду деплою, щоб явно передавати -p.
  3. Аудитуйте Compose-файли на предмет container_name: та явних name: для мереж/томів; видаліть або формалізуйте їх.
  4. Додайте передпольовий чек в автоматизацію, що виводить розв’язане ім’я проєкту і відмовляється, якщо воно загальне або неочікуване.
  5. Запустіть інвентаризаційні команди з цієї статті на вашому найзавантаженішому хості і знайдіть колізії до того, як вони знайдуть вас.
← Попередня
Адаптивні вбудовування, які не ламають макет: YouTube, iframes і карти в продакшн
Наступна →
Пагінація проти нескінченного скролу: шаблони інтерфейсу, що не дратують користувачів

Залишити коментар