docs/hub-deploy.md walks through the image, a read-only hub-reader user and token on each server, the DNS record, .env, the compose service, checks, updates and troubleshooting. Co-Authored-By: Claude Opus 5.5 <[email protected]>
16 KiB
Хаб: запуск по шагам
Хаб показывает статистику двух серверов Technitium DNS (dns.shnt.top и ndns.shnt.top) в одной консоли. Это один маленький контейнер. Он отдаёт ту же консоль и сам ходит в API обоих серверов по их токенам:
- запрос «All servers» хаб отправляет на оба сервера и складывает ответы;
- запрос к одному серверу хаб пересылает только ему;
- запись хаб не выполняет никогда.
Серверы Technitium не меняются, кластер не нужен. В инструкции хаб ставится на хост, где уже работают Traefik и technitium-smart (docs/compose.shnt.example.yaml). Адрес хаба: https://hub.shnt.top.
браузер ─► Traefik (hub.shnt.top, TLS) ─► technitium-hub:8080
├─► https://dns.shnt.top/api/... (DNS_TOKEN)
└─► https://ndns.shnt.top/api/... (NDNS_TOKEN)
Что хаб показывает: Dashboard (сумма или один сервер), Logs (View Logs и Query Logs, объединённые с колонкой Server) и About. Остальные разделы скрыты, ими управляют в консоли каждого сервера.
Шаг 0. Образ хаба в реестре
Образ git.shx.one/denozord/technitium-hub собирает отдельный workflow .gitea/workflows/hub.yaml.
- Отправьте коммиты хаба на
git.shx.one:git push origin main - В репозитории на
git.shx.oneоткройте Actions и дождитесь зелёного прогона Hub:- job
checkпроверяет типы и тесты хаба; - job
imageсобирает образ и кладёт его в реестр.
- job
- Убедитесь, что образ появился: в репозитории или профиле откройте Packages, там должен быть
technitium-hubс тегомlatest.
Если job image упал на входе в реестр, проверьте секрет ACTION_PAT (или ACTIONS_PAT) в настройках репозитория: это токен Gitea с правом write:package. Он тот же, что использует образ консоли.
Выпуск версии с номером (необязательно):
git tag hub-v1.0.0 && git push origin hub-v1.0.0
Тег даёт образы 1.0.0 и 1.0 в дополнение к latest.
Шаг 1. Пользователь только для чтения на каждом сервере
Сделайте это на обоих серверах: https://dns.shnt.top и https://ndns.shnt.top. Войдите в консоль сервера как администратор.
-
Группа. Administration › Groups › Add Group: имя
hub-readers, описание любое. -
Права группы. Administration › Permissions: у группы
hub-readersотметьте только View в четырёх разделах:Раздел Зачем хабу Dashboard статистика, топы, график по часам, живые метрики Logs список файлов логов и Query Logs Apps список приложений логирования, из которых Query Logs читает записи Settings состояние блокировки на Dashboard Modify и Delete не отмечайте нигде. Остальные разделы оставьте без прав.
-
Пользователь. Administration › Users › Add User: имя
hub-reader, длинный случайный пароль. Затем в карточке пользователя добавьте его в группуhub-readers. Из группыEveryoneможно убрать, если у неё есть лишние права. -
API-токен. Нужен токен именно для
hub-reader, а не для администратора:- выйдите из консоли и войдите под
hub-reader; - в меню пользователя внизу слева выберите Create API Token, имя токена
hub; - скопируйте токен сразу: его показывают один раз.
Можно и под администратором: Administration › Sessions › Create Token, пользователь
hub-reader. - выйдите из консоли и войдите под
-
Проверьте токен с любой машины. В ответе должно быть
"status":"ok":curl -s "https://dns.shnt.top/api/dashboard/stats/get?type=LastHour&token=ТОКЕН" | head -c 200"status":"invalid-token"значит, что скопирован не тот токен;"status":"error"с сообщением об отказе в доступе значит, что у группы не хватает права View.
Результат шага: два токена, DNS_TOKEN (с dns.shnt.top) и NDNS_TOKEN (с ndns.shnt.top).
Шаг 2. DNS-запись для hub.shnt.top
В Cloudflare (зона shnt.top) создайте запись hub той же формы, что dns: A на IP хоста или CNAME на dns.shnt.top.
Сертификат Traefik получит сам. В compose уже настроен certresolver=myresolver с DNS-challenge через Cloudflare (CF_DNS_API_TOKEN), отдельно открывать порт 80 для проверки не нужно.
Шаг 3. Файл .env
На хосте в каталоге с docker-compose.yml откройте .env (там уже лежат CF_DNS_API_TOKEN и DNS_SERVER_ADMIN_PASSWORD) и добавьте:
# Хаб: токены пользователей hub-reader с шага 1
DNS_TOKEN=вставьте_токен_dns
NDNS_TOKEN=вставьте_токен_ndns
# Вход в сам хаб (это НЕ логин Technitium)
HUB_USER=admin
HUB_PASSWORD=длинный_случайный_пароль
Пароль хаба нужен длинный: у хаба нет ограничения числа попыток входа. Сгенерировать можно так:
openssl rand -base64 24
Закройте .env от посторонних:
chmod 600 .env
Шаг 4. Сервис в compose
Добавьте в docker-compose.yml в раздел services: блок из docs/compose.hub.example.yaml:
technitium-hub:
image: git.shx.one/denozord/technitium-hub:latest
container_name: technitium-hub
restart: unless-stopped
networks:
- dns-net
environment:
# id=адрес, по порядку; токен каждого берётся из <ID>_TOKEN
- HUB_SERVERS=dns=https://dns.shnt.top,ndns=https://ndns.shnt.top
- DNS_TOKEN=${DNS_TOKEN}
- NDNS_TOKEN=${NDNS_TOKEN}
- HUB_USER=${HUB_USER}
- HUB_PASSWORD=${HUB_PASSWORD}
- HUB_DOMAIN=hub.shnt.top
- TZ=Asia/Novosibirsk
labels:
- "traefik.enable=true"
- "traefik.docker.network=dns-net"
- "traefik.http.routers.hub.rule=Host(`hub.shnt.top`)"
- "traefik.http.routers.hub.entrypoints=websecure"
- "traefik.http.routers.hub.tls.certresolver=myresolver"
- "traefik.http.routers.hub.service=hub-svc"
- "traefik.http.services.hub-svc.loadbalancer.server.port=8080"
Если хаб стоит в том же файле, что Traefik, отдельный блок networks: с external: true из примера не нужен: сеть dns-net в этом файле уже объявлена.
Про имена в HUB_SERVERS:
- имя слева от
=видно в консоли в селекторе «Server»; - из имени получается переменная токена:
dnsдаётDNS_TOKEN,ndnsдаётNDNS_TOKEN,my-dnsдал быMY_DNS_TOKEN; - первый в списке сервер считается основным.
Адреса серверов указываются публичные (https://dns.shnt.top). API Technitium доступно по тому же адресу, что и консоль (роутер panel-old в Traefik), отдельный порт не нужен.
Шаг 5. Запуск
Если реестр закрытый, один раз войдите в него. Нужен токен Gitea с правом read:package:
docker login git.shx.one
Скачайте образ и запустите хаб:
docker compose pull technitium-hub
docker compose up -d technitium-hub
Проверьте контейнер:
docker compose ps technitium-hub # STATUS: Up ... (healthy)
docker compose logs -f technitium-hub
В логе при старте не должно быть ошибок конфигурации. Если чего-то не хватает, хаб не запустится и назовёт переменную, например NDNS_TOKEN is required for the server "ndns".
Шаг 6. Проверка
- Откройте
https://hub.shnt.topи войдите сHUB_USER/HUB_PASSWORDиз.env. - На Dashboard над заголовком должен быть селектор Server: All servers.
- Сверьте цифры. Total Queries в «All servers» должна равняться сумме значений в консолях
dns.shnt.topиndns.shnt.topза тот же период. Расхождение в пределах минуты нормально: хаб кеширует ответы на 15 секунд, а Last Hour обновляется на ходу. - Выберите в селекторе
dns, затемndns: цифры каждого сервера должны совпасть с его собственной консолью. - Откройте Logs › Query Logs, нажмите Query. Записи обоих серверов идут вместе, в колонке Server видно, чья запись.
- Кнопка статуса справа в шапке показывает
hub.shnt.top. Если какой-то сервер не ответил, там будет «1 server not answering», а по нажатию появится причина.
Проверка без браузера (подставьте пароль хаба):
T=$(curl -s -X POST https://hub.shnt.top/api/user/login \
--data-urlencode "user=admin" --data-urlencode "pass=ПАРОЛЬ_ХАБА" | sed -E 's/.*"token":"([^"]+)".*/\1/')
curl -s -H "Authorization: Bearer $T" "https://hub.shnt.top/api/dashboard/stats/get?type=LastHour" | head -c 300
curl -s -H "Authorization: Bearer $T" "https://hub.shnt.top/api/dashboard/stats/get?type=LastHour&node=ndns" | head -c 300
Обновление
После нового прогона Hub в Actions:
docker compose pull technitium-hub && docker compose up -d technitium-hub
Сессии хаба хранятся в памяти: после перезапуска контейнера нужно войти заново.
Чтобы закрепить версию, укажите в compose тег вместо latest, например technitium-hub:1.0.0.
Остановка и удаление
docker compose stop technitium-hub # остановить
docker compose rm -f technitium-hub # удалить контейнер
Чтобы отозвать доступ хаба к серверам, на каждом сервере удалите токен в Administration › Sessions или отключите пользователя hub-reader.
Настройки хаба
| Переменная | По умолчанию | Что задаёт |
|---|---|---|
HUB_SERVERS |
обязательна | Серверы по порядку: id=https://адрес,... |
<ID>_TOKEN |
обязательна для каждого | API-токен сервера (DNS_TOKEN, NDNS_TOKEN, ...) |
HUB_USER, HUB_PASSWORD |
обязательны | Вход в хаб |
HUB_DOMAIN |
hub |
Имя, которое консоль показывает как имя сервера |
HUB_CACHE_SECONDS |
15 |
Сколько секунд хранить ответы серверов (0 выключает кеш) |
HUB_TIMEOUT_MS |
8000 |
Сколько ждать ответа сервера, мс |
HUB_PORT |
8080 |
Порт внутри контейнера |
Третий сервер добавляется строкой в HUB_SERVERS и его токеном:
- HUB_SERVERS=dns=https://dns.shnt.top,ndns=https://ndns.shnt.top,lab=https://lab.shnt.top
- LAB_TOKEN=${LAB_TOKEN}
Если что-то не так
| Признак | Причина и что делать |
|---|---|
| Контейнер сразу завершается | В docker compose logs technitium-hub будет названа недостающая переменная. Проверьте .env и что compose его видит (docker compose config | grep TOKEN). |
https://hub.shnt.top не открывается, ошибка сертификата |
Нет DNS-записи hub (шаг 2) или Traefik ещё получает сертификат: смотрите docker logs traefik. |
| 404 от Traefik | Контейнер не в сети dns-net или опечатка в Host(\hub.shnt.top`)`. |
| Вход в хаб: «Invalid username or password» | Неверные HUB_USER/HUB_PASSWORD в .env. После правки .env нужен docker compose up -d technitium-hub. |
В шапке «1 server not answering», в подсказке ndns: its API token was refused |
Токен NDNS_TOKEN отозван или скопирован с ошибкой. Создайте новый (шаг 1.4) и перезапустите хаб. |
В подсказке ... did not answer: ... или ECONNREFUSED / timeout |
Хаб не достучался до сервера. Проверьте с хоста хаба: docker exec technitium-hub node -e "fetch('https://ndns.shnt.top/api/status').then(r=>console.log(r.status))". |
В подсказке ndns: ... с сообщением об отказе в доступе |
У группы hub-readers на этом сервере не хватает View в каком-то разделе (шаг 1.2). |
| Query Logs пустой или «нет приложения» | На сервере не установлено приложение логирования (Query Logs (Sqlite) и подобные) или нет права View на Apps. |
| Цифры «All servers» больше суммы на 1-2 клиента | Clients считается по каждому серверу отдельно: клиент, который ходит на оба сервера, учтён дважды. Это ожидаемо. |
| Нажатие на что-то пишет «The hub is read-only.» | Хаб только читает. Изменения делаются в консоли нужного сервера. |
Безопасность
- Хаб только читает: любая запись получает отказ ещё в хабе, а токены выданы пользователю с одними правами View.
- Токены лежат только в
.envна хосте и в памяти контейнера. В браузер они не попадают, в ответы и логи хаба не пишутся. - У хаба один общий логин и нет ограничения попыток входа. Нужен длинный пароль, а если хаб не должен быть виден из интернета, добавьте к роутеру
hubв TraefikipallowlistилиbasicAuth.