Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ body:
attributes:
label: Версии
description: Panel, Node Agent, HAProxy и ОС ноды.
placeholder: "Panel 2.0.0, Agent 2.0.0, HAProxy 3.4, Ubuntu 24.04"
placeholder: "Panel 2.0.1, Agent 2.0.1, HAProxy 3.4, Ubuntu 24.04"
validations:
required: true
- type: textarea
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 2.0.1 — Толерантность на одном DNS-пуле

- Исправлено: маршрут с одним сервером-доменом в режиме DNS-пула терял «Толерантность» и алгоритм «Меньше соединений» при сохранении — панель сворачивала его в старый формат одиночного маршрута, где эти настройки не хранятся. Теперь такой маршрут остаётся пулом серверов, и толерантность сохраняется и применяется агентом. Маршруты без толерантности и с закреплением по хешу IP рендерятся как раньше, байт в байт.
- Агент для этого не меняется; номер 2.0.1 у Panel и Node Agent общий.

## 2.0.0 — Готовые образы и расширенная балансировка

Panel и Node Agent — 2.0.0, HAProxy renderer v21. Первый стабильный релиз после 1.0.8; в него вошли все изменения предварительных сборок 1.1.x. Заметки к релизу с инструкцией по обновлению: [docs/releases/2.0.0.md](docs/releases/2.0.0.md).
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ curl -fsSL https://raw.githubusercontent.com/NodeFlow-dev/nodeflow/main/install.
2. предложит режим доступа: Caddy cookie-gate (ссылка активации ставит защищённую cookie) или без него — токен администратора Panel нужен в любом случае;
3. установит Docker и **Caddy** — Caddy сам получит сертификат Let's Encrypt и будет проксировать HTTPS на Panel, которая слушает только `127.0.0.1:8080`;
4. скачает install kit последнего релиза, проверит его по SHA-256, который GitHub публикует для каждого asset релиза, возьмёт из него `compose.release.yaml`, сгенерирует `.env`, CA, сертификат mTLS и ключ подписи обновлений в `/opt/nodeflow`;
5. выполнит `docker compose pull && docker compose up -d` с образом `ghcr.io/nodeflow-dev/nodeflow-panel:2.0.0` — на сервере ничего не собирается, миграции БД применяет сам образ;
5. выполнит `docker compose pull && docker compose up -d` с образом `ghcr.io/nodeflow-dev/nodeflow-panel:2.0.1` — на сервере ничего не собирается, миграции БД применяет сам образ;
6. опубликует подписанные релизы Node Agent (amd64 и arm64) и сохранит реквизиты входа в `~/nodeflow-credentials.txt` (права `0600`).

Файрвол установщик не меняет. Для автоматизации без вопросов передайте переменные `NODEFLOW_DOMAIN`, `NODEFLOW_AUTH_MODE=cookie|none` и при необходимости `NODEFLOW_VERSION=2.0.0` (по умолчанию — последний релиз):
Expand Down Expand Up @@ -90,7 +90,7 @@ sudo NODE_AGENT_TOKEN=<токен> bash install-node.sh
Команды выполняются от root (`sudo -i`): каталог `/opt/nodeflow` закрыт для остальных пользователей.

```bash
v=2.0.0
v=2.0.1
kit=NodeFlow-Panel-$v-Agent-$v-install-kit
curl -fsSLO "https://github.com/NodeFlow-dev/nodeflow/releases/download/v$v/$kit.tar.gz"
# сверьте с digest asset'а на странице релиза или в API:
Expand Down Expand Up @@ -119,7 +119,7 @@ curl -fsSL https://raw.githubusercontent.com/NodeFlow-dev/nodeflow/main/install.

Найдя `/opt/nodeflow/.env`, он работает как апгрейдер: делает `pg_dump` и архив конфигурации в `/var/backups/nodeflow/`, сохраняет `.env`, `tls/`, `pki/` и Caddy-сниппет `/etc/caddy/conf.d/nodeflow-panel.caddy`, переводит установку со сборки из исходников на образ ghcr.io, запускает миграции и проверяет, что Panel отвечает версией 2.0.0. При ошибке возвращаются прежние `compose.yaml` и `.env`; дамп БД остаётся (миграции автоматически не откатываются).

Затем в **Настройки → Node Agent** обновите ноды до 2.0.0 — updater заменит Agent атомарно и откатит его при неудачном запуске. Самообновление не меняет systemd-юнит, поэтому на нодах, установленных версиями 1.0.x, один раз добавьте права на настройку pipe-буферов ядра (без этого splice остаётся на 256 КиБ) — или переустановите Agent из меню ноды:
Затем в **Настройки → Node Agent** обновите ноды до 2.0.1 — updater заменит Agent атомарно и откатит его при неудачном запуске. Самообновление не меняет systemd-юнит, поэтому на нодах, установленных версиями 1.0.x, один раз добавьте права на настройку pipe-буферов ядра (без этого splice остаётся на 256 КиБ) — или переустановите Agent из меню ноды:

```bash
sudo install -d /etc/systemd/system/nodeflow-node-agent.service.d
Expand Down
2 changes: 1 addition & 1 deletion cmd/node-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/nodeflow/nodeflow/internal/agent"
)

var version = "2.0.0"
var version = "2.0.1"

func main() {
showVersion := flag.Bool("version", false, "print Node Agent version and exit")
Expand Down
2 changes: 1 addition & 1 deletion cmd/panel-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

// panelVersion is the published Panel release version. Release builds may
// replace it with -ldflags "-X main.panelVersion=...".
var panelVersion = "2.0.0"
var panelVersion = "2.0.1"

func main() {
if len(os.Args) > 1 {
Expand Down
4 changes: 2 additions & 2 deletions compose.release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ services:
<<: *hardening

migrate:
image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.0.0}
image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.0.1}
restart: "no"
command: ["migrate"]
depends_on:
Expand Down Expand Up @@ -79,7 +79,7 @@ services:
<<: *hardening

panel-api:
image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.0.0}
image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.0.1}
restart: unless-stopped
depends_on:
migrate:
Expand Down
8 changes: 4 additions & 4 deletions docs/install/README-NODE-AGENT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ NODEFLOW NODE AGENT — SIGNED RELEASE UPLOAD
===========================================

Бинарники Agent — отдельные assets GitHub Release:
nodeflow-node-agent-2.0.0-linux-amd64
nodeflow-node-agent-2.0.0-linux-arm64
nodeflow-node-agent-2.0.1-linux-amd64
nodeflow-node-agent-2.0.1-linux-arm64

install.sh публикует их в Panel сам. Для ручной загрузки скачайте файл и
сверьте SHA-256 с digest, который GitHub показывает для asset'а:

v=2.0.0 f=nodeflow-node-agent-2.0.0-linux-amd64
v=2.0.1 f=nodeflow-node-agent-2.0.1-linux-amd64
curl -fsSLO https://github.com/NodeFlow-dev/nodeflow/releases/download/v$v/$f
curl -fsSL https://api.github.com/repos/NodeFlow-dev/nodeflow/releases/tags/v$v \
| jq -r --arg n "$f" '.assets[] | select(.name == $n) | .digest'
Expand All @@ -17,7 +17,7 @@ install.sh публикует их в Panel сам. Для ручной загр
В Panel откройте:
Настройки → Node Agent (подписанные релизы)

Версия 2.0.0, ОС linux, архитектура amd64 или arm64 — по файлу. Выберите
Версия 2.0.1, ОС linux, архитектура amd64 или arm64 — по файлу. Выберите
бинарник и нажмите «Загрузить и подписать». Затем назначьте релиз на
странице нужной ноды.

Expand Down
6 changes: 3 additions & 3 deletions docs/install/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ <h1>Как установить Panel и подключить первую HAPro
<div class="file"><b>Корень комплекта</b><code>install.sh</code><p>Установщик и апгрейдер Panel, <code>compose.release.yaml</code> для готового образа <code>ghcr.io/nodeflow-dev/nodeflow-panel</code>, шаблон <code>nodeflow.env.example</code>, <code>SHA256SUMS</code> файлов комплекта.</p></div>
<div class="file"><b>scripts/ · configs/ · docs/</b><code>scripts/install-node.sh</code><p>Скрипты PKI (<code>init-mtls-pki.sh</code>, <code>init-update-signing-key.sh</code>), ручная установка Agent и подготовка UFW ноды, systemd-юнит Agent, эта инструкция и примеры reverse proxy в <code>docs/install/reverse-proxy/</code>.</p></div>
</div>
<div class="important" style="margin-top:12px"><b>Бинарники Agent в комплект не входят.</b> <code>nodeflow-node-agent-2.0.0-linux-amd64</code> и <code>nodeflow-node-agent-2.0.0-linux-arm64</code> лежат отдельными assets GitHub Release. <code>install.sh</code> скачивает оба, сверяет SHA-256 с digest, который GitHub публикует для asset'а, и публикует их в Panel.</div>
<div class="important" style="margin-top:12px"><b>Бинарники Agent в комплект не входят.</b> <code>nodeflow-node-agent-2.0.1-linux-amd64</code> и <code>nodeflow-node-agent-2.0.1-linux-arm64</code> лежат отдельными assets GitHub Release. <code>install.sh</code> скачивает оба, сверяет SHA-256 с digest, который GitHub публикует для asset'а, и публикует их в Panel.</div>
</div></section>

