diff --git a/containers_part2.go b/containers_part2.go index cd98b7b..be08502 100644 --- a/containers_part2.go +++ b/containers_part2.go @@ -14,28 +14,51 @@ import ( "time" ) -// isAngieEstabOnPort проверяет есть ли активные ESTAB соединения Angie на данном порту -func isAngieEstabOnPort(port int) bool { +// getBusyPortsFromList проверяет список портов одним вызовом ss и возвращает занятые +func getBusyPortsFromList(ports []int) []int { cmd := exec.Command("ss", "-tnap") output, err := cmd.Output() 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") { fields := strings.Fields(line) if len(fields) < 4 { continue } - if fields[0] != "ESTAB" { + localAddr := fields[3] + lastColon := strings.LastIndex(localAddr, ":") + if lastColon < 0 { continue } - localAddr := fields[3] - if strings.HasSuffix(localAddr, portStr) && strings.Contains(line, "angie") { - return true + var port int + if _, err := fmt.Sscanf(localAddr[lastColon+1:], "%d", &port); err != nil { + 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 проверяет занят ли порт процессом который мешает биндингу. @@ -115,50 +138,24 @@ func recreateContainer(containerFullName, composeFile, containerDir, clientTitle } } - // Reload Angie после остановки контейнера чтобы закрыть keepalive ESTAB соединения + // Reload Angie после остановки контейнера чтобы закрыть keepalive соединения log.Printf(" → Reload Angie для закрытия keepalive соединений к остановленному контейнеру...") 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) if len(portsToFree) > 0 { log.Printf(" → Ожидаем освобождения %d портов...", len(portsToFree)) allPortsFree := false for attempt := 0; attempt < 180; attempt++ { - portsBusy := 0 - busyPortsList := make([]int, 0) - for _, port := range portsToFree { - if isPortBusyByAnyProcess(port) { - portsBusy++ - busyPortsList = append(busyPortsList, port) - } - } - if portsBusy == 0 { + // Один вызов ss для всех портов сразу + busyPortsList := getBusyPortsFromList(portsToFree) + if len(busyPortsList) == 0 { allPortsFree = true log.Printf(" ✓ Все порты освобождены (попытка %d)", attempt+1) break } - if attempt%5 == 0 && attempt > 0 { - log.Printf(" → Ещё заняты %d портов: %v (попытка %d/180)", portsBusy, busyPortsList, attempt+1) + if attempt%10 == 0 { + log.Printf(" → Ещё заняты %d портов: %v (попытка %d/180)", len(busyPortsList), busyPortsList, attempt+1) } time.Sleep(1 * time.Second) }