Конфигурация и журнал настройки отказоустойчивого Traefik для homelab.
Find a file
2026-08-01 13:25:17 +06:00
README.md Document Traefik HA deployment 2026-08-01 13:25:17 +06:00

Журнал настройки отказоустойчивого Traefik

Дата работ: 2026-08-01.
Обозначения: pve — 192.168.1.10, pve01 — 192.168.1.13.

Секреты, SSH-ключи и пароль VRRP намеренно заменены на <redacted>.

Итоговая схема

Роль Узел LXC Реальный IP Состояние на момент журнала
Active pve 301 traefik 192.168.1.98 Traefik + keepalived active, владеет VIP
Standby pve01 301 traefik 192.168.1.71 keepalived active, Traefik stopped
VIP — — 192.168.1.9 TCP 80/443 с роутера направлены на него

1. Обнаружение основного узла

Первое подключение по имени не сработало:

$ ssh -o BatchMode=yes pve '...'
ssh: Could not resolve hostname pve: nodename nor servname provided, or not known

$ ssh -o BatchMode=yes pve.home '...'
ssh: Could not resolve hostname pve.home: nodename nor servname provided, or not known

Из SSH-конфигурации и known_hosts определён адрес pve.home: 192.168.1.10:22933.

ssh -p 22933 -o BatchMode=yes root@192.168.1.10 \
  'hostname; pveversion --verbose | head -3; ip -brief address; pct list; qm list'

Ключевой вывод:

pve
proxmox-ve: 9.2.0
pve-manager: 9.2.6
vmbr0 UP 192.168.1.10/24

VMID 301  running  traefik

2. Инвентаризация действующего Traefik

ssh -p 22933 root@192.168.1.10 \
  'pct config 301; pct exec 301 -- sh -lc "ip -brief address; systemctl status traefik; traefik version"'

Результат:

LXC 301:
  hostname: traefik
  net0: ... bridge=vmbr0, ip=dhcp
  memory: 2048 MB
  rootfs: 4G
  onboot: 1

IP LXC: 192.168.1.98/24
Traefik: active (running)
Version: 3.7.9

Проверены конфигурация и маршруты:

pct exec 301 -- sed -E '/(token|secret|password|apikey|email)/I s/:.*/: <redacted>/' \
  /etc/traefik/traefik.yaml
pct exec 301 -- grep -RHE '^[[:space:]-]*url:' /etc/traefik/conf.d

Установлено:

  • файловый provider: /etc/traefik/conf.d/;
  • ACME TLS challenge, хранилище: /etc/traefik/ssl/acme.json;
  • CrowdSec bouncer подключается к 127.0.0.1:8080;
  • Traefik зависит от crowdsec.service через systemd drop-in;
  • около 20 маршрутов на сервисы в локальной сети.

Проверена существующая схема keepalived у AdGuard:

AdGuard primary: 192.168.1.11
AdGuard VIP:     192.168.1.8
keepalived:      active
virtual_router_id: 42

Для Traefik выбран независимый VRID 43.

3. Обнаружение и доступ к pve01

На pve выполнено:

getent hosts pve01 pve01.home

Вывод:

192.168.1.13 pve01

SSH на 22/tcp был закрыт, на 22933/tcp доступен. После добавления пользователем публичного ключа:

ssh -p 22933 -o BatchMode=yes root@192.168.1.13 \
  'hostname; pveversion --verbose | head -3; ip -brief address; pct list; pvesm status'

Ключевой вывод:

pve01
vmbr0 UP 192.168.1.13/24
ОЗУ доступно: ~29 GiB
local: доступно ~34 GiB
PBS и synology_nfs: active

4. Резервная копия и перенос LXC

Попытка использовать NFS как backup storage:

vzdump 301 --storage synology_nfs --mode snapshot --compress zstd

Ошибка:

can't use storage 'synology_nfs' for backups - wrong content type

Успешная резервная копия в PBS:

vzdump 301 --storage backup --mode snapshot --compress zstd \
  --notes-template 'HA standby seed for pve01'

Ключевой вывод:

Starting Backup of VM 301 (lxc)
root.pxar: reused 4.179 GiB (94.3%)
uploaded: 78.792 MiB
Finished Backup of VM 301
PBS snapshot: ct/301/2026-08-01T06:48:06Z

Первое восстановление с исходным диском 4 GiB:

pct restore 301 pbs:backup/ct/301/2026-08-01T06:48:06Z \
  --storage local --unique 1 --start 0

Ошибка из task log:

failed to extract file ... journal:
No space left on device (os error 28)

Остаток был проверен (/var/lib/lxc/301, 8 KiB, конфигурация LXC отсутствовала) и удалён. Повторное восстановление:

rm -rf /var/lib/lxc/301
pct restore 301 pbs:backup/ct/301/2026-08-01T06:48:06Z \
  --storage local --rootfs local:8 --unique 1 --start 0
pct set 301 --onboot 0

Успешный результат:

LXC 301 restored on pve01
rootfs: local:301/vm-301-disk-0.raw,size=8G
MAC: BC:24:11:D8:A3:16

Перед первым запуском резервного LXC Traefik был выключен офлайн:

unlink /var/lib/lxc/301/rootfs/etc/systemd/system/multi-user.target.wants/traefik.service
pct start 301

Полученный DHCP адрес резервного LXC:

192.168.1.71/24

5. Установка зависимостей

Команда обновления списка пакетов выявила уже существующую проблему:

