Change timings
This commit is contained in:
parent
7ce59d633b
commit
4719864c78
5 changed files with 68 additions and 31 deletions
21
config.go
21
config.go
|
|
@ -21,9 +21,6 @@ const (
|
||||||
// Время ожидания перед удалением контейнера при миграции между хостами (в секундах)
|
// Время ожидания перед удалением контейнера при миграции между хостами (в секундах)
|
||||||
// Увеличенное время для безопасного переключения трафика на новый хост
|
// Увеличенное время для безопасного переключения трафика на новый хост
|
||||||
MigrationDrainTimeout = 3600 // 1 час
|
MigrationDrainTimeout = 3600 // 1 час
|
||||||
|
|
||||||
// Максимальное количество контейнеров на одного клиента
|
|
||||||
MaxContainersPerClient = 20
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Константы диапазонов портов для Docker
|
// Константы диапазонов портов для Docker
|
||||||
|
|
@ -46,7 +43,7 @@ const (
|
||||||
DefaultPrimaryDBHost = "10.100.10.8"
|
DefaultPrimaryDBHost = "10.100.10.8"
|
||||||
DefaultSecondaryDBHost = "10.100.13.5"
|
DefaultSecondaryDBHost = "10.100.13.5"
|
||||||
DefaultDockerImage = "ptaf-core-nginx-agent:release-4.1.6.409465"
|
DefaultDockerImage = "ptaf-core-nginx-agent:release-4.1.6.409465"
|
||||||
DefaultDockerImageURL = "" // пустой дефолт — не скачивать если не задан в env
|
DefaultDockerImageURL = ""
|
||||||
DefaultWorkerProcesses = 4
|
DefaultWorkerProcesses = 4
|
||||||
DefaultWorkerConnections = 64000
|
DefaultWorkerConnections = 64000
|
||||||
)
|
)
|
||||||
|
|
@ -65,7 +62,7 @@ type Config struct {
|
||||||
WorkerProcesses int
|
WorkerProcesses int
|
||||||
WorkerConnections int
|
WorkerConnections int
|
||||||
DockerImage string
|
DockerImage string
|
||||||
DockerImageURL string // URL для скачивания tar-архива образа если отсутствует локально
|
DockerImageURL string // URL для скачивания tar-архива образа
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadEnvFile загружает переменные окружения из файла /etc/magos/magos.env
|
// loadEnvFile загружает переменные окружения из файла /etc/magos/magos.env
|
||||||
|
|
@ -113,15 +110,15 @@ func getDefaultConfig() Config {
|
||||||
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
|
SecondaryDBHost: getEnv("SECONDARY_DB_HOST", DefaultSecondaryDBHost),
|
||||||
APIToken: getEnv("API_TOKEN", ""),
|
APIToken: getEnv("API_TOKEN", ""),
|
||||||
AntibotNetworks: []string{
|
AntibotNetworks: []string{
|
||||||
"185.66.23.0/24",
|
"185.66.85.0/24",
|
||||||
"185.66.25.0/24",
|
"185.66.86.0/24",
|
||||||
"185.35.26.0/24",
|
"185.35.5.0/24",
|
||||||
"185.35.26.0/24",
|
"185.35.6.0/24",
|
||||||
"212.67.25.0/24",
|
"212.67.26.0/24",
|
||||||
},
|
},
|
||||||
WAFNetworks: []string{
|
WAFNetworks: []string{
|
||||||
"109.238.9.0/24",
|
"109.238.89.0/24",
|
||||||
"89.20.3.0/24",
|
"89.20.63.0/24",
|
||||||
},
|
},
|
||||||
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
|
WorkerProcesses: getEnvInt("WORKER_PROCESSES", DefaultWorkerProcesses),
|
||||||
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
|
WorkerConnections: getEnvInt("WORKER_CONNECTIONS", DefaultWorkerConnections),
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,7 @@ http {
|
||||||
gzip on;
|
gzip on;
|
||||||
server_names_hash_bucket_size 512;
|
server_names_hash_bucket_size 512;
|
||||||
server_tokens off;
|
server_tokens off;
|
||||||
ptaf_fallback 503;
|
ptaf_fallback 418;
|
||||||
proxy_read_timeout 120s;
|
proxy_read_timeout 120s;
|
||||||
proxy_send_timeout 120s;
|
proxy_send_timeout 120s;
|
||||||
send_timeout 60s;
|
send_timeout 60s;
|
||||||
|
|
|
||||||
40
drain.go
40
drain.go
|
|
@ -12,6 +12,35 @@ import (
|
||||||
|
|
||||||
var _ = sql.ErrNoRows // ensure 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() {
|
func processDrainingAngieConfigs() {
|
||||||
currentTime := time.Now().Unix()
|
currentTime := time.Now().Unix()
|
||||||
timeout := int64(MigrationDrainTimeout)
|
timeout := int64(MigrationDrainTimeout)
|
||||||
|
|
@ -117,7 +146,8 @@ func processAllDrainingContainersGlobally(db *sql.DB, hostname string) {
|
||||||
if !shouldBeHere && !skipDrainCheck {
|
if !shouldBeHere && !skipDrainCheck {
|
||||||
log.Printf(" ⚠ Клиент %s не должен быть на этом хосте (waf_instance='%s')", clientTitle, clientInfo.WAFInstance)
|
log.Printf(" ⚠ Клиент %s не должен быть на этом хосте (waf_instance='%s')", clientTitle, clientInfo.WAFInstance)
|
||||||
hasExistingMarkers := false
|
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 {
|
if _, err := os.Stat(fmt.Sprintf("/tmp/ptaf-drain-ptaf_%s_%03d", clientTitle, containerNum)); err == nil {
|
||||||
hasExistingMarkers = true
|
hasExistingMarkers = true
|
||||||
break
|
break
|
||||||
|
|
@ -125,7 +155,7 @@ func processAllDrainingContainersGlobally(db *sql.DB, hostname string) {
|
||||||
}
|
}
|
||||||
if !hasExistingMarkers {
|
if !hasExistingMarkers {
|
||||||
log.Printf(" → Помечаю контейнеры для миграции")
|
log.Printf(" → Помечаю контейнеры для миграции")
|
||||||
for containerNum := 1; containerNum <= MaxContainersPerClient; containerNum++ {
|
for containerNum := 1; containerNum <= actualCount; containerNum++ {
|
||||||
containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientTitle, containerNum)
|
containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientTitle, containerNum)
|
||||||
exists, _ := checkContainerStatus(containerFullName)
|
exists, _ := checkContainerStatus(containerFullName)
|
||||||
if exists {
|
if exists {
|
||||||
|
|
@ -167,7 +197,8 @@ func processAllDrainingContainersGlobally(db *sql.DB, hostname string) {
|
||||||
|
|
||||||
func checkAndProcessExistingDrainMarkers(clientTitle string, containersCount int, processedCount *int) {
|
func checkAndProcessExistingDrainMarkers(clientTitle string, containersCount int, processedCount *int) {
|
||||||
hasAnyDrainMarkers := false
|
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 {
|
if _, err := os.Stat(fmt.Sprintf("/tmp/ptaf-drain-ptaf_%s_%03d", clientTitle, containerNum)); err == nil {
|
||||||
hasAnyDrainMarkers = true
|
hasAnyDrainMarkers = true
|
||||||
break
|
break
|
||||||
|
|
@ -197,7 +228,8 @@ func processDrainingContainers(clientTitle string, maxContainerNum int) {
|
||||||
db := connectToDB(tempConfig)
|
db := connectToDB(tempConfig)
|
||||||
defer db.Close()
|
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)
|
containerName := fmt.Sprintf("%s-ptaf-agent%03d", clientTitle, containerNum)
|
||||||
containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientTitle, containerNum)
|
containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientTitle, containerNum)
|
||||||
containerDir := filepath.Join(baseDir, containerName)
|
containerDir := filepath.Join(baseDir, containerName)
|
||||||
|
|
|
||||||
22
main.go
22
main.go
|
|
@ -71,7 +71,8 @@ func main() {
|
||||||
// Проверка наличия Docker образа только если на хосте есть PTAF-клиенты
|
// Проверка наличия Docker образа только если на хосте есть PTAF-клиенты
|
||||||
if hasPTAFClients(db, instanceNames) {
|
if hasPTAFClients(db, instanceNames) {
|
||||||
if err := ensureDockerImageExists(config); err != nil {
|
if err := ensureDockerImageExists(config); err != nil {
|
||||||
log.Fatalf("❌ Ошибка подготовки Docker образа: %v", err)
|
log.Printf("⚠ Ошибка подготовки Docker образа: %v", err)
|
||||||
|
log.Printf(" → Продолжаем работу, но новые PTAF-контейнеры могут не запуститься")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("ℹ PTAF-клиентов на хосте нет, проверка Docker образа пропускается")
|
log.Printf("ℹ PTAF-клиентов на хосте нет, проверка Docker образа пропускается")
|
||||||
|
|
@ -175,8 +176,6 @@ func ensureDockerImageExists(config Config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadFile скачивает файл по URL и сохраняет в destPath.
|
// downloadFile скачивает файл по URL и сохраняет в destPath.
|
||||||
// Использует общий apiClient с таймаутом 120 сек (для больших tar-архивов может быть мало —
|
|
||||||
// при необходимости увеличь таймаут в apiClient или создай отдельный клиент).
|
|
||||||
func downloadFile(destPath, url string) error {
|
func downloadFile(destPath, url string) error {
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 30 * time.Minute, // образы могут быть большими
|
Timeout: 30 * time.Minute, // образы могут быть большими
|
||||||
|
|
@ -188,10 +187,6 @@ func downloadFile(destPath, url string) error {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return fmt.Errorf("HTTP %d при скачивании %s", resp.StatusCode, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Create(destPath)
|
f, err := os.Create(destPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ошибка создания файла %s: %w", destPath, err)
|
return fmt.Errorf("ошибка создания файла %s: %w", destPath, err)
|
||||||
|
|
@ -204,7 +199,12 @@ func downloadFile(destPath, url string) error {
|
||||||
return fmt.Errorf("ошибка записи файла: %w", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,7 +287,8 @@ func handleClientMigration(clientInfo ClientInfo, hostname string) {
|
||||||
clientInfo.ClientTitle, clientInfo.WAFInstance, hostname)
|
clientInfo.ClientTitle, clientInfo.WAFInstance, hostname)
|
||||||
log.Printf(" Инициирую удаление контейнеров с текущего хоста (миграция)")
|
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)
|
containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientInfo.ClientTitle, containerNum)
|
||||||
exists, _ := checkContainerStatus(containerFullName)
|
exists, _ := checkContainerStatus(containerFullName)
|
||||||
if exists {
|
if exists {
|
||||||
|
|
@ -321,7 +322,8 @@ func cleanStaleDrainMarkers(clientInfo ClientInfo) {
|
||||||
cleanedContainers := 0
|
cleanedContainers := 0
|
||||||
cleanedAngieMarker := false
|
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)
|
containerFullName := fmt.Sprintf("ptaf_%s_%03d", clientInfo.ClientTitle, containerNum)
|
||||||
markerFile := fmt.Sprintf("/tmp/ptaf-drain-%s", containerFullName)
|
markerFile := fmt.Sprintf("/tmp/ptaf-drain-%s", containerFullName)
|
||||||
|
|
||||||
|
|
|
||||||
14
nginx.go
14
nginx.go
|
|
@ -153,9 +153,11 @@ func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, conta
|
||||||
for _, customPort := range httpsPortsToListen {
|
for _, customPort := range httpsPortsToListen {
|
||||||
upstreamName := fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName)
|
upstreamName := fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName)
|
||||||
f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
|
f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
|
||||||
|
maxFails := getMaxFails(res.AppsSettings)
|
||||||
|
failTimeout := getFailTimeout(res.AppsSettings)
|
||||||
for _, ports := range containerPorts {
|
for _, ports := range containerPorts {
|
||||||
if dockerPort, ok := ports.HTTPSPorts[customPort]; ok {
|
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)
|
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 {
|
for _, customPort := range httpPortsToListen {
|
||||||
upstreamName := fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName)
|
upstreamName := fmt.Sprintf("unsecure%d_%d_%s", res.L7ResourceID, customPort, res.ServerName)
|
||||||
f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
|
f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
|
||||||
|
maxFails := getMaxFails(res.AppsSettings)
|
||||||
|
failTimeout := getFailTimeout(res.AppsSettings)
|
||||||
for _, ports := range containerPorts {
|
for _, ports := range containerPorts {
|
||||||
if dockerPort, ok := ports.HTTPPorts[customPort]; ok {
|
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)
|
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)
|
len(customServerBlocks), res.L7ResourceID)
|
||||||
for _, block := range customServerBlocks {
|
for _, block := range customServerBlocks {
|
||||||
var upstreamName string
|
var upstreamName string
|
||||||
|
maxFails := getMaxFails(res.AppsSettings)
|
||||||
|
failTimeout := getFailTimeout(res.AppsSettings)
|
||||||
if block.IsHTTPS {
|
if block.IsHTTPS {
|
||||||
upstreamName = fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, block.Port, block.ServerName)
|
upstreamName = fmt.Sprintf("secure%d_%d_%s", res.L7ResourceID, block.Port, block.ServerName)
|
||||||
f.WriteString(fmt.Sprintf("upstream %s {\n", upstreamName))
|
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 {
|
for _, ports := range containerPorts {
|
||||||
if dockerPort, ok := ports.HTTPSPorts[resolvedHTTPSPort]; ok {
|
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 {
|
} else {
|
||||||
|
|
@ -226,7 +232,7 @@ func writeAngieResourceConfig(f *os.File, config Config, res ResourceData, conta
|
||||||
}
|
}
|
||||||
for _, ports := range containerPorts {
|
for _, ports := range containerPorts {
|
||||||
if dockerPort, ok := ports.HTTPPorts[resolvedHTTPPort]; ok {
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue