Тег latest в Docker — бомба замедленного действия. Наш Metabase упал на 3 часа из-за автообновления образа. Решение — всегда пинить конкретную версию в docker-compose.yml. Инцидент затронул дашборды клиента, а причина нашлась случайно: одно слово в конфиге.

Мы ведем аналитику для музыкального лейбла в Metabase. Дашборды, метрики артистов, финансы — все в одном месте. Инструмент, который настроили один раз и перестали о нем думать. Работало месяцами.

Утро, когда Metabase перестал отвечать

Одним утром Metabase перестал отвечать.

Сергей полез разбираться: логи чистые, конфигурация не менялась, сервер в порядке. Перезапуск контейнера, проверка базы, чтение issues на GitHub. Три часа по кругу — ни одна из очевидных причин не подтвердилась.

Пока шла диагностика, клиент не мог посмотреть аналитику по свежим релизам. Три часа даунтайма из-за причины, которая нашлась случайно.

Одно слово: latest

Docker-образ стоял на теге latest. При рестарте контейнера хостинг подтянул свежую версию автоматически. Metabase рекомендует делать бекап перед каждым обновлением — даже минорным. На форумах десятки тредов с тем же сценарием: latest подтянул новую версию, что-то сломалось.

«Они тайком в старые версии подпихивают новые фиксы — и не понимаешь, что изменилось», — Сергей после трех часов диагностики.

Попытка откатиться не помогла. Старая версия уже содержала чужие изменения. Пришлось перезагружать сервер из веб-панели хостера вручную. Не через SSH, не через Docker CLI — из веб-панели. Контейнер повис наглухо.

Цепочка выглядит нелепо: локальный кеш обновился → непредусмотренный апгрейд → потеря сервиса → аварийный ребут. Из-за одной строки в docker-compose.yml.

docker-compose.yml: до и после

Было — образ обновляется при каждом рестарте:

services:
  metabase:
    image: metabase/metabase:latest
    ports:
      - "3000:3000"

Стало — версия закреплена, обновление только вручную:

services:
  metabase:
    image: metabase/metabase:v0.52.2
    ports:
      - "3000:3000"

Теперь все образы закреплены

(Мы знали правило: закрепляй версии в production. Применяли для клиентских сервисов. Но к «внутреннему инструменту, который и так работает» — не применили. Оказалось, именно такие ломаются первыми. Никто не мониторит то, что «и так работает».)

Теперь все Docker-образы в наших проектах — включая внутренние — закреплены на конкретных версиях. Обновляем вручную, после проверки changelog. Скучнее. И предсказуемее.

Похожий принцип — вкладывать в контекст заранее, чтобы потом не разгребать последствия. А если вы строите оповещения, которые чинят себя сами, закрепленные версии — первый шаг к предсказуемой инфраструктуре.

Частые вопросы

Почему тег latest опасен в production?

При каждом рестарте контейнера Docker подтягивает свежий образ. Новая версия может содержать breaking changes, и вы узнаете об этом только когда сервис упадет.

Как узнать текущую версию Docker-образа?

docker inspect --format='{{.Config.Image}}' container_name покажет образ. Для Metabase — версия видна в footer интерфейса или через API /api/health.

Как автоматизировать обновление Docker-образов?

Используйте Dependabot или Renovate для отслеживания новых версий. Они создают PR с обновлением, который можно проверить перед мержем.