ข้าม​ไป​ยัง​เนื้อหา

OPERATE — ขับ​เคลื่อน incident จริง & ยั่งยืน​ที่ scale

เจ็ด​บท​ที่​ผ่าน​มา​คุณ สร้าง​ชั้น​สังเกตการณ์ คร่อม platform Order: ตั้ง stack ได้ trace แรก (บท2), ตาม trace ข้าม RabbitMQ (บท3), วัด RED/USE โดย​ไม่​ให้ cardinality ระเบิด (บท4), ต่อ log เข้า​กับ trace (บท5), สำรวจ​ใน HyperDX (บท6), แล้ว​วาง SLO + burn-rate alert (บท7) บท​นี้​คือ​การ ใช้งาน​มัน​จริง — ตี​สาม​ที่ pager ดัง คุณ​คือ on-call และ​ต้อง​ไล่​จาก สัญญาณ​เตือน ไป​ถึง สาเหตุ ด้วย​สาม​สัญญาณ​ที่​สัมพันธ์​กัน แล้ว​ทำให้​ทั้ง​ระบบ​นี้ อยู่​รอด​ที่ scale โดย​ไม่​ให้​บิล​ระเบิด และ​ปิด​คอร์ส​ด้วย​ความ​จริง​ที่​หนัก​ที่สุด: คุณค่า​ของ observability ถูก​ตัดสิน​ไป​แล้ว ก่อน incident จะ​เกิด

📦 code ตัวอย่าง

บท​นี้​ขับ​เคลื่อน incident บน platform Order ตัว​เดิม​ที่ #8 สร้าง — โดย​เฉพาะ หลัง Delivery ถูก​แยก​ออก​เป็น service เดี่ยว (delivery-service) เพื่อ​ให้ fault ข้าม process + broker จริง — repo kaen-food-ordering (กำลัง​จัด​ทำ) snippet sampling เป็น code สอน​ที่​คัด​ลอก​ไป​รัน​ได้ ส่วน​การ​ไล่ metric→trace→logs เล่า​เทียบ​กับ​สิ่ง​ที่​คุณ​จะ​เห็น​ใน HyperDX จริง (เรา​จะ ไม่ ยืนยัน screenshot ที่​ตรวจ​ไม่​ได้) นี่​คือ​บท​ปิด — มัน​ประกอบ​ทั้ง​เจ็ด​บท​เข้า​เป็น runbook เดียว​และ​ปิด honesty spine ข้อ (D)

เพื่อ​ฝึก​ให้​เหมือน​จริง เรา​จงใจ ทำให้​พัง หนึ่ง​จุด: WhenOrderConfirmed handler ฝั่ง delivery-service ต้อง​เรียก geocoding API ภายนอก​เพื่อ​หา​โซน​ส่ง วัน​นี้​ปลายทาง​นั้น ช้า​ลง — request ที่​เคย​ตอบ 40ms กลาย​เป็น​ค้าง​จน timeout ที่ 30 วินาที ผลลัพธ์​เป็น​ลูกโซ่:

  1. handler ของ Delivery ค้าง​รอ geocoding → span ของ​มัน​ยาว 30 วินาที
  2. Wolverine consumer ประมวล​ผล OrderConfirmedIntegrationEvent ได้​ช้า​ลง​มาก → ข้อความ​ค้าง​ใน​คิว RabbitMQ, ความ​ลึก​คิว​พุ่ง (Saturation ฝั่ง USE)
  3. บาง​ข้อความ retry จน​หมด​ครั้ง​แล้ว​ตกลง dead-letter queue (wolverine-dead-letter-queue ไต่​ขึ้น)
  4. เวลา​จาก “ยืนยัน​ออเดอร์” ถึง “จ่าย​งาน​ส่ง” ยืด​ออก — ประสบการณ์​ลูกค้า​เสีย

code business ไม่​ได้​เปลี่ยน​สัก​บรรทัด — แต่ อาการ โผล่​ใน​สัญญาณ​ที่​เรา​วาง​ไว้​ตั้งแต่​บท4 นี่​คือ incident ที่​จับ​ต้อง​ได้ ไม่ใช่​แบบฝึกหัด​สมมติ

