Historia o brakujących pakietach DNS od pomocy technicznej Google Cloud

Z Edytora bloga Google: Czy zastanawiałeś się kiedyś, jak inżynierowie Google Cloud Technical Solutions (TSE) radzą sobie z Twoimi prośbami o pomoc? Inżynierowie pomocy technicznej TSE są odpowiedzialni za identyfikowanie i korygowanie źródeł problemów zgłaszanych przez użytkowników. Niektóre z tych problemów są dość proste, ale czasami można spotkać się z problemem wymagającym uwagi kilku inżynierów jednocześnie. W tym artykule jeden z pracowników TSE opowie nam o jednym bardzo trudnym problemie ze swojej niedawnej praktyki - w przypadku brakujących pakietów DNS. W tej historii zobaczymy, jak inżynierom udało się rozwiązać tę sytuację i jakich nowych rzeczy nauczyli się podczas naprawiania błędu. Mamy nadzieję, że ta historia nie tylko poinformuje Cię o głęboko zakorzenionym błędzie, ale także umożliwi wgląd w procesy związane ze złożeniem zgłoszenia do pomocy technicznej w Google Cloud.

Historia o brakujących pakietach DNS od pomocy technicznej Google Cloud

Rozwiązywanie problemów jest zarówno nauką, jak i sztuką. Wszystko zaczyna się od zbudowania hipotezy na temat przyczyny niestandardowego zachowania systemu, po czym jest on testowany pod kątem wytrzymałości. Zanim jednak postawimy hipotezę, musimy jasno zdefiniować i precyzyjnie sformułować problem. Jeśli pytanie brzmi zbyt niejasno, będziesz musiał wszystko dokładnie przeanalizować; Na tym polega „sztuka” rozwiązywania problemów.

W Google Cloud takie procesy stają się wykładniczo bardziej złożone, ponieważ Google Cloud dokłada wszelkich starań, aby zagwarantować prywatność swoich użytkowników. Z tego powodu inżynierowie TSE nie mają dostępu do edycji systemów ani możliwości przeglądania konfiguracji w tak szerokim zakresie, jak robią to użytkownicy. Dlatego też, aby przetestować którąkolwiek z naszych hipotez, my (inżynierowie) nie możemy szybko modyfikować systemu.

Niektórzy użytkownicy uważają, że naprawimy wszystko jak mechanik w serwisie samochodowym i po prostu prześlemy nam identyfikator wirtualnej maszyny, podczas gdy w rzeczywistości proces odbywa się w formie konwersacyjnej: zbieranie informacji, formułowanie i potwierdzanie (lub obalanie) hipotez, i ostatecznie problemy decyzyjne opierają się na komunikacji z klientem.

Przedmiotowy problem

Dziś mamy historię z dobrym zakończeniem. Jednym z powodów pomyślnego rozstrzygnięcia proponowanej sprawy jest bardzo szczegółowy i dokładny opis problemu. Poniżej możesz zobaczyć kopię pierwszego biletu (zredagowanego w celu ukrycia poufnych informacji):
Historia o brakujących pakietach DNS od pomocy technicznej Google Cloud
Ta wiadomość zawiera wiele przydatnych dla nas informacji:

  • Określono konkretną maszynę wirtualną
  • Wskazany jest sam problem - DNS nie działa
  • Wskazane jest, gdzie objawia się problem - maszyna wirtualna i kontener
  • Wskazane są kroki podjęte przez użytkownika w celu zidentyfikowania problemu.

Zgłoszenie zostało zarejestrowane jako „P1: Wpływ krytyczny – Usługa nie nadaje się do użytku produkcyjnego”, co oznacza stały monitoring sytuacji 24/7 zgodnie ze schematem „Follow the Sun” (więcej można przeczytać nt. priorytety żądań użytkowników), z jego przeniesieniem z jednego zespołu wsparcia technicznego do drugiego przy każdej zmianie strefy czasowej. Tak naprawdę, zanim problem dotarł do naszego zespołu w Zurychu, okrążył już cały świat. Do tego czasu użytkownik podjął działania łagodzące, ale obawiał się powtórzenia sytuacji na produkcji, ponieważ pierwotna przyczyna nie została jeszcze odkryta.

Zanim bilet dotarł do Zurychu, mieliśmy już pod ręką następujące informacje:

  • Treść /etc/hosts
  • Treść /etc/resolv.conf
  • Wniosek iptables-save
  • Zmontowane przez zespół ngrep plik pcap

Mając te dane, mogliśmy rozpocząć fazę „dochodzenia” i rozwiązywania problemów.

Nasze pierwsze kroki

W pierwszej kolejności sprawdziliśmy logi i status serwera metadanych oraz upewniliśmy się, że działa on poprawnie. Serwer metadanych odpowiada na adres IP 169.254.169.254 i odpowiada m.in. za kontrolowanie nazw domen. Sprawdziliśmy również dwukrotnie, czy zapora sieciowa działa poprawnie z maszyną wirtualną i nie blokuje pakietów.

To był jakiś dziwny problem: sprawdzenie nmap obaliło naszą główną hipotezę o utracie pakietów UDP, więc w myślach wymyśliliśmy kilka dodatkowych opcji i sposobów ich sprawdzenia:

  • Czy pakiety są odrzucane selektywnie? => Sprawdź reguły iptables
  • Czy nie jest za mały? MTU? => Sprawdź wyjście ip a show
  • Czy problem dotyczy tylko pakietów UDP, czy też TCP? => Odjedź dig +tcp
  • Czy pakiety wygenerowane przez dig są zwracane? => Odjedź tcpdump
  • Czy libdns działa poprawnie? => Odjedź strace aby sprawdzić transmisję pakietów w obu kierunkach

Tutaj decydujemy się zadzwonić do użytkownika, aby rozwiązać problemy na żywo.

Podczas rozmowy jesteśmy w stanie sprawdzić kilka rzeczy:

  • Po kilku sprawdzeniach wykluczamy reguły iptables z listy przyczyn
  • Sprawdzamy interfejsy sieciowe i tablice routingu oraz dwukrotnie sprawdzamy, czy MTU jest prawidłowe
  • Odkrywamy to dig +tcp google.com (TCP) działa tak, jak powinien, ale dig google.com (UDP) nie działa
  • Po odjechaniu tcpdump podczas pracy dig, stwierdzamy, że pakiety UDP są zwracane
  • Odjeżdżamy strace dig google.com i widzimy, jak dig poprawnie wywołuje sendmsg() и recvms(), jednakże drugi zostaje przerwany przez przekroczenie limitu czasu

Niestety nadchodzi koniec zmiany i zmuszeni jesteśmy eskalować problem do kolejnej strefy czasowej. Zgłoszenie wzbudziło jednak zainteresowanie naszego zespołu i kolega sugeruje utworzenie początkowego pakietu DNS przy użyciu modułu scrapy Python.

from scapy.all import *

answer = sr1(IP(dst="169.254.169.254")/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname="google.com")),verbose=0)
print ("169.254.169.254", answer[DNS].summary())

Ten fragment tworzy pakiet DNS i wysyła żądanie do serwera metadanych.

Użytkownik uruchamia kod, zwracana jest odpowiedź DNS, a aplikacja ją odbiera, potwierdzając, że na poziomie sieci nie ma problemu.

Po kolejnej „podróży dookoła świata” prośba wraca do naszego zespołu, a ja całkowicie przekazuję ją sobie, myśląc, że dla użytkownika będzie wygodniej, jeśli prośba przestanie krążyć z miejsca na miejsce.

W międzyczasie użytkownik wyraża zgodę na dostarczenie migawki obrazu systemu. To bardzo dobra wiadomość: możliwość samodzielnego przetestowania systemu znacznie przyspiesza rozwiązywanie problemów, ponieważ nie muszę już prosić użytkownika o uruchamianie poleceń, przesyłanie mi wyników i ich analizę, wszystko mogę zrobić sam!

Koledzy zaczynają mi trochę pozazdrościć. Podczas lunchu omawiamy konwersję, ale nikt nie ma pojęcia, co się dzieje. Na szczęście użytkownik sam podjął już działania mające na celu złagodzenie skutków i nie spieszy się, więc mamy czas na przeanalizowanie problemu. A skoro mamy obraz, to możemy przeprowadzić dowolne testy, które nas interesują. Świetnie!

Cofając się o krok

Jedno z najpopularniejszych pytań podczas rozmów kwalifikacyjnych na stanowiska inżynierów systemów brzmi: „Co się stanie, gdy wykonasz polecenie ping www.google.com? Pytanie jest świetne, ponieważ kandydat musi opisać wszystko, od powłoki po przestrzeń użytkownika, jądro systemu, a następnie sieć. Uśmiecham się: czasami pytania na rozmowie kwalifikacyjnej okazują się przydatne w prawdziwym życiu...

