Unha historia sobre a falta de paquetes DNS do soporte técnico de Google Cloud

Desde Google Blog Editor: Algunha vez te preguntas como xestionan os enxeñeiros de Google Cloud Technical Solutions (TSE) as túas solicitudes de asistencia? Os Enxeñeiros de Soporte Técnico de TSE son responsables de identificar e corrixir as fontes de problemas informadas polo usuario. Algúns destes problemas son bastante sinxelos, pero ás veces atopas un ticket que require a atención de varios enxeñeiros á vez. Neste artigo, un dos empregados de TSE falaranos dun problema moi complicado da súa práctica recente: caso de faltar paquetes DNS. Nesta historia, veremos como os enxeñeiros lograron resolver a situación e que cousas novas aprenderon ao corrixir o erro. Agardamos que esta historia non só te eduque sobre un erro profundamente arraigado, senón que tamén che ofreza información sobre os procesos para presentar un ticket de asistencia con Google Cloud.

Unha historia sobre a falta de paquetes DNS do soporte técnico de Google Cloud

A resolución de problemas é tanto unha ciencia como unha arte. Todo comeza coa construción dunha hipótese sobre a razón do comportamento non estándar do sistema, despois de que se proba a súa resistencia. Porén, antes de formular unha hipótese, debemos definir claramente e formular o problema con precisión. Se a pregunta soa demasiado vaga, entón terás que analizar todo con coidado; Esta é a "arte" da resolución de problemas.

Baixo Google Cloud, estes procesos vólvense exponencialmente máis complexos, xa que Google Cloud fai todo o posible para garantir a privacidade dos seus usuarios. Debido a isto, os enxeñeiros de TSE non teñen acceso para editar os seus sistemas, nin a capacidade de ver as configuracións tan amplamente como os usuarios. Polo tanto, para probar calquera das nosas hipóteses, nós (os enxeñeiros) non podemos modificar rapidamente o sistema.

Algúns usuarios cren que arranxaremos todo como a mecánica nun servizo de coches, e simplemente enviarnos o ID dunha máquina virtual, mentres que en realidade o proceso ten lugar nun formato conversacional: recollendo información, formando e confirmando (ou refutando) hipóteses, e, ao final, os problemas de decisión baséanse na comunicación co cliente.

Problema en cuestión

Hoxe temos unha historia cun bo final. Unha das razóns para a resolución exitosa do caso proposto é unha descrición moi detallada e precisa do problema. A continuación podes ver unha copia do primeiro ticket (editado para ocultar información confidencial):
Unha historia sobre a falta de paquetes DNS do soporte técnico de Google Cloud
Esta mensaxe contén moita información útil para nós:

  • VM específico especificado
  • O problema en si está indicado: o DNS non funciona
  • Indícase onde se manifesta o problema: máquina virtual e contenedor
  • Indícanse os pasos que fixo o usuario para identificar o problema.

A solicitude rexistrouse como "P1: Impacto crítico - Servizo inutilizable na produción", o que significa un seguimento constante da situación 24/7 segundo o esquema "Follow the Sun" (podes ler máis sobre prioridades das solicitudes dos usuarios), co seu traslado dun equipo de soporte técnico a outro con cada cambio de fuso horario. De feito, cando o problema chegou ao noso equipo en Zúric, xa dera a volta ao globo. Nese momento, o usuario tomara medidas de mitigación, pero tiña medo de que se repetise a situación na produción, xa que aínda non se descubriu a causa raíz.

Cando o billete chegou a Zúric, xa tiñamos a seguinte información a man:

  • Contido /etc/hosts
  • Contido /etc/resolv.conf
  • Saída iptables-save
  • Montado polo equipo ngrep ficheiro pcap

Con estes datos, estabamos preparados para comezar a fase de "investigación" e solución de problemas.

Os nosos primeiros pasos

En primeiro lugar, comprobamos os rexistros e o estado do servidor de metadatos e asegurámonos de que funcionaba correctamente. O servidor de metadatos responde ao enderezo IP 169.254.169.254 e, entre outras cousas, é o encargado de controlar os nomes de dominio. Tamén comprobamos dúas veces que o firewall funciona correctamente coa máquina virtual e non bloquea paquetes.

Foi unha especie de problema estraño: a comprobación nmap refutou a nosa hipótese principal sobre a perda de paquetes UDP, polo que mentalmente se nos ocorreron varias opcións e formas de verificalos:

  • Bótanse os paquetes selectivamente? => Comproba as regras de iptables
  • Non é moi pequeno? MTU? => Comprobar a saída ip a show
  • O problema afecta só aos paquetes UDP ou tamén ao TCP? => Vaia dig +tcp
  • Devólvense os paquetes xerados por dig? => Vaia tcpdump
  • ¿Libdns funciona correctamente? => Vaia strace para comprobar a transmisión de paquetes en ambas direccións

Aquí decidimos chamar ao usuario para solucionar problemas en directo.