คุณ​ไม่​ได้​นั่ง​จ้อง dashboard ตอน​ตี​สาม — alert มา​หา​คุณ จาก​บท7 เรา​มี​สอง​ตัว​ที่​จะ​จุด​ขึ้น​พร้อม​กัน:

  • burn-rate alert บน SLO ของ order-intake — เมื่อ​อาการ​ลาม​ถึง​ฝั่ง​รับ request อัตรา​เผา Error BudgetError Budget100% ลบ SLO — ปริมาณ​ความ​ไม่​น่า​เชื่อถือ​ที่​ยอม​ให้​ได้​ใน​งวด​นั้น (99.9% ≈ 43.2 นาที/30 วัน)Process ทะลุ​เกณฑ์ (จำ table บท7: 14.4 ต่อ​หน้าต่าง 1ชม.+5น. = เผา 2% ของ​งบ 30 วัน → Page) budget 30 วัน​ของ SLO 99.9% คือ ~43.2 นาที และ burn rate สูง​แปล​ว่า​มัน​กำลัง​จะ​หมด​เร็ว​กว่า​หน้าต่าง​มาก (S42)
  • DLQ-backlog alert บน delivery-service — Search/SQL alert นับ​ข้อความ​ใน dead-letter queue ที่​โต​เกิน​เกณฑ์​ใน​หน้า​ต่างสั้นๆ

alert เป็น symptom-based (อาการ​ที่​ผู้​ใช้​เห็น) ไม่ใช่ cause-based — มัน​บอกว่า “ระบบ​กำลัง​เจ็บ” ไม่​ได้​บอกว่า “เพราะ​อะไร” งาน​ของ​คุณ​คือ​เดิน​จาก​อาการ​ไป​หาเหตุ

alert ลด MTTR — ไม่​ได้​กำจัด incident (spine C)

alert ที่​ดี​ทำให้​คุณ รู้​เร็ว และ​ซ่อม เร็ว​ขึ้น — มัน​ลด MTTR (mean time to recovery) แต่​มัน​ไม่​ได้ ป้องกัน ไม่​ให้ geocoding ภายนอก​ล่ม dashboard สวย​แค่​ไหน​ก็​ไม่​หยุด incident ได้ observability คือ​เครื่องมือ ตอบ​สนอง ที่​เฉียบ​ขึ้น ไม่ใช่​เกราะ​กัน​เหตุ — อย่า​ขาย​มัน​เกิน​จริง

นี่​คือ​หัวใจ​ของ observability ที่​รัน​ได้​จริง และ​เป็น​จุด​ที่ honesty spine ข้อ (D) กลาย​เป็น​รูปธรรม สาม​สัญญาณ​ที่​เรา สัมพันธ์​กัน ไว้​ทำให้​คุณ​กระโดด​จาก​ชั้น​หนึ่ง​ไป​อีก​ชั้น​ได้​โดย​ไม่​ต้อง deploy อะไร​ใหม่

Metric → ยืนยัน “อะไร​พัง กว้าง​แค่​ไหน”: เปิด​จาก alert ไป​ที่ metric ฝั่ง Delivery — wolverine-execution-time (Duration ฝั่ง consumer) p95 ของ WhenOrderConfirmed พุ่ง​จาก​หลัก​สิบ​มิลลิ​วินาที​เป็น​หลัก วินาที; rabbitmq.queue.depth (ObservableGauge จาก​บท4) ไต่​ขึ้น​เรื่อยๆ; wolverine-dead-letter-queue เริ่ม​นับ metric บอก ขนาด และ รูปร่าง ของ​ปัญหา — แต่​มัน​เป็น​ค่า​สรุป​รวม ไม่​บอกว่า​ออเดอร์​ใบ​ไหน​เจอ​อะไร (S4) เรา​ต้อง pivot

