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 จะเกิด
บทนี้ขับเคลื่อน 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)
ฉาก: fault ถูกฉีดในโทโพโลยีที่แยกออกไปแล้ว
หัวข้อที่มีชื่อว่า “ฉาก: fault ถูกฉีดในโทโพโลยีที่แยกออกไปแล้ว”เพื่อฝึกให้เหมือนจริง เราจงใจ ทำให้พัง หนึ่งจุด: WhenOrderConfirmed handler ฝั่ง delivery-service ต้องเรียก geocoding API ภายนอกเพื่อหาโซนส่ง วันนี้ปลายทางนั้น ช้าลง — request ที่เคยตอบ 40ms กลายเป็นค้างจน timeout ที่ 30 วินาที ผลลัพธ์เป็นลูกโซ่:
- handler ของ Delivery ค้างรอ geocoding → span ของมันยาว 30 วินาที
- Wolverine consumer ประมวลผล
OrderConfirmedIntegrationEventได้ช้าลงมาก → ข้อความค้างในคิว RabbitMQ, ความลึกคิวพุ่ง (Saturation ฝั่ง USE) - บางข้อความ retry จนหมดครั้งแล้วตกลง dead-letter queue (
wolverine-dead-letter-queueไต่ขึ้น) - เวลาจาก “ยืนยันออเดอร์” ถึง “จ่ายงานส่ง” ยืดออก — ประสบการณ์ลูกค้าเสีย
code business ไม่ได้เปลี่ยนสักบรรทัด — แต่ อาการ โผล่ในสัญญาณที่เราวางไว้ตั้งแต่บท4 นี่คือ incident ที่จับต้องได้ ไม่ใช่แบบฝึกหัดสมมติ
ขั้นที่ 0 — alert เด้ง: จุดเริ่มของ on-call
หัวข้อที่มีชื่อว่า “ขั้นที่ 0 — alert เด้ง: จุดเริ่มของ on-call”คุณไม่ได้นั่งจ้อง 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 (mean time to recovery) แต่มันไม่ได้ ป้องกัน ไม่ให้ geocoding ภายนอกล่ม dashboard สวยแค่ไหนก็ไม่หยุด incident ได้ observability คือเครื่องมือ ตอบสนอง ที่เฉียบขึ้น ไม่ใช่เกราะกันเหตุ — อย่าขายมันเกินจริง
ขั้นที่ 1→2→3 — ไล่ metric → trace → logs จนถึงเหตุ
หัวข้อที่มีชื่อว่า “ขั้นที่ 1→2→3 — ไล่ metric → trace → logs จนถึงเหตุ”นี่คือหัวใจของ 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 และตอนนี้คุณเห็นราคาของมันกับตา
ทำให้ยั่งยืน: sampling & ต้นทุนที่ scale
หัวข้อที่มีชื่อว่า “ทำให้ยั่งยืน: sampling & ต้นทุนที่ scale”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)
รูปแบบ tail sampling อยู่ที่ชั้น OTel Collector ซึ่ง ClickStack แจกเป็น distro เฉพาะ — ชื่อ processor, field policy และ default ที่ให้มา ต่างกันได้ตาม version ของ collector อย่าคัดลอก YAML จากที่ไหนมาแปะโดยไม่ตรวจ ให้ยึดหลักการ (tail = ตัดสินหลัง trace จบ, ต้อง buffer) แล้ว เปิดเอกสาร collector version ที่คุณรันจริง เพื่อดู knob ที่ถูกต้อง — เราจะไม่ยืนยัน field ที่ตรวจสดไม่ได้
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 วัน) — อีกคันโยกต้นทุนที่ต้องตัดสินใจ ตอนตั้งระบบ ไม่ใช่ตอนบิลมา
Runbook: incident-response canon แบบพอดี
หัวข้อที่มีชื่อว่า “Runbook: incident-response canon แบบพอดี”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 คนถัดไปไม่ต้องเริ่มจากศูนย์ตอนตีสาม
Production-readiness checklist — ผลงานส่งมอบของ capstone
หัวข้อที่มีชื่อว่า “Production-readiness checklist — ผลงานส่งมอบของ capstone”นี่คือ deliverable ของทั้งคอร์ส — checklist ที่บอกว่า platform Order พร้อม operate จริงหรือยัง แต่ละแถวโยงกลับไปบทที่วางมันไว้:
| ด้าน | เกณฑ์ที่ต้องผ่าน | วางไว้ที่ |
|---|---|---|
| Identity | service.name บนทุก context (ordering-service/kitchen-service/delivery-service/payment-service) | บท2 |
| Traces | auto span (AspNetCore/Http/EFCore) เป็นพื้น + domain span ที่เขียนเอง (order.id, ผลจ่ายเงิน) | บท2·บท3 |
| Metrics | RED ต่อ context ฝั่งรับ request + USE ฝั่ง consumer/RabbitMQ (queue depth, DLQ) | บท4 |
| Logs | structured log ผูกกับ trace อัตโนมัติ (TraceId/SpanId stamp) | บท5 |
| Cardinality | order_id/customer_id เป็น span attribute เท่านั้น — ไม่ใช่ metric label | บท4 |
| PII 🔒 | ไม่มี PII ดิบ (email/เบอร์/ที่อยู่/PAN) ใน span หรือ log attribute — ใช้ id ที่ opaque | บท4·บท8 |
| SLO/Alert | SLO 30 วัน + burn-rate/DLQ alert เดินสายเข้า channel จริง (webhook ฝั่ง OSS) | บท7 |
| Sustainability | ตัดสินใจ sampling (head/tail) + retention/TTL แล้ว | บท8 |
| Runbook | มี runbook ที่ เริ่มจาก alert + postmortem แบบ blameless | บท8 |
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 แต่ละสัญญาณตอบคำถามคนละแบบ ข้อใดอธิบายบันไดสามขั้นนี้ถูกต้อง และทำไมการกระโดดข้ามขั้นถึงเกิดได้?