Историята за изгубените DNS пакети от техническата поддръжка на Google Cloud

От редактора на блога на Google: Интересували ли сте се някога как инженерите на Google Cloud Technical Solutions (TSE) обработват вашите запитвания за поддръжка? Техническите инженери TSE отговарят за откритията и решаването на проблеми, посочени от потребителите. Някои от тези проблеми са сравнително прости, но понякога се срещат запитвания, изискващи вниманието на няколко инженера. В тази статия един от служителите на TSE ще ни разкаже за един много сложен проблем от наскоро преживяна практика — случай с изчезващи DNS пакети. По време на разказа ще видим как инженерите успяха да разрешат ситуацията и какво ново научиха при отстраняването на грешката. Надяваме се, че тази история не само ще ви информира за дълбоко залегнал бъг, но и ще предостави разбиране за процесите, преминаващи при подаването на запитване за поддръжка на Google Cloud.

Историята за изгубените DNS пакети от техническата поддръжка на Google Cloud

Отстраняването на проблеми е едновременно наука и изкуство. Всичко започва с изграждането на хипотеза относно причината за необичайното поведение на системата, след което тя се тества за здравина. Въпреки това, преди да формулираме хипотезата, трябва ясно да определим и точно да формулираме проблема. Ако въпросът звучи прекалено неясно, ще трябва да направите задълбочен анализ; в това се състои „изкуството“ на отстраняване на проблеми.

В условията на Google Cloud подобни процеси се усложняват значително, тъй като Google Cloud се старае да гарантира конфиденциалността на своите потребители. Поради това инженерите TSE нямат достъп до редактиране на вашите системи и нямат възможността да преглеждат конфигурации в такъв обхват, какъвто имат потребителите. Следователно, за да проверим някоя от нашите хипотези, ние (инженерите) не можем бързо да модифицираме системата.

Някои потребители смятат, че ние ще поправим всичко, сякаш сме механици в сервиз, и просто ни изпращат id на виртуалната машина, докато в действителност процесът протича във формат на разговор: събиране на информация, формулиране и потвърждаване (или опровергаване) на хипотези, и в крайна сметка решението на проблема се изгражда на основата на комуникацията с клиента.

Рассматриваемая проблема

Днес пред нас е история с добър край. Една от причините за успешното разрешение на предложеното дело е много подробно и точно описание на проблема. По-долу можете да видите копие на първия билет (редактирано, с цел да се скрие конфиденциалната информация):
Историята за изгубените DNS пакети от техническата поддръжка на Google Cloud
В това съобщение има много полезна информация за нас:

  • Указана е конкретна VM
  • Посочен е самият проблем — не работи DNS
  • Указано е къде проблемът се проявява — VM и контейнер
  • Указани са стъпките, които е предприел потребителят за определяне на проблема

Обращението е регистрирано като «P1: Критичен Въздействие — Услугата е неизползваема в продукция», което означава постоянно наблюдение на ситуацията 24/7 по схемата «Следи Слънцето» (по връзката можете да прочетете повече за приоритетите на потребителските обращения), с прехвърляне на второстепенното между различни екипи за техническа поддръжка при всяко изменение на часовите зони. Всъщност, до момента, в който проблемът стигна до нашия екип в Цюрих, той бе успял да заобиколи земята. В това време потребителят е предприел мерки за намаляване на последиците, но е бил загрижен от повторение на ситуацията в продукция, тъй като основната причина все още не е била установена.

Когато билетът стигна до Цюрих, ние вече разполагахме с следната информация:

  • Съдържание /etc/hosts
  • Съдържание /etc/resolv.conf
  • Извод iptables-save
  • Събрани от екипа ngrep файл pcap

С тези данни бяхме готови да преминем към етапа «разследване» и отстраняване на проблеми.

Нашите първи стъпки

Първо проверихме логовете и статуса на сървъра за метаданни и се уверихме, че той работи коректно. Сървърът за метаданни отговаря на IP адреса 169.254.169.254 и, освен всичко друго, е отговорен за контрола на имената на домейните. Освен това, ние проверихме, че фаерволът работи правилно с VM и не блокира пакетите.

Това беше някакъв странен проблем: проверката с nmap опроверга основната ни хипотеза за загуба на UDP пакети, затова мислено извеждахме още няколко варианта и начини за проверка:

  • Премахват ли се пакетите селективно? => Проверете правилата на iptables
  • Не е ли прекалено малък MTU? => Проверить вывод ip a show
  • Засегнат ли е проблемът само UDP пакети или и TCP? => Изпълнете dig +tcp
  • Връщат ли се генерираните dig пакети? => Изпълнете tcpdump
  • Коректно ли работи libdns? => Изпълнете strace за проверка на предаването на пакети в двете посоки

Тук решаваме да се свържем с потребителя за отстраняване на проблеми на живо.

