การตรวจสอบบันทึกอย่างละเอียด

เมื่อผมเข้าทำงานที่ Amazon หลังเรียนจบ แบบฝึกหัดแรกๆ ของพนักงานใหม่อย่างผมคือการทำให้เว็บเซิร์ฟเวอร์ amazon.com ทำงานบนเดสก์ท็อปนักพัฒนาของผมได้ พอลองทำครั้งแรกแล้วออกมาผิด ผมก็ไม่แน่ใจว่าทำผิดตรงไหน เพื่อนร่วมงานช่วยแนะนำให้ผมดูบันทึกเพื่อดูว่ามีอะไรผิดปกติ บอกว่าผมควรจะ "แคทไฟล์บันทึก" ผมเชื่อว่าพวกเขากำลังแกล้งผมเล่นหรือเล่นมุกเกี่ยวกับแมวที่ผมไม่เข้าใจ สมัยอยู่มหาลัย ผมใช้แค่ Linux เพื่อคอมไพล์ ใช้ตัวควบคุมต้นฉบับ และใช้ตัวแก้ไขข้อความ ผมจึงไม่รู้ว่า "cat" เป็นคำสั่งให้พิมพ์ไฟล์ไปยังเทอร์มินัล ที่ผมสามารถป้อนเข้าไปในโปรแกรมอื่นเพื่อค้นหารูปแบบได้

เพื่อนร่วมงานชี้ให้ผมเห็นถึงเครื่องมืออย่าง cat, grep, sed และ awk เมื่อมีชุดเครื่องมือใหม่นี้แล้ว ผมได้ลงลึกในเรื่องบันทึกของเว็บเซิร์ฟเวอร์ amazon.com บนเดสก์ท็อปนักพัฒนา แอปพลิเคชันเว็บเซิร์ฟเวอร์ได้รับการติดตั้งอยู่ก่อนแล้วเพื่อปล่อยข้อมูลที่เป็นประโยชน์ทุกชนิดลงในบันทึกของมัน ทำให้ผมเห็นว่าการกำหนดค่าใดที่ผมพลาดไปที่ทำให้เว็บเซิร์ฟเวอร์เริ่มต้นทำงานไม่ได้ โดยแสดงจุดที่อาจมีการหยุดทำงาน หรือจุดที่สื่อสารกับบริการดาวน์สตรีมไม่ได้ เว็บไซต์ประกอบด้วยกลไกจำนวนมาก และเป็นเหมือนกล่องดำสำหรับผมในช่วงเริ่มแรก แต่หลังจากลงลึกในเรื่องระบบ ผมได้เรียนรู้จนเข้าใจวิธีการทำงานของเซิร์ฟเวอร์และวิธีการโต้ตอบกับส่วนที่ขึ้นต่อกันแค่เพียงดูผลลัพธ์ของการอินสตรูเมนต์

ทำไมต้องอินสตรูเมนต์

ตลอดหลายปีที่ผมได้ร่วมงานกับทีมต่างๆ ใน Amazon ผมก็พบว่าการอินสตรูเมนต์เป็นมุมมองล้ำค่าซึ่งผมและคนอื่นๆ ใน Amazon ใช้เพื่อเรียนรู้ว่าระบบทำงานอย่างไร แต่การอินสตรูเมนต์มีประโยชน์มากกว่าเพียงแค่เป็นกระบวนการเรียนรู้เกี่ยวกับระบบ คือเป็นแก่นวัฒนธรรมการดำเนินงานที่ Amazon การอินสตรูเมนต์ที่ยอดเยี่ยมช่วยให้เราเห็นประสบการณ์ที่เรามอบให้แก่ลูกค้าของเรา
 
ทั่วทั้งบริษัทต่างก็มุ่งเน้นไปที่ผลการดำเนินงานในลักษณะนี้ ภายในบริการที่เกี่ยวข้องกับ amazon.com เวลาแฝงที่เพิ่มขึ้นย่อมหมายถึงประสบการณ์การช็อปปิ้งที่แย่และทำให้อัตราคอนเวอร์ชันลดลง ลูกค้าที่ใช้ AWS จะพึ่งพาความพร้อมใช้งานสูงและเวลาแฝงต่ำของบริการของ AWS
 
ที่ Amazon เราไม่ได้คิดคำนึงแค่เวลาแฝงโดยเฉลี่ยเท่านั้น เรา มุ่งเน้นเจาะลึกยิ่งขึ้นเกี่ยวกับค่าผิดปกติของเวลาแฝง เช่น เปอร์เซ็นต์ไทล์ที่ 99.9 และ 99.99 เพราะว่าถ้าหนึ่งคำขอจาก 1,000 หรือ 10,000 เกิดอาการช้า ก็ยังคงถือเป็นประสบการณ์ที่แย่ เราพบว่าเมื่อเราลดเวลาแฝงที่เปอร์เซ็นต์ไทล์สูงในระบบ ความพยายามของเรามีผลข้างเคียงคือช่วยลดเวลาแฝงมัธยฐานด้วย ในทางตรงกันข้าม เราพบว่าเมื่อเราลดเวลาแฝงมัธยฐาน เวลาแฝงที่เปอร์เซ็นต์ไทล์สูงกลับลดลงไม่บ่อยเท่า
 
เหตุผลอื่นที่เรามุ่งเน้นเวลาแฝงที่เปอร์เซ็นต์ไทล์สูง คือเวลาแฝงสูงในบริการหนึ่งอาจส่งผลเสียเป็นทวีคูณในบริการอื่นๆ ได้ Amazon สร้างขึ้นจากสถาปัตยกรรมที่มุ่งเน้นบริการ บริการจำนวนมากทำงานร่วมกันเพื่อทำสิ่งต่างๆ เช่น แสดงผลเว็บเพจใน amazon.com ผลก็คือ การเพิ่มขึ้นของเวลาแฝงของการบริการที่ลึกลงไปในห่วงโซ่การเรียก แม้ว่าการเพิ่มขึ้นจะเป็นเปอร์เซ็นต์ไทล์สูง จะมีผลกระทบต่อเนื่องใหญ่หลวงต่อเวลาแฝงที่ผู้ใช้ได้สัมผัส
 
ระบบขนาดใหญ่ที่ Amazon ประกอบด้วยหลายบริการที่ทำงานร่วมกัน แต่ละบริการได้รับการพัฒนาและดำเนินการโดยทีมเดียว (เบื้องหลัง "บริการ" ขนาดใหญ่ประกอบด้วยบริการหรือส่วนประกอบหลายอย่าง) ทีมที่เป็นเจ้าของบริการจะเป็นที่เรียกกันว่า เจ้าของบริการ สมาชิกทุกคนในทีมนั้นคิดเหมือนเจ้าของและผู้ให้บริการ ไม่ว่าสมาชิกนั้นจะเป็นนักพัฒนาซอฟต์แวร์ วิศวกรเครือข่าย ผู้จัดการ หรือบทบาทอื่นใด ในฐานะเจ้าของ ทีมตั้งเป้าหมายด้านผลการดำเนินงานของบริการที่เกี่ยวข้องทั้งหมด เรายังตรวจสอบให้แน่ใจด้วยว่าเราสามารถมองเห็นผลการดำเนินงานของบริการเพื่อรับประกันว่าเราจะบรรลุเป้าหมายเหล่านั้น เพื่อจัดการกับปัญหาที่เกิดขึ้น และได้ตามเป้าหมายที่สูงยิ่งขึ้นในปีต่อไป ในการตั้งเป้าหมายและมีความสามารถในการมองเห็นนั้น ทีมต้องอินสตรูเมนต์ระบบ
การอินสตรูเมนต์ยังช่วยให้เราตรวจหาและตอบสนองต่อเหตุการณ์การดำเนินงานอย่างมีชั้นเชิงได้อีกด้วย
 
การอินสตรูเมนต์ป้อนข้อมูลลงในแดชบอร์ดการดำเนินงาน เพื่อให้ผู้ให้บริการดูตัววัดแบบเรียลไทม์ได้ นอกจากนี้ ยังป้อนข้อมูลลงในการแจ้งเตือนซึ่งจะทริกเกอร์และมีส่วนร่วมกับผู้ให้บริการเมื่อระบบมีพฤติกรรมในลักษณะที่ไม่คาดคิด ผู้ให้บริการใช้รายละเอียดผลลัพธ์ของการอินสตรูเมนต์เพื่อวินิจฉัยสาเหตุของสิ่งผิดปกติอย่างรวดเร็ว จากจุดนั้นเองที่เราสามารถบรรเทาปัญหา และกลับมาใหม่ในภายหลังเพื่อป้องกันไม่ให้ปัญหาเกิดขึ้นอีก หากไม่มีการอินสตรูเมนต์ที่ดีทั่วทั้งโค้ด เราจะเสียเวลาอันมีค่าไปกับการวินิจฉัยปัญหา

ต้องวัดอะไร