Trace → หา “ที่ไหน” ใน​เส้นทาง​เดียว: จาก metric ที่ breach คุณ pivot ไปหา trace ที่​ช้า หนึ่ง​ใบ (บท6 สอน​ไว้ — filter root span, เรียง​ตาม Duration มาก​สุด) waterfall เล่า​เรื่อง​ทันที: span ของ ordering-service เร็ว​ปกติ → ข้าม​ผ่าน RabbitMQ (บท3) → span consumer ของ delivery-service ยาว 30 วินาที และ ข้าง​ใน มัน​มี child span HttpClient ไป​ยัง geocoding API ที่​กิน​เวลา​เกือบ​ทั้งหมด (S3) trace ชี้ ตำแหน่ง ได้​แม่นยำ — แต่​ยัง ไม่​บอกว่า​ทำไม HTTP call นั้น​ถึง​ช้า

Logs → ปิด​เกม​ที่ “ทำไม”: คลิก​ที่ span HttpClient นั้น → HyperDX โชว์ log ที่ สัมพันธ์​กับ span เดียวกัน ได้​ทันที เพราะ​บท5 ทำให้​ทุก LogRecord ถูก​ประทับ TraceId/SpanId อัตโนมัติ (S5) log บรรทัด​นั้น​เขียน​ว่า:

WARN Geocoding request timed out after 30000ms for order {OrderId} zone-lookup

{OrderId} เป็น attribute ที่ query ได้ (structured logging บท5) — คุณ​เห็น root cause กับ​ตา: ปลายทาง geocoding ภายนอก degrade, handler ของ Delivery block รอ​มัน, event เลย backup ปิด​เคส

flowchart TD
  AL["🔔 alert เด้ง<br/>burn-rate + DLQ backlog"] --> M["METRIC<br/>wolverine-execution-time p95 พุ่ง<br/>queue.depth + DLQ ไต่ขึ้น"]
  M -->|"pivot: filter trace ช้าสุด"| T["TRACE (waterfall)<br/>span Delivery consumer = 30s<br/>child HttpClient geocoding = 30s"]
  T -->|"คลิก span → log ที่ TraceId เดียวกัน"| L["LOGS (correlated)<br/>'Geocoding timed out 30000ms'<br/>order = OrderId"]
  L --> R["🎯 ROOT CAUSE<br/>geocoding ภายนอก degrade<br/>→ handler block → event backup"]

  M -.- MN["ได้เพราะ AddMeter Wolverine* (บท4)"]
  T -.- TN["ได้เพราะ AddSource Wolverine + Http (บท2/3)"]
  L -.- LN["ได้เพราะ IncludeScopes + TraceId stamp (บท5)"]

  classDef sig fill:#0e7490,stroke:#155e75,color:#f0fdff;
  classDef req fill:#1e293b,stroke:#0f172a,color:#cbd5e1;
  class MN,TN,LN req;
  classDef alert fill:#7c2d12,stroke:#431407,color:#fed7aa;
  classDef root fill:#166534,stroke:#14532d,color:#f0fdf4;
  class M,T,L sig;
  class AL alert;
  class R root;

คำ​บรรยาย​ภาพ: การ​ไล่ metric → trace → logs เป็น​บันได​สาม​ขั้น — metric บอก อะไร/กว้าง​แค่​ไหน, trace บอก ที่ไหน ใน​เส้นทาง​เดียว, logs บอก ทำไม และ​การกระโดด​ข้าม​ขั้น​แต่ละ​ครั้ง​เกิด​ได้ ก็​ต่อ​เมื่อ เรา​วาง instrumentation ไว้​ก่อนหน้า: metric ต้อง​มี AddMeter("Wolverine*") จาก​บท4, trace ต้อง​มี AddSource("Wolverine") + HttpClient auto-instrumentation จาก​บท2/3, และ log กระโดด​ไป trace ได้​เพราะ TraceId/SpanId ถูก​ประทับ​อัตโนมัติ​จาก​บท5 — สาม​สัญญาณ​ที่ สัมพันธ์​กัน คือ​ผล​ตอบแทน​ที่​จับ​ต้อง​ได้

