ProHoster > Blog > yönetim > Google Cloud teknik desteğindeki eksik DNS paketleriyle ilgili bir hikaye
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.
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):
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
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:
Uygulama libdns gibi bir sistem kütüphanesini çağırır
libdns, iletişim kurması gereken DNS sunucusunun sistem yapılandırmasını kontrol eder (şemada bu 169.254.169.254, meta veri sunucusudur)
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
Sysctl arayüzü aracılığıyla UDP yığınını çekirdek düzeyinde yapılandırabilirsiniz.
Çekirdek, ağ arayüzü aracılığıyla paketleri ağ üzerinden iletmek için donanımla etkileşime girer.
Hipervizör, paketi temasa geçtiğinde yakalar ve meta veri sunucusuna iletir.
Meta veri sunucusu, sihri sayesinde DNS adını belirler ve aynı yöntemi kullanarak bir yanıt döndürür.
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 fonksiyonlarprintk 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.