<section id="prepare"><div class="head"><div class="step">1</div><div><h2>Подготовьте сервер Panel</h2><p>Нужен домен, публичный IP и чистый Ubuntu или Debian.</p></div></div><div class="body">
Expand Down Expand Up @@ -158,7 +158,7 @@ <h1>Как установить Panel и подключить первую HAPro

<section id="release"><div class="head"><div class="step">5</div><div><h2>Опубликуйте Node Agent</h2><p>Без совместимого подписанного релиза Panel не начнёт установку ноды.</p></div></div><div class="body">
<p>Автоматический установщик публикует Agent сам. Вручную:</p>
<ol><li>Скачайте <code>nodeflow-node-agent-2.0.0-linux-amd64</code> (или <code>-arm64</code>) из assets GitHub Release.</li><li>Сверьте <code>sha256sum</code> файла с digest asset'а на странице релиза (или в <code>https://api.github.com/repos/NodeFlow-dev/nodeflow/releases/tags/v2.0.0</code>, поле <code>digest</code>).</li><li>Откройте <b>Настройки → Node Agent (подписанные релизы)</b>.</li><li>Версия: <code>2.0.0</code>.</li><li>ОС: <code>linux</code>.</li><li>Архитектура: <code>amd64</code> или <code>arm64</code> — по файлу.</li><li>Выберите файл и нажмите «Загрузить и подписать».</li></ol>
<ol><li>Скачайте <code>nodeflow-node-agent-2.0.1-linux-amd64</code> (или <code>-arm64</code>) из assets GitHub Release.</li><li>Сверьте <code>sha256sum</code> файла с digest asset'а на странице релиза (или в <code>https://api.github.com/repos/NodeFlow-dev/nodeflow/releases/tags/v2.0.0</code>, поле <code>digest</code>).</li><li>Откройте <b>Настройки → Node Agent (подписанные релизы)</b>.</li><li>Версия: <code>2.0.0</code>.</li><li>ОС: <code>linux</code>.</li><li>Архитектура: <code>amd64</code> или <code>arm64</code> — по файлу.</li><li>Выберите файл и нажмите «Загрузить и подписать».</li></ol>
<div class="important">Panel подписывает бинарник собственным Ed25519-ключом. Нода проверяет подпись, SHA-256 и порядок релиза; при неудачном запуске updater возвращает прежний Agent.</div>
</div></section>

Expand All @@ -177,7 +177,7 @@ <h1>Как установить Panel и подключить первую HAPro
<div class="code"><div class="code-label">3. Проверить обновление<button class="copy">Копировать</button></div><pre>cd /opt/nodeflow
sudo docker compose ps
curl -fsS http://127.0.0.1:8080/healthz</pre></div>
<p>После обновления Panel обновите Agent на нодах: «Настройки → Node Agent» → «Обновить до 2.0.0». Для нод, установленных версиями 1.0.x, см. раздел «Обновление с 1.0.x» в заметках к релизу 2.0.0 (однократный systemd drop-in для pipe-буферов ядра).</p>
<p>После обновления Panel обновите Agent на нодах: «Настройки → Node Agent» → «Обновить до 2.0.1». Для нод, установленных версиями 1.0.x, см. раздел «Обновление с 1.0.x» в заметках к релизу 2.0.0 (однократный systemd drop-in для pipe-буферов ядра).</p>
</div></section>

<section id="check"><div class="head"><div class="step">8</div><div><h2>Проверьте результат</h2><p>Четыре короткие проверки после установки или обновления.</p></div></div><div class="body">
Expand Down
16 changes: 16 additions & 0 deletions docs/releases/2.0.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# NodeFlow 2.0.1

Panel **2.0.1** · Node Agent **2.0.1** · HAProxy renderer **v21**

Исправление к 2.0.0.

## Исправлено