Postanawiam zastosować to pytanie HR do bieżącego problemu. Z grubsza rzecz biorąc, gdy próbujesz określić nazwę DNS, dzieje się co następuje:

  1. Aplikacja wywołuje bibliotekę systemową, taką jak libdns
  2. libdns sprawdza konfigurację systemu, z którym serwerem DNS ma się skontaktować (na schemacie jest to 169.254.169.254, serwer metadanych)
  3. libdns używa wywołań systemowych do utworzenia gniazda UDP (SOKET_DGRAM) i wysyłania pakietów UDP z zapytaniem DNS w obu kierunkach
  4. Poprzez interfejs sysctl możesz skonfigurować stos UDP na poziomie jądra
  5. Jądro współdziała ze sprzętem, aby przesyłać pakiety w sieci za pośrednictwem interfejsu sieciowego
  6. Hypervisor przechwytuje i przesyła pakiet do serwera metadanych po kontakcie z nim
  7. Serwer metadanych w magiczny sposób określa nazwę DNS i zwraca odpowiedź w ten sam sposób

Historia o brakujących pakietach DNS od pomocy technicznej Google Cloud
Przypomnę, jakie hipotezy już rozważaliśmy:

Hipoteza: uszkodzone biblioteki

  • Test 1: uruchom strace w systemie, sprawdź, czy dig wywołuje właściwe wywołania systemowe
  • Wynik: wywoływane są prawidłowe wywołania systemowe
  • Test 2: za pomocą srapy sprawdzimy, czy uda nam się ustalić nazwy z pominięciem bibliotek systemowych
  • Wynik: możemy
  • Test 3: uruchom polecenierpm –V na plikach pakietu libdns i biblioteki md5sum
  • Wynik: kod biblioteki jest całkowicie identyczny z kodem w działającym systemie operacyjnym
  • Test 4: zamontuj obraz systemu root użytkownika na maszynie wirtualnej bez takiego zachowania, uruchom chroot i sprawdź, czy DNS działa
  • Wynik: DNS działa poprawnie

Wnioski na podstawie testów: problem nie leży w bibliotekach

Hipoteza: Wystąpił błąd w ustawieniach DNS

  • Test 1: sprawdź tcpdump i sprawdź, czy pakiety DNS są poprawnie wysyłane i zwracane po uruchomieniu dig
  • Wynik: pakiety są przesyłane poprawnie
  • Test 2: sprawdź dwukrotnie serwer /etc/nsswitch.conf и /etc/resolv.conf
  • Wynik: wszystko jest w porządku

Wnioski na podstawie testów: problem nie leży w konfiguracji DNS

Hipoteza: uszkodzony rdzeń

  • Testuj: zainstaluj nowe jądro, sprawdź podpis, uruchom ponownie
  • Wynik: podobne zachowanie

Wnioski na podstawie testów: jądro nie jest uszkodzone

Hipoteza: nieprawidłowe zachowanie sieci użytkownika (lub interfejsu sieciowego hypervisora)

  • Test 1: Sprawdź ustawienia zapory sieciowej
  • Wynik: zapora przekazuje pakiety DNS zarówno na hoście, jak i na GCP
  • Test 2: przechwytuje ruch i monitoruje poprawność transmisji oraz zwrot żądań DNS
  • Wynik: tcpdump potwierdza, że ​​host odebrał pakiety zwrotne

Wnioski na podstawie testów: problem nie leży w sieci

Hipoteza: serwer metadanych nie działa

  • Test 1: sprawdź dzienniki serwera metadanych pod kątem anomalii
  • Wynik: w logach nie ma żadnych anomalii
  • Test 2: Omiń serwer metadanych poprzez dig @8.8.8.8
  • Wynik: rozdzielczość jest przerywana nawet bez użycia serwera metadanych

Wnioski na podstawie testów: problem nie dotyczy serwera metadanych

Podsumowując: przetestowaliśmy wszystkie podsystemy z wyjątkiem ustawienia czasu wykonania!

Zanurz się w ustawieniach środowiska wykonawczego jądra

Aby skonfigurować środowisko wykonawcze jądra, możesz użyć opcji wiersza poleceń (grub) lub interfejsu sysctl. Zajrzałem /etc/sysctl.conf i pomyśl, odkryłem kilka ustawień niestandardowych. Czując, że się czegoś złapałem, odrzuciłem wszystkie ustawienia inne niż sieciowe lub inne niż TCP, pozostając przy ustawieniach górskich net.core. Następnie udałem się do miejsca, gdzie w maszynie wirtualnej znajdowały się uprawnienia hosta i zacząłem stosować ustawienia jeden po drugim, na uszkodzonej maszynie wirtualnej, aż znalazłem winowajcę:

