Dlaczego połączenia w stanie FIN_WAIT2 nie są zamykane przez jądro Linuksa?

11

Mam problem w długotrwałym procesie zwanym kube-proxy będącym częścią Kubernetes .

Problem polega na tym, że od czasu do czasu połączenie pozostaje w stanie FIN_WAIT2.

$ sudo netstat -tpn | grep FIN_WAIT2
tcp6       0      0 10.244.0.1:33132        10.244.0.35:48936       FIN_WAIT2   14125/kube-proxy
tcp6       0      0 10.244.0.1:48340        10.244.0.35:56339       FIN_WAIT2   14125/kube-proxy
tcp6       0      0 10.244.0.1:52619        10.244.0.35:57859       FIN_WAIT2   14125/kube-proxy
tcp6       0      0 10.244.0.1:33132        10.244.0.50:36466       FIN_WAIT2   14125/kube-proxy

Połączenia te nakładają się z czasem, co powoduje, że proces jest nieprawidłowy. Zgłosiłem już problem do modułu śledzenia błędów Kubernetes, ale chciałbym zrozumieć, dlaczego takie połączenia nie są zamykane przez jądro Linuksa.

Zgodnie z jego dokumentacją (poszukiwanie tcp_fin_timeout) połączenie w stanie FIN_WAIT2 powinno zostać zamknięte przez jądro po X sekundach, z których X można odczytać z / proc. Na moim komputerze jest ustawiony na 60:

$ cat /proc/sys/net/ipv4/tcp_fin_timeout
60

więc jeśli dobrze to rozumiem, takie połączenia powinny zostać zamknięte o 60 sekund. Ale tak nie jest, pozostają w takim stanie przez wiele godzin.

Chociaż rozumiem również, że połączenia FIN_WAIT2 są dość nietypowe (oznacza to, że host czeka na potwierdzenie ACK ze zdalnego końca połączenia, które może już zniknąć), nie rozumiem, dlaczego połączenia te nie są „zamykane” przez system .

Czy mogę coś z tym zrobić?

Pamiętaj, że ponowne uruchomienie powiązanego procesu jest ostatecznością.

Adam Romanek
źródło
1
Nawiasem mówiąc, w FIN-WAIT2 połączenie nie czeka na ACK (wysłany FIN został już potwierdzony, dlatego nie jesteśmy w FIN-WAIT1). Zamiast tego drugi koniec nadal ma opcję wysyłania nieograniczonej ilości danych.
Hagen von Eitzen

Odpowiedzi:

14

Limit czasu jądra ma zastosowanie tylko wtedy, gdy połączenie jest osierocone. Jeśli połączenie jest nadal podłączone do gniazda, program, który jest właścicielem tego gniazda, jest odpowiedzialny za przekroczenie limitu czasu zamknięcia połączenia. Prawdopodobnie zadzwonił shutdowni czeka na czyste zamknięcie połączenia. Aplikacja może czekać tak długo, jak chce na zakończenie zamykania.

Typowy przebieg czystego zamykania wygląda następująco:

  1. Aplikacja decyduje się zamknąć połączenie i zamyka stronę zapisu połączenia.

  2. Aplikacja czeka, aż druga strona zamknie połowę połączenia.

  3. Aplikacja wykrywa zamknięcie połączenia przez drugą stronę i zamyka gniazdo.

Aplikacja może czekać na kroku 2 tak długo, jak chce.

Wygląda na to, że aplikacja potrzebuje limitu czasu. Gdy zdecyduje się zamknąć połączenie, powinien zrezygnować z czekania, aż druga strona wykona czyste zamknięcie po pewnym rozsądnym czasie.

David Schwartz
źródło
Sprawdzę te informacje z programistami Kubernetes, aby sprawdzić, czy taki limit czasu został wdrożony. Przyjmuję odpowiedź po jej zweryfikowaniu. Niemniej jednak dziękuję za szybką odpowiedź.
Adam Romanek
Chciałbym bardziej szczegółowo zrozumieć twoją odpowiedź. Czy mógłbyś wyjaśnić, co to jest osierocone połączenie?
Adam Romanek
1
@AdamRomanek Osierocone połączenie to takie, które nie ma powiązanych gniazd, to znaczy takie, do którego dostęp może uzyskać tylko samo jądro i na którym żaden proces nie może wykonać operacji.
David Schwartz
To by pomogło ... ” blog.cloudflare.com/...
John Greene
2

Jeśli gniazdo jest jeszcze shutdown (), ale jeszcze nie close (), gniazdo pozostanie w stanie FIN_WAIT2. A ponieważ aplikacja nadal posiada deskryptor pliku, jądro nie zawracałoby sobie głowy czyszczeniem.

L. Yan
źródło
Jest to już wspomniane w zaakceptowanej odpowiedzi.
RalfFriedl
Dodałem konkretnie, że close () nie jest wywoływany.
L. Yan