ในการให้บริการตามมาตรฐานระดับสูงของเราในด้านความพร้อมใช้งานและเวลาแฝง เราในฐานะเจ้าของบริการจำเป็นต้องวัดว่าระบบของเรามีพฤติกรรมอย่างไร

เพื่อจะให้มีการวัดและส่งข้อมูลทางไกลที่จำเป็น เจ้าของบริการจะวัดผลการดำเนินงานจากหลายๆ ที่เพื่อให้มีหลายมุมมองว่าสิ่งต่างๆ มีพฤติกรรมอย่างไรตั้งแต่ต้นจนจบ ซึ่งมีความซับซ้อนแม้กระทั่งในสถาปัตยกรรมที่เรียบง่าย พิจารณาบริการที่ลูกค้าโทรผ่านโหลดบาลานเซอร์: บริการสื่อสารกับแคชระยะไกลและฐานข้อมูลระยะไกล เราต้องการให้แต่ละส่วนประกอบสร้างตัววัดเกี่ยวกับพฤติกรรมของตัวเอง และเราต้องการตัววัดว่าแต่ละส่วนประกอบรับรู้พฤติกรรมของส่วนประกอบอื่นๆ อย่างไร เมื่อตัววัดจากมุมมองเหล่านี้ทั้งหมดนำมารวมกัน เจ้าของบริการจะติดตามที่มาของปัญหาได้รวดเร็วและขุดค้นเพื่อหาสาเหตุ

บริการของ AWS จำนวนมากให้ข้อมูลเชิงลึกโดยอัตโนมัติเกี่ยวกับทรัพยากรของคุณ เช่น Amazon DynamoDB ให้บริการตัววัด Amazon CloudWatch เกี่ยวกับอัตราความสำเร็จและอัตราข้อผิดพลาดและเวลาแฝงที่วัดโดยบริการ แต่เมื่อเราสร้างระบบที่ใช้บริการเหล่านี้ เราต้องมีความสามารถในการมองเห็นมากขึ้นกว่านี้ว่าระบบของเรามีพฤติกรรมอย่างไร การอินสตรูเมนต์จำเป็นต้องใช้โค้ดที่ชัดเจนซึ่งบันทึกระยะเวลาของงาน ความถี่การใช้บางเส้นทางของโค้ด เมตาดาต้าเกี่ยวกับงานที่กำลังทำอยู่ และส่วนของงานที่สำเร็จหรือล้มเหลว หากทีมไม่ได้เพิ่มการอินสตรูเมนต์ที่ชัดเจน ก็จะถูกบังคับให้มอบบริการของตนเองเสมือนเป็นกล่องดำ

เช่น หากเราให้บริการ API ที่ดึงข้อมูลผลิตภัณฑ์ตาม ID ผลิตภัณฑ์ โค้ดอาจมีลักษณะตัวอย่างดังต่อไปนี้ โค้ดนี้จะค้นหาข้อมูลผลิตภัณฑ์ในแคชภายในเครื่อง ตามด้วยแคชระยะไกล ตามด้วยฐานข้อมูล:

public GetProductInfoResponse getProductInfo(GetProductInfoRequest request) {

  // check our local cache
  ProductInfo info = localCache.get(request.getProductId());
  
  // check the remote cache if we didn't find it in the local cache
  if (info == null) {
    info = remoteCache.get(request.getProductId());
	
	localCache.put(info);
  }
  
  // finally check the database if we didn't have it in either cache
  if (info == null) {
    info = db.query(request.getProductId());
	
	localCache.put(info);
	remoteCache.put(info);
  }
  
  return info;
}

หากผมให้บริการนี้ ผมต้องมีการอินสตรูเมนต์จำนวนมากในโค้ดนี้เพื่อให้สามารถเข้าใจพฤติกรรมของบริการในการผลิต ผมต้องมีความสามารถในการแก้ไขปัญหาคำขอที่ล้มเหลวหรือช้า และตรวจสอบแนวโน้มและสัญญาณว่าส่วนที่ขึ้นต่อกันต่างๆ ต่ำกว่าขนาดหรือมีพฤติกรรมผิดปกติ นี่คือโค้ดเดียวกัน ซึ่งมีคำถามสองสามข้อเพิ่มเติมประกอบที่ผมต้องสามารถตอบเกี่ยวกับระบบการผลิตโดยรวม หรือสำหรับคำขอเฉพาะ:

public GetProductInfoResponse getProductInfo(GetProductInfoRequest request) {

  // Which product are we looking up?
  // Who called the API? What product category is this in?

  // Did we find the item in the local cache?
  ProductInfo info = localCache.get(request.getProductId());
  
  if (info == null) {
    // Was the item in the remote cache?
    // How long did it take to read from the remote cache?
    // How long did it take to deserialize the object from the cache?
    info = remoteCache.get(request.getProductId());
	
    // How full is the local cache?
    localCache.put(info);
  }
  
  // finally check the database if we didn't have it in either cache
  if (info == null) {
    // How long did the database query take?
    // Did the query succeed? 
    // If it failed, is it because it timed out? Or was it an invalid query? Did we lose our database connection?
    // If it timed out, was our connection pool full? Did we fail to connect to the database? Or was it just slow to respond?
    info = db.query(request.getProductId());
	
    // How long did populating the caches take? 
    // Were they full and did they evict other items? 
    localCache.put(info);
    remoteCache.put(info);
  }
  
  // How big was this product info object? 
  return info;
}

โค้ดสำหรับการตอบคำถามเหล่านี้ทั้งหมด (และอื่นๆ) นั้นค่อนข้างยาวกว่าตรรกะทางธุรกิจจริงเล็กน้อย บางไลบรารีอาจช่วยลดจำนวนโค้ดการอินสตรูเมนต์ แต่นักพัฒนายังต้องถามคำถามเกี่ยวกับการมองเห็นที่ไลบรารีจะต้องมี และจากนั้นนักพัฒนาจะต้องตั้งใจเกี่ยวกับการเดินสายในการอินสตรูเมนต์

เมื่อคุณแก้ไขปัญหาคำขอที่ส่งผ่านระบบแบบกระจาย อาจเป็นเรื่องยากที่จะเข้าใจว่าเกิดอะไรขึ้นถ้าคุณดูที่คำขอนั้นโดยอิงตามการโต้ตอบเพียงครั้งเดียว ในการต่อจิ๊กซอว์เข้าด้วยกัน เราพบว่ามีประโยชน์ในการดึงทุกการวัดเกี่ยวกับทุกระบบเหล่านี้มารวมไว้ในที่เดียว ก่อนที่เราจะทำแบบนั้นได้ แต่ละบริการต้องมีการอินสตรูเมนต์เพื่อบันทึก ID การติดตามสำหรับแต่ละงาน และเพื่อเผยแพร่ ID การติดตามนั้นไปยังแต่ละบริการอื่นๆ ที่ทำงานนั้นร่วมกัน การรวบรวมการอินสตรูเมนต์ทั่วทั้งระบบสำหรับ ID การติดตามที่กำหนดสามารถทำได้หลังเกิดเหตุการณ์ขึ้นแล้วตามต้องการ หรือไม่ก็แบบใกล้เคียงเรียลไทม์โดยใช้บริการ เช่น AWS X-Ray

การเจาะลึก

การอินสตรูเมนต์ช่วยแก้ไขปัญหาในหลายระดับ ตั้งแต่การมองตัววัดแบบเผินๆ เพื่อดูว่ามีความผิดปกติที่แนบเนียนเกินกว่าจะทริกเกอร์การแจ้งเตือนบ้างหรือไม่ ไปจนถึงการสอบสวนเพื่อค้นหาสาเหตุของความผิดปกติเหล่านั้น

ในระดับสูงสุด การอินสตรูเมนต์จะรวมเข้าไปในตัววัดที่สามารถทริกเกอร์การแจ้งเตือนและแสดงผลบนแดชบอร์ด ตัววัดรวมเหล่านี้ช่วยให้ผู้ให้บริการได้ตรวจสอบอัตราคำขอโดยรวม เวลาแฝงในการเรียกใช้บริการ และอัตราความผิดพลาด การแจ้งเตือนและตัววัดเหล่านี้ทำให้เราตระหนักถึงความผิดปกติหรือการเปลี่ยนแปลงที่ควรได้รับการตรวจสอบ

หลังจากที่เราเห็นความผิดปกติ เราต้องหาสาเหตุว่าทำไมความผิดปกตินั้นจึงเกิดขึ้น เพื่อที่ตอบคำถามนั้น เราต้องพึ่งพาตัววัดที่เป็นไปได้โดยการอินสตรูเมนต์มากยิ่งกว่าเดิม โดยการอินสตรูเมนต์เวลาที่ใช้ในการดำเนินการตามส่วนต่าง ๆ ของการให้บริการตามคำขอ เราจะเห็นว่าส่วนใดของการประมวลผลที่ช้ากว่าปกติหรือทำให้เกิดข้อผิดพลาดบ่อยขึ้น

