จากเครื่องมือแก้ไขบล็อกของ Google: คุณเคยสงสัยหรือไม่ว่าวิศวกรโซลูชันทางเทคนิคของ Google Cloud (TSE) จัดการกับคำขอการสนับสนุนของคุณอย่างไร วิศวกรฝ่ายสนับสนุนทางเทคนิคของ TSE มีหน้าที่รับผิดชอบในการระบุและแก้ไขสาเหตุของปัญหาที่ผู้ใช้รายงาน ปัญหาเหล่านี้บางส่วนค่อนข้างง่าย แต่บางครั้งคุณอาจเจอตั๋วที่ต้องได้รับการดูแลจากวิศวกรหลายคนในคราวเดียว ในบทความนี้ พนักงาน TSE คนหนึ่งจะบอกเราเกี่ยวกับปัญหาที่ยุ่งยากอย่างหนึ่งจากการปฏิบัติครั้งล่าสุดของเขา -
การแก้ไขปัญหาเป็นทั้งศาสตร์และศิลป์ ทุกอย่างเริ่มต้นด้วยการสร้างสมมติฐานเกี่ยวกับสาเหตุของพฤติกรรมที่ไม่ได้มาตรฐานของระบบ หลังจากนั้นจะมีการทดสอบความแข็งแกร่ง อย่างไรก็ตาม ก่อนที่เราจะตั้งสมมติฐาน เราจะต้องกำหนดและกำหนดปัญหาให้ชัดเจนก่อน หากคำถามฟังดูคลุมเครือเกินไป คุณจะต้องวิเคราะห์ทุกอย่างอย่างรอบคอบ นี่คือ “ศิลปะ” ของการแก้ไขปัญหา
ภายใต้ Google Cloud กระบวนการดังกล่าวมีความซับซ้อนมากขึ้นอย่างมาก เนื่องจาก Google Cloud พยายามอย่างดีที่สุดเพื่อรับประกันความเป็นส่วนตัวของผู้ใช้ ด้วยเหตุนี้ วิศวกรของ TSE จึงไม่มีสิทธิ์เข้าถึงเพื่อแก้ไขระบบของคุณ หรือไม่สามารถดูการกำหนดค่าในวงกว้างได้เช่นเดียวกับที่ผู้ใช้ทำ ดังนั้นเพื่อทดสอบสมมติฐานใดๆ ของเรา เรา (วิศวกร) จึงไม่สามารถปรับเปลี่ยนระบบได้อย่างรวดเร็ว
ผู้ใช้บางคนเชื่อว่าเราจะซ่อมแซมทุกอย่าง เช่น ช่างเครื่องในบริการรถยนต์ และเพียงส่ง ID ของเครื่องเสมือนมาให้เรา ในขณะที่ในความเป็นจริงกระบวนการเกิดขึ้นในรูปแบบการสนทนา: การรวบรวมข้อมูล การสร้างและยืนยัน (หรือหักล้าง) สมมติฐาน และท้ายที่สุดแล้ว ปัญหาในการตัดสินใจจะขึ้นอยู่กับการสื่อสารกับลูกค้า
ปัญหาที่เป็นปัญหา
วันนี้เรามีเรื่องราวที่มีตอนจบดีๆมาฝาก สาเหตุหนึ่งที่ทำให้การแก้ไขกรณีที่เสนอประสบผลสำเร็จคือคำอธิบายปัญหาที่ละเอียดและแม่นยำมาก ด้านล่างคุณจะเห็นสำเนาตั๋วใบแรก (แก้ไขเพื่อซ่อนข้อมูลที่เป็นความลับ):
ข้อความนี้มีข้อมูลที่เป็นประโยชน์มากมายสำหรับเรา:
- ระบุ VM เฉพาะแล้ว
- ระบุปัญหาแล้ว - DNS ไม่ทำงาน
- มีการระบุว่าปัญหาปรากฏที่ใด - VM และคอนเทนเนอร์
- ขั้นตอนที่ผู้ใช้ดำเนินการเพื่อระบุปัญหาจะถูกระบุ
คำขอได้รับการลงทะเบียนเป็น “P1: ผลกระทบร้ายแรง - บริการใช้งานไม่ได้ในการผลิต” ซึ่งหมายถึงการติดตามสถานการณ์อย่างต่อเนื่องทุกวันตลอด 24 ชั่วโมงตามโครงการ “Follow the Sun” (คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ
เมื่อตั๋วไปถึงซูริก เราก็มีข้อมูลต่อไปนี้อยู่ในมือแล้ว:
- เนื้อหา
/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
- แพ็กเก็ตที่สร้างโดยขุดถูกส่งคืนหรือไม่ => ขับรถออกไป
tcpdump
- libdns ทำงานอย่างถูกต้องหรือไม่? => ขับรถออกไป
strace
เพื่อตรวจสอบการส่งแพ็กเก็ตทั้งสองทิศทาง
ที่นี่เราตัดสินใจโทรหาผู้ใช้เพื่อแก้ไขปัญหาแบบสดๆ
ในระหว่างการโทร เราสามารถตรวจสอบได้หลายอย่าง:
- หลังจากการตรวจสอบหลายครั้ง เราจะแยกกฎ iptables ออกจากรายการเหตุผล
- เราตรวจสอบอินเทอร์เฟซเครือข่ายและตารางเส้นทาง และตรวจสอบความถูกต้องของ MTU อีกครั้ง
- เราค้นพบสิ่งนั้น
dig +tcp google.com
(TCP) ทำงานอย่างที่ควรจะเป็นแต่dig google.com
(UDP) ไม่ทำงาน - ขับออกไปแล้ว
tcpdump
มันยังทำงานอยู่dig
เราพบว่าแพ็กเก็ต UDP กำลังถูกส่งคืน - เราขับรถออกไป
strace dig google.com
และเราจะเห็นว่าการขุดเรียกอย่างถูกต้องอย่างไรsendmsg()
иrecvms()
อย่างไรก็ตาม อันที่สองถูกขัดจังหวะด้วยการหมดเวลา
น่าเสียดายที่จุดสิ้นสุดของกะมาถึงแล้ว และเราถูกบังคับให้ขยายปัญหาไปยังเขตเวลาถัดไป อย่างไรก็ตาม คำขอดังกล่าวกระตุ้นความสนใจในทีมของเรา และเพื่อนร่วมงานแนะนำให้สร้างแพ็คเกจ DNS เริ่มต้นโดยใช้โมดูล 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())
ส่วนนี้จะสร้างแพ็กเก็ต DNS และส่งคำขอไปยังเซิร์ฟเวอร์ข้อมูลเมตา
ผู้ใช้เรียกใช้โค้ด การตอบสนอง DNS จะถูกส่งกลับ และแอปพลิเคชันได้รับโค้ดดังกล่าว เพื่อยืนยันว่าไม่มีปัญหาในระดับเครือข่าย
หลังจาก "การเดินทางรอบโลก" อีกครั้ง คำขอก็กลับมาที่ทีมของเรา และฉันก็โอนมันให้กับตัวเองโดยสมบูรณ์ โดยคิดว่ามันจะสะดวกกว่าสำหรับผู้ใช้หากคำขอหยุดวนจากที่หนึ่งไปยังอีกที่หนึ่ง
ในระหว่างนี้ ผู้ใช้กรุณาตกลงที่จะจัดเตรียมสแนปช็อตของอิมเมจระบบ นี่เป็นข่าวดีมาก: ความสามารถในการทดสอบระบบด้วยตัวเองทำให้การแก้ไขปัญหาเร็วขึ้นมาก เพราะฉันไม่ต้องขอให้ผู้ใช้เรียกใช้คำสั่งอีกต่อไป ส่งผลลัพธ์และวิเคราะห์ให้ฉัน ฉันทำทุกอย่างด้วยตัวเองได้!
เพื่อนร่วมงานของฉันเริ่มอิจฉาฉันนิดหน่อย ในช่วงรับประทานอาหารกลางวัน เราคุยกันเรื่องการเปลี่ยนใจเลื่อมใส แต่ไม่มีใครรู้ว่าเกิดอะไรขึ้น โชคดีที่ผู้ใช้เองได้ดำเนินมาตรการเพื่อบรรเทาผลที่ตามมาแล้วและไม่รีบร้อน ดังนั้นเราจึงมีเวลาวิเคราะห์ปัญหา และเนื่องจากเรามีอิมเมจ เราจึงสามารถทำการทดสอบใดๆ ที่เราสนใจได้ ยอดเยี่ยม!
ถอยหลังหนึ่งก้าว
คำถามสัมภาษณ์งานยอดนิยมสำหรับตำแหน่งวิศวกรระบบคือ “จะเกิดอะไรขึ้นเมื่อคุณ Ping
ฉันตัดสินใจใช้คำถามด้านทรัพยากรบุคคลนี้กับปัญหาปัจจุบัน โดยทั่วไปแล้ว เมื่อคุณพยายามระบุชื่อ DNS สิ่งต่อไปนี้จะเกิดขึ้น:
- แอปพลิเคชันเรียกไลบรารีระบบเช่น libdns
- libdns ตรวจสอบการกำหนดค่าระบบที่เซิร์ฟเวอร์ DNS ที่ควรติดต่อ (ในแผนภาพนี่คือ 169.254.169.254 เซิร์ฟเวอร์ข้อมูลเมตา)
- libdns ใช้การเรียกของระบบเพื่อสร้างซ็อกเก็ต UDP (SOKET_DGRAM) และส่งแพ็กเก็ต UDP พร้อมแบบสอบถาม DNS ในทั้งสองทิศทาง
- ผ่านอินเทอร์เฟซ sysctl คุณสามารถกำหนดค่าสแต็ก UDP ในระดับเคอร์เนลได้
- เคอร์เนลโต้ตอบกับฮาร์ดแวร์เพื่อส่งแพ็กเก็ตผ่านเครือข่ายผ่านอินเทอร์เฟซเครือข่าย
- ไฮเปอร์ไวเซอร์จะจับและส่งแพ็กเก็ตไปยังเซิร์ฟเวอร์ข้อมูลเมตาเมื่อติดต่อกับมัน
- เซิร์ฟเวอร์ข้อมูลเมตาใช้เวทย์มนตร์ในการกำหนดชื่อ DNS และส่งคืนการตอบกลับโดยใช้วิธีการเดียวกัน
ฉันขอเตือนคุณว่าเราพิจารณาสมมติฐานอะไรบ้าง:
สมมติฐาน: ห้องสมุดที่เสียหาย
- ทดสอบ 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: ตรวจสอบการตั้งค่าไฟร์วอลล์ของคุณ
- ผลลัพธ์: ไฟร์วอลล์ส่งแพ็กเก็ต DNS ทั้งโฮสต์และ GCP
- การทดสอบที่ 2: สกัดกั้นการรับส่งข้อมูลและตรวจสอบความถูกต้องของการส่งและการส่งคืนคำขอ DNS
- ผลลัพธ์: tcpdump ยืนยันว่าโฮสต์ได้รับแพ็กเก็ตส่งคืน
ข้อสรุปจากการทดสอบ: ปัญหาไม่ได้อยู่ในเครือข่าย
สมมติฐาน: เซิร์ฟเวอร์ข้อมูลเมตาไม่ทำงาน
- การทดสอบที่ 1: ตรวจสอบบันทึกเซิร์ฟเวอร์ข้อมูลเมตาเพื่อหาความผิดปกติ
- ผลลัพธ์: ไม่มีความผิดปกติในบันทึก
- การทดสอบ 2: ข้ามเซิร์ฟเวอร์ข้อมูลเมตาผ่านทาง
dig @8.8.8.8
- ผลลัพธ์: ความละเอียดใช้งานไม่ได้แม้ว่าจะไม่ได้ใช้เซิร์ฟเวอร์ข้อมูลเมตาก็ตาม
ข้อสรุปจากการทดสอบ: ปัญหาไม่ได้อยู่ที่เซิร์ฟเวอร์ข้อมูลเมตา
บรรทัดด้านล่าง: เราทดสอบระบบย่อยทั้งหมดยกเว้น การตั้งค่ารันไทม์!
ดำน้ำในการตั้งค่ารันไทม์เคอร์เนล
ในการกำหนดค่าสภาพแวดล้อมการดำเนินการเคอร์เนล คุณสามารถใช้ตัวเลือกบรรทัดคำสั่ง (ด้วง) หรืออินเทอร์เฟซ sysctl ฉันมองเข้าไป /etc/sysctl.conf
และลองคิดดูว่า ฉันค้นพบการตั้งค่าแบบกำหนดเองหลายอย่าง รู้สึกราวกับว่าฉันคว้าอะไรบางอย่างได้ ฉันจึงละทิ้งการตั้งค่าที่ไม่ใช่เครือข่ายหรือไม่ใช่ TCP ทั้งหมด เหลือเพียงการตั้งค่าบนภูเขา net.core
. จากนั้นฉันก็ไปที่ตำแหน่งที่สิทธิ์ของโฮสต์อยู่ใน VM และเริ่มใช้การตั้งค่าทีละรายการกับ VM ที่เสียหาย จนกระทั่งฉันพบผู้กระทำผิด:
net.core.rmem_default = 2147483647
นี่คือการกำหนดค่าที่ทำลาย DNS! ฉันพบอาวุธสังหารแล้ว แต่ทำไมสิ่งนี้ถึงเกิดขึ้น? ฉันยังต้องการแรงจูงใจ
ขนาดบัฟเฟอร์แพ็กเก็ต DNS พื้นฐานได้รับการกำหนดค่าผ่าน net.core.rmem_default
. ค่าทั่วไปจะอยู่ที่ประมาณ 200KiB แต่หากเซิร์ฟเวอร์ของคุณได้รับแพ็กเก็ต DNS จำนวนมาก คุณอาจต้องการเพิ่มขนาดบัฟเฟอร์ หากบัฟเฟอร์เต็มเมื่อมีแพ็กเก็ตใหม่มาถึง เช่น เนื่องจากแอปพลิเคชันไม่ประมวลผลเร็วพอ คุณจะเริ่มสูญเสียแพ็กเก็ต ลูกค้าของเราเพิ่มขนาดบัฟเฟอร์อย่างถูกต้องเพราะเขากลัวข้อมูลสูญหาย เนื่องจากเขาใช้แอปพลิเคชันเพื่อรวบรวมตัวชี้วัดผ่านแพ็คเก็ต DNS ค่าที่เขาตั้งไว้คือค่าสูงสุดที่เป็นไปได้: 231-1 (หากตั้งเป็น 231 เคอร์เนลจะส่งกลับ "อาร์กิวเมนต์ที่ไม่ถูกต้อง")
ทันใดนั้นฉันก็รู้ว่าทำไม nmap และ scapy จึงทำงานได้อย่างถูกต้อง: พวกมันใช้ซ็อกเก็ตดิบ! ซ็อกเก็ตแบบ Raw นั้นแตกต่างจากซ็อกเก็ตทั่วไป: พวกมันเลี่ยงผ่าน iptable และไม่ถูกบัฟเฟอร์!
แต่เหตุใด "บัฟเฟอร์ใหญ่เกินไป" จึงทำให้เกิดปัญหา มันไม่ทำงานตามที่ตั้งใจไว้อย่างชัดเจน
ณ จุดนี้ ฉันสามารถสร้างปัญหาซ้ำบนเคอร์เนลหลายตัวและการแจกแจงหลายตัวได้ ปัญหาปรากฏบนเคอร์เนล 3.x แล้ว และตอนนี้ก็ปรากฏบนเคอร์เนล 5.x ด้วย
แน่นอนเมื่อเริ่มต้น
sysctl -w net.core.rmem_default=$((2**31-1))
DNS หยุดทำงาน
ฉันเริ่มมองหาค่าการทำงานผ่านอัลกอริธึมการค้นหาแบบไบนารี่ธรรมดาและพบว่าระบบใช้งานได้กับ 2147481343 แต่ตัวเลขนี้เป็นชุดตัวเลขที่ไม่มีความหมายสำหรับฉัน ฉันแนะนำให้ลูกค้าลองใช้หมายเลขนี้ และเขาตอบว่าระบบใช้งานได้กับ google.com แต่ยังคงแสดงข้อผิดพลาดกับโดเมนอื่น ดังนั้นฉันจึงดำเนินการตรวจสอบต่อไป
ฉันได้ติดตั้งแล้ว udp_queue_rcv_skb
. ฉันดาวน์โหลดเคอร์เนลซอร์สและเพิ่มบางส่วน printk
if
และจ้องมองมันสักพัก เพราะตอนนั้นเองที่ทุกอย่างมารวมกันเป็นภาพรวม: 231-1 จำนวนที่ไม่มีความหมาย โดเมนที่ไม่ทำงาน... มันเป็นโค้ดชิ้นหนึ่งใน __udp_enqueue_schedule_skb
:
if (rmem > (size + sk->sk_rcvbuf))
goto uncharge_drop;
โปรดทราบ:
rmem
เป็นประเภท intsize
เป็นประเภท u16 (int สิบหกบิตที่ไม่ได้ลงชื่อ) และจัดเก็บขนาดแพ็คเก็ตsk->sk_rcybuf
เป็นประเภท int และเก็บขนาดบัฟเฟอร์ซึ่งตามคำจำกัดความจะเท่ากับค่าในnet.core.rmem_default
เมื่อ sk_rcvbuf
เข้าใกล้ 231 โดยสรุปขนาดแพ็กเก็ตอาจส่งผลให้
ข้อผิดพลาดสามารถแก้ไขได้ด้วยวิธีเล็กน้อย: โดยการแคสต์ unsigned int
. ฉันใช้การแก้ไขแล้วรีสตาร์ทระบบ และ DNS ก็ทำงานได้อีกครั้ง
รสชาติแห่งชัยชนะ
ฉันส่งต่อสิ่งที่ค้นพบของฉันไปยังลูกค้าแล้วส่งไป
เป็นเรื่องที่ควรค่าแก่การตระหนักว่ากรณีนี้เกิดขึ้นไม่บ่อยนัก และโชคดีที่เราแทบไม่ได้รับคำขอที่ซับซ้อนเช่นนี้จากผู้ใช้
ที่มา: will.com