Durante a chamada podemos comprobar varias cousas:

  • Despois de varias comprobacións, excluímos as regras de iptables da lista de motivos
  • Comprobamos as interfaces de rede e as táboas de enrutamento e comprobamos dúas veces a corrección da MTU
  • Descubrimos iso dig +tcp google.com (TCP) funciona como debería, pero dig google.com (UDP) non funciona
  • Tendo afastado tcpdump aínda está funcionando dig, atopamos que se están devolvendo paquetes UDP
  • Conducimos strace dig google.com e vemos como dig chama correctamente sendmsg() и recvms(), porén o segundo vese interrompido por un tempo morto

Desafortunadamente, chega o final da quenda e vémonos obrigados a escalar o problema á seguinte zona horaria. A solicitude, con todo, espertou interese no noso equipo e un colega suxire crear o paquete DNS inicial usando o módulo scrapy de 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())

Este fragmento crea un paquete DNS e envía a solicitude ao servidor de metadatos.

O usuario executa o código, devólvese a resposta DNS e a aplicación recíbea, confirmando que non hai ningún problema a nivel de rede.

Despois doutra "viaxe ao mundo", a solicitude volve ao noso equipo e transfiro por completo a min, pensando que será máis conveniente para o usuario se a solicitude deixa de circular dun lugar a outro.

Mentres tanto, o usuario acepta proporcionar unha instantánea da imaxe do sistema. Esta é unha moi boa noticia: a posibilidade de probar o sistema por min mesmo fai que a resolución de problemas sexa moito máis rápida, porque xa non teño que pedirlle ao usuario que execute comandos, que me envíe os resultados e que os analice, ¡podo facelo todo eu!

Os meus compañeiros comezan a envexarme un pouco. Durante o xantar comentamos a conversión, pero ninguén ten idea do que está a pasar. Afortunadamente, o propio usuario xa tomou medidas para mitigar as consecuencias e non ten présa, polo que temos tempo para analizar o problema. E xa que temos unha imaxe, podemos facer as probas que nos interesen. Genial!

Dar un paso atrás

Unha das preguntas de entrevista máis populares para os postos de enxeñeiro de sistemas é: "Que pasa cando fai ping www.google.com? A pregunta é xenial, xa que o candidato precisa describir todo, desde o shell ata o espazo do usuario, ata o núcleo do sistema e despois ata a rede. Sorrío: ás veces as preguntas das entrevistas resultan útiles na vida real...

Decido aplicar esta pregunta de recursos humanos a un problema actual. En liñas xerais, cando tentas determinar un nome DNS, ocorre o seguinte:

  1. A aplicación chama a unha biblioteca do sistema como libdns
  2. libdns comproba a configuración do sistema co servidor DNS que debe contactar (no diagrama é 169.254.169.254, servidor de metadatos)
  3. libdns usa chamadas de sistema para crear un socket UDP (SOKET_DGRAM) e enviar paquetes UDP cunha consulta DNS en ambas direccións
  4. A través da interface sysctl pode configurar a pila UDP a nivel do núcleo
  5. O núcleo interactúa co hardware para transmitir paquetes pola rede a través da interface de rede
  6. O hipervisor captura e transmite o paquete ao servidor de metadatos ao entrar en contacto con el
  7. O servidor de metadatos, pola súa maxia, determina o nome DNS e devolve unha resposta usando o mesmo método

Unha historia sobre a falta de paquetes DNS do soporte técnico de Google Cloud
Permíteme recordar que hipóteses xa consideramos:

Hipótese: Bibliotecas rotas

  • Proba 1: executa strace no sistema, comprobe que dig chama as chamadas correctas ao sistema
  • Resultado: chámanse as chamadas correctas do sistema
  • Proba 2: usar srapy para comprobar se podemos determinar nomes sen pasar as bibliotecas do sistema
  • Resultado: podemos
  • Proba 3: executa rpm –V no paquete libdns e nos ficheiros da biblioteca md5sum
  • Resultado: o código da biblioteca é completamente idéntico ao código do sistema operativo de traballo
  • Proba 4: montar a imaxe do sistema raíz do usuario nunha máquina virtual sen este comportamento, executar chroot, ver se o DNS funciona
  • Resultado: DNS funciona correctamente

Conclusión baseada en probas: o problema non está nas bibliotecas

Hipótese: hai un erro na configuración do DNS

  • Proba 1: verifique tcpdump e vexa se os paquetes DNS se envían e se devolven correctamente despois de executar dig
  • Resultado: os paquetes transmítense correctamente
  • Proba 2: comproba dúas veces no servidor /etc/nsswitch.conf и /etc/resolv.conf
  • Resultado: todo é correcto

Conclusión baseada en probas: o problema non está na configuración do DNS

Hipótese: núcleo danado

  • Proba: instalar o novo núcleo, comprobar a sinatura, reiniciar
  • Resultado: comportamento similar

Conclusión baseada en probas: o núcleo non está danado

Hipótese: comportamento incorrecto da rede de usuario (ou interface de rede do hipervisor)

  • Proba 1: comproba a configuración do teu firewall
  • Resultado: o firewall pasa paquetes DNS tanto no host como no GCP
  • Proba 2: interceptar o tráfico e supervisar a corrección da transmisión e devolución das solicitudes de DNS
  • Resultado: tcpdump confirma que o host recibiu paquetes de devolución

