Change docker restart

This commit is contained in:
Magnus Root 2026-05-06 18:09:26 +03:00
parent 38ab8369cb
commit 853ada564b

View file

@ -14,28 +14,51 @@ import (
"time" "time"
) )
// isAngieEstabOnPort проверяет есть ли активные ESTAB соединения Angie на данном порту // getBusyPortsFromList проверяет список портов одним вызовом ss и возвращает занятые
func isAngieEstabOnPort(port int) bool { func getBusyPortsFromList(ports []int) []int {
cmd := exec.Command("ss", "-tnap") cmd := exec.Command("ss", "-tnap")
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
return false return nil
} }
portStr := fmt.Sprintf(":%d", port)
// Строим set портов для быстрого поиска
portSet := make(map[int]bool, len(ports))
for _, p := range ports {
portSet[p] = true
}
busy := make(map[int]bool)
for _, line := range strings.Split(string(output), "\n") { for _, line := range strings.Split(string(output), "\n") {
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) < 4 { if len(fields) < 4 {
continue continue
} }
if fields[0] != "ESTAB" { localAddr := fields[3]
lastColon := strings.LastIndex(localAddr, ":")
if lastColon < 0 {
continue continue
} }
localAddr := fields[3] var port int
if strings.HasSuffix(localAddr, portStr) && strings.Contains(line, "angie") { if _, err := fmt.Sscanf(localAddr[lastColon+1:], "%d", &port); err != nil {
return true continue
}
if !portSet[port] {
continue
}
state := fields[0]
if state == "LISTEN" {
busy[port] = true
} else if state == "ESTAB" && strings.Contains(line, "docker-proxy") {
busy[port] = true
} }
} }
return false
result := make([]int, 0, len(busy))
for p := range busy {
result = append(result, p)
}
return result
} }
// isPortBusyByAnyProcess проверяет занят ли порт процессом который мешает биндингу. // isPortBusyByAnyProcess проверяет занят ли порт процессом который мешает биндингу.
@ -115,50 +138,24 @@ func recreateContainer(containerFullName, composeFile, containerDir, clientTitle
} }
} }
// Reload Angie после остановки контейнера чтобы закрыть keepalive ESTAB соединения // Reload Angie после остановки контейнера чтобы закрыть keepalive соединения
log.Printf(" → Reload Angie для закрытия keepalive соединений к остановленному контейнеру...") log.Printf(" → Reload Angie для закрытия keepalive соединений к остановленному контейнеру...")
reloadAngie() reloadAngie()
// Ждём пока Angie закроет ESTAB соединения к портам старого контейнера
log.Printf(" → Ожидаем закрытия ESTAB соединений Angie на портах %v...", portsToFree)
for attempt := 0; attempt < 30; attempt++ {
angieEstab := false
for _, port := range portsToFree {
if isAngieEstabOnPort(port) {
angieEstab = true
break
}
}
if !angieEstab {
log.Printf(" ✓ Все ESTAB соединения Angie закрыты (попытка %d)", attempt+1)
break
}
if attempt%5 == 0 && attempt > 0 {
log.Printf(" → Angie ещё держит соединения (попытка %d/30)...", attempt+1)
}
time.Sleep(1 * time.Second)
}
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
if len(portsToFree) > 0 { if len(portsToFree) > 0 {
log.Printf(" → Ожидаем освобождения %d портов...", len(portsToFree)) log.Printf(" → Ожидаем освобождения %d портов...", len(portsToFree))
allPortsFree := false allPortsFree := false
for attempt := 0; attempt < 180; attempt++ { for attempt := 0; attempt < 180; attempt++ {
portsBusy := 0 // Один вызов ss для всех портов сразу
busyPortsList := make([]int, 0) busyPortsList := getBusyPortsFromList(portsToFree)
for _, port := range portsToFree { if len(busyPortsList) == 0 {
if isPortBusyByAnyProcess(port) {
portsBusy++
busyPortsList = append(busyPortsList, port)
}
}
if portsBusy == 0 {
allPortsFree = true allPortsFree = true
log.Printf(" ✓ Все порты освобождены (попытка %d)", attempt+1) log.Printf(" ✓ Все порты освобождены (попытка %d)", attempt+1)
break break
} }
if attempt%5 == 0 && attempt > 0 { if attempt%10 == 0 {
log.Printf(" → Ещё заняты %d портов: %v (попытка %d/180)", portsBusy, busyPortsList, attempt+1) log.Printf(" → Ещё заняты %d портов: %v (попытка %d/180)", len(busyPortsList), busyPortsList, attempt+1)
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }