From 4719864c7803f6794ec1cb34617da0078c2fc09a Mon Sep 17 00:00:00 2001 From: Magnus Root Date: Fri, 13 Mar 2026 12:25:51 +0300 Subject: [PATCH] Change timings --- config.go | 21 +++++++++------------ containers_part2.go | 2 +- drain.go | 40 ++++++++++++++++++++++++++++++++++++---- main.go | 22 ++++++++++++---------- nginx.go | 14 ++++++++++---- 5 files changed, 68 insertions(+), 31 deletions(-) diff --git a/config.go b/config.go index 2d01904..0813b2f 100644 --- a/config.go +++ b/config.go @@ -21,9 +21,6 @@ const ( // Время ожидания перед удалением контейнера при миграции между хостами (в секундах) // Увеличенное время для безопасного переключения трафика на новый хост MigrationDrainTimeout = 3600 // 1 час - - // Максимальное количество контейнеров на одного клиента - MaxContainersPerClient = 20 ) // Константы диапазонов портов для Docker @@ -46,7 +43,7 @@ const ( DefaultPrimaryDBHost = "10.100.10.8" DefaultSecondaryDBHost = "10.100.13.5" DefaultDockerImage = "ptaf-core-nginx-agent:release-4.1.6.409465" - DefaultDockerImageURL = "" // пустой дефолт — не скачивать если не задан в env + DefaultDockerImageURL = "" DefaultWorkerProcesses = 4 DefaultWorkerConnections = 64000 ) @@ -65,7 +62,7 @@ type Config struct { WorkerProcesses int WorkerConnections int DockerImage string - DockerImageURL string // URL для скачивания tar-архива образа если отсутствует локально + DockerImageURL string // URL для скачивания tar-архива образа } // loadEnvFile загружает переменные окружения из файла /etc/magos/magos.env @@ -113,15 +110,15 @@ func getDefaultConfig() Config { SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost), APIToken: getEnv("API_TOKEN", ""), AntibotNetworks: []string{ - "185.66.23.0/24", - "185.66.25.0/24", - "185.35.26.0/24", - "185.35.26.0/24", - "212.67.25.0/24", + "185.66.85.0/24", + "185.66.86.0/24", + "185.35.5.0/24", + "185.35.6.0/24", + "212.67.26.0/24", }, WAFNetworks: []string{ - "109.238.9.0/24", - "89.20.3.0/24", + "109.238.89.0/24", + "89.20.63.0/24", }, WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses), WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections), diff --git a/containers_part2.go b/containers_part2.go index b447931..4129ed6 100644 --- a/containers_part2.go +++ b/containers_part2.go @@ -395,7 +395,7 @@ http { gzip on; server_names_hash_bucket_size 512; server_tokens off; - ptaf_fallback 503; + ptaf_fallback 418; proxy_read_timeout 120s; proxy_send_timeout 120s; send_timeout 60s; diff --git a/drain.go b/drain.go index 5d86a5a..a75fa01 100644 --- a/drain.go +++ b/drain.go @@ -12,6 +12,35 @@ import ( var _ = sql.ErrNoRows // ensure import +// getActualContainerCount возвращает реальное количество контейнеров клиента — +// максимум из: существующих директорий на диске и drain-маркеров в /tmp. +// Используется вместо захардкоженного MaxContainersPerClient. +func getActualContainerCount(clientTitle string) int { + max := 0 + + // Считаем по директориям на диске + baseDir := filepath.Join("/home/install/ptaf", clientTitle) + for num := 1; ; num++ { + containerName := fmt.Sprintf("%s-ptaf-agent%03d", clientTitle, num) + containerDir := filepath.Join(baseDir, containerName) + if _, err := os.Stat(containerDir); err != nil { + break + } + max = num + } + + // Считаем по drain-маркерам в /tmp (контейнеры могли быть уже удалены с диска) + for num := max + 1; ; num++ { + markerFile := fmt.Sprintf("/tmp/ptaf-drain-ptaf_%s_%03d", clientTitle, num) + if _, err := os.Stat(markerFile); err != nil { + break + } + max = num + } + + return max +} + func processDrainingAngieConfigs() { currentTime := time.Now().Unix() timeout := int64(MigrationDrainTimeout) @@ -117,7 +146,8 @@ func processAllDrainingContainersGlobally(db *sql.DB, hostname string) { if !shouldBeHere && !skipDrainCheck { log.Printf(" ⚠ Клиент %s не должен быть на этом хосте (waf_instance='%s')", clientTitle, clientInfo.WAFInstance) hasExistingMarkers := false - for containerNum := 1; containerNum <= MaxContainersPerClient; containerNum++ { + actualCount := getActualContainerCount(clientTitle) + for containerNum := 1; containerNum <= actualCount; containerNum++ { if _, err := os.Stat(fmt.Sprintf("/tmp/ptaf-drain-ptaf_%s_%03d", clientTitle, containerNum)); err == nil { hasExistingMarkers = true break @@ -125,7 +155,7 @@ func processAllDrainingContainersGlobally(db *sql.DB, hostname string) { } if !hasExistingMarkers { log.Printf(" → Помечаю контейнеры для миграции") - for containerNum := 1; containerNum <= MaxContainersPerClient; containerNum++ { + for containerNum := 1; containerNum <= actualCount; containerNum++ { containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientTitle, containerNum) exists, _ := checkContainerStatus(containerFullName) if exists { @@ -167,7 +197,8 @@ func processAllDrainingContainersGlobally(db *sql.DB, hostname string) { func checkAndProcessExistingDrainMarkers(clientTitle string, containersCount int, processedCount *int) { hasAnyDrainMarkers := false - for containerNum := 1; containerNum <= MaxContainersPerClient; containerNum++ { + actualCount := getActualContainerCount(clientTitle) + for containerNum := 1; containerNum <= actualCount; containerNum++ { if _, err := os.Stat(fmt.Sprintf("/tmp/ptaf-drain-ptaf_%s_%03d", clientTitle, containerNum)); err == nil { hasAnyDrainMarkers = true break @@ -197,7 +228,8 @@ func processDrainingContainers(clientTitle string, maxContainerNum int) { db := connectToDB(tempConfig) defer db.Close() - for containerNum := maxContainerNum + 1; containerNum <= MaxContainersPerClient; containerNum++ { + actualCount := getActualContainerCount(clientTitle) + for containerNum := maxContainerNum + 1; containerNum <= actualCount; containerNum++ { containerName := fmt.Sprintf("%s-ptaf-agent%03d", clientTitle, containerNum) containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientTitle, containerNum) containerDir := filepath.Join(baseDir, containerName) diff --git a/main.go b/main.go index 963a3a4..c494bcf 100644 --- a/main.go +++ b/main.go @@ -71,7 +71,8 @@ func main() { // Проверка наличия Docker образа только если на хосте есть PTAF-клиенты if hasPTAFClients(db, instanceNames) { if err := ensureDockerImageExists(config); err != nil { - log.Fatalf("❌ Ошибка подготовки Docker образа: %v", err) + log.Printf("⚠ Ошибка подготовки Docker образа: %v", err) + log.Printf(" → Продолжаем работу, но новые PTAF-контейнеры могут не запуститься") } } else { log.Printf("ℹ PTAF-клиентов на хосте нет, проверка Docker образа пропускается") @@ -175,8 +176,6 @@ func ensureDockerImageExists(config Config) error { } // downloadFile скачивает файл по URL и сохраняет в destPath. -// Использует общий apiClient с таймаутом 120 сек (для больших tar-архивов может быть мало — -// при необходимости увеличь таймаут в apiClient или создай отдельный клиент). func downloadFile(destPath, url string) error { client := &http.Client{ Timeout: 30 * time.Minute, // образы могут быть большими @@ -188,10 +187,6 @@ func downloadFile(destPath, url string) error { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("HTTP %d при скачивании %s", resp.StatusCode, url) - } - f, err := os.Create(destPath) if err != nil { return fmt.Errorf("ошибка создания файла %s: %w", destPath, err) @@ -204,7 +199,12 @@ func downloadFile(destPath, url string) error { return fmt.Errorf("ошибка записи файла: %w", err) } - log.Printf(" → Скачано: %.1f МБ", float64(written)/1024/1024) + if written == 0 { + os.Remove(destPath) + return fmt.Errorf("скачан пустой файл (HTTP %d)", resp.StatusCode) + } + + log.Printf(" → Скачано: %.1f МБ (HTTP %d)", float64(written)/1024/1024, resp.StatusCode) return nil } @@ -287,7 +287,8 @@ func handleClientMigration(clientInfo ClientInfo, hostname string) { clientInfo.ClientTitle, clientInfo.WAFInstance, hostname) log.Printf(" Инициирую удаление контейнеров с текущего хоста (миграция)") - for containerNum := 1; containerNum <= MaxContainersPerClient; containerNum++ { + actualCount := getActualContainerCount(clientInfo.ClientTitle) + for containerNum := 1; containerNum <= actualCount; containerNum++ { containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientInfo.ClientTitle, containerNum) exists, _ := checkContainerStatus(containerFullName) if exists { @@ -321,7 +322,8 @@ func cleanStaleDrainMarkers(clientInfo ClientInfo) { cleanedContainers := 0 cleanedAngieMarker := false - for containerNum := 1; containerNum <= MaxContainersPerClient; containerNum++ { + actualCount := getActualContainerCount(clientInfo.ClientTitle) + for containerNum := 1; containerNum <= actualCount; containerNum++ { containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientInfo.ClientTitle, containerNum) markerFile := fmt.Sprintf("/tmp/ptaf-drain-%s", containerFullName) diff --git a/nginx.go b/nginx.go index 7876ba9..fc81f17 100644 --- a/nginx.go +++ b/nginx.go @@ -153,9 +153,11 @@ func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, conta for _, customPort := range httpsPortsToListen { upstreamName := fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName) f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName)) + maxFails := getMaxFails(res.AppsSettings) + failTimeout := getFailTimeout(res.AppsSettings) for _, ports := range containerPorts { if dockerPort, ok := ports.HTTPSPorts[customPort]; ok { - f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50;\n", dockerPort)) + f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50 max_fails=%d fail_timeout=%ds;\n", dockerPort, maxFails, failTimeout)) } } writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamAngieCustom, res.AppsSettings.SNI) @@ -166,9 +168,11 @@ func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, conta for _, customPort := range httpPortsToListen { upstreamName := fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName) f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName)) + maxFails := getMaxFails(res.AppsSettings) + failTimeout := getFailTimeout(res.AppsSettings) for _, ports := range containerPorts { if dockerPort, ok := ports.HTTPPorts[customPort]; ok { - f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50;\n", dockerPort)) + f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50 max_fails=%d fail_timeout=%ds;\n", dockerPort, maxFails, failTimeout)) } } writeCustomUpstreamOrDefault(f, res.AppsSettings.UpstreamAngieCustom, res.AppsSettings.SNI) @@ -186,6 +190,8 @@ func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, conta len(customServerBlocks), res.L7ResourceID) for _, block := range customServerBlocks { var upstreamName string + maxFails := getMaxFails(res.AppsSettings) + failTimeout := getFailTimeout(res.AppsSettings) if block.IsHTTPS { upstreamName = fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, block.Port, block.ServerName) f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName)) @@ -205,7 +211,7 @@ func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, conta } for _, ports := range containerPorts { if dockerPort, ok := ports.HTTPSPorts[resolvedHTTPSPort]; ok { - f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50;\n", dockerPort)) + f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50 max_fails=%d fail_timeout=%ds;\n", dockerPort, maxFails, failTimeout)) } } } else { @@ -226,7 +232,7 @@ func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, conta } for _, ports := range containerPorts { if dockerPort, ok := ports.HTTPPorts[resolvedHTTPPort]; ok { - f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50;\n", dockerPort)) + f.WriteString(fmt.Sprintf(" server 127.0.0.1:%d weight=50 max_fails=%d fail_timeout=%ds;\n", dockerPort, maxFails, failTimeout)) } } }