แม้ว่าตัววัดและตัวจับเวลารวมอาจช่วยให้เราแยกแยะสาเหตุหรือเน้นส่วนที่ต้องตรวจสอบได้ แต่ก็ไม่ได้ให้คำอธิบายครบถ้วน เช่น เราอาจบอกได้จากตัววัดว่าข้อผิดพลาดมาจากการดำเนินการ API เฉพาะ แต่ตัววัดอาจเปิดเผยข้อมูลไม่เฉพาะเจาะจงมากพอว่าทำไมการดำเนินการนั้นจึงล้มเหลว ณ จุดนี้ เราจะดูข้อมูลบันทึกรายละเอียดดิบที่ปล่อยออกมาจากบริการสำหรับช่วงเวลานั้นๆ บันทึกดิบจะแสดงที่มาของปัญหา – ไม่ว่าจะเป็นข้อผิดพลาดเฉพาะที่เกิดขึ้น หรือแง่มุมเฉพาะของคำขอที่ทริกเกอร์กรณี Edge บางรายการ

เราอินสตรูเมนต์อย่างไร

การอินสตรูเมนต์ต้องมีการเข้ารหัส หมายความว่าเมื่อเราใช้งานฟังก์ชันใหม่ เราต้องใช้เวลาในการเพิ่มโค้ดพิเศษเพื่อระบุว่าเกิดอะไรขึ้น ว่าสำเร็จหรือล้มเหลว และว่าต้องใช้เวลานานเท่าใด เนื่องจากการอินสตรูเมนต์เป็นงานเขียนโค้ดที่ใช้กันทั่วไป แนวทางปฏิบัติจึงเกิดขึ้นที่ Amazon ในช่วงหลายปีที่ผ่านมาเพื่อกำหนดรูปแบบทั่วไป: การวางมาตรฐานสำหรับไลบรารี และการวางมาตรฐานสำหรับการรายงานตัววัดตามบันทึกที่มีโครงสร้าง

การวางมาตรฐานของไลบรารีอินสตรูเมนต์ตัววัดช่วยให้ผู้สร้างไลบรารีได้ให้ผู้บริโภคได้มองเห็นว่าไลบรารีของตนดำเนินงานอย่างไร ตัวอย่างเช่น ไคลเอ็นต์ HTTP ที่ใช้กันทั่วไปจะรวมเข้ากับไลบรารีทั่วไปเหล่านี้ ดังนั้นหากทีมบริการดำเนินการเรียกระยะไกลไปยังบริการอื่น ก็จะได้มีการอินสตรูเมนต์การเรียกเหล่านั้นโดยอัตโนมัติ

เมื่อแอปพลิเคชันที่มีการอินสตรูเมนต์ดำเนินงานและทำงาน ข้อมูลการวัดและส่งข้อมูลทางไกลที่ได้จะมีการเขียนไปยังไฟล์บันทึกที่มีโครงสร้าง โดยทั่วไปจะมีการปล่อยออกเป็นหนึ่งรายการบันทึกต่อ "หน่วยของงาน" ไม่ว่าจะเป็นคำขอไปยังบริการ HTTP หรือข้อความที่ดึงมาจากคิว

ที่ Amazon การวัดในแอปพลิเคชันจะไม่มีการรวบรวมและปล่อยออกเป็นครั้งคราวไปยังระบบการรวมตัววัด ตัวจับเวลาและตัวนับทั้งหมดสำหรับงานทุกชิ้นจะถูกเขียนในไฟล์บันทึก จากนั้นระบบอื่นจะประมวลผลบันทึกและคำนวณตัววัดรวมหลังเกิดเหตุการณ์แล้ว ด้วยวิธีนี้เราจะลงเอยด้วยการมีทุกอย่างตั้งแต่ตัววัดการดำเนินงานรวมระดับสูงไปจนถึงข้อมูลการแก้ไขปัญหาโดยละเอียดในระดับคำขอ ทั้งหมดล้วนใช้แนวทางเดียวกับการอินสตรูเมนต์โค้ด ที่ Amazon เราบันทึกก่อน แล้วสร้างตัววัดรวมภายหลัง

การอินสตรูเมนต์ผ่านการบันทึก

เรามักจะอินสตรูเมนต์บริการของเราเพื่อปล่อยข้อมูลบันทึกสองประเภท: ข้อมูลคำขอและข้อมูลการดีบัก โดยทั่วไปข้อมูลบันทึกคำขอจะแสดงเป็นรายการบันทึกที่มีโครงสร้างเดียวสำหรับแต่ละหน่วยของงาน ข้อมูลนี้มีคุณสมบัติเกี่ยวกับคำขอและใครเป็นผู้ร้องขอ คำขอนั้นมีไว้เพื่ออะไร ตัวนับว่าเกิดขึ้นบ่อยครั้งแค่ไหน และตัวจับเวลาว่าใช้เวลานานแค่ไหน บันทึกคำขอทำหน้าที่เป็นบันทึกการตรวจสอบและการติดตามสำหรับทุกสิ่งที่เกิดขึ้นในบริการ ข้อมูลการดีบักรวมถึงข้อมูลที่ไม่มีโครงสร้างหรือมีโครงสร้างหลวมๆ ของบรรทัดการดีบักใดๆ ที่แอปพลิเคชันปล่อยออกมา โดยทั่วไปจะเป็นรายการบันทึกที่ไม่มีโครงสร้าง เช่น ข้อผิดพลาด Log4j หรือบรรทัดบันทึกเตือน ที่ Amazon ข้อมูลทั้งสองประเภทนี้มักจะปล่อยลงในไฟล์บันทึกแยกต่างหาก บางส่วนเป็นเพราะเหตุผลทางประวัติศาสตร์ แต่บางส่วนก็เพราะความสะดวกในการวิเคราะห์บันทึกในรายการบันทึกที่เป็นรูปแบบเดียวกัน

เอเจนต์ เช่น CloudWatch Logs Agent ประมวลผลข้อมูลบันทึกทั้งสองชนิดแบบเรียลไทม์และจัดส่งบันทึกไปยัง CloudWatch Logs ในทางกลับกัน CloudWatch Logs จะรวบรวมตัววัดเกี่ยวกับบริการเกือบจะแบบเรียลไทม์ Amazon CloudWatch Alarms อ่านตัววัดรวมเหล่านี้แล้วทริกเกอร์การแจ้งเตือน

แม้ว่าการบันทึกรายละเอียดมากมายทุกคำขออาจมีราคาแพง แต่ที่ Amazon เราพบว่านี่เป็นเรื่องสำคัญอย่างยิ่ง เพราะท้ายที่สุดเราต้องตรวจสอบปัญหาชั่วคราวด้านความพร้อมใช้งาน เวลาแฝงพุ่งสูง และปัญหาที่ลูกค้ารายงาน หากไม่มีบันทึกอย่างละเอียด เราไม่สามารถให้คำตอบกับลูกค้าได้ และจะไม่สามารถปรับปรุงบริการของพวกเขาได้  

การลงรายละเอียดต่างๆ

การตรวจสอบและการแจ้งเตือนนั้นเป็นหัวข้อที่กว้างมาก ในบทความนี้เราจะไม่ครอบคลุมหัวข้อ เช่น การตั้งค่าและการปรับขีดแบ่งการแจ้งเตือน การจัดระเบียบแดชบอร์ดการดำเนินงาน การวัดประสิทธิภาพจากทั้งฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ การใช้งานแอปพลิเคชัน "คานารี" อย่างต่อเนื่อง และการเลือกระบบที่ใช่เพื่อใช้ในการรวบรวมตัววัดและวิเคราะห์บันทึก

บทความนี้มุ่งเน้นไปที่ความจำเป็นในการอินสตรูเมนต์แอปพลิเคชันของเราในการผลิตข้อมูลการวัดดิบที่ถูกต้อง เราจะอธิบายถึงสิ่งต่างๆ ที่ทีม Amazon พยายามรวมถึง (หรือหลีกเลี่ยง) เมื่ออินสตรูเมนต์แอปพลิเคชันของตน

แนวปฏิบัติที่ดีที่สุดของบันทึกคำขอ

ในส่วนนี้ผมจะอธิบายถึงนิสัยที่ดีที่เราได้เรียนรู้ทีละเล็กละน้อยที่ Amazon เกี่ยวกับการบันทึกข้อมูล "ต่อหน่วยของงาน" ที่มีโครงสร้าง บันทึกที่ตรงกับเกณฑ์เหล่านี้มีตัวนับที่แสดงว่าสิ่งต่างๆ เกิดขึ้นบ่อยแค่ไหน ตัวจับเวลาว่าใช้เวลานานแค่ไหน และคุณสมบัติที่มีเมตาดาต้าเกี่ยวกับแต่ละหน่วยของงาน

เราบันทึกอย่างไร

