Google Cloud teknik desteğindeki eksik DNS paketleriyle ilgili bir hikaye

Google Blog Düzenleyici'den: Google Cloud Teknik Çözümler (TSE) mühendislerinin destek taleplerinizi nasıl karşıladığını hiç merak ettiniz mi? TSE Teknik Destek Mühendisleri, kullanıcı tarafından bildirilen sorun kaynaklarının tespit edilmesi ve düzeltilmesinden sorumludur. Bu sorunlardan bazıları oldukça basittir ancak bazen birden fazla mühendisin aynı anda ilgilenmesini gerektiren bir sorunla karşılaşırsınız. Bu makalede TSE çalışanlarından biri bize son zamanlardaki uygulamasından kaynaklanan çok çetrefilli bir sorundan bahsedecek: DNS paketlerinin eksik olması durumu. Bu hikayede mühendislerin durumu nasıl çözmeyi başardıklarını ve hatayı düzeltirken ne gibi yeni şeyler öğrendiklerini göreceğiz. Bu hikayenin sizi yalnızca köklü bir hata hakkında bilgilendirmekle kalmayıp aynı zamanda Google Cloud'a destek bildirimi gönderme süreçlerine dair fikir vermesini de umuyoruz.

Google Cloud teknik desteğindeki eksik DNS paketleriyle ilgili bir hikaye

Sorun giderme hem bilim hem de sanattır. Her şey sistemin standart dışı davranışının nedeni hakkında bir hipotez oluşturmakla başlar ve ardından sistemin gücü test edilir. Ancak bir hipotez formüle etmeden önce sorunu açıkça tanımlamalı ve kesin olarak formüle etmeliyiz. Soru çok belirsiz geliyorsa her şeyi dikkatli bir şekilde analiz etmeniz gerekecektir; Bu sorun giderme “sanatıdır”.

Google Cloud, kullanıcılarının gizliliğini garanti altına almak için elinden gelenin en iyisini yaptığından, Google Cloud altında bu tür süreçler katlanarak daha karmaşık hale geliyor. Bu nedenle, TSE mühendislerinin sistemlerinizi düzenleme erişimi veya konfigürasyonları kullanıcılar kadar geniş bir şekilde görüntüleme yeteneği yoktur. Bu nedenle, hipotezlerimizden herhangi birini test etmek için biz (mühendisler) sistemi hızlı bir şekilde değiştiremeyiz.

Bazı kullanıcılar, bir araba servisindeki mekanik gibi her şeyi düzelteceğimize ve bize sadece sanal bir makinenin kimliğini göndereceğimize inanıyor; oysa gerçekte süreç, konuşma biçiminde gerçekleşir: bilgi toplamak, hipotezleri oluşturmak ve onaylamak (veya çürütmek), ve sonuçta karar problemleri müşteriyle iletişime dayanmaktadır.

Söz konusu sorun

Bugün güzel sonla biten bir hikayemiz var. Önerilen vakanın başarılı bir şekilde çözümlenmesinin nedenlerinden biri, sorunun çok ayrıntılı ve doğru bir şekilde tanımlanmasıdır. Aşağıda ilk biletin bir kopyasını görebilirsiniz (gizli bilgileri gizlemek için düzenlenmiştir):
Google Cloud teknik desteğindeki eksik DNS paketleriyle ilgili bir hikaye
Bu mesaj bizim için birçok yararlı bilgi içeriyor:

  • Belirli VM belirtildi
  • Sorunun kendisi belirtiliyor - DNS çalışmıyor
  • Sorunun nerede ortaya çıktığı belirtilir - VM ve kapsayıcı
  • Kullanıcının sorunu tanımlamak için attığı adımlar belirtilir.

Talep, “P1: Kritik Etki - Hizmet Üretimde Kullanılamaz” olarak kaydedildi; bu, “Güneşi Takip Et” şemasına göre durumun 24/7 sürekli izlenmesi anlamına geliyor (hakkında daha fazlasını okuyabilirsiniz) kullanıcı isteklerinin öncelikleri), her saat dilimi değişiminde bir teknik destek ekibinden diğerine aktarılmasıyla. Aslında sorun Zürih'teki ekibimize ulaştığında zaten tüm dünyayı sarmıştı. Bu zamana kadar kullanıcı hafifletici önlemler almıştı ancak temel neden henüz keşfedilmediği için durumun üretimde tekrarlanmasından korkuyordu.

Bilet Zürih'e ulaştığında elimizde şu bilgiler zaten vardı:

  • içindekiler /etc/hosts
  • içindekiler /etc/resolv.conf
  • Aviator apk iptables-save
  • Ekip tarafından toplandı ngrep pcap dosyası

Bu verilerle “soruşturma” ve sorun giderme aşamasına başlamaya hazırdık.

İlk adımlarımız

Öncelikle metadata sunucusunun loglarını ve durumunu kontrol ettik ve düzgün çalıştığından emin olduk. Meta veri sunucusu 169.254.169.254 IP adresine yanıt verir ve diğer şeylerin yanı sıra alan adlarının kontrolünden sorumludur. Ayrıca güvenlik duvarının VM ile düzgün çalıştığını ve paketleri engellemediğini de iki kez kontrol ettik.

Bu bir tür garip sorundu: nmap kontrolü, UDP paketlerinin kaybı hakkındaki ana hipotezimizi çürüttü, bu yüzden zihinsel olarak bunları kontrol etmek için birkaç seçenek ve yol daha bulduk:

  • Paketler seçici olarak mı düşürülüyor? => iptables kurallarını kontrol edin
  • Çok küçük değil mi? MTU? => Çıkışı kontrol edin ip a show
  • Sorun yalnızca UDP paketlerini mi yoksa TCP'yi de mi etkiliyor? => Uzaklaşın dig +tcp
  • Dig tarafından oluşturulan paketler iade ediliyor mu? => Uzaklaşın tcpdump
  • Libdns düzgün çalışıyor mu? => Uzaklaşın strace paketlerin her iki yönde iletimini kontrol etmek için

Burada sorunları canlı olarak gidermek için kullanıcıyı aramaya karar veriyoruz.

Görüşme sırasında birkaç şeyi kontrol edebiliyoruz:

  • Birkaç kontrolden sonra iptables kurallarını nedenler listesinden çıkarıyoruz
  • Ağ arayüzlerini ve yönlendirme tablolarını kontrol ediyoruz ve MTU'nun doğru olup olmadığını bir kez daha kontrol ediyoruz
  • Bunu keşfediyoruz dig +tcp google.com (TCP) olması gerektiği gibi çalışıyor, ancak dig google.com (UDP) çalışmıyor
  • Uzaklaştıktan tcpdump çalışırken digUDP paketlerinin döndürüldüğünü görüyoruz
  • Biz uzaklaşıyoruz strace dig google.com ve kazma çağrılarının ne kadar doğru olduğunu görüyoruz sendmsg() и recvms()ancak ikincisi bir zaman aşımı nedeniyle kesintiye uğradı

Ne yazık ki vardiyanın sonu geliyor ve sorunu bir sonraki saat dilimine taşımak zorunda kalıyoruz. Ancak bu istek ekibimizde ilgi uyandırdı ve bir meslektaşımız, ilk DNS paketini scrapy Python modülünü kullanarak oluşturmayı önerdi.

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

Bu parça bir DNS paketi oluşturur ve isteği meta veri sunucusuna gönderir.

Kullanıcı kodu çalıştırır, DNS yanıtı döndürülür ve uygulama bunu alarak ağ düzeyinde bir sorun olmadığını doğrular.

Başka bir "dünya turu" sonrasında istek ekibimize geri dönüyor ve isteğin bir yerden bir yere dolaşmayı bırakmasının kullanıcı için daha uygun olacağını düşünerek onu tamamen kendime aktarıyorum.

Bu arada kullanıcı, sistem görüntüsünün anlık görüntüsünü sağlamayı kabul eder. Bu çok iyi bir haber: Sistemi kendim test edebilmem sorun gidermeyi çok daha hızlı hale getiriyor çünkü artık kullanıcıdan komutları çalıştırmasını, sonuçları bana göndermesini ve analiz etmesini istemem gerekmiyor, her şeyi kendim yapabilirim!

Meslektaşlarım beni biraz kıskanmaya başlıyor. Öğle yemeğinde dönüşümü tartışıyoruz ama kimsenin neler olduğu hakkında hiçbir fikri yok. Neyse ki, kullanıcının kendisi sonuçları hafifletmek için zaten önlemler almıştır ve acelesi yoktur, bu nedenle sorunu incelemek için zamanımız vardır. Elimizde bir görüntü olduğu için ilgimizi çeken her türlü testi yapabiliriz. Harika!

Geri adım atmak

Sistem mühendisi pozisyonları için en popüler mülakat sorularından biri şudur: "Ping attığınızda ne olur?" www.google.com? Soru harika çünkü adayın kabuktan kullanıcı alanına, sistem çekirdeğine ve ardından ağa kadar her şeyi tanımlaması gerekiyor. Gülümsüyorum: Bazen röportaj sorularının gerçek hayatta faydalı olduğu ortaya çıkıyor...

Bu İK sorusunu güncel bir soruna uygulamaya karar verdim. Kabaca söylemek gerekirse, bir DNS adı belirlemeye çalıştığınızda aşağıdakiler gerçekleşir:

  1. Uygulama libdns gibi bir sistem kütüphanesini çağırır
  2. libdns, iletişim kurması gereken DNS sunucusunun sistem yapılandırmasını kontrol eder (şemada bu 169.254.169.254, meta veri sunucusudur)
  3. libdns, bir UDP soketi (SOKET_DGRAM) oluşturmak ve her iki yönde de bir DNS sorgusu ile UDP paketleri göndermek için sistem çağrılarını kullanır
  4. Sysctl arayüzü aracılığıyla UDP yığınını çekirdek düzeyinde yapılandırabilirsiniz.
  5. Çekirdek, ağ arayüzü aracılığıyla paketleri ağ üzerinden iletmek için donanımla etkileşime girer.
  6. Hipervizör, paketi temasa geçtiğinde yakalar ve meta veri sunucusuna iletir.
  7. Meta veri sunucusu, sihri sayesinde DNS adını belirler ve aynı yöntemi kullanarak bir yanıt döndürür.

Google Cloud teknik desteğindeki eksik DNS paketleriyle ilgili bir hikaye
Daha önce hangi hipotezleri dikkate aldığımızı size hatırlatmama izin verin:

Hipotez: Bozuk kütüphaneler

  • Test 1: sistemde strace'yi çalıştırın, dig'in doğru sistem çağrılarını çağırdığını kontrol edin
  • Sonuç: Doğru sistem çağrıları çağrılır
  • Test 2: sistem kitaplıklarını atlayarak adları belirleyip belirleyemeyeceğimizi kontrol etmek için srapy kullanmak
  • Sonuç: yapabiliriz
  • Test 3: libdns paketinde ve md5sum kitaplık dosyalarında rpm –V komutunu çalıştırın
  • Sonuç: Kütüphane kodu, çalışan işletim sistemindeki kodla tamamen aynıdır
  • Test 4: Kullanıcının kök sistem görüntüsünü bu davranış olmadan bir VM'ye bağlayın, chroot'u çalıştırın, DNS'nin çalışıp çalışmadığına bakın
  • Sonuç: DNS düzgün çalışıyor

Testlere dayalı sonuç: sorun kütüphanelerde değil

Hipotez: DNS ayarlarında bir hata var

  • Test 1: tcpdump'ı kontrol edin ve dig'i çalıştırdıktan sonra DNS paketlerinin doğru şekilde gönderilip gönderilmediğine bakın
  • Sonuç: paketler doğru şekilde iletiliyor
  • Test 2: sunucuyu iki kez kontrol edin /etc/nsswitch.conf и /etc/resolv.conf
  • Sonuç: her şey doğru

Testlere dayalı sonuç: sorun DNS yapılandırmasında değil

Hipotez: çekirdek hasarlı

  • Test: yeni çekirdeği yükleyin, imzayı kontrol edin, yeniden başlatın
  • Sonuç: benzer davranış

Testlere dayalı sonuç: çekirdek zarar görmemiş

Hipotez: kullanıcı ağının (veya hipervizör ağ arayüzünün) yanlış davranışı

  • Test 1: Güvenlik duvarı ayarlarınızı kontrol edin
  • Sonuç: Güvenlik duvarı DNS paketlerini hem ana makinede hem de GCP'de iletir
  • Test 2: trafiğin kesilmesi ve DNS isteklerinin iletiminin ve geri dönüşünün doğruluğunun izlenmesi
  • Sonuç: tcpdump, ana bilgisayarın dönüş paketlerini aldığını doğrular

Testlere dayalı sonuç: sorun ağda değil

Hipotez: meta veri sunucusu çalışmıyor

  • Test 1: anormallikler için meta veri sunucusu günlüklerini kontrol edin
  • Sonuç: günlüklerde herhangi bir anormallik yok
  • Test 2: Meta veri sunucusunu üzerinden atlayın dig @8.8.8.8
  • Sonuç: Meta veri sunucusu kullanılmadan bile çözünürlük bozuluyor

Testlere dayalı sonuç: sorun meta veri sunucusunda değil

Alt satırda: hariç tüm alt sistemleri test ettik çalışma zamanı ayarları!

Çekirdek Çalışma Zamanı Ayarlarına Dalış

Çekirdek yürütme ortamını yapılandırmak için komut satırı seçeneklerini (grub) veya sysctl arayüzünü kullanabilirsiniz. içine baktım /etc/sysctl.conf ve bir düşünün, birkaç özel ayar keşfettim. Sanki bir şeye tutunmuşum gibi hissederek, ağ dışı veya TCP dışı tüm ayarları attım ve dağ ayarlarında kaldım net.core. Daha sonra VM'de ana bilgisayar izinlerinin bulunduğu yere gittim ve suçluyu bulana kadar ayarları bozuk VM ile birer birer uygulamaya başladım:

net.core.rmem_default = 2147483647

İşte karşınızda, DNS'yi bozan bir yapılandırma! Cinayet silahını buldum. Peki bu neden oluyor? Hâlâ bir nedene ihtiyacım vardı.

Temel DNS paket arabelleği boyutu şu şekilde yapılandırılır: net.core.rmem_default. Tipik bir değer 200KiB civarındadır, ancak sunucunuz çok sayıda DNS paketi alıyorsa arabellek boyutunu artırmak isteyebilirsiniz. Yeni bir paket geldiğinde arabellek doluysa (örneğin, uygulamanın yeterince hızlı işlememesi nedeniyle), paketleri kaybetmeye başlarsınız. Müşterimiz, DNS paketleri aracılığıyla metrikleri toplamak için bir uygulama kullandığından, veri kaybından korktuğu için arabellek boyutunu doğru bir şekilde artırdı. Ayarladığı değer mümkün olan maksimum değerdi: 231-1 (eğer 231 olarak ayarlanırsa çekirdek "GEÇERSİZ ARGUMENT" değerini döndürecektir).

Aniden nmap ve scapy'nin neden doğru çalıştığını anladım: ham soketler kullanıyorlardı! Ham soketler normal soketlerden farklıdır: iptables'ları atlarlar ve ara belleğe alınmazlar!

Peki "arabellek çok büyük" neden sorunlara neden oluyor? Açıkça amaçlandığı gibi çalışmıyor.

Bu noktada sorunu birden çok çekirdekte ve birden çok dağıtımda yeniden üretebildim. Sorun zaten 3.x çekirdeğinde ortaya çıktı ve şimdi 5.x çekirdeğinde de ortaya çıktı.

Aslında, başlangıçta

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

DNS çalışmayı durdurdu.

Basit bir ikili arama algoritması üzerinden çalışma değerlerini aramaya başladım ve sistemin 2147481343 ile çalıştığını gördüm ancak bu sayı benim için anlamsız bir sayı dizisiydi. Müşteriye bu numarayı denemesini önerdim ve o da sistemin google.com ile çalıştığını ancak diğer alan adlarında hala hata verdiğini söyledi, ben de araştırmama devam ettim.

indirdim düşme saati, daha önce kullanılması gereken bir araç: bir paketin çekirdeğin tam olarak neresinde bulunduğunu gösterir. Suçlu işlevdi udp_queue_rcv_skb. Çekirdek kaynaklarını indirdim ve birkaç tane ekledim fonksiyonlar printk Paketin tam olarak nerede bittiğini izlemek için. Hızlı bir şekilde doğru koşulu buldum ifve bir süre ona baktım, çünkü o zaman her şey sonunda bir bütün halinde bir araya geldi: 231-1, anlamsız bir numara, çalışmayan bir alan... Bu, bir kod parçasıydı. __udp_enqueue_schedule_skb:

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

Lütfen dikkat:

  • rmem int türündedir
  • size u16 (imzasız on altı bit int) türündedir ve paket boyutunu saklar
  • sk->sk_rcybuf int türündedir ve tanımı gereği içindeki değere eşit olan arabellek boyutunu saklar. net.core.rmem_default

Ne zaman sk_rcvbuf 231'e yaklaşıldığında paket boyutunun toplanması şu sonuçla sonuçlanabilir: tamsayı taşması. Ve bu bir int olduğundan değeri negatif olur, dolayısıyla koşul yanlış olması gerekirken doğru olur (bununla ilgili daha fazla bilgiyi şu adreste bulabilirsiniz: bağlantı).

Hata önemsiz bir şekilde düzeltilebilir: döküm yaparak unsigned int. Düzeltmeyi uyguladım ve sistemi yeniden başlattım ve DNS tekrar çalıştı.

Zaferin tadı

Bulgularımı müşteriye ilettim ve gönderdim LKML çekirdek yaması. Memnun oldum: bulmacanın her parçası birbirine uyuyor, gözlemlediklerimizi neden gözlemlediğimizi tam olarak açıklayabiliyorum ve en önemlisi ekip çalışması sayesinde soruna çözüm bulabildik!

Durumun nadir olduğunu belirtmekte fayda var ve neyse ki kullanıcılardan nadiren bu tür karmaşık talepler alıyoruz.

Google Cloud teknik desteğindeki eksik DNS paketleriyle ilgili bir hikaye


Kaynak: habr.com

Yorum ekle