net.core.rmem_default = 2147483647

Oto konfiguracja łamiąca DNS! Znalazłem narzędzie zbrodni. Ale dlaczego tak się dzieje? Nadal potrzebowałem motywu.

Podstawowy rozmiar bufora pakietów DNS konfiguruje się za pomocą net.core.rmem_default. Typowa wartość wynosi około 200 KB, ale jeśli Twój serwer odbiera dużo pakietów DNS, możesz chcieć zwiększyć rozmiar bufora. Jeśli bufor będzie pełny w momencie nadejścia nowego pakietu, na przykład dlatego, że aplikacja nie przetwarza go wystarczająco szybko, zaczniesz tracić pakiety. Nasz klient poprawnie zwiększył rozmiar bufora, gdyż obawiał się utraty danych, gdyż korzystał z aplikacji do zbierania metryk poprzez pakiety DNS. Wartość, którą ustawił, była maksymalna możliwa: 231-1 (jeśli ustawiona na 231, jądro zwróci „NIEPRAWIDŁOWY ARGUMENT”).

Nagle zdałem sobie sprawę, dlaczego nmap i scapy działały poprawnie: używały surowych gniazd! Surowe gniazda różnią się od zwykłych gniazd: omijają iptables i nie są buforowane!

Ale dlaczego „bufor zbyt duży” powoduje problemy? To wyraźnie nie działa zgodnie z przeznaczeniem.

W tym momencie mogłem odtworzyć problem w wielu jądrach i wielu dystrybucjach. Problem pojawił się już w jądrze 3.x, a teraz pojawił się także w jądrze 5.x.

Rzeczywiście, przy uruchomieniu

sysctl -w net.core.rmem_default=$((2**31-1))

DNS przestał działać.

Zacząłem szukać wartości roboczych za pomocą prostego algorytmu wyszukiwania binarnego i odkryłem, że system działał z 2147481343, ale ta liczba była dla mnie bezsensownym zbiorem liczb. Zasugerowałem klientowi wypróbowanie tego numeru, a on odpowiedział, że system działa z google.com, ale nadal wyświetla błąd w innych domenach, więc kontynuowałem dochodzenie.

zainstalowałem dropwatch, narzędzie, którego należało użyć wcześniej: pokazuje dokładnie, gdzie w jądrze trafia pakiet. Winowajcą była funkcja udp_queue_rcv_skb. Pobrałem źródła jądra i dodałem kilka funkcje printk aby śledzić, gdzie dokładnie trafia pakiet. Szybko znalazłem odpowiedni stan if, i po prostu przez jakiś czas się na to gapiłem, bo wtedy wszystko w końcu ułożyło się w całość: 231-1, liczba bez znaczenia, niedziałająca domena... To był kawałek kodu w __udp_enqueue_schedule_skb:

if (rmem > (size + sk->sk_rcvbuf))
		goto uncharge_drop;

Proszę zwrócić uwagę:

  • rmem jest typu int
  • size jest typu u16 (szesnastobitowy int bez znaku) i przechowuje rozmiar pakietu
  • sk->sk_rcybuf jest typu int i przechowuje rozmiar bufora, który z definicji jest równy wartości in net.core.rmem_default

Kiedy sk_rcvbuf zbliża się do 231, co może skutkować zsumowaniem rozmiaru pakietu całkowitą przepełnienie. A ponieważ jest to int, jego wartość staje się ujemna, więc warunek staje się prawdziwy, gdy powinien być fałszywy (więcej na ten temat możesz przeczytać na stronie powiązanie).

Błąd można skorygować w banalny sposób: poprzez rzutowanie unsigned int. Zastosowałem poprawkę i ponownie uruchomiłem system, a DNS znów zaczął działać.

Smak zwycięstwa

Wyniki przekazałem klientowi i wysłałem LKML poprawka do jądra. Jestem zadowolony: każdy element układanki pasuje do siebie, potrafię dokładnie wyjaśnić, dlaczego zaobserwowaliśmy to, co zaobserwowaliśmy, a co najważniejsze, dzięki pracy zespołowej udało nam się znaleźć rozwiązanie problemu!

Warto przyznać, że przypadek okazał się rzadki i na szczęście rzadko otrzymujemy od użytkowników tak skomplikowane żądania.

Historia o brakujących pakietach DNS od pomocy technicznej Google Cloud


Źródło: www.habr.com

Dodaj komentarz