- **«Толерантность» пропадала после сохранения** на маршрутах, где единственный сервер — домен в режиме DNS-пула (например, `co-de…` со всеми его A/AAAA-адресами), а первый сервер выбирается «Меньше соединений». Панель сохраняла такой маршрут в старом формате одиночного маршрута, в котором нет полей алгоритма и толерантности, и при следующем открытии поле было пустым. Теперь маршрут остаётся пулом серверов: толерантность сохраняется, а агент каждые 5 с выравнивает веса адресов пула.
- Маршруты без толерантности и с закреплением по хешу IP клиента не меняются: конфигурация HAProxy для них рендерится байт в байт как в 2.0.0.

## Обновление

Повторно запустите `install.sh` или обновите образ до `ghcr.io/nodeflow-dev/nodeflow-panel:2.0.1`. Миграций нет. Node Agent обновлять не обязательно — 2.0.0 уже умеет толерантность; 2.0.1 можно назначить в «Настройки → Node Agent» для единой версии.

После обновления откройте затронутые маршруты, снова задайте толерантность и сохраните.
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "nodeflow-frontend",
"private": true,
"version": "2.0.0",
"version": "2.0.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
68 changes: 68 additions & 0 deletions internal/panel/route_single_pool_tolerance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package panel

import (
"strings"
"testing"
)

// A single DNS-pool server with source_table + leastconn tolerance must keep
// the tolerance (it used to collapse to the legacy single-target route and
// silently drop balance_algorithm and the tolerance).
func TestValidateRouteSingleDNSPoolKeepsLeastConnTolerance(t *testing.T) {
port, enabled, check := 443, true, true
tolerance := 0.2
in := routeInput{
Name: "de", ListenerIP: "*", ListenerPort: &port, MatchMode: "sni",
SNIs: []string{"co-de.example.com"}, ProxyProtocol: "v2", QuotaAction: "observe",
Enabled: &enabled, HealthCheck: &check, BalanceMode: "pool",
StickyMode: StickyModeSourceTable, BalanceAlgorithm: BalanceAlgorithmLeastConn,
BalanceTolerance: &tolerance,
Servers: []routeServerInput{{Name: "de", TargetType: "tcp", Host: "co-de.example.com", Port: 443, DNSPool: true}},
}
spec, err := validateRoute(in, true)
if err != nil {
t.Fatal(err)
}
if len(spec.Servers) != 1 || !spec.Servers[0].DNSPool {
t.Fatalf("servers = %+v, want one explicit DNS-pool server", spec.Servers)
}
if spec.BalanceAlgorithm != BalanceAlgorithmLeastConn || spec.LeastPingTolerance != 0.2 {
t.Fatalf("algorithm=%q tolerance=%v, want leastconn 0.2", spec.BalanceAlgorithm, spec.LeastPingTolerance)
}
if !specNeedsLeastConnAgent(spec) {
t.Fatal("tolerance route must require the leastconn Agent controller")
}

// Without a tolerance the same input stays the byte-identical legacy route.
in.BalanceTolerance = nil
in.BalanceAlgorithm = ""
spec, err = validateRoute(in, true)
if err != nil {
t.Fatal(err)
}
if len(spec.Servers) != 0 || !spec.DNSPool {
t.Fatalf("plain single DNS pool must stay legacy: servers=%d dns_pool=%v", len(spec.Servers), spec.DNSPool)
}

// sticky source ignores algorithm and tolerance: stays legacy too.
in.StickyMode, in.BalanceTolerance = StickyModeSource, &tolerance
spec, err = validateRoute(in, true)
if err != nil {
t.Fatal(err)
}
if len(spec.Servers) != 0 {
t.Fatalf("source-hash single DNS pool must stay legacy, got %d servers", len(spec.Servers))
}
}

func TestSingleDNSPoolToleranceRendersAgentManagedTemplate(t *testing.T) {
_, got := renderPayload(t, `{"name":"de","match_mode":"fallback","balance_mode":"pool","balance_algorithm":"leastconn","sticky_mode":"source_table","balance_tolerance":0.2,
"servers":[{"name":"de","host":"co-de.example.com","port":443,"dns_pool":true}]}`)
backend := backendSection(got.Config)
if !strings.Contains(backend, "algo=leastconn tolerance=0.20") {
t.Fatalf("missing Agent tolerance annotation:\n%s", backend)
}
if !strings.Contains(backend, "server-template ") || !strings.Contains(backend, "stick on src") {
t.Fatalf("want DNS-pool server-template with source_table stickiness:\n%s", backend)
}
}
Loading
Loading