ปล่อยหนึ่งรายการบันทึกคำขอสำหรับทุกหน่วยของงาน โดยทั่วไปหน่วยของงานนั้นเป็นคำขอที่บริการของเราที่ได้รับหรือข้อความที่ดึงมาจากคิว เราเขียนหนึ่งรายการบันทึกบริการสำหรับแต่ละคำขอที่บริการของเราได้รับ เราไม่ได้รวมงานหลายหน่วยของงานเข้าด้วยกัน ด้วยวิธีนี้เมื่อเราแก้ไขปัญหาคำขอที่ล้มเหลว เราก็มีรายการบันทึกเดียวที่จะดู รายการนี้มีพารามิเตอร์อินพุตที่เกี่ยวข้องเกี่ยวกับคำขอเพื่อดูว่าพยายามทำอะไร ข้อมูลว่าใครเป็นผู้เรียกใช้ และข้อมูลเวลาและตัวนับทั้งหมดในที่เดียว
ปล่อยมากกว่าหนึ่งรายการบันทึกคำขอสำหรับคำขอที่ระบุ การใช้งานบริการแบบไม่มีการบล็อกอาจดูสะดวกในการปล่อยรายการบันทึกแยกต่างหากสำหรับแต่ละขั้นตอนในไปป์ไลน์การประมวลผล แต่เราประสบความสำเร็จมากขึ้นในการแก้ไขปัญหาระบบเหล่านี้โดยการพลัมบ์ตัวจัดการกับ "วัตถุตัววัด" ตัวเดียวรอบๆ ระหว่างขั้นตอนต่างๆ ในไปป์ไลน์ จากนั้นให้ซีเรียลไลซ์ตัววัดออกเป็นหน่วยหลังจากขั้นตอนทั้งหมดเสร็จสมบูรณ์ การมีหลายรายการบันทึกต่อหน่วยของงานทำให้การวิเคราะห์บันทึกทำได้ยากขึ้นและเพิ่มค่าใช้จ่ายประจำในการบันทึกที่แพงอยู่แล้วเป็นทวีคูณ หากเรากำลังเขียนบริการใหม่ที่ไม่มีการบล็อก เราพยายามวางแผนวงจรชีวิตการบันทึกตัววัดไว้ล่วงหน้า เพราะการปรับโครงสร้างของโค้ดใหม่และแก้ไขในภายหลังเป็นเรื่องยากมาก
แบ่งงานที่ดำเนินการมานานออกเป็นหลายรายการบันทึก ตรงกันข้ามกับคำแนะนำก่อนหน้านี้ หากเรามีงานที่คล้ายกับเวิร์กโฟลว์ที่ดำเนินการมานานหลายนาทีหรือหลายชั่วโมง เราอาจตัดสินใจที่จะปล่อยรายการบันทึกแยกต่างหากเป็นระยะเพื่อให้เราทราบได้ว่ามีความคืบหน้าหรือไม่ หรือจุดไหนที่ช้าลง
บันทึกรายละเอียดเกี่ยวกับคำขอก่อนทำสิ่งต่างๆ เช่น การตรวจสอบความถูกต้อง เราพบว่าการแก้ไขปัญหาและการตรวจสอบการบันทึกเพื่อบันทึกข้อมูลคำขอให้เพียงพอเป็นสิ่งสำคัญ เพื่อให้เรารู้ว่าบันทึกกำลังพยายามทำอะไรอยู่ นอกจากนี้เรายังพบว่าการบันทึกข้อมูลนี้ให้เร็วที่สุดเป็นเรื่องสำคัญ ก่อนที่การตรวจสอบความถูกต้อง การตรวจสอบสิทธิ์ หรือตรรกะการควบคุมปริมาณ มีโอกาสที่จะปฏิเสธคำขอ หากเรากำลังบันทึกข้อมูลจากคำขอขาเข้า เราจะต้องแน่ใจว่ามีการกรองอินพุต (เข้ารหัส หลีก และตัดให้สั้นลง) ก่อนที่เราจะบันทึก เช่น เราไม่ต้องการรวมสายอักขระยาว 1 MB ไว้ในรายการบันทึกบริการของเราหากผู้เรียกใช้ปล่อยผ่านเข้ามา แต่ถ้ารวมไว้ก็จะมีความเสี่ยงที่ดิสก์ของเราจะเต็มและทำให้เรามีต้นทุนมากกว่าที่คาดไว้ในการจัดเก็บบันทึก อีกตัวอย่างหนึ่งของการกรองคือการกรองอักขระควบคุม ASCII หรือหลีกลำดับที่เกี่ยวข้องกับรูปแบบบันทึก ซึ่งอาจทำให้เกิดความสับสนหากผู้เรียกใช้ส่งผ่านรายการบันทึกบริการของตนเองและอาจแทรกลงในบันทึกของเรา! ดูเพิ่มเติมที่: https://xkcd.com/327/
วางแผนสำหรับวิธีการบันทึกด้วยรายละเอียดที่เพิ่มขึ้น สำหรับการแก้ไขปัญหาบางประเภท บันทึกจะมีรายละเอียดไม่เพียงพอเกี่ยวกับคำขอที่มีปัญหาในการหาสาเหตุว่าทำไมจึงล้มเหลว ข้อมูลนั้นอาจมีอยู่ในบริการ แต่ปริมาณข้อมูลอาจมากเกินกว่าที่จะอ้างเหตุผลได้ว่าทำไมจึงควรบันทึกตลอดเวลา อาจเป็นประโยชน์ที่จะมีตัวกำหนดค่าที่คุณสามารถต่อเลขหมายเพื่อเพิ่มรายละเอียดของบันทึกชั่วคราวในขณะที่คุณตรวจสอบปัญหา คุณอาจปรับการกำหนดค่าไปที่แต่ละโฮสต์ หรือลูกค้าแต่ละราย หรือในอัตราการสุ่มตัวอย่างทั่วทั้งฟลีต สิ่งสำคัญคือต้องจำไว้ว่าให้ปรับการกำหนดค่าเป็นแบบเดิมเมื่อทำเสร็จแล้ว
ตั้งชื่อตัววัดสั้นๆ (แต่ไม่สั้นเกินไป) Amazon ได้ใช้การซีเรียลไลซ์บันทึกบริการแบบเดิมมานานกว่า 15 ปี ในการซีเรียลไลซ์นี้ แต่ละชื่อตัวนับและตัวจับเวลาจะมีการทำซ้ำเป็นข้อความธรรมดาในทุกรายการบันทึกบริการ เพื่อช่วยลดค่าใช้จ่ายประจำในการบันทึก เราจึงใช้ชื่อตัวจับเวลาที่สั้น แต่เห็นภาพชัด Amazon กำลังเริ่มนำการซีเรียลไลซ์รูปแบบใหม่มาใช้ โดยอิงตามโปรโตคอลการซีเรียลไลซ์ไบนารีที่รู้จักกันในชื่อ Amazon Ion ที่สุดแล้ว สิ่งสำคัญคือต้องเลือกรูปแบบที่เครื่องมือการวิเคราะห์บันทึกสามารถเข้าใจได้ว่ามีประสิทธิภาพในการซีเรียลไลซ์ ดีซีเรียลไลซ์ และจัดเก็บได้มากที่สุด
ตรวจสอบให้แน่ใจว่าปริมาณบันทึกมีขนาดใหญ่พอที่จะจัดการการบันทึกที่อัตราความเร็วสูงสุดได้ เราทำการ load test บริการของเราด้วยโหลดคงที่สูงสุด (หรือกระทั่งโอเวอร์โหลด) เป็นเวลาหลายชั่วโมง เราต้องตรวจสอบให้แน่ใจว่าเมื่อบริการของเราจัดการปริมาณการใช้ส่วนเกิน บริการยังคงมีทรัพยากรในการจัดส่งบันทึกนอกระบบในอัตราที่สร้างรายการบันทึกใหม่ มิฉะนั้นดิสก์จะเต็มในที่สุด คุณยังสามารถกำหนดค่าการบันทึกที่จะเกิดขึ้นในพาร์ติชันระบบไฟล์ที่แตกต่างจากพาร์ติชันรากได้อีกด้วย เพื่อให้ระบบไม่หยุดทำงานหากเกิดการบันทึกที่มากเกินไป เราจะหารือเกี่ยวกับมาตรการบรรเทาผลกระทบอื่นๆ สำหรับเรื่องนี้ในภายหลัง เช่นการใช้การสุ่มตัวอย่างแบบไดนามิกที่ได้สัดส่วนกับอัตราความเร็ว แต่ไม่ว่าจะใช้กลยุทธ์ใดก็ตาม ก็จำเป็นต้องทดสอบ
พิจารณาพฤติกรรมของระบบเมื่อดิสก์เต็ม เมื่อดิสก์เซิร์ฟเวอร์เต็ม ก็จะไม่สามารถบันทึกไปยังดิสก์ได้อีกต่อไป เมื่อเกิดเหตุการณ์นี้ขึ้น บริการควรหยุดรับคำขอหรือลบบันทึกแล้วยังคงทำงานต่อโดยไม่ต้องตรวจสอบหรือไม่ การทำงานโดยไม่บันทึกถือว่าเสี่ยง ดังนั้นเราจึงทดสอบระบบเพื่อให้แน่ใจว่าเซิร์ฟเวอร์ที่มีดิสก์เกือบเต็มถูกตรวจพบ
ซิงโครไนซ์นาฬิกา แนวคิดเรื่อง "เวลา" ในระบบแบบกระจายขึ้นชื่อว่าซับซ้อนมาก เราไม่พึ่งพาการซิงโครไนซ์นาฬิกาในอัลกอริทึมแบบกระจาย แต่จำเป็นต่อการทำความเข้าใจบันทึก เราใช้งาน daemons เช่น Chrony หรือ ntpd สำหรับการซิงโครไนซ์นาฬิกา และเราตรวจสอบเซิร์ฟเวอร์เพื่อตรวจสอบการเลื่อนของนาฬิกา เพื่อให้ง่ายขึ้น ให้ดูที่ Amazon Time Sync Service
ปล่อยศูนย์บัญชีสำหรับตัววัดความพร้อมใช้งาน การนับข้อผิดพลาดนั้นมีประโยชน์ แต่เปอร์เซ็นต์ความผิดพลาดก็มีประโยชน์เช่นกัน ในการอินสตรูเมนต์ตัววัด "เปอร์เซ็นต์ความพร้อมใช้งาน" กลยุทธ์ที่เราพบว่ามีประโยชน์คือการปล่อย 1 เมื่อคำขอประสบความสำเร็จและ 0 เมื่อคำขอล้มเหลว จากนั้นสถิติ "เฉลี่ย" ของตัววัดผลที่ได้คืออัตราความพร้อมใช้งาน การปล่อยจุดข้อมูล 0 โดยเจตนาจะมีประโยชน์ในสถานการณ์อื่นเช่นกัน เช่น หากแอปพลิเคชันสร้างการเลือกตั้งผู้นำ การปล่อย 1 เป็นระยะเมื่อกระบวนการเป็นผู้นำ และ 0 เมื่อกระบวนการไม่ใช่ผู้นำอาจมีประโยชน์สำหรับการตรวจสอบสภาพของผู้ติดตาม ดังนั้นหากกระบวนการหยุดปล่อย 0 ก็ง่ายขึ้นที่จะได้รู้ว่ามีบางสิ่งผิดปกติและจะไม่สามารถรับช่วงต่อได้หากมีบางอย่างเกิดขึ้นกับผู้นำ