По време на обаждането успяваме да проверим няколко неща:

  • След няколко проверки изключваме правилата iptables от списъка с причини
  • Проверяваме мрежовите интерфейси и таблиците за маршрутизация и повторно проверяваме коректността на MTU
  • Установяваме, че dig +tcp google.com (TCP) работи както трябва, но dig google.com (UDP) не работи
  • Пробягвайки tcpdump все още работи dig, откриваме, че UDP пакетите се връщат
  • Пробягваме strace dig google.com и виждаме как dig коректно извиква sendmsg() и recvms(), но вторият се прекъсва поради таймаут

За съжаление, смяната приключва и налага да предадем проблема на следващата времева зона. Въпреки това, запитването предизвика интерес в нашия екип и колега предлага да създаде изходен DNS пакет с питоновия модул scrapy.

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())

Този фрагмент създава DNS пакет и изпраща заявка до сървъра за метаданни.

Потребителят стартира кода, DNS отговорът се връща и приложението го получава, което потвърдява липсата на проблем на мрежово ниво.

След поредното "околоходно пътуване" запитването се връща обратно към нашия екип и аз напълно поемам отговорността, считам, че на потребителя ще му бъде по-удобно, ако запитването спре да се движи наоколо.

Междувременно, потребителят любезно се съгласява да предостави снимка на системния образ. Това е много добра новина: възможността сам да тествам системата значително ускорява отстраняването на проблеми, тъй като вече не трябва да моля потребителя да изпълнява команди, да ми изпраща резултати и да ги анализирам, мога да направя всичко сам!

Колегите започват да ми завиждат. На обяд обсъждаме запитването, но никой няма идеи какво се случва. За щастие, самият потребител вече е предприел мерки за смекчаване на последиците и не бърза, така че имаме време да разгледаме проблема. И тъй като имаме образ, можем да проведем всички интересни тестове. Чудесно!

Връщайки се на стъпка назад

Един от най-популярните въпроси на интервю за работа за системен инженер е: "Какво се случва, когато пингувате www.google.com?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…

Решавам да приложа този HR въпрос към настоящия проблем. Грубо казано, когато се опитвате да определите DNS име, се случва следното:

  1. Приложението извиква системната библиотека, например libdns
  2. libdns проверява конфигурацията на системата за кой DNS сървър да се свърже (в диаграмата това е 169.254.169.254, сървър на метаданни)
  3. libdns използва системни повиквания за създаване на UDP сокет (SOKET_DGRAM) и за предаване на UDP пакети с DNS запитване в двете посоки
  4. Чрез интерфейса sysctl може да се настрои UDP стек на ниво ядро
  5. Ядрото взаимодействува с хардуера за предаване на пакети по мрежата през мрежовия интерфейс
  6. Хипервизорът улавя и предава пакета на сървъра на метаданни при контакт
  7. Сървърът на метаданни по своя магия определя DNS името и по същия метод връща отговора

Историята за изгубените DNS пакети от техническата поддръжка на Google Cloud
Напомням какви хипотези вече разгледахме:

Хипотеза: Проблем с библиотеките

  • Тест 1: да се проведе strace в системата, за да се провери дали dig извиква коректни системни повиквания
  • Резултат: извикват се коректни системни повиквания
  • Тест 2: с помощта на srapy да проверим можем ли да определяме имена извън системните библиотеки
  • Резултат: можем
  • Тест 3: да се проведе rpm –V на пакета libdns и md5sum на файловете на библиотеката
  • Резултат: кодът на библиотеката е напълно идентичен на кода в работещата операционна система
  • Тест 4: да се монтира образът на кореновата система на потребителя на VM без подобно поведение, да се проведе chroot, да се провери дали работи DNS
  • Резултат: DNS работи коректно

Извод на базата на тестовете: проблемът не е в библиотеките

Хипотеза: Има грешка в настройките на DNS

  • Тест 1: да се провери tcpdump и да се наблюдава дали DNS пакетите се изпращат и получават коректно след пускане на dig
  • Резултат: пакетите се предават коректно
  • Тест 2: да се провери отново на сървера /etc/nsswitch.conf и /etc/resolv.conf
  • Резултат: всичко е коректно

Извод на базата на тестовете: проблемът не е в конфигурацията на DNS

Хипотеза: ядрото е повредено

  • Тест: да се инсталира ново ядро, да се провери подписа, да се рестартира
  • Резултат: аналогично поведение

Извод на базата на тестовете: ядрото не е повредено

Хипотеза: некоректно поведение на потребителската мрежа (или интерфейса на мрежата на хипервизора)

  • Тест 1: да се проверят настройките на firewall-а
  • Резултат: firewall-а пропуска DNS пакетите и на хоста, и на GCP
  • Тест 2: да се прихване трафикът и да се проследи коректността на предаване и връщане на DNS запитванията
  • Резултат: tcpdump потвърждава получаването на обратните пакети от хоста

