«invalid reference format» — це спосіб Docker сказати: «Я не можу розібрати те, що ви вважаєте ім’ям образу». Це не проблема мережі. Це не проблема автентифікації. Зазвичай це навіть не проблема Docker.
Це проблема рядка. Невелика. Така, що доходить до продакшену, бо виглядає нормально на рев’ю та ламається лише коли змінна пуста, в рядок проникає пробіл або тег форматують добренамірений крок збірки.
Що насправді означає помилка (і чому вона така нерелевантна)
Docker викидає «invalid reference format», коли намагається розібрати посилання на образ (або іноді ім’я контейнера, але переважно це образ) і рядок не відповідає тому, що Docker вважає дійсним посиланням.
Проблема в тому, що Docker повідомляє про помилку на неправильному рівні абстракції. Ви думаєте: «Я запускаю контейнер». Docker думає: «Я розбираю посилання за певною граматикою». Та ця граматика сувора і, в деяких місцях, несподівано догматична:
- Компоненти репозиторію здебільшого мають бути в нижньому регістрі.
- Теги мають власні правила щодо символів і обмеження довжини.
- Дайджести — це зовсім інша синтаксична форма і їх не слід змішувати з тегами бездумно.
- Пробіли не «ігноруються». Пробіли — це проблема.
Найпоширеніші винуватці в реальних системах:
- Приховані пробіли (з копіювання/вставки, форматування YAML, Windows-рядки завершення або скрипт, що echo-ить з кінцевим новим рядком).
- Порожні змінні, які зливають два роздільники в щось нелегальне (наприклад
myapp:абоrepo//image). - Великі літери в іменах репозиторіїв (Docker це за замовчуванням суворо трактує).
- Неправильно розташовані двокрапки, коли одночасно вказані порт реєстру й тег.
- Розумні лапки і не-ASCII пунктуація з чатів і систем трекінгу.
Практичне ставлення: ставтеся до посилання на образ як до введення, яке потрібно перевірити й нормалізувати, як URL. Тому що фактично воно таким і є — просто з більшою кількістю нюансів і меншою кількістю корисних повідомлень про помилки.
Короткий жарт #1: Docker каже «invalid reference format» так само, як тостер каже «bread error». Технічно правильно, емоційно марно.
Перш ніж перейти до тактики, давайте визначимо, що Docker вважає посиланням. Як тільки ви це зрозумієте, 90% таких інцидентів перестануть бути таємничими й стануть продуктивно незручними.
Фактична граматика посилання на образ
Посилання на образ Docker не є довільними рядками. Вони слідують структурі, реалізованій парсером distribution/reference, що використовується по всій екосистемі (Docker Engine, реєстри, інструменти). Концептуально:
- Ім’я (за потреби з хостом реєстру та простором імен)
- Опціональний тег, що позначається
: - Опціональний дайджест, що позначається
@
Ментальна модель, яку я використовую в інцидент-респонсі:
[registryhost[:port]/]path/name[:tag][@digest]- Де
path/name— це один або кілька компонентів, розділених слешем. - Де
:tagта@digestчасто є взаємовиключними в багатьох робочих процесах (технічно деякі контексти дозволяють обидва, але не робіть цього, якщо ви не точно розумієте, як інструмент їх обробляє).
Що дозволено в імені репозиторію?
Імена репозиторіїв зазвичай повинні бути в нижньому регістрі і можуть містити роздільники як _, . і - в певних позиціях. Слеші розділяють компоненти шляху. Найбезпечніше правило для продакшену простіше за специфікацію:
- Використовуйте малі літери, цифри, дефіси та слеші для шляху репозиторію.
- Не використовуйте великі літери. Не сперечайтеся з цим — ви витратите час і гідність.
Що дозволено в тегу?
Теги більш дозволені, ніж імена репозиторіїв, але теж не довільні. Найчастіші помилки, які я бачу:
- Включення слеша в тег (
feature/new-ui), бо гілки Git часто містять слеші. - Включення пробілів або символів нового рядка, зазвичай через змінні.
- Початок тега з дефіса в деяких контекстах інструментів (не завжди заборонено, але часто проблематично при передачі через скрипти).
Якщо потрібно зіставити назви гілок із тегами — санітизируйте їх. Не сподівайтеся, що Docker прийме їх як є.
Хости реєстрів і порти: пастка двокрапки
Двокрапка може означати «порт» або «тег», і Docker вирішує значення за контекстом:
registry.example.com:5000/team/app:1.2.3— дійсне (порт, потім тег).team/app:5000— також дійсне (тег — 5000).registry.example.com:team/app:1.2.3— недійсне (порт має бути числовим).
Коли ви бачите «invalid reference format» і в рядку є двокрапка, перевірте випадок з «двома двокрапками» і визначте, яка з них мала б бути портом.
Дайджести: зафіксовані образи, інший синтаксис
Дайджести виглядають як @sha256:<hex>. Вони чудові для відтворюваності і жахливі для людей. Якщо ви генеруєте їх у пайплайні, переконайтеся, що випадково не додаєте пробіли або не обрізаєте їх. Один відсутній символ перетворює фіксацію в помилку парсера.
Цитата, за якою живуть операційники
Надія — не стратегія.
— генерал Gordon R. Sullivan
Ось таке мислення тут корисне. Не «надійтеся», що ваш тег валідний. Перевіряйте його. Нормалізуйте його. І падайте раніше з власною зрозумілою помилкою, ніж дозволяти парсеру Docker засмічувати тікети.
Цікаві факти та історичний контекст (бо правила не з’явилися вчора)
Це дрібниці, але вони пояснюють, чому правила іменування виглядають дивно суворими і чому та сама помилка з’являється в різних інструментах.
- Іменування образів передувало теперішній популярності Docker. Правила еволюціонували разом із ранніми реалізаціями реєстрів, яким потрібні були детерміновані шляхи і кешування.
- Нижній регістр у назвах репозиторіїв — частково ставка на сумісність. Файлові системи з нечутливістю до регістру (і люди) ускладнюють змішані регістри, тож екосистема схиляється до нижнього регістру.
- Парсер «reference» спільний. Багато інструментів покладаються на одну й ту ж логіку парсингу, тому «invalid reference format» з’являється в Docker, Compose та інших інструментах з однаковими кореневими причинами.
- Теги призначалися для легких міток, а не для дампів метаданих. Люди все ще вміщують у теги назви гілок, часи та контекст збірки, а потім дивуються обмеженням символів чи забороненим роздільникам.
- Дайджести існують, щоб вирішити «дрейф latest». Синтаксис дайджесту набув значення, коли команди усвідомили, що теги можна переміщувати, а «latest» — це куля з сюрпризом у смокінгу.
- Синтаксис хоста реєстру + порт запозичений з URL-конвенцій. Непевність двокрапки (порт vs тег) — це неминуща ціна за зрозумілі посилання.
- Docker Hub вплинув на значення за замовчуванням імен. Припущення про
library/для офіційних образів і короткі імена якnginxсформували способи написання коротких імен. - Compose зробив проблему голоснішою. YAML додає цитування, пробіли й інтерполяцію змінних — добре для конфігурації, але відмінно приховує опечатки.
- CI пайплайни зробили теги динамічнішими. Як тільки теги стали функцією змінних оточення, відмови через порожні рядки стали щоденним явищем.
Швидкий план діагностики
Ось порядок дій, який я використовую при виклику. Він оптимізований під «знайти вузьке місце швидко», а не під вишуканість.
Перше: зафіксуйте точний рядок посилання, який парсить Docker
- Скопіюйте повну команду так, як вона виконувалася, а не те, що ви думаєте, що виконували.
- Якщо це в CI, виведіть команду зі змінними після інтерполяції (безпечно) або echo лише обчисленого посилання на образ.
- Шукайте пробіли, нові рядки та невидимі символи.
Друге: визначте, який токен є посиланням на образ
- У
docker runце перший нефлаговий аргумент після опцій. - У
docker buildце зазвичай значення-t. - У Compose це під
image:або виводиться зbuild:+ іменування проекту.
Третє: зведіть до мінімального відтворювача
- Замініть змінні на буквальні значення.
- Приберіть усе, крім посилання на образ.
- Спробуйте
docker image inspectабоdocker pullна тому ж посиланні. Якщо парсинг ламається — ви ізолювали проблему.
Четверте: перевірте ці основні підозри в порядку
- Великі літери в шляху репозиторію.
- Тег містить слеш (
/) або пробіл. - Посилання закінчується на
:(порожній тег). - Подвійні роздільники:
//,@@,::не в тому місці. - Плутанина хоста реєстру:порт (нечисловий порт, відсутній слеш після хоста).
- Проблеми з екрануванням шелу та переносами рядків.
Якщо робити лише одну річ: виведіть посилання на образ інструментом, що показує невидимі символи. Більшість «таємничих» випадків закінчуються саме тут.
Практичні завдання: команди, виводи та рішення (12+)
Це перевірені в полі кроки. Кожне завдання включає: команду, що означає її вивід, і рішення, яке слід прийняти. Запускайте їх локально або на CI-ранері, який падає. Використайте їх, щоб перетворити розмите повідомлення про помилку на конкретне виправлення.
Завдання 1: Відтворіть помилку парсингу за допомогою docker pull
cr0x@server:~$ docker pull 'MyTeam/MyApp:1.0.0'
invalid reference format
Що це означає: Docker відкинув посилання ще до звернення до реєстру. Великі літери в шляху репозиторію — поширений тригер.
Рішення: Нормалізуйте імена репозиторіїв до нижнього регістру в скриптах збірки/публікації (myteam/myapp:1.0.0).
Завдання 2: Виявлення прихованих пробілів в обчисленому рядку образу
cr0x@server:~$ IMAGE_REF="registry.local/team/app:${TAG} "
cr0x@server:~$ printf '%q\n' "$IMAGE_REF"
registry.local/team/app:${TAG}\
Що це означає: Трейлінг-пробіл реальний. Docker вважатиме його частиною посилання і провалить парсинг.
Рішення: Обрізайте або санітизуйте змінні. Не конкатенуйте з ненадійними входами. Використовуйте printf замість echo у скриптах.
Завдання 3: Підтвердіть, що змінна пуста (класичний CI-фейл)
cr0x@server:~$ TAG=""
cr0x@server:~$ docker build -t "team/app:${TAG}" .
invalid reference format
Що це означає: Порожній тег створив нелегальне посилання, що закінчується на :.
Рішення: Падайте раніше: застосуйте ${TAG:?TAG must be set} або за замовчуванням використайте безпечний тег, наприклад dev.
Завдання 4: Показати, як назви гілок ламають теги
cr0x@server:~$ BRANCH="feature/new-ui"
cr0x@server:~$ docker tag alpine:3.20 "team/app:${BRANCH}"
Error parsing reference: "team/app:feature/new-ui" is not a valid repository/tag: invalid reference format
Що це означає: Теги не можуть містити слешів. Гілки Git часто їх мають.
Рішення: Санітизуйте назви гілок (замініть / на -, видаліть заборонені символи) перед формуванням тега.
Завдання 5: Перевірте свою санітизацію в шеллі
cr0x@server:~$ BRANCH="feature/new-ui"
cr0x@server:~$ SAFE_TAG="$(printf '%s' "$BRANCH" | tr '[:upper:]' '[:lower:]' | sed 's#[^a-z0-9_.-]#-#g')"
cr0x@server:~$ printf '%s\n' "$SAFE_TAG"
feature-new-ui
Що це означає: Ви перетворили недійсний тег у валідний на вигляд.
Рішення: Використовуйте спільну функцію/бібліотеку для санітизації тегів у всіх пайплайнах. Не пишіть різні реалізації в кожному репозиторії.
Завдання 6: Вловіть Windows CRLF-забруднення (так, це трапляється)
cr0x@server:~$ TAG="$(printf '1.2.3\r')"
cr0x@server:~$ printf '%q\n' "$TAG"
$'1.2.3\r'
Що це означає: У тегі є символ carriage return. Docker може викидати «invalid reference format» або поводитися непослідовно залежно від контексту.
Рішення: Видаляйте \r зі змінних CI та файлів: санітизуйте входи з Git, релізних інструментів та кроків на Windows.
Завдання 7: Розрізнення порту реєстру та двокрапкового тега
cr0x@server:~$ docker pull registry.local:5000/team/app:1.2.3
Error response from daemon: manifest for registry.local:5000/team/app:1.2.3 not found: manifest unknown
Що це означає: Посилання успішно розпарсилося. Тепер ви за межами форматування і у сфері реєстру/змісту (manifest not found).
Рішення: Перестаньте шукати опечатки у форматі посилання. Почніть перевіряти, чи існує тег у реєстрі.
Завдання 8: Викличте режим помилки «нечисловий порт»
cr0x@server:~$ docker pull registry.local:five000/team/app:1.2.3
invalid reference format
Що це означає: Docker намагався розпарсити registry.local:five000 як host:port і відкинув його, бо порт не є числовим.
Рішення: Аудитуйте шаблони, що складають імена хостів реєстрів. Не інтерполюйте «назви оточень» там, де очікується порт.
Завдання 9: Підтвердіть позицію токена посилання в docker run
cr0x@server:~$ docker run --rm -e FOO=bar 'team/app:1.0.0 '
Unable to find image 'team/app:1.0.0 ' locally
invalid reference format
Що це означає: Посилання містило трейлінг-пробіл. Docker спочатку виводить його як є, потім падає при парсингу/пуллі.
Рішення: Перестаньте копіювати команди з відформатованих документів/чатів без перевірки останнього символу. Також: цитуйте змінні, а не весь складений рядок з невідомими пробілами.
Завдання 10: Використовуйте docker image inspect, щоб відділити «формат» від «не існує»
cr0x@server:~$ docker image inspect team/app:1.0.0
[]
Error: No such image: team/app:1.0.0
Що це означає: Посилання валідне, але образ відсутній локально.
Рішення: Якщо ви очікували образ локально — виправте кроки збірки/пуллу. Якщо ні — переходьте до docker pull і перевірок реєстру.
Завдання 11: Підтвердіть, що інтерполяція в Compose дала те, що ви очікували
cr0x@server:~$ cat docker-compose.yml
services:
api:
image: "registry.local/team/api:${TAG}"
cr0x@server:~$ TAG= docker compose config
services:
api:
image: registry.local/team/api:
Що це означає: Compose відрендерив образ з порожнім тегом. Той трейлінг-двоокрапка пізніше вибухне як «invalid reference format» або під час пулу/запуску.
Рішення: Використовуйте розширення за замовчуванням у Compose: ${TAG:-dev} або перевіряйте обов’язковість змінних у CI перед викликом Compose.
Завдання 12: Вловіть YAML-цитування та неочікувані нові рядки
cr0x@server:~$ python3 - <<'PY'
import os
ref = "registry.local/team/app:1.2.3\n"
print(repr(ref))
PY
'registry.local/team/app:1.2.3\n'
Що це означає: Нові рядки можуть проскакувати, коли рядки беруться з файлів, шаблонів або блокових скалярів YAML.
Рішення: Використовуйте однорядкові YAML-строки для посилань на образи. Уникайте блокових скалярів для всього, що перетворюється в токен командного рядка.
Завдання 13: Підтвердіть, що шелл не розбив посилання
cr0x@server:~$ REF="team/app:1.0.0"
cr0x@server:~$ set -x
cr0x@server:~$ docker run --rm $REF
+ docker run --rm team/app:1.0.0
Unable to find image 'team/app:1.0.0' locally
docker: Error response from daemon: pull access denied for team/app, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
See 'docker run --help'.
Що це означає: Це не помилка формату; це auth/реєстр/доступність.
Рішення: Перестаньте дебажити синтаксис. Почніть дебажити креденшали, дозволи реєстру чи правильний шлях до репозиторію.
Завдання 14: Перевірте посилання з дайджестом окремо від тега
cr0x@server:~$ docker pull alpine@sha256:deadbeef
invalid reference format
Що це означає: Дайджест сформований неправильно (занадто короткий, не hex тощо). Docker відкидає його як помилку парсингу посилання.
Рішення: Не вписуйте дайджести вручну. Копіюйте точно і нехай інструменти перевіряють довжину/формат дайджеста перед використанням.
Короткий жарт #2: Трейлінг-пробіл у тегі образу — як гліттер удома: ви його не побачите, але він зіпсує вечір.
Три міні-історії з корпоративного життя
Міні-історія 1: Інцидент через невірне припущення
Команда стандартизувала «теги за гілками» для preview-середовищ. Фічеві гілки збирались в образи і розгортались у ефермальних неймспейсах. Усі були в захваті — поки в четвер о 15:00 релізні деплої не почали падати в кількох репозиторіях одночасно.
На виклику інженерія спочатку трактувала це як збій реєстру. Повідомлення в логах: «invalid reference format». Це повідомлення приваблює неправильний тип дебагу, бо з’являється поруч із pull/run. Вони перевірили здоров’я реєстру, мережі, DNS і токени автентифікації. Все виглядало нормально. Деякі збірки все ще пушили щось для певних сервісів, але не для інших.
Неправильне припущення: «Якщо падає під час деплою — значить проблема в системі деплою». Насправді деплой був першим місцем, де став помітним некоректний тег. Зміна, яка це викликала — політика в репозиторії: назви гілок почали містити префікси з великих літер для типів робіт. Наприклад Feature/New-UI і Hotfix/.... Git приймає це. Люди приймають це. Docker теги не приймають слеш, а шляхи репозиторіїв у багатьох контекстах не приймають великі літери.
Діагноз настав, коли хтось надрукував обчислене посилання на образ з видимим екрануванням і побачив team/api:Feature/New-UI. Виправлення було нудним: санітизувати назву гілки у безпечний тег і примусити нижній регістр. Також додали guardrail, щоб пайплайн падав з ясним повідомленням до виклику Docker.
Постмортем не про Docker. Він про припущення. Системи виходять з ладу там, де обмеження зустрічаються з реальністю. Іменування гілок Git має інші обмеження, ніж теги контейнерів, і «працювало минулого тижня» не є контрактом.
Міні-історія 2: Оптимізація, що вистрілила собі в ногу
Платформна команда вирішила прискорити CI, генеруючи теги образів з повного Git-ref, короткого SHA і таймстемпу збірки. Мета — трасуваність без додаткових запитів. На папері: круто. В пайплайнах: повільна комедія.
Оптимізацію реалізували як невелику shell-функцію, що повторно використовувалася в репозиторіях. Вона щедро використовувала echo, пропускала через кілька текстових утиліт і завершувалася новим рядком. Цей новий рядок не мав значення в логах. Але мав велике значення, коли інтерполювався в тег для docker build -t. Деякі ранери його обрізали, деякі — ні. В певних шеллах він ставав трейлінг-пробілом або буквальним новим рядком залежно від розширення змінних.
Більшість збірок проходили. Потім підмножина почала падати з «invalid reference format», ніби навмання. Розробники звинувачували версії Docker, образ раннера і «флейкі CI». On-call звинувачував місяць.
Коренева причина виявилася в тому, що «трасувальний тег» іноді перевищував обмеження та часто містив заборонені символи, запозичені з формату Git ref. Формат таймстемпу також включав двокрапки, які в деяких контекстах легальні в тегах, але ризиковані в комбінації з іншими кроками парсингу і форматування логів. Новий рядок був фінальним ударом.
Відкат був миттєвим. Замінник був простим: теги стали ${sanitized_branch}-${short_sha} з жорсткими лімітами довжини, а багата метадані перемістили в лейбли (org.opencontainers.image.revision, org.opencontainers.image.source), де їм і місце. Трасуваність покращилася. CI перестав «випадково» ламатися.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Інша організація мала звичку, що здавалася надмірно обережною: кожен крок пайплайна, що обчислював посилання на образ, записував його у файл, потім валідовував за допомогою невеликого скрипта, а потім друкував у форматі безпечному для екранування. Ніхто не був у захваті від зайвих рядків коду. Але підхід був послідовний, перевірений і спільний.
Одного дня додали новий сервіс командою, що в основному працювала в Windows-інструментах. Їх пайплайн інжектував рядок версії, зчитаний із файлу, котрий містив CRLF-завершення рядків. Тег виглядав нормально в логах. Але обчислений рядок містив прихований \r. Docker почав викидати «invalid reference format».
Ось де нудна практика окупилася: валідатор скрипт вказав на те, що тег містить carriage return, і впав раніше з повідомленням, яке вказувало на точне значення байта і файл-джерело. Жодної дебаг-сесії реєстру. Жодних зайвих ходів у Compose. Жодного «працює на моїй машині».
Виправлення — однорядкова нормалізація в пайплайні плюс політика репозиторію, що версійні файли мають бути з LF-рядками. Це ніколи не стало інцидентом. Це стало закритим тікетом з чітким коренем і загартованою системою.
Хороша операційна робота часто просто відмова дозволяти неоднозначним рядкам текти через систему без перевірки. Це не гламурно. Це ефективно.
Типові помилки: симптом → причина → виправлення
Цей розділ призначений для використання, коли ви дивитесь у логи. Кожна позиція зіставляє впізнаваний симптом з найімовірнішою кореневою причиною і конкретним виправленням.
1) Симптом: «invalid reference format» при використанні docker build -t
Коренева причина: Змінна тега пуста або містить пробіли/нові рядки.
Виправлення: Примусьте наявність обов’язкових змінних і обрізайте входи. Використовуйте ${TAG:?TAG must be set} і генеруйте теги через printf замість echo.
2) Симптом: Працює локально, падає в CI з тією самою на вигляд командою
Коренева причина: CI-змінні містять невидимі символи (\r, трейлінг-пробіл) або інтерполюються по-іншому шеллом.
Виправлення: Надрукуйте обчислене посилання через printf '%q\n'. Нормалізуйте завершення рядків і використовуйте суворі опції шелу (set -euo pipefail).
3) Симптом: «Error parsing reference … is not a valid repository/tag»
Коренева причина: Тег містить / (назва гілки) або репозиторій містить великі літери.
Виправлення: Санітизуйте назви гілок у теги; примусьте нижній регістр для шляхів репозиторію.
4) Симптом: Invalid reference format з хостом реєстру + портом
Коренева причина: Нечисловий порт, відсутній слеш після хоста або випадкова зайва двокрапка.
Виправлення: Переконайтеся, що формат host:port/path/name:tag. Розділяйте побудову хоста реєстру і побудову тега.
5) Симптом: Падає тільки при копіюванні з чату або тікетів
Коренева причина: Розумні лапки, нерозривні пробіли або Unicode-пунктуація.
Виправлення: Наберіть лапки вручну; перевірте через python3 з repr() або printf '%q' у шеллі.
6) Симптом: Compose каже invalid reference format, але YAML виглядає нормально
Коренева причина: Інтерполяція змінних створила порожній тег або блоковий скаляр YAML вставив новий рядок.
Виправлення: Виконайте docker compose config і перевірте відрендерений результат. Використовуйте ${VAR:-default} або примусьте обов’язковість змінних.
7) Симптом: Спробували використати дайджест, і він одразу падає
Коренева причина: Дайджест сформований неправильно або обрізаний.
Виправлення: Копіюйте повний дайджест; додайте перевірки на префікс sha256: і очікувану довжину перед використанням.
8) Симптом: Помилка з’являється після того, як ви «оптимізували» теги для трасуваності
Коренева причина: Тег містить заборонені символи або стає надто довгим; метадані загружають у теги.
Виправлення: Перенесіть метадані в OCI-лейбли. Тримайте теги короткими, санітизованими й стабільними.
9) Симптом: Посилання виглядає валідним, але Docker все одно скаржиться
Коренева причина: Невидимі пробіли, особливо трейлінг-пробіл або новий рядок.
Виправлення: Друкуйте з екрануванням і перевіряйте байтовий вміст. Розглядайте всі рядки як ворожі, поки не доведено протилежне.
Чеклісти / покроковий план
Чекліст A: Коли ви бачите «invalid reference format» в інциденті
- Витягніть точний рядок посилання з логів або виводу кроку CI. Поки не набирайте його заново.
- Відкрийте невидимі символи: виведіть з екрануванням (
printf '%q\n') або з представленням (python3 -cзrepr). - Підтвердіть, який інструмент це згенерував (Docker CLI, Compose, система збірки). Знайдіть момент, де посилання складається.
- Перевірте змінні на порожнечу і дефолти. Порожні рядки — причина №1 для помилки з трейлінг-двоокрапкою.
- Проскануйте на заборонені символи: пробіли, слеші в тегах, великі літери в шляху репо,
\r. - Зведіть до мінімального відтворювача:
docker pullабоdocker image inspectз точним посиланням. - Виправляйте в джерелі: на кроці побудови рядка, а не в виклику Docker.
- Додайте префлайт-валідатор, що падає з людською помилкою до того, як Docker її побачить.
Чекліст B: Надійний спосіб генерувати теги в CI
- Виберіть входи: назва гілки, короткий SHA, опціональний номер збірки.
- Переведіть усе в нижній регістр (сумісність важливіша за естетику).
- Замініть заборонені символи на
-. - Скорочуйте повторні роздільники (уникати
---). - Обріжте провідні/кінцеві роздільники.
- Задайте максимальну довжину (наприклад 60–80 символів) щоб уникнути країв.
- Виводьте через
printfбез несподіваних нових рядків. - Зберігайте обчислене посилання у файлі-артефакт і використовуйте його скрізь.
Чекліст C: Контроли запобігання, які я рекомендую
- Одна спільна бібліотека для тегування на організацію (shell-функція, Make-ціль, невеликий Go/Python хелпер). Ніякої саморобщини в кожному репозиторії.
- Перевірки змінних, що швидко падають перед викликом Docker або Compose.
- Відрендерений конфіг Compose як артефакт CI для дебагу (
docker compose configвихід). - OCI-лейбли для метаданих, а не накачування тегів.
- Лінтинг посилань на образи в CI (навіть regex-ворота краще за інтуїцію).
FAQ
1) Чи означає «invalid reference format», що реєстр впав?
Майже ніколи. Зазвичай парсинг ламається ще до мережевого виклику. Якщо бачите цю помилку — спочатку перевіряйте рядок. Якщо посилання пройде парсинг, ви отримаєте помилки типу «manifest unknown», «pull access denied» або проблеми TLS/автентифікації.
2) Чому Docker не любить великі літери?
Це питання консистентності та сумісності між файловими системами, реєстрами та інструментами. Змішані регістри створюють плутанину та операційні проблеми. Екосистема обрала суворість замість хаосу.
3) Чи може Docker-тег містити слеш?
Ні. Слеш — це роздільник шляху в імені репозиторію. Якщо ви намагаєтеся покласти гілку Git як feature/foo у тег — отримаєте помилку парсингу. Санітизуйте її спочатку.
4) У чому практична різниця між тегом і дайджестом?
Тег — змінний вказівник (може вказувати на інший образ з часом). Дайджест — адресація за вмістом і фактично незмінна. Використовуйте дайджести для відтворюваності, теги — для робочих процесів і навігації людини.
5) Compose показує помилку, але рядок образу виглядає правильно. Що далі?
Запустіть docker compose config. Compose може інтерполювати змінні в порожній тег або вставляти пробіли/нові рядки через YAML-форматування. Дебажте відрендерений конфіг, а не вихідний YAML.
6) Чому вчора працювало, а сьогодні падає з тим самим пайплайном?
Тому що змінилися входи: назви гілок, рядки версії, змінні оточення, ОС раннера або завершення рядків. Ваш пайплайн — фабрика рядків. Будь-який новий символ може стати інцидентом у продакшені.
7) Як безпечно вивести посилання для дебагу без витоку секретів?
Посилання на образи не повинні містити секретів. Якщо ваші містять (наприклад вбудовані креденшали в рядку реєстру) — припиніть і виправте архітектуру. Для дебагу виводьте лише обчислене посилання і тримайте креденшали у docker login або в хелперах для креденшалів.
8) Чи пов’язане «latest» з цією помилкою?
Прямо ні. latest — валідний тег. Він просто породжує інші проблеми (дрейф, несподівані розгортання). Використовуйте явні теги або дайджести і тримайте latest лише для локальної розробки максимум.
9) Який найшвидший спосіб довести, що це пробіли?
У шеллі: printf '%q\n' "$REF". У Python: print repr(REF). Якщо бачите \r, \n або екранований пробіл наприкінці — ви знайшли винуватця.
Висновок: наступні кроки, які реально запобігають повторенням
«invalid reference format» — це скарга парсера, а не філософський вислів. Ставтеся до неї як до зіпсованого URL: ізолюйте точний рядок, виявляйте приховані символи, перевіряйте входи і перестаньте дозволяти довільному тексту визначати поведінку продакшену.
Практичні кроки, які я впровадив би цього тижня:
- Додати префлайт-крок в кожен CI-пайплайн, який виводить обчислене посилання на образ з екрануванням і падає, якщо знаходить заборонені символи або пустоту.
- Уніфікувати генерацію тегів в спільному хелпері з санітизацією, лімітами довжини та консистентним нижнім регістром.
- Перенести метадані в OCI-лейбли замість накачування тегів, що рано чи пізно виводить їх з ладу.
- Зробити видимим відрендерений Compose: зберігати
docker compose configяк артефакт кожного джобу деплою.
Виправлення рідко складне. Дисципліна — ось що важко. Хороша новина: як тільки ви перестанете ставитися до посилань на образи як до випадкових рядків, ця конкретна витрата часу здебільшого зникне з вашого життя. І ви повернете кілька годин, які Docker тихо відбирав у вас.