Mam dziś dla ciebie tajemnicę. Na platformie Azure prowadzimy mały, trzy węzłowy klaster Elasticsearch oparty na CoreOS (2023.5.0 / Linux 4.19.25-coreos). Elasticsearch jest uruchamiany w kontenerze dokera w trybie sieci hosta. Po prawie prawie bezobsługowym działaniu od ponad roku obserwujemy, jak maszyny wchodzą w bardzo interesujący stan.
Aktualizacja
Ten problem został rozwiązany przez poprawkę sterownika w jądrze systemu Linux . Zobacz odpowiedź poniżej.
Objawy
Zasadniczo sieć między dotkniętym komputerem a pozostałymi dwoma węzłami umiera. Wszystkie są w tej samej sieci wirtualnej i tej samej podsieci i mogą zwyczajowo komunikować się ze wszystkimi innymi. Dotknięty węzeł może być nadal dostępny z innych podsieci (mogę w nim ssh) i z innej sprawdzonej sieci wirtualnej. Maszyna ma również (bardzo nierówne) połączenie z Internetem, ale większość żądań po prostu kończy się.
Zauważyliśmy, że w węźle dotkniętym problemem liczba zgłoszonych „gniazd” /proc/net/sockstat
jest bardzo wysoka (~ 4,5 k zamiast ~ 300 w zdrowym węźle). Monitorowanie pokazuje, że liczba ta gwałtownie rośnie od momentu, gdy węzeł stanie się niedostępny.
Zabawne jest to, że nie możemy zidentyfikować źródła tych używanych gniazd:
# cat /proc/net/sockstat
sockets: used 4566
TCP: inuse 2 orphan 0 tw 2 alloc 98 mem 4
UDP: inuse 1 mem 0
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
# cat /proc/net/sockstat6
TCP6: inuse 98
UDP6: inuse 1
UDPLITE6: inuse 0
RAW6: inuse 1
FRAG6: inuse 0 memory 0
Poza tym maszyna wydaje się w porządku. Nie działają żadne podejrzane procesy, użycie procesora jest minimalne, a ilość dostępnej pamięci jest duża.
Pingowanie „nieosiągalnej” maszyny wirtualnej w tej samej podsieci skutkuje kilkoma EAGAIN
odpowiedziami, recvmsg
a następnie przejściem do ENOBUFS
powrotu sendmsg
. Strace ping wyjściowy tutaj
Zebrałem dodatkowe dane wyjściowe (przed wprowadzeniem jakichkolwiek modyfikacji w systemie) i opublikowałem je w tej liście: https://gist.github.com/privatwolke/e7e2e7eb0272787765f5d3726f37107c
Analiza
Próbowaliśmy zamknąć wszystko, co możemy wymyślić na serwerze, a elasticsearch jest pierwszym podejrzanym. Ale zamknięcie pojemnika elasticsearch nie zwalnia zużytych gniazd. To samo dotyczy wszystkich procesów związanych z CoreOS (silnik aktualizacji, locksmithd, ...), a nawet całego środowiska wykonawczego Docker lub rzeczy specyficznych dla platformy Azure. Wydawało się, że nic nie pomaga.
Ale teraz robi się jeszcze dziwniej: próbowaliśmy uruchomić tcpdump
maszynę, aby zobaczyć, co się dzieje. I oto: problem sam się rozwiązał, przywrócono łączność. Naszą teorią było to, że tcpdump wykonuje pewnego rodzaju syscall, który to rozwiązuje. Uruchomiliśmy tcpdump z gdb i ustawiliśmy punkty przerwania dla wszystkich wywołań systemowych. Po przejściu przez wiele punktów przerwania, w końcu odkryliśmy, że ustawianie trybu promisującego na gnieździe przechwytującym (szczególnie ta linia w libpcap ) jest rzeczą, która resetuje licznik używanych gniazd i przywraca nas do normalnego stanu.
Dodatkowe ustalenia
- Sprawdziliśmy, że uruchomienie
tcpdump
z-p/--no-promiscuous-mode
flagą nie usuwa licznika używanych gniazd i przywraca maszynę do stanu używalności. - Uruchomienie
ifconfig eth0 txqueuelen 1001
resetuje licznik używanych gniazd, ale łączność nie jest przywracana. - Ręczne ustawienie trybu promis
ip link set eth0 promisc on
również nie przywraca łączności.net.ipv4.xfrm4_gc_thresh
jest ustawiony na 32768, a jego nieznaczne zwiększenie nie rozwiązuje problemu.
Jesteśmy w kontakcie z Azure, które są tak samo zaskoczone jak my. Rozumiem, że to prawdopodobnie nie jest problem, ale tylko objaw. Ale to jedyna namacalna rzecz, jaką do tej pory znalazłem. Mam nadzieję, że dzięki zrozumieniu symptomu mogę zbliżyć się do pierwotnej przyczyny. Interfejsy sieciowe na platformie Azure są uruchamiane przy użyciu tego sterownika sieciowego .
Może winę ponosi CoreOS / Kernel?
Z punktu widzenia osi czasu problemy zaczęły się 11.03.2019, czyli w dniu, w którym CoreOS automatycznie zaktualizował się do najnowszej wersji. Zgodnie z informacjami o wersji ta aktualizacja zawierała aktualizację jądra z 4.15.23 do 4.19.25 . Nadal przeglądam dzienniki zmian, aby sprawdzić, czy coś może tam stanowić problem. Do tej pory odkryłem tylko, że sterownik sieci hyperv otrzymał w ostatnich miesiącach sporo aktualizacji , z których nie wszystkie wydają się być częścią 4.19.25. Zestaw poprawek zastosowany przez CoreOS w 4.19.25 nie jest aż tak imponujący , ale łatka wprowadzająca fałszywy moduł nf_conntrack_ipv4 jest nowa.
Aktualizacja: Możliwa powiązana przychodząca łata do jądra?
Wsparcie!
Jak dotąd mamy następujące pytania:
Co może spowodować, że ten wskaźnik „wykorzystanych gniazd” gwałtownie wzrośnie? Przeczytałem źródła jądra dla tej metryki i wydaje się, że jest to tylko licznik bez odniesienia do tego, jakie to są gniazda lub co je stworzyło.
Dlaczego liczba ta wynosi około 4,5 tys.? Który limit by to spowodował?
Czy coś istotnego zmieniło się między jądrem 4.14.96 a 4.19.25?
Dlaczego
setsockopt()
wywołanie w libpcap resetuje stan?
Powiązany błąd CoreOS: https://github.com/coreos/bugs/issues/2572
źródło
Odpowiedzi:
Przede wszystkim dziękuję za bardzo dobrze napisane pytanie!
Ponieważ poziom szczegółowości, który opisałeś, jest bardzo wysoki i jesteś już na poziomie gdb, zakładam, że moja odpowiedź nie przyda ci się zbytnio. W każdym razie oto próba:
ss -ae
ilsof -n
?dmesg
zwraca coś interesującego, gdy tak się dzieje?ip link set [interface] promisc on
), czy to również rozwiąże problem?Mam nadzieję, że to pomoże.
źródło
ss
,lsof
anetstat
nie z „gniazd używanych” w/proc/net/sockstat
. Tylko całkowita liczba (która wydaje się po prostu odczytana z tego pliku) jest taka sama.iptables
działa, ale nie ma specjalnych zasad (patrz gist), nie próbowałem sam ustawiać trybu promis lub ciągłego uruchamiania tcpdump. Zrobię to następnym razem.ss -aepi
do mojej kolekcji danych wyjściowych: gist.github.com/privatwolke/... - Niestety, dmesg nie zwraca dokładnie nic, gdy tak się dzieje. W rzeczywistości ostatni wpis przed incydentem ma 5 dni.dmesg / journalctl -k
wyjście.ip link set eth0 promisc on
sam nie przywraca komputera do stanu używalności.xfrm4_gc_thresh - INTEGER
The threshold at which we will start garbage collecting for IPv4
destination cache entries. At twice this value the system will
refuse new allocations.
O ile mogę stwierdzić, że jest to związane z IPsec, ale nie wydaje się, aby tutaj również działał.Było to spowodowane błędem w sterowniku hv_netsvc w jądrze Linuksa. Możemy rozwiązać ten problem z programistą Microsoft i udało nam się uzyskać poprawkę zastosowaną wcześniej.
Zacytuję tutaj komunikat zatwierdzenia, ponieważ dość dobrze podsumowuje problem:
Do przyszłego użytku zatwierdzenie, które to naprawia, to https://github.com/torvalds/linux/commit/6d9cfab853ca60b2f77b5e4c40443216988cba1f .
źródło