เราบันทึกอะไร

บันทึกความพร้อมใช้งานและเวลาแฝงของสิ่งที่ขึ้นต่อกันทั้งหมด เราได้พบว่าสิ่งนี้มีประโยชน์เป็นอย่างมากในการตอบคำถามว่า “เหตุใดคำขอจึงล่าช้า” หรือ “เหตุใดคำขอถึงล้มเหลว” หากไม่มีบันทึกนี้ เราจะสามารถเปรียบเทียบกราฟของสิ่งที่ขึ้นต่อกันด้วยกราฟของบริการเท่านั้น และต้องเดาว่าเวลาแฝงที่พุ่งขึ้นสูงของบริการที่ขึ้นต่อกันหนึ่งจุดนั้นจะทำให้คำขอที่เราตรวจสอบอยู่นั้นล้มเหลวหรือไม่ หลายเฟรมเวิร์กของบริการและไคลเอ็นต์จะพลัมบ์ตัววัดโดยอัตโนมัติ แต่เฟรมเวิร์กอื่นๆ (ตัวอย่างเช่น AWS SDK) จะต้องมีการอินสตรูเมนต์ด้วยตนเอง
แยกตัววัดการขึ้นต่อกันต่อหนึ่งการเรียกใช้ ทรัพยากร โค้ดสถานะ และอื่นๆ ถ้าเราตอบโต้กับสิ่งที่ขึ้นต่อกันตัวเดิมเป็นเวลาหลายครั้งในหน่วยของงานเดิม เราจะรวมตัววัดเกี่ยวกับแต่ละการเรียกใช้โดยแยกจากกัน และทำให้ชัดเจนว่าทรัพยากรใดบ้างที่แต่ละคำขอได้ติดต่อด้วย ตัวอย่างเช่น เมื่อเรียกใช้ Amazon DynamoDB บางทีมจะพบว่าการรวมตัววัดช่วงเวลาและเวลาแฝงต่อตาราง ต่อโค้ดข้อผิดพลาด และต่อจำนวนการลองใหม่นั้นมีประโยชน์ ซึ่งจะทำให้การแก้ไขกรณีบริการล่าช้าจากการลองใหม่เนื่องจากการล้มเหลวการตรวจสอบสภาพต่างๆ นั้นเป็นไปอย่างง่ายดายขึ้น ตัววัดเหล่านี้ยังได้เผยให้เห็นว่ากรณีที่การเพิ่มขึ้นของเวลาแฝงที่ไคลเอ็นต์รู้สึกนั้นแท้จริงแล้วเกิดจากการควบคุมการลองใหม่หรือการแบ่งหน้าผ่านชุดผลลัพธ์ และไม่ได้มาจากการสูญเสียแพคเก็ตหรือเวลาแฝงเครือข่าย
บันทึกความลึกของคิวในหน่วยความจำเมื่อประเมินความลึก ถ้าคำขอโต้ตอบกับคิว และเรากำลังดึงอ็อบเจ็กต์ออกจากคิว หรือใส่บางสิ่งลงในคิว เราจะบันทึกความลึกของคิวล่าสุดลงในอ็อบเจ็กต์ตัววัดขณะที่เรายังอยู่ในนั้น สำหรับคิวในหน่วยความจำ ข้อมูลนี้จะได้มาในราคาที่ถูกมาก สำหรับคิวแบบกระจาย เมตาดาต้าอาจพร้อมให้บริการฟรีในการตอบกลับการเรียกใช้ API การบันทึกนี้จะช่วยค้นหางานที่ยังไม่เสร็จและที่มาของเวลาแฝงในอนาคต นอกจากนี้ เมื่อเรานำสิ่งต่างๆ ออกจากคิว เราจะวัดระยะที่สิ่งเหล่านั้นอยู่ในคิว ซึ่งหมายความว่าเราต้องเพิ่มตัววัด “เวลาที่เข้าคิว” ลงในข้อความก่อนที่จะเราจะได้เพิ่มรายการลงในคิวตั้งแต่แรก
เพิ่มตัวนับเพิ่มเติมสำหรับทุกสาเหตุของข้อผิดพลาด พิจารณาเพิ่มโค้ดที่นับสาเหตุของข้อผิดพลาดที่เจาะจงสำหรับทุกคำขอที่ล้มเหลว การบันทึกแอปพลิเคชันจะประกอบด้วยข้อมูลที่นำไปสู่การล้มเหลว และข้อความยกเว้นโดยละเอียด อย่างไรก็ตาม เรายังพบว่าการดูแนวโน้มสาเหตุข้อผิดพลาดในตัววัดในแต่ละช่วงเวลาโดยไม่ต้องขุดหาข้อมูลดังกล่าวในการบันทึกแอปพลิเคชันนั้นเป็นสิ่งที่มีประโยชน์อย่างมาก โดยเริ่มได้อย่างง่ายดายด้วยตัววัดที่แยกกันสำหรับแต่ละระดับการยกเว้นความล้มเหลว
จัดการข้อผิดพลาดตามประเภทของสาเหตุ ถ้าข้อผิดพลาดทั้งหมดไปกระจุกอยู่ที่ตัววัดเดียว ตัววัดจะยุ่งเหยิงและไม่สามารถใช้ประโยชน์ได้ อย่างน้อยที่สุด เราได้พบว่าการแยกข้อผิดพลาดที่เป็น “ความผิดของไคลเอ็นต์” จากข้อผิดพลาดที่เป็น “ความผิดของเซิร์ฟเวอร์” ออกจากกันนั้นเป็นสิ่งสำคัญ ยิ่งไปกว่านั้น การแยกย่อยที่ละเอียดมากขึ้นก็อาจเป็นประโยชน์แก่คุณได้ ตัวอย่างเช่น ใน DynamoDB ไคลเอ็นต์สามารถสร้างคำขอ การเขียนอย่างมีเงื่อนไข ที่จะส่งกลับข้อผิดพลาดถ้ารายการที่ไคลเอ็นต์กำลังปรับเปลี่ยนอยู่นั้นไม่ตรงกับเงื่อนไขเบื้องต้นในคำขอ ข้อผิดพลาดเหล่านี้อาจเป็นข้อผิดพลาดที่ตั้งใจให้เกิดขึ้น และเราก็คาดว่ามันจะเกิดขึ้นอยู่บ่อยครั้ง ในทางตรงกันข้าม ข้อผิดพลาดที่เป็น “คำขอที่ไม่ถูกต้อง” จากไคลเอ็นต์ส่วนใหญ่จะเป็นบั๊กที่เราต้องแก้ไข
บันทึกเมตาดาต้าที่สำคัญเกี่ยวกับหน่วยของงาน ในการบันทึกตัววัดที่มีโครงสร้าง เรายังได้รวมเมตาดาต้าเกี่ยวกับคำขอมากพอที่จะให้เราสามารถระบุได้ในภายหลังว่าคำขอมาจากใคร และคำขอมีจุดประสงค์ที่จะทำอะไร ซึ่งรวมถึงเมตาดาต้าที่ลูกค้าคาดหวังให้เรามีในบันทึกของเราเมื่อพบกับปัญหา ตัวอย่างเช่น DynamoDB จะบันทึกชื่อของตารางคำขอที่โต้ตอบด้วย รวมถึงเมตาดาต้าที่แสดงว่าการดำเนินการอ่านนั้นเป็นการอ่านที่สม่ำเสมอหรือไม่ อย่างไรก็ตาม มันจะไม่บันทึกข้อมูลที่เก็บไว้หรือที่ดึงมาจากฐานข้อมูล
ปกป้องบันทึกด้วยการควบคุมการเข้าถึงและการเข้ารหัส เนื่องจากบันทึกประกอบด้วยระดับความละเอียดอ่อนของข้อมูล เราได้ทำการวัดเพื่อปกป้องและรักษาความปลอดภัยให้กับข้อมูลดังกล่าว ตัววัดเหล่านี้จะประกอบด้วยบันทึกการเข้ารหัส การจำกัดการเข้าถึงผู้ปฏิบัติงานที่กำลังแก้ไขปัญหา และกำลังเบสไลน์การเข้าถึงอยู่เป็นประจำ
หลีกเลี่ยงการใส่ข้อมูลที่ละเอียดอ่อนมากเกินไปในบันทึก บันทึกจำเป็นต้องมีข้อมูลที่ละเอียดอ่อนบ้างเพื่อให้ทำงานได้อย่างมีประโยชน์ ที่ Amazon เราพบว่าการที่บันทึกมีข้อมูลเพียงพอที่จะทราบได้ว่าใครเป็นผู้ส่งคำขอนั้นเป็นสิ่งที่สำคัญ แต่เราจะไม่สนใจข้อมูลที่ละเอียดอ่อนมากเกินไป เช่นพารามิเตอร์คำขอที่ไม่ได้ส่งผลกระทบกับการกำหนดเส้นทางหรือพฤติกรรมของการประมวลคำขอ ตัวอย่างเช่น ถ้าโค้ดแยกข้อความของลูกค้า และการแยกนั้นล้มเหลว การไม่บันทึกส่วนข้อมูลเพื่อปกป้องความเป็นส่วนตัวของลูกค้าจึงเป็นเรื่องสำคัญ และมีความยากพอๆ กับการแก้ไขปัญหาในภายหลัง เราใช้เครื่องมือเพื่อตัดสินใจเกี่ยวกับสิ่งที่สามารถบันทึกได้แบบเลือกใช้แทนที่จะเป็นแบบไม่เลือก เพื่อป้องกันไม่ให้บันทึกพารามิเตอร์ที่ละเอียดอ่อนที่เพิ่มเข้ามาในภายหลัง บริการอย่าง Amazon API Gateway จะอนุญาตการกำหนดค่าว่าข้อมูลใดที่จะนำมารวมใน บันทึกการเข้าถึง ซึ่งทำหน้าที่เป็นกลไกการเลือกใช้ที่ดี
บันทึก ID รายการติดตามและกระจายลงในการเรียกใช้แบบแบ็คเอนด์ คำขอจากลูกค้าที่ได้รับมักจะเกี่ยวข้องกับหลายบริการที่ทำงานร่วมกัน ซึ่งอาจมีจำนวนน้อยเพียงสองถึงสามบริการสำหรับหลายคำขอ AWS ไปจนถึงจำนวนคำขอที่มากขึ้นสำหรับคำขอ amazon.com เพื่อเข้าใจถึงสิ่งที่เกิดขึ้นเมื่อเราแก้ไขระบบแบบกระจาย เราได้กระจาย ID รายการติดตามระหว่างระบบเหล่านี้เพื่อให้เราสามารถจัดการบันทึกจากหลายระบบเพื่อดูว่าเกิดความล้มเหลวขึ้นที่จุดใด ID รายการติดตามนั้นเป็น ID คำขอเมตาที่ถูกประทับลงในหน่วยของงานแบบกระจายโดยบริการ “ประตูหน้า” ที่เป็นจุดเริ่มต้นของหน่วยของงาน AWS X-Ray เป็นหนึ่งในบริการที่จะช่วยโดยมอบบางส่วนของการกระจายนี้ให้ เราพบว่าการส่งต่อรายการติดตามไปยังสิ่งที่ขึ้นต่อกันของเรานั้นเป็นสิ่งที่สำคัญ ในสภาพแวดล้อมแบบหลายเธรด การให้เฟรมเวิร์กดำเนินการกระจายนี้ในส่วนของเรานั้นเป็นเรื่องที่ยากมากและมีแนวโน้มที่จะเกิดข้อผิดพลาด เราจึงมักจะส่งต่อ ID รายการติดตาม และเนื้อหาคำขออื่นๆ (เช่นอ็อบเจ็กต์ตัววัด) ในลายเซ็นเมธอดของเรา เรายังพบด้วยว่าการส่งอ็อบเจ็กต์บริบทของเราเวียนไปรอบๆ นั้นเป็นเรื่องที่สะดวก โดยทำให้ให้เราไม่ต้องปรับโครงสร้างของโค้ดใหม่เมื่อค้นหารูปแบบที่คล้ายคลึงกันเพื่อการส่งเวียนในอนาคต สำหรับทีม AWS มันไม่ใช่แค่เรื่องของการแก้ไขปัญหาในระบบของเรา แต่ยังเป็นเรื่องเกี่ยวกับการที่ลูกค้าจะแก้ไขปัญหาของตนอีกด้วย ลูกค้าอาจพึ่งพารายการติดตาม AWS X-Ray ที่ส่งระหว่างบริการของ AWS เมื่อพวกเขาโต้ตอบซึ่งกันและกันในส่วนของลูกค้า โดยบังคับให้เรากระจาย ID รายการติดตาม AWS X-Ray ของลูกค้าระหว่างบริการเพื่อให้พวกเขาสามารถรับข้อมูลการติดตามที่สมบูรณ์ได้
บันทึกตัววัดเวลาแฝงที่แตกต่างกันโดนขึ้นอยู่กับโค้ดสถานะและขนาด ข้อผิดพลาดมักจะเกิดขึ้นอย่างรวเร็ว เช่น การเข้าถึงที่ถูกปฏิเสธ และการตอบสนองต่อข้อผิดพลาดด้านการควบคุมและการยืนยัน ถ้าไคลเอ็นต์เริ่มได้รับการควบคุมในระดับที่สูง อาจทำให้ดูเหมือนว่าเวลาแฝงนั้นมีลักษณะที่ดีได้ เพื่อที่จะหลีกเลี่ยงผลกระทบต่อตัววัด เราได้บันทึกตัวจับเวลาที่แยกต่างหากสำหรับการตอบกลับที่สำเร็จ และมุ่งเน้นไปที่ตัววัดในแดชบอร์ดและการแจ้งเตือนของเราแทนที่จะใช้ตัววัดเวลาทั่วไป ในลักษณะเดียวกันนี้ ถ้ามีการดำเนินการที่อาจทำงานช้าลงโดยขึ้นอยู่กับขนาดอินพุทหรือขนาดของการตอบกลับ เราจะพิจารณาการกระจายตัววัดเวลาแฝงที่ถูกแยกประเภท เช่น SmallRequestLatency และ LargeRequestLatency นอกจากนี้ เรายังตรวจสอบให้แน่ใจว่าคำขอและการตอบกลับของเรานั้นถูกกำหนดขอบเขตไว้อย่างเหมาะสมเพื่อหลีกเลี่ยงโหมดหยุดทำงานและโหมดล้มเหลว แต่แม้แต่ในบริการที่ได้รับการออกแบบมาอย่างรอบคอบที่สุด เทคนิดการบัคเก็ตตัววัดนี้สามารถแยกพฤติกรรมของลูกค้าและนำสิ่งรบกวนอื่นๆ ออกจากแดชบอร์ดได้

แนวปฏิบัติที่ดีที่สุดของบันทึกแอปพลิเคชัน

ในส่วนนี้จะอธิบายถึง ลักษณะนิสัยที่ดีที่ได้เราได้เรียนรู้ที่ Amazon เกี่ยวกับการบันทึกข้อมูลบันทึกการแก้ไขจุดบกพร่องที่ไม่มีโครงสร้าง

รักษาให้บันทึกแอปพลิเคชันปลอดสแปมเสมอ แม้ว่าเราอาจมีข้อความบันทึกระดับ INFO และ DEBUG บนเส้นทางคำขอสำหรับช่วยในการปรับใช้และการแก้ไขจุดบกพร่องในสภาพแวดล้อมการทดสอบ เราพิจารณาปิดใช้งานระดับบันทึกเหล่านี้ในการผลิต แทนที่จะพึ่งพาบันทึกแอปพลิเคชันสำหรับคำขอที่ติดตามข้อมูล เราจะนึกถึงบันทึกบริการในฐานะตำแหน่งสำหรับข้อมูลการติดตามที่เราสามารถสร้างตัววัดขึ้นได้อย่างง่ายดาย และมองเห็นแนวโน้มการรวมในแต่ละช่วงเวลา อย่างไรก็ตาม ที่นี่ก็ไม่มีกฎตายตัว แนวทางปฏิบัติของเราก็คือการตรวจสอบบันทึกของเราอยู่เสมอเพื่อดูว่ามีสิ่งรบกวนมากเกินไปหรือไม่ (หรือว่างมากเกินไป) แล้วปรับระดับบันทึกอยู่เสมอ ตัวอย่างเช่น เมื่อเราเจาะลึกเกี่ยวกับบันทึก เรามักจะพบว่าข้อความบันทึกนั้นมีสิ่งรบกวนมากเกินไป หรือไม่พบตัววัดที่เราต้องการ โชคดีคือ การปรับปรุงเหล่านี้มักจะง่ายดาย เราจึงมักจะจัดเก็บรายการติดตามงานค้างอย่างเร็วเพื่อทำให้บันทึกของเราเรียบร้อยอยู่เสมอ
รวม ID คำขอที่สัมพันธ์กัน เมื่อเราแก้ไขข้อผิดพลาดในบันทึกแอปพลิเคชัน เรามักจะต้องการดูรายละเอียดเกี่ยวกับคำขอหรือผู้เรียกใช้ที่ทริกเกอร์ข้อผิดพลาด ถ้าทั้งสองบันทึกมี ID คำขอเหมือนกัน เราจะสามารถข้ามจากบันทึกหนึ่งไปอีกบันทึกหนึ่งได้อย่างง่ายดาย ไลบรารีการบันทึกแอปพลิเคชันจะเขียน ID คำขอที่สัมพันธ์กันถ้ามีการกำหนดค่าที่เหมาะสม และ ID คำขอนั้นจะตั้งเป็น ThreadLocal ถ้าแอปพลิเคชันมีหลายเธรด ให้พิจารณาการดูแลเป็นพิเศษเพื่อตั้ง ID คำขอที่ถูกต้องเมื่อเธรดเริ่มทำงานบนคำขอใหม่
การจำกัดอัตราสแปมข้อผิดพลาดของบันทึกแอปพลิเคชัน โดยทั่วไปแล้ว บริการจะไม่กระจายไปยังบันทึกแอปพลิเคชันเท่าไรนัก แต่ถ้ามีการเริ่มแสดงข้อผิดพลาดที่มีปริมาณมาก บริการอาจเริ่มเขียนรายการบันทึกที่มีขนาดใหญ่มากให้เต็มด้วยรายการติดตามสแตกในทันที วิธีหนึ่งที่เราค้นพบเพื่อป้องกันเหตุการณ์เช่นนี้คือการจำกัดอัตราว่าตัวบันทึกที่กำหนดจะบันทึกบ่อยเท่าไร
นิยมใช้สตริงรูปแบบมากกว่า String#format หรือการเรียงสตริงต่อกัน การดำเนินการ API บันทึกแอปพลิเคชันที่เก่ากว่าจะรับข้อความสตริงแบบเดี่ยวแทน API สตริงรูปแบบ varargs ของ log4j2 ถ้าโค้ดถูกอินสตรูเมนต์ด้วยข้อความ DEBUG แต่การผลิตถูกกำหนดค่าในระดับ ERROR ก็เป็นไปได้ว่าจะเสียงานที่จัดรูปแบบสตริงข้อความ DEBUG ที่ถูกมองข้ามนั้นไป การดำเนินการ API ที่บันทึกบางรายการจะสนับสนุนการส่งต่ออ็อบเจ็กต์ที่กำหนดเองได้ซึ่งจะมีเมธอด toString() ซึ่งเรียกใช้เฉพาะกรณที่รายการบันทึกจะถูกลบออก
บันทึก ID คำขอจากการเรียกใช้บริการที่ล้มเหลว ถ้ามีการเรียกใช้บริการและส่งคืนข้อผิดพลาด มีแนวโน้มว่าบริการจะส่งคืน ID คำขอ เราพบว่าการรวม ID คำขอลงในบันทึกของเรานั้นมีประโยชน์มาก สำหรับในกรณีที่เราต้องติดตามเจ้าของบริการ เราจะมีวิธีที่ให้พวกเขาพบรายการบันทึกบริการที่สัมพันธ์กันของตนได้อย่างง่ายดาย ข้อผิดพลาดการหมดเวลาทำให้กรณีนี้ซับซ้อนขึ้นเนื่องจากบริการอาจยังไม่ส่งกลับ ID คำขอ หรือไลบรารีไคลเอ็นต์อาจไม่ได้ทำการแยก อย่างไรก็ตาม ถ้าเราได้ ID คำขอกลับคืนมาจากบริการ เราก็จะบันทึกมัน

แนวทางปฏิบัติที่ดีที่สุดของบริการปริมาณการประมวลผลที่สูง

สำหรับบริการส่วนใหญ่จำนวนมากของ Amazon การบันทึกทุกคำขอไม่ได้ทำให้เกิดค่าใช้จ่ายที่เกิดขึ้นอย่างไม่สมเหตุสมผล บริการปริมาณการประมวลผลที่มากขึ้นจะอยู่ในส่วนที่มีแนวโน้มล้มเหลวมากกว่า แต่เรามักจะยังบันทึกทุกคำขอ ตัวอย่างเช่น เป็นเรื่องธรรมดาที่จะสันนิษฐานว่า DynamoDB ที่แสดงคำขออย่างมากที่สุดกว่า 20 ล้านรายการต่อวินาทีของการรับส่งข้อมูลภายใน Amazon อย่างเดียวนั้นจะไม่บันทึกอะไรมากนัก แต่ในความเป็นจริงแล้วมันจะบันทึกทุกคำขอสำหรับการแก้ไขปัญหาและสำหรับเหตุผลด้านการตรวจสอบและการปฏิบัติตามกฎข้อบังคับ ต่อไปนี้คือเคล็ดลับระดับสูงที่เราใช้ที่ Amazon เพื่อทำให้การบันทึกนั้นมีประสิทธิภาพมากขึ้นเมื่อมีปริมาณการประมวลผลมากขึ้นต่อหนึ่งโฮสต์

การสุ่มตัวอย่างบันทึก แทนที่จะเขียนทุกรายการใหม่ ลองพิจารณาเขียนทุกๆ N รายการ แต่ละรายการยังรวมจำนวนรายการที่ข้ามไป เพื่อให้ระบบการรวมตัววัดสามารถประมาณจำนวนบันทึกที่แท้จริงได้ในตัววัดที่บันทึกประมวลผล อัลกอริทึมการสุ่มตัวอย่างอื่นๆ อย่าง การสุ่มตัวอย่างแหล่งกักเก็บ จะมอบตัวอย่างที่เป็นตัวแทนมากขึ้น อัลกอริทึมอื่นๆ จะให้ความสำคัญกับข้อผิดพลาดในการบันทึกหรือคำขอที่ล่าช้ามากกว่าคำขอที่รวดเร็วและประสบความสำเร็จ อย่างไรก็ตาม ด้วยการสุ่มตัวอย่าง ความสามารถนี้จะทำให้ไม่สามารถช่วยเหลือลูกค้าและแก้ไขปัญหาความล้มเหลวเฉพาะได้ ข้อกำหนดการปฏิบัติตามกฎข้อบังคับบางประการจะไม่อนุญาตให้ทำงานพร้อมกัน
ถ่ายข้อมูลการซีเรียลไลซ์การตัดจ่ายบันทึกเพื่อแยกเธรด นี่คือการเปลี่ยนแปลงที่ง่ายและใช้กันทั่วไป
การหมุนเวียนบันทึกบ่อย การหมุนเวียนไฟล์บันทึกทุกชั่วโมงอาจดูสะดวกเนื่องจากคุณจะมีไฟล์ที่ต้องจัดการน้อยลง แต่การหมุนเวียนทุกนาทีจะช่วยปรับปรุงในหลายๆ เรื่อง เช่น เอเจนต์ที่อ่านและบีบอัดไฟล์บันทึกจะอ่านไฟล์จากแคชของเพจแทนดิสก์ และ CPU และ IO จากการบีบอัดและจัดส่งบันทึกจะแบ่งกระจายภายในชั่วโมงแทนที่จะทริกเกอร์ตอนท้ายชั่วโมง
เขียนบันทึกที่บีบอัดไว้ล่วงหน้า หากเอเจนต์การจัดส่งบันทึกบีบอัดบันทึกก่อนที่จะส่งไปยังบริการเก็บถาวร CPU ของระบบและดิสก์จะพุ่งสูงขึ้นเป็นระยะ เป็นไปได้ที่จะตัดจำหน่ายต้นทุนนี้และลดดิสก์ IO ลงครึ่งหนึ่ง โดยการสตรีมไฟล์บันทึกที่บีบอัดออกสู่ดิสก์ แต่นี่ก็มาพร้อมกับความเสี่ยงด้วย เราพบว่าเป็นประโยชน์ในการใช้อัลกอริทึมการบีบอัดที่สามารถจัดการไฟล์ที่ถูกตัดทอนในกรณีที่แอปพลิเคชันหยุดทำงาน
เขียนไปยัง ramdisk/tmpfs อาจจะง่ายกว่าสำหรับบริการที่จะเขียนบันทึกไปยังหน่วยความจำจนกว่าจะถูกส่งออกจากเซิร์ฟเวอร์แทนที่จะเขียนบันทึกไปยังดิสก์ จากประสบการณ์ของเรา นี่จะทำงานได้ดีที่สุดกับการหมุนเวียนบันทึกทุกๆ นาทีเมื่อเทียบกับการหมุนเวียนบันทึกทุกชั่วโมง
ผลรวมในหน่วยความจำ หากจำเป็นต้องจัดการกับธุรกรรมหลายแสนรายการต่อวินาทีในเครื่องเดียว การเขียนรายการบันทึกเดียวต่อคำขออาจแพงเกินไป แต่หากข้ามขั้นนี้ไป คุณจะสูญเสียความสามารถในการสังเกตไปมาก เราจึงพบว่าการไม่ปรับให้เหมาะสมก่อนกำหนดนั้นมีประโยชน์
ตรวจสอบการใช้ทรัพยากร เราใส่ใจว่าเราใกล้ถึงขีดจำกัดการปรับขนาดแค่ไหนแล้ว เราวัดค่า CPU และ IO ต่อเซิร์ฟเวอร์ของเรา และว่าเอเจนต์การบันทึกใช้งานทรัพยากรเหล่านั้นไปเท่าไร เมื่อเราทำการ load test เราใช้เวลานานพอที่จะพิสูจน์ได้ว่าเอเจนต์จัดส่งบันทึกของเราตามอัตราความเร็วของเราได้ทัน