https://packagecloud.io/crowdsec/crowdsec/debian trixie Release
404 Not Found

Репозиторий не изменялся. Установка из доступного Debian-кэша успешно выполнена на обоих LXC:

apt-get install -y keepalived rsync

Установлены:

keepalived 2.3.3-1
rsync 3.4.1+ds1-5+deb13u4
ipvsadm и зависимости

6. Конфигурация VIP и health-check

На обоих LXC созданы:

/etc/keepalived/keepalived.conf
/usr/local/sbin/check-traefik.sh
/usr/local/sbin/traefik-ha-state.sh

Основные параметры конфигурации:

VIP:               192.168.1.9/24
VRID:              43
advert_int:        1
pve priority:      150
pve01 priority:    100
unicast peers:     192.168.1.98 <-> 192.168.1.71
nopreempt:         включён
health weight:     -60

Health-check в финальном виде:

#!/bin/sh
set -eu
/usr/bin/systemctl is-active --quiet traefik
/usr/bin/curl -fsS --max-time 2 -o /dev/null http://127.0.0.1/

Скрипт состояния:

master        -> systemctl start traefik
backup/fault  -> systemctl stop traefik

Допущена и исправлена ошибка первой доставки: файлы были кратко записаны на хосты Proxmox вместо LXC. keepalived на хостах был inactive и VIP не назначался. Созданные файлы были удалены, затем конфигурация записана корректно через pct exec 301.

Проверка синтаксиса:

keepalived -t -f /etc/keepalived/keepalived.conf

Результат: код возврата 0 на обоих LXC.

7. Активация keepalived

На primary:

systemctl enable --now keepalived

Ключевой вывод:

eth0: 192.168.1.98/24 192.168.1.9/24
keepalived: active
Traefik: active

На standby:

systemctl enable --now keepalived

Ключевой вывод:

eth0: 192.168.1.71/24
keepalived: active
Traefik: inactive

Локальная проверка VIP:

curl -ksS --resolve pve.qubo.su:443:192.168.1.9 https://pve.qubo.su/

Вывод:

VIP HTTPS pve.qubo.su: 200

8. Синхронизация конфигурации и сертификатов

На каждом LXC создан выделенный SSH-ключ синхронизации; публичный ключ второго LXC добавлен в /root/.ssh/authorized_keys первого. Приватные ключи не выводились.

На обоих LXC созданы:

/usr/local/sbin/sync-traefik-standby.sh
/etc/systemd/system/traefik-standby-sync.service
/etc/systemd/system/traefik-standby-sync.timer

Таймер запускается раз в две минуты. Скрипт сначала проверяет, владеет ли локальный LXC VIP 192.168.1.9; только активный узел синхронизирует:

/etc/traefik/conf.d/
/etc/traefik/traefik.yaml
/etc/traefik/ssl/acme.json

Текущая схема двунаправленная по роли: кто владеет VIP, тот отправляет состояние на второй LXC. Это предотвращает параллельное обновление ACME.

9. Сетевые изменения, выполненные пользователем

Пользователь подтвердил и выполнил:

VIP: 192.168.1.9

TCP 80  -> 192.168.1.9:80
TCP 443 -> 192.168.1.9:443

DHCP reservation:
192.168.1.98 -> BC:24:11:AC:25:91
192.168.1.71 -> BC:24:11:D8:A3:16

Проверка через публичное имя:

curl -ksS -o /dev/null -w 'Public DNS HTTPS status: %{http_code}\n' https://pve.qubo.su/

Вывод:

Public DNS HTTPS status: 200

10. Контролируемый failover

Тест перехода pve -> pve01:

# на pve, внутри LXC 301
systemctl stop keepalived

Через ~7 секунд на pve01:

inet 192.168.1.9/24 ... proto keepalived
keepalived: active
Traefik: active

Keepalived: Entering MASTER STATE
Traefik health-check: succeeded

Внешняя проверка после перехода:

Public DNS HTTPS after failover: 200

После этого pve возвращён в standby, обратная синхронизация принудительно проверена с активного pve01.

11. Контролируемый failback на pve

По запросу пользователя выполнено:

# на pve01, внутри LXC 301
systemctl stop keepalived

# после захвата VIP pve
# на pve01, внутри LXC 301
systemctl start keepalived

Финальные проверки:

pve:
  192.168.1.9 назначен
  keepalived active
  traefik active

pve01:
  VIP отсутствует
  keepalived active
  traefik inactive

Public DNS HTTPS final: 200

Автозапуск обеих LXC включён:

pve LXC 301:   onboot: 1
pve01 LXC 301: onboot: 1

12. Проверка CrowdSec после клонирования

Команда на обоих LXC:

systemctl is-active crowdsec traefik
ss -ltn | grep ':8080'
cscli console status
cscli metrics

Итог:

pve:
  crowdsec active
  traefik active
  LAPI 127.0.0.1:8080 listening
  bouncer обновляет decision stream раз в минуту

pve01:
  crowdsec active
  traefik inactive, потому что standby
  LAPI 127.0.0.1:8080 listening
  во время failover Traefik+bouncer работали корректно

Выявлен нюанс клонирования:

/etc/machine-id на pve:   1b6954d643b04911be2a413da2139804
/etc/machine-id на pve01: 1b6954d643b04911be2a413da2139804

Поэтому CrowdSec Console сейчас видит оба LXC как один агент. Для раздельной статистики и независимой идентичности pve01 нужно отдельно переинициализировать и зарегистрировать в CrowdSec Console.