Conclusión baseada en probas: o problema non está na rede

Hipótese: o servidor de metadatos non funciona

  • Proba 1: comprobe os rexistros do servidor de metadatos para detectar anomalías
  • Resultado: non hai anomalías nos rexistros
  • Proba 2: ignora o servidor de metadatos mediante dig @8.8.8.8
  • Resultado: a resolución está rota aínda sen utilizar un servidor de metadatos

Conclusión baseada en probas: o problema non é co servidor de metadatos

A liña inferior: probamos todos os subsistemas excepto configuración de tempo de execución!

Mergullo na configuración do tempo de execución do núcleo

Para configurar o ambiente de execución do núcleo, pode usar as opcións da liña de comandos (grub) ou a interface sysctl. Mirei dentro /etc/sysctl.conf e pensa, descubrín varias opcións personalizadas. Sentindo como se me agarrara a algo, descartei todas as configuracións que non son de rede ou non tcp, quedando coa configuración de montaña net.core. Despois fun ata onde estaban os permisos do host na máquina virtual e comecei a aplicar a configuración unha a unha, unha tras outra, coa máquina virtual rota, ata que atopei o culpable:

net.core.rmem_default = 2147483647

Aquí está, unha configuración que rompe o DNS! Atopei a arma asasina. Pero por que está a suceder isto? Aínda necesitaba un motivo.

O tamaño básico do búfer de paquetes DNS configúrase mediante net.core.rmem_default. Un valor típico rolda os 200 KiB, pero se o teu servidor recibe moitos paquetes DNS, quizais queiras aumentar o tamaño do búfer. Se o búfer está cheo cando chega un novo paquete, por exemplo porque a aplicación non o está procesando o suficientemente rápido, entón comezará a perder paquetes. O noso cliente aumentou correctamente o tamaño do búfer porque tiña medo á perda de datos, xa que utilizaba unha aplicación para recoller métricas a través de paquetes DNS. O valor que estableceu foi o máximo posible: 231-1 (se se establece en 231, o núcleo devolverá "ARGUMENTO NON VÁLIDO").

De súpeto decateime de por que nmap e scapy funcionaban correctamente: estaban usando sockets en bruto! Os sockets en bruto son diferentes dos sockets normais: evitan iptables e non están almacenados en búfer.

Pero por que "buffer demasiado grande" causa problemas? Está claro que non funciona como se pretende.

Neste punto podería reproducir o problema en varios núcleos e varias distribucións. O problema xa apareceu no kernel 3.x e agora tamén apareceu no kernel 5.x.

De feito, ao iniciarse

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

O DNS deixou de funcionar.

Comecei a buscar valores de traballo a través dun simple algoritmo de busca binaria e descubrín que o sistema funcionaba con 2147481343, pero este número era un conxunto de números sen sentido para min. Suxerinlle ao cliente que probase con este número, e el respondeume que o sistema funcionaba con google.com, pero aínda deu un erro con outros dominios, polo que seguín coa investigación.

teño instalado dropwatch, unha ferramenta que debería ter sido usada antes: mostra exactamente onde está no núcleo un paquete. O culpable foi a función udp_queue_rcv_skb. Baixei as fontes do núcleo e engadín algunhas funcións printk para rastrexar onde acaba exactamente o paquete. Axiña atopei a condición correcta if, e simplemente mirou para el durante algún tempo, porque foi entón cando finalmente todo se uniu nunha imaxe completa: 231-1, un número sen sentido, un dominio que non funcionaba... Era un anaco de código en __udp_enqueue_schedule_skb:

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

Por favor, teña en conta:

  • rmem é do tipo int
  • size é de tipo u16 (int de dezaseis bits sen asinar) e almacena o tamaño do paquete
  • sk->sk_rcybuf é de tipo int e almacena o tamaño do búfer que, por definición, é igual ao valor en net.core.rmem_default

Cando sk_rcvbuf achégase a 231, sumando o tamaño do paquete pode resultar desbordamento de enteiros. E como é un int, o seu valor vólvese negativo, polo que a condición faise verdadeira cando debería ser falsa (podes ler máis sobre isto en Ligazón).

O erro pódese corrixir dun xeito trivial: mediante o casting unsigned int. Apliquei a corrección e reiniciei o sistema e o DNS volveu funcionar.

Gusto de vitoria

Enviei os meus descubrimentos ao cliente e enviémolo LKML parche do núcleo. Estou satisfeito: cada peza do crebacabezas encaixa, podo explicar exactamente por que observamos o que observamos e, o máis importante, puidemos atopar unha solución ao problema grazas ao noso traballo en equipo!

Paga a pena recoñecer que o caso resultou ser raro e, afortunadamente, raramente recibimos solicitudes tan complexas dos usuarios.

Unha historia sobre a falta de paquetes DNS do soporte técnico de Google Cloud


Fonte: www.habr.com

Engadir un comentario