Еще одна уведомлялка про обновления Docker — но с прокси и без автопилота - QubStore

Еще одна уведомлялка про обновления Docker — но с прокси и без автопилота

Простите, но я сделал еще один продукт, который уведомляет, если есть обновления. Многие следят за обновлениями своего зоопарка docker-контейнеров, и я не исключение. На рынке есть несколько готовых решений, и я честно попробовал самые популярные.
Watchtower на тот момент показался мне заброшенным (про живые форки я тогда не знал) и что-то с ним не задалось, что именно, если честно, уже не помню. Это был первый кандидат, когда активно начал использовать docker.
С Diun не сложилось из-за темплейтов уведомлений — я не осилил их настолько, чтобы получить читаемое сообщение, точнее позже осилил, но из коробки — это мрак.
WUD оказался ближе всего к тому, что я хотел, и честно, будь в нём прокси для уведомлений в телегу, я бы, скорее всего, на нём и остался. Но прокси не было, а напрямую до Telegram некоторые мои серверы не достают. Плюс особенность зашивать имя хоста в имя переменной конфига — это что-то с чем-то. Первым делом форкнул WUD, сделал патч и PR, чтобы добавить прокси для телеги. Посмотрел, как часто там выпускаются релизы, всплакнул и пошел пилить свое. Самое смешное, что PR в итоге приняли за неделю (спустя полгода от предыдущего релиза), но к тому времени я уже переехал полностью на свою поделку.

Мне не нужно автообновление контейнеров, предпочитаю только узнавать о новых релизах и уже самостоятельно принимать решение, надо мне обновляться или нет. Считаю, что автообновление, которое сломает что-то без твоего ведома — хуже, чем сидеть на более старой версии. Еще один момент, я никогда не ставлю latest, всегда указываю конкретную версию. Максимум вместо конкретной версии редко использую floating-тег и как раз на нем вылезла проблема, о чем будет дальше. Не знаю, сколько людей так делает, но это мой путь.

В WUD есть прикольная фича, которая позволяет следить только за версиями, которые описываются с помощью regexp в labels. Но есть проблема, он прописывается внутри compose файла. Управлять этим через лейблы на каждом контейнере — боль. Мне показалось, что хорошей идеей будет вынести это в отдельный конфиг именно проверялки. Все настройки в одном месте и не затрагивают работающий контейнер и не надо его перезапускать, если надо внести правки в regexp.

А с версиями у разработчиков тот еще зоопарк. Вместо нормальных 1.2.3 часто используются такие: 17.10-alpine3.24, v4.15.1-ce, 1.0.0-beta.8, 1.27-rootless. В общем, кто во что горазд и искать нужный тег можно только задав regexp правилами. И тут было над чем поработать. Теперь мой рабочий конфиг для правил выглядит примерно так:

rules:
— image: «adguard/adguardhome»
include: «^vd+.d+.d+$»
— image: «linuxserver/bookstack»
include: «^d+.d+.d{1,2}$»
— image: «chatwoot/chatwoot»
include: «^vd+.d+.d+-ce$»
— image: «gitea/gitea»
include: «^d+.d+.d+$»
— image: «gethomepage/homepage»
include: «^vd+.d+.d+$»
— image: «postgres»
include: «^17.d+-alpine3.d+$»
— image: «rustfs/rustfs»
include: «^d+.d+.d+-beta.d+$»
— image: “valkey/valkey”
track: digest
exclude_containers:
— chatwoot-notify

Все остальные контейнеры нормальные и для них не надо никаких правил, потому что версии типа 1.2.3 отлично чекаются без дополнительных телодвижений.

Так и появился Versentry.

В процессе разработки столкнулся с кучей неочевидных вещей.
Например, разные мажорные версии postgres в разных стеках на одном сервере: один 16, другой 17. Оба лезут в официальный склад postgres — образ же общий, отличаются только теги. А состояние я поначалу хранил по имени образа, логика простая: один образ — одна запись. Вот только контейнера два, и они стали затирать записи друг друга: один проверился, записал, второй проверился, записал поверх — и посыпались повторные уведомления. Так ключ из «по образу» превратился в «по контейнеру».
Как писал выше, на тестах всплыли проблемы с плавающими тегами. Таким образом появился режим, когда мы отслеживаем только digest. Проявилось это на valkey 9-alpine, где semver-парсер принял за версию и предложил обновиться на 9.1. Тут, после многих итераций, я решил, что угадывать версии, на которые стоит обновиться, вообще плохая затея, лучше отслеживать digest для плавающих тегов вроде 9-alpine, которые semver не разбирает.
Еще изначально был выбор завести список известных суффиксов дистрибутивов (-alpine, -slim…). Но сегодня он такой, завтра другой. Поэтому в конечном итоге пришел к выводу, что лучше не делать вообще никаких угадываний, только следование определенным правилам. Да, хотелось сделать умно, но выбрал предсказуемость.
Дальше еще была борьба со Scout от DockerHub, который параноидально ищет по уязвимостям в контейнере. Ему вообще наплевать, используется эта уязвимость в вызовах или нет, поэтому было много ложных срабатываний и вместо alpine перешел на бинарник в контейнере, что, кстати, уменьшило образ почти в два раза — до чуть меньше 5Мб. Мелочь, а приятно.Теперь в CI прогоняю govulncheck, который в отличие от Scout смотрит, достижим ли уязвимый код.

Что в итоге получилось:
Умеет из коробки Docker Hub, GHCR, Quay, GitLab + приватные OCI.
Работа по расписанию или через интервал, поддерживается timezone.
Централизованные regex-правила тегов в конфиге или labels на сервис + режим слежения за digest для плавающих тегов.
Уведомления в Telegram / Discord / Gotify / ntfy / webhook — можно легко добавить какие-то еще.
Все умеет через SOCKS5/HTTP прокси. По одному уведомлению на контейнер или сводный список в одном сообщении + настраиваемые шаблоны для уведомлений.
Имя сервера для того, чтобы понять, от кого уведомление, берется из hosts или задается отдельно.

Чего в инструменте нет:
web-UI — есть в WUD, но никогда не пользовался и не понял, зачем он вообще нужен.
Автообновления — об этом уже говорил выше.
Только Docker — в моем случае нет K8s/Swarm/Podman. Мне без надобности, но легко добавляется, при необходимости.

Попробовать проще всего через compose.
Минимальный рабочий пример:

services:
versentry:
image: blackraincoat/versentry:latest # или конкретный тег — 1.2.3 на момент написания статьи
container_name: versentry
restart: unless-stopped
volumes:
— /var/run/docker.sock:/var/run/docker.sock:ro
— ./config.yaml:/etc/versentry/config.yaml:ro
— ./data:/data
environment:
— VERSENTRY_TELEGRAM_TOKEN=…
— VERSENTRY_TELEGRAM_CHAT_ID=…

Рядом с compose положить config.yaml:

provider:
type: docker
notifiers:
— type: stdout
— type: telegram
config:
parse_mode: HTML
# ключи для уведомлений также можно указать в конфиге вместо compose
# token: «123456:ABC-DEF»
# chat_id: «123456789»
schedule: «0 13 * * *» # или interval: 24h

Можно проверить сразу, не дожидаясь расписания: docker exec versentry versentry check

Доволен тем, что получилось и это полностью закрывает мой сценарий использования. Возможно, кому-то тоже будет полезно как альтернатива существующим решениям.

Больше информации по конфигам, командам и т.д. — в документации на GitHub.

Ссылки:
DockerHub https://hub.docker.com/r/blackraincoat/versentry
GitHub https://github.com/BlackRaincoat/versentry

А чем пользуетесь вы для таких задач?

Ссылка на основную публикацию