Извод на базата на тестовете: проблемът не е в мрежата

Хипотеза: сървърът на метаданни не работи

  • Тест 1: да се проверят логовете на сървъра на метаданни за аномалии
  • Резултат: не са открити аномалии в логовете
  • Тест 2: заобиколка на сървър за метаданни чрез dig @8.8.8.8
  • Резултат: разрешението се нарушава дори без да се използва сървъра за метаданни

Извод на базата на тестовете: проблемът не е в сървъра за метаданни

Резултат: изпитахме всички подсистеми с изключение на настройките на средата за изпълнение!

Потапяйки се в настройките на средата за изпълнение на ядрото

За настройка на средата за изпълнение на ядрото можете да използвате опции от командния ред (grub) или интерфейса sysctl. Надникнах в /etc/sysctl.conf и си помислих, че открих няколко персонализирани настройки. Чувствайки, че съм се хванал за нещо, отхвърлих всички несвързани с мрежата или не-tcp настройки, остана ми само куп настройки net.core. След това се обърнах към мястото, където за VM са разрешенията на хоста, и започнах да прилагам една след друга, настройки от счупената VM, докато не намерих нарушителя:

net.core.rmem_default = 2147483647

Ето я, настройката, която чупи DNS конфигурацията! Намерих оръдието на престъплението. Но защо се случва това? Все още ми трябваше мотив.

Настройката на основния размер на буфера на DNS пакетите става чрез net.core.rmem_default. Типичната стойност варира около 200КиБ, обаче, ако вашият сървър получава много DNS пакети, можете да увеличите размера на буфера. Ако в момента на получаване на нов пакет буферът е пълен, например защото приложението не го обработва достатъчно бързо, ще започнете да губите пакети. Нашият клиент правилно увеличи размера на буфера, тъй като се опасяваше от загуба на данни, докато използваше приложение за събиране на метрики чрез DNS пакети. Стойността, която зададе, беше максимално допустимата: 231-1 (ако зададете 231, ядрото ще върне „НВАЛИДЕН АРГУМЕНТ“).

Изведнъж осъзнах защо nmap и scapy работеха коректно: те използваха сурови сокети! Суровите сокети се различават от обикновените: те работят извън iptables и не се буферират!

Но защо „твърде голям буфер“ предизвиква проблеми? Очевидно не работи както е замислено.

Към този момент можех да възпроизведа проблема на различни ядра и множество дистрибуции. Проблемът вече се проявяваше на ядро 3.x и сега също така се проявяваше и на ядро 5.x.

Наистина, при стартиране на

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

DNS спираше да работи.

Започнах да търся работещи стойности чрез прост алгоритъм за двоично търсене и открих, че системата работи с 2147481343, но това число беше безсмислен набор от цифри за мен. Предложих на клиента да пробва това число и той ми отговори, че системата заработила с google.com, но все пак показвала грешка с други домейни, затова продължих моето разследване.

Инсталирах dropwatch, инструмент, който трябваше да използвам по-рано: той показва къде точно в ядрото попада пакет. Виновна се оказа функцията udp_queue_rcv_skb. Свалих изходния код на ядрото и добавих няколко функции printk за да проследя къде конкретно попада пакетът. Бързо открих нужното условие ако, и известно време просто се взирах в него, защото именно тогава всичко най-накрая съвпадна в цялостна картина: 231-1, безсмислено число, неработещ домейн… Ставаше въпрос за парче код в __udp_enqueue_schedule_skb:

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

Обърнете внимание:

  • rmem е от тип int
  • размер е от тип u16 (беззнаков 16-битов int) и съхранява размера на пакета
  • sk->sk_rcybuf е от тип int и съхранява размера на буфера, който по дефиниция е равен на стойността в net.core.rmem_default

Когато sk_rcvbuf наближаваща 231, сумата на размера на пакета може да доведе до целочислено препълване. И така, тъй като това е int, неговата стойност става отрицателна, така че условието става истинно, когато трябва да бъде лъжливо (повече за това можете да научите от връзката).

Грешката се поправя тривиално: чрез преобразуване към unsigned int. Приложих поправката и рестартирах системата, след което DNS отново заработи.

Вкусът на победата

Препратих находките си на клиента и изпратих LKML пач на ядрото. Доволен съм: всяко парче от пъзела се събра в едно цяло, мога точно да обясня защо наблюдавахме това, което наблюдавахме, и най-важното, успяхме да намерим решение на проблема благодарение на съвместната работа!

Трябва да призная, че случайът се оказа рядък и за щастие рядко получаваме толкова сложни запитвания от потребителите.

Историята за изгубените DNS пакети от техническата поддръжка на Google Cloud


Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster