Change timings

This commit is contained in:
Magnus Root 2026-03-13 12:25:51 +03:00
parent 7ce59d633b
commit 4719864c78
5 changed files with 68 additions and 31 deletions

View file

@ -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),

View file

@ -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;

View file

@ -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)

22
main.go
View file

@ -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)

View file

@ -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))
}
}
}