คำ​สารภาพ​เรื่อง blind spot: คุณ debug สิ่ง​ที่​ไม่​ได้ instrument ไม่​ได้ (spine D ปิด)

หัวข้อ​ที่​มีชื่อ​ว่า “คำ​สารภาพ​เรื่อง blind spot: คุณ debug สิ่ง​ที่​ไม่​ได้ instrument ไม่​ได้ (spine D ปิด)”

ตอน​นี้​หยุด​คิด​สัก​ครู่ — ทำไม​คุณ​ถึง​ไล่​จนถึง​เหตุ​ได้? เพราะ​ทุก​จุด​บน​เส้นทาง​นั้น​ปล่อย​สัญญาณ​ไว้: HttpClient span มี​เพราะ AddHttpClientInstrumentation() (พื้น​ฟรี​จาก​บท2), consumer span มี​เพราะ AddSource("Wolverine") (บท3), log ผูก​กับ trace เพราะ IncludeScopes (บท5)

ลอง​จินตนาการ version ที่ geocoding ถูก​เรียก​ผ่าน​เส้นทาง​ที่ ไม่​ได้ instrument — สมมติ​เป็น raw socket หรือ library ที่ OTel ไม่รู้จัก waterfall จะมี ช่องว่าง​ดำ 30 วินาที ที่​ไม่มี​คำ​อธิบาย: คุณ​เห็น​ว่า span consumer ช้า แต่ ไม่มี​ทาง​รู้​ว่า​เพราะ​อะไร — ไม่มี child span, ไม่มี log ที่​ผูก​ไว้ ไม่มี backend ไหน​ใน​โลก​กู้​ข้อมูล​ที่ ไม่​เคย​ถูก​ปล่อย​ออก​มา ได้ (S2)

คุณค่า​ของ observability ถูก​ตัดสิน​ไป​แล้ว ก่อน incident เกิด — ที่​เวลา instrument ไม่ใช่​เวลา debug auto-instrumentation คือ พื้น ไม่ใช่ เพดาน; ช่อง​ทาง​ที่​มี​ความหมาย​เชิง domain ที่​คุณ​เลือก instrument เอง (order.id บน span, ผลลัพธ์​การ​จ่าย​เงิน) คือ​ส่วน​ที่​ทำให้​เคส​นี้ กู้​ได้ incident นี้ กู้​ไม่​ได้ ถ้า​เส้นทาง​นั้น​มืด

นี่​คือ spine (D) ที่​ปิด​วง: เรา​เปิด​คอร์ส​ด้วย​ประโยค​นี้​ใน​บท1 และ​ตอน​นี้​คุณ​เห็น​ราคา​ของ​มัน​กับ​ตา

incident จบ​แล้ว แต่​ทุก request ที่​ระบบ​รับ​ปล่อย trace เต็ม​ใบ — ที่ scale ของ production นั่น​คือ เงิน SamplingSamplingการ​เก็บ​เทรซ​เพียง​บาง​ส่วน​เพื่อ​ลด​ต้นทุน (head vs tail); เป็น 'เครื่องมือ​หยาบ' ที่​ซ่อน​ข้อมูล — wide-event store ลด​แต่​ไม่​ลบ​ต้นทุน​นี้Process คือ​การ​เก็บ trace เพียง บาง​ส่วน เพื่อ​ลด​ต้นทุน storage/ingest และ​มัน​มี​สอง​ทรง​หลัก​ที่​ต้อง​เข้าใจความ​ต่าง:

  • Head sampling — ตัดสิน​ใจ ตอน​เริ่ม trace (เช่น สุ่ม​เก็บ 10% ด้วย ratio ที่​กำหนด​ไว้​ล่วงหน้า) ถูก​และ​ง่าย ทำ​ที่ SDK ได้​เลย แต่​มัน​ตัดสิน​ใจ​ทั้ง​ที่​ยัง​ไม่รู้​ผล — trace ที่​มี error หรือ​ช้า​ผิด​ปกติ (แบบ geocoding timeout ข้าง​บน) มี​โอกาส​ถูก​ทิ้ง​เท่าๆ กับ trace ปกติ คุณ​อาจ ทิ้ง trace ที่​มี​ค่าที่สุด ไป
  • Tail sampling — ตัดสิน​ใจ หลัง trace จบ (เก็บ​ทุก​ใบ​ที่ error หรือ​ช้า​เกิน​เกณฑ์ + สุ่ม​ส่วน​ที่​เหลือ) เก็บ​ของ​มี​ค่า​ไว้​ได้ แต่​ต้อง​มี collector ที่ buffer ทั้ง trace ไว้​ใน​หน่วย​ความ​จำ​จนกว่า​จะ​จบ — แพง​กว่า​ใน​การ​รัน​และ config ยาก​กว่า

Head sampling ทำ​ที่ SDK ด้วย TracerProvider sampler — code สอน (คัด​ลอก​รัน​ได้​ตาม​จริง):

using OpenTelemetry.Trace;
builder.Services.AddOpenTelemetry()
.WithTracing(t => t
// head sampling: เก็บ ~10% ของ trace; ParentBased รักษาการตัดสินใจให้ต่อเนื่องทั้ง trace
.SetSampler(new ParentBasedSampler(new TraceIdRatioBasedSampler(0.10)))
.AddSource(DiagnosticConfig.ActivitySourceName)
.AddSource("Wolverine")
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation());

ParentBasedSampler สำคัญ: มัน​ทำให้​ทุก span ใน1 trace ตัดสิน​ใจ เหมือน​กัน (เก็บ​ทั้ง​ใบ​หรือ​ทิ้ง​ทั้ง​ใบ) — ไม่​งั้น trace ข้าม service จะ​ขาด​เป็น​ท่อนๆ Tail sampling ตรง​ข้าม อยู่​ที่ collector ไม่ใช่ SDK: collector distro รวบ span ของ trace เดียวกัน​ไว้​แล้ว​ค่อย​ตัดสิน​ใจ​ตาม policy (เก็บ​ถ้า​มี status = Error หรือ latency เกิน threshold)

knob ของ tail sampling ใน ClickStack ต่าง​ตาม version — ยืนยัน​กับ collector ของ​คุณ​เอง

รูปแบบ tail sampling อยู่​ที่​ชั้น OTel Collector ซึ่ง ClickStack แจก​เป็น distro เฉพาะ — ชื่อ processor, field policy และ default ที่​ให้​มา ต่าง​กัน​ได้​ตาม version ของ collector อย่า​คัด​ลอก YAML จาก​ที่ไหน​มา​แปะ​โดย​ไม่​ตรวจ ให้​ยึด​หลักการ (tail = ตัดสิน​หลัง trace จบ, ต้อง buffer) แล้ว เปิด​เอกสาร collector version ที่​คุณ​รัน​จริง เพื่อ​ดู knob ที่​ถูกต้อง — เรา​จะ​ไม่​ยืนยัน field ที่​ตรวจ​สด​ไม่​ได้

sampling คือ 'เครื่องมือ​หยาบ' ที่​ซ่อน​ข้อมูล (spine C)

sampling ลด​บิล​ได้​จริง แต่​มัน​ทำงาน​ด้วย​การ ทิ้ง​ข้อมูล — มัน​เป็น เครื่องมือ​หยาบ (blunt/lossy) ที่​แลก​ความ​ครบถ้วน​กับ​ต้นทุน trace ที่​คุณ​สุ่ม​ทิ้ง​ไป คือ trace ที่​คุณ ตอบ​คำถาม​กับ​มัน​ไม่​ได้​อีก (S47) wide-event store แบบ ClickHouse ที่​เก็บ​ทุก​อย่าง​เป็น​แถว columnar เดียว ลด แรง​กดดัน​ให้ sample (เพราะ​เก็บ​ถูก​กว่า) แต่ ไม่​ลบ ต้นทุน​ทิ้ง — ที่ scale ใหญ่​พอ ยังไง​คุณ​ก็​ต้อง​ตัดสิน​ใจ นี่​คือ trade-off ตรงๆ: sample มาก = ประหยัด แต่​ตาบอด​ใน​รายละเอียด; sample น้อย = เห็น​ครบ แต่​จ่าย​มาก อย่า​ให้​ใคร​ขาย​ว่า​มี “ของ​ฟรี”

นอกจาก sampling ยัง​มี retention / TTL: ClickHouse ตั้ง TTL ต่อ table ให้​ข้อมูล​เก่า​หลุด​ออก​อัตโนมัติ (เช่น เก็บ trace 30 วัน, log 14 วัน) — อีก​คัน​โยก​ต้นทุน​ที่​ต้อง​ตัดสิน​ใจ ตอน​ตั้ง​ระบบ ไม่ใช่​ตอน​บิล​มา

incident หนึ่ง​ครั้ง​ไม่​ได้​จบ​ที่​ซ่อม — มัน​ควร​ทำให้​ระบบ ฉลาด​ขึ้น Google SRE วาง​หลัก​ไว้​สอง​ข้อ​ที่​ควร​รู้ (S40):

  • Incident Command — พอ​เหตุ​ใหญ่ อย่า​ให้​ทุก​คน​รุม​แก้​มั่ว กำหนด​บทบาท​ชัด: Incident Commander (ตัดสิน​ใจ, ไม่​ลงมือ​แก้​เอง), Operations lead (คน​ลงมือ​แก้​จริง), Communications lead (สื่อสาร​สถานะ​ออก​ไป) โครง​นี้​ทำให้ 20 คนใน incident ไม่​เหยียบ​กันเอง
  • Blameless postmortem — หลัง​จบ เขียน postmortem ที่​โฟกัส สาเหตุ​เชิง​ระบบ ไม่ใช่ ตัว​บุคคล คำถาม​คือ “อะไร​ใน​ระบบ​ทำให้ error นี้​เป็น​ไป​ได้” ไม่ใช่ “ใคร​ผิด” — เพื่อ​ให้​คน​กล้า​เล่า​ความ​จริง​และ​ระบบ​ได้​เรียนรู้ (SRE Book Ch.14 จัดการ incident, Ch.15 postmortem)

runbook ที่​ดี เริ่ม​จาก alert เสมอ: “เมื่อ DLQ-backlog alert ของ delivery-service เด้ง → เปิด metric X → pivot ไป trace ช้า​สุด → คลิก log ที่​ผูก​กับ span → เช็ก dependency ภายนอก” — เขียน​ไว้​ล่วงหน้า เพื่อ​คน on-call คน​ถัด​ไป​ไม่​ต้อง​เริ่ม​จาก​ศูนย์​ตอน​ตี​สาม

นี่​คือ deliverable ของ​ทั้ง​คอร์ส — checklist ที่​บอกว่า platform Order พร้อม operate จริง​หรือ​ยัง แต่ละ​แถว​โยง​กลับ​ไป​บท​ที่​วาง​มัน​ไว้:

ด้านเกณฑ์​ที่​ต้อง​ผ่านวาง​ไว้​ที่
Identityservice.name บน​ทุก context (ordering-service/kitchen-service/delivery-service/payment-service)บท2
Tracesauto span (AspNetCore/Http/EFCore) เป็น​พื้น + domain span ที่​เขียน​เอง (order.id, ผล​จ่าย​เงิน)บท2·บท3
MetricsRED ต่อ context ฝั่ง​รับ request + USE ฝั่ง consumer/RabbitMQ (queue depth, DLQ)บท4
Logsstructured log ผูก​กับ trace อัตโนมัติ (TraceId/SpanId stamp)บท5
Cardinalityorder_id/customer_id เป็น span attribute เท่านั้น — ไม่ใช่ metric labelบท4
PII 🔒ไม่มี PII ดิบ (email/เบอร์/ที่​อยู่/PAN) ใน span หรือ log attribute — ใช้ id ที่ opaqueบท4·บท8
SLO/AlertSLO 30 วัน + burn-rate/DLQ alert เดินสาย​เข้า channel จริง (webhook ฝั่ง OSS)บท7
Sustainabilityตัดสิน​ใจ sampling (head/tail) + retention/TTL แล้วบท8
Runbookมี runbook ที่ เริ่ม​จาก alert + postmortem แบบ blamelessบท8
ห้าม​ลืม​แถว PII — สะพาน​เชื่อม #18 data-leakage

high-cardinality id (order.id, customer_id แบบ GUID) เป็น​เพื่อน​ของ debuggability — ใส่​ใน span ได้​เต็ม​ที่ แต่ PII ดิบ​ไม่ใช่ id telemetry ทุก​ชิ้น​ไหล​ออก​ไป​ที่ backend และ​กลาย​เป็น​ช่อง​รั่ว​ได้​ทันที (บทเรียน data-leakage ของ #18) แถว​นี้​ใน checklist ไม่ใช่​ทาง​เลือก — มัน​คือ​เส้น​แบ่ง​ความ​ปลอดภัย​ที่​ต้อง​รักษา​ไว้​ทุก deploy

คอร์ส #19 สังเกต platform ที่​คอร์ส​อื่น​สร้าง — มัน​ไม่​ได้​ยืน​ลำพัง ขีด​เส้น​ให้​ชัด​ว่า​เรื่อง​ไหน​อยู่​ที่​คอร์ส​ไหน:

  • platform ที่​เรา observe → #8 🔁 bounded contexts integration — 4 context, WolverineFx outbox, RabbitMQ, การ​แยก Delivery ออก​เป็น service ล้วน​เป็น​งาน​ของ #8 คอร์ส​นี้ ไม่​เคย สอน messaging mechanics ซ้ำ — เรา​แค่​ห่อ​ชั้น​สังเกตการณ์​คร่อม​มัน trace ที่​ข้าม broker ในบท3/บท8 คือ อาการ​มอง​เห็น​ได้ ของ outbox ที่ #8 สร้าง
  • high-cardinality id ↔ PII → #18 🔁 ai-agent security — กฎ “ใส่ id เพื่อ debug แต่​ห้าม PII ดิบ” ในบท4/บท8 คือ​ด้าน​กลับ​ของ​บทเรียน data-leakage ของ #18 เส้น​แบ่ง​เดียวกัน คนละ​มุม: #18 กัน​ข้อมูล​รั่ว​ออก​จาก agent, #19 กัน​ข้อมูล​รั่ว​ออก​ทาง telemetry
  • ทฤษฎี​ของ​ระบบ​กระจาย​ใต้ #8 → คอร์ส​อนาคตทำไม event ถึง backup, ทำไม ต้อง idempotent, การ​รับมือ partial failure/consistency เชิง​ลึก​เป็น​เรื่อง​ของ​ชั้น​ทฤษฎี distributed systems ที่​จะ​มา​เสริม​ใต้ #8 คอร์ส​นี้ สังเกต อาการ​ของ​มัน แต่​การ​ออกแบบ​ให้​ทน​ต่อ​อาการ​นั้น​เป็น​อีก​วิชา​หนึ่ง

capstone นี้​ประกอบ​ทั้ง​เจ็ด​บท​เข้า​เป็น runbook เดียว: fault ถูก​ฉีด​ใน delivery-service ที่​แยก​ออก​ไป → alert เด้ง (burn-rate + DLQ) → คุณ​ไล่ metric บอก​อะไร → trace บอก​ที่ไหน → logs บอก​ทำไม จนถึง geocoding ที่ degrade — สาม​สัญญาณ​ที่ สัมพันธ์​กัน คือ​สิ่ง​ที่​ทำให้การกระโดด​ข้าม​ชั้น​เกิด​ขึ้น​ได้ จาก​นั้น​คุณ​ทำให้​ระบบ​ยั่งยืน​ด้วย sampling (head vs tail — “เครื่องมือ​หยาบ” ที่​ซ่อน​ข้อมูล, wide-event store ลด​แต่​ไม่​ลบ​ต้นทุน) + retention แล้ว​ปิด​ด้วย checklist ที่​เป็น deliverable จริง และ incident-response canon แบบ​พอดี

แต่​คำ​สุดท้าย​คือ spine (D) ที่​เรา​เปิด​คอร์ส​ไว้​และ​ตอน​นี้​ปิด​วง: คุณ debug สิ่ง​ที่​คุณ​ไม่​ได้ instrument ไม่​ได้ เคส geocoding นี้​กู้​ได้​เพราะ​ทุก​จุด​บน​เส้นทาง​ปล่อย​สัญญาณ​ไว้ ก่อน incident จะ​เกิด — auto-instrumentation เป็น​พื้นที่​ให้​คุณ, แต่ span/attribute ที่​มี​ความหมาย​เชิง domain ที่​คุณ​เลือก​เขียน​เอง​คือ​ส่วน​ที่​ทำให้​ระบบ​มืด​กลาย​เป็น​ระบบ​ที่​ตอบ​คำถาม​ได้ observability ไม่ใช่ dashboard ที่​ซื้อ​มา มัน​คือ​การ​ตัดสิน​ใจ​ที่​คุณ​ทำ วัน​นี้ เพื่อ​คุณ​ใน​คืน​ที่ pager ดัง​ตอน​ตี​สาม — และ​คอร์ส​นี้​จะ​ไม่​แกล้ง​ทำ​เป็น​ว่า​มัน​ฟรี​หรือ​กำจัด incident ได้ ตั้งแต่​บท​แรก​จน​บท​สุดท้าย


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้

บท​นี้​อิง​ต้นทาง​ที่​ลง​วัน​ที่​กำกับ อ่าน​ต่อ​ได้​โดยตรง:

  • Google SRE Book — Ch.14 Managing Incidents (2016) — Incident Command System (บทบาท Incident Commander/Operations/Communications) และ Ch.15 postmortem แบบ blameless: โฟกัส​สาเหตุ​เชิง​ระบบ ไม่ใช่​ตัว​บุคคล
  • Google SRE Workbook — Ch.5 Alerting on SLOs (2018) — multi-window multi-burn-rate: 14.4 ต่อ​หน้าต่าง 1ชม.+5น. = เผา 2% ของ​งบ 30 วัน → Page; time_to_exhaust = SLO_window / burn_rate
  • Honeycomb — “The cost crisis in observability tooling” (2024-12-18) — sampling เป็นการ​ควบคุม​ต้นทุน​แบบ​หยาบ​ที่​ลด​ข้อมูล; ต้นทุน​ของ​การ​เก็บ​ซ้ำ​หลาย​ที่
  • OpenTelemetry — Observability Primer (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — app ต้อง​ปล่อย​สัญญาณ​ก่อน คุณ​ถึง​จะ​ตั้ง​คำถาม​กับ​มัน​ได้ (spine D)
  • OpenTelemetry — Traces (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — span/child span/HttpClient span ที่​ประกอบ​เป็น waterfall
  • OpenTelemetry — Metrics (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — metric เป็น​ค่า​สรุป​รวม ไม่ใช่ per-request lifecycle
  • OpenTelemetry — Logs (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — LogRecord ถูก​ประทับ TraceId/SpanId อัตโนมัติ = การกระโดด log→trace
  • ClickStack — Schemas (เข้าถึง 2026-07-22) — โครง​ตาราง/TTL ของ ClickHouse; ยืนยัน knob ของ collector/retention กับ deployment ของ​คุณ​เอง

เช็กความเข้าใจ — บทที่ 8

ข้อ 1 / 3

ในการไล่ metric → trace → logs เพื่อหาเหตุของ geocoding timeout แต่ละสัญญาณตอบคำถามคนละแบบ ข้อใดอธิบายบันไดสามขั้นนี้ถูกต้อง และทำไมการกระโดดข้ามขั้นถึงเกิดได้?