มีเครื่องมือวิเคราะห์ที่เหมาะสมในตัว

ใน Amazon เราดำเนินการบริการที่เราเขียน ดังนั้นเราทุกคนจำเป็นต้องเป็นผู้เชี่ยวชาญในการแก้ไขปัญหา ซึ่งรวมถึงความสามารถในการวิเคราะห์บันทึกได้อย่างง่ายดาย เรามีเครื่องมือมากมายไว้ใช้เมื่อต้องการ นับตั้งแต่การวิเคราะห์บันทึกภายในเครื่องเพื่อดูจำนวนบันทึกที่ค่อนข้างน้อย จนถึงการวิเคราะห์บันทึกแบบกระจายเพื่อตรวจสอบอย่างใกล้ชิดและรวบรวมผลลัพธ์จากบันทึกปริมาณมหาศาล

เราพบว่าการลงทุนในเครื่องมือและรันบุ๊คของทีมเป็นสิ่งสำคัญสำหรับการวิเคราะห์บันทึก หากบันทึกมีขนาดเล็กในขณะนี้ แต่บริการคาดว่าจะโตขึ้นเมื่อเวลาผ่านไป เราจะใส่ใจเมื่อเครื่องมือปัจจุบันของเราหยุดการปรับขนาด เพื่อให้เราสามารถลงทุนในการใช้โซลูชันการวิเคราะห์บันทึกแบบกระจาย

การวิเคราะห์บันทึกภายในเครื่อง

กระบวนการวิเคราะห์บันทึกอาจต้องมีประสบการณ์ในยูทิลิตีบรรทัดคำสั่งต่างๆ ของ Linux เช่น "ค้นหาที่อยู่ IP ของตัวสื่อสารชั้นนำในบันทึก" โดยทั่วไปนั้นเป็นเพียง:

cat log | grep -P "^RemoteIp=" | cut -d= -f2 | sort | uniq -c | sort -nr | head -n20

แต่มีเครื่องมืออื่นๆ ที่เป็นประโยชน์สำหรับการตอบคำถามที่ซับซ้อนกว่านี้ด้วยบันทึกของเรา รวมถึง:

• jq: https://stedolan.github.io/jq/
• RecordStream: https://github.com/benbernard/RecordStream

การวิเคราะห์บันทึกแบบกระจาย

บริการวิเคราะห์บิ๊กดาต้าสามารถนำมาใช้เพื่อทำการวิเคราะห์บันทึกแบบกระจายได้ (เช่น Amazon EMR, Amazon Athena, Amazon Aurora และ Amazon Redshift) แต่บริการบางอย่างมาพร้อมกับระบบบันทึก เช่น Amazon CloudWatch Logs.

CloudWatch Logs Insights
• AWS X-Ray: https://thinkwithwp.com/xray/
• Amazon Athena: https://thinkwithwp.com/athena/

สรุป

ในฐานะเจ้าของบริการและนักพัฒนาซอฟต์แวร์ ผมใช้เวลามหาศาลในการดูผลลัพธ์ของการอินสตรูเมนต์ – กราฟบนแดชบอร์ด ไฟล์บันทึกแต่ละไฟล์ – และการใช้เครื่องมือวิเคราะห์บันทึกแบบกระจาย เช่น CloudWatch Logs Insights นี่คือสิ่งต่างๆ ที่ผมชอบทำ เมื่อผมต้องการพักหลังจากจบงานที่ท้าทาย ผมจะชาร์จแบตโดยให้รางวัลตัวเองเป็นการลงลึกในบันทึก ผมจะเริ่มต้นด้วยคำถาม เช่น "ทำไมตัววัดนี้ถึงพุ่งสูงขึ้นที่จุดนี้" หรือ " เวลาแฝงของการดำเนินงานนี้จะลดลงได้หรือไม่" เมื่อคำถามของผมไม่ทำให้เกิดคำตอบ ผมมักจะนึกถึงการวัดบางแบบที่จะเป็นประโยชน์ในโค้ด ผมจึงเพิ่มการอินสตรูเมนต์ ทดสอบ และส่งการรีวิวโค้ดไปให้เพื่อนร่วมทีม

แม้จะมีข้อเท็จจริงที่ว่าตัววัดจำนวนมากมาพร้อมกับบริการที่มีการจัดการที่เราใช้ เราจำเป็นต้องคิดให้มากในการอินสตรูเมนต์บริการของเราเองเพื่อให้เรามีความสามารถในการมองเห็นที่เราต้องการดำเนินงานอย่างมีประสิทธิผล ในช่วงเหตุการณ์การดำเนินงาน เราต้องพิจารณาอย่างรวดเร็วว่าเหตุใดเราจึงมีปัญหาและเราจะทำอะไรได้เพื่อบรรเทาปัญหานั้น การมีตัววัดที่ถูกต้องบนแดชบอร์ดของเราเป็นสิ่งสำคัญ เพื่อให้เราวินิจฉัยได้อย่างรวดเร็ว นอกจากนี้ เนื่องจากเราเปลี่ยนบริการของเรา เพิ่มคุณสมบัติใหม่ และเปลี่ยนวิธีการโต้ตอบกับส่วนที่ขึ้นต่อกัน เราจะอัปเดตและเพิ่มการอินสตรูเมนต์ที่เหมาะสมอย่างต่อเนื่องตลอดไป

• “Look at your data,” โดยอดีตชาว Amazon John Rauser: https://www.youtube.com/watch?v=coNDCIMH8bk (รวมถึงช่วงที่ 13:22 ซึ่งเขาพิมพ์บันทึกออกมาเลยเพื่อให้พิจารณาได้ดีขึ้น)
• “Investigating anomalies” โดยอดีตชาว Amazon John Rauser: https://www.youtube.com/watch?v=-3dw09N5_Aw
• “How humans see data” โดยอดีตชาว Amazon John Rauser: https://www.youtube.com/watch?v=fSgEeI2Xpdc
https://www.akamai.com/uk/en/about/news/press/2017-press/akamai-releases-spring-2017-state-of-online-retail-performance-report.jsp


เกี่ยวกับผู้เขียน

David Yanacek เป็นวิศวกรหลักอาวุโสที่ทำงานกับ AWS Lambda David เป็นนักพัฒนาซอฟต์แวร์ที่ Amazon ตั้งแต่ปี 2006 โดยก่อนหน้านี้ทำงานเกี่ยวกับ Amazon DynamoDB และ AWS IoT รวมถึงเฟรมเวิร์กบริการเว็บภายในและระบบปฏิบัติการฟลีตอัตโนมัติ กิจกรรมการทำงานอย่างหนึ่งที่ David โปรดปรานคือการวิเคราะห์บันทึกและตรวจสอบตัวชี้วัดการดำเนินงานอย่างใกล้ชิดเพื่อหาวิธีที่จะทำให้ระบบทำงานได้ราบรื่นยิ่งๆ ขึ้นไป

การใช้การกำจัดโหลดเพื่อหลีกเลี่ยงโอเวอร์โหลด การหลีกเลี่ยงงานค้างในคิวที่ยากเกินจะจัดการได้