เจาะลึกอัลกอริทึมการกระจายทราฟฟิก (Weighted Round-Robin & Consistent Hashing) พร้อมกลไกเฝ้าระวัง Active Probing และ Passive Circuit Breaker เพื่อสร้างระบบ High-Availability ไร้ Downtime
ลองจินตนาการว่าคุณมี Backend Pods จำนวน 4 ตัวรับผิดชอบระบบประมวลผลการชำระเงิน หากคุณใช้เพียงระบบกระจายงานภายนอก หรือชี้ Service เข้าไปที่ DNS โฮสต์เดียว เมื่อ Pod หมายเลข 3 เกิดปัญหา Out of Memory (OOM) หรือฐานข้อมูลเชื่อมต่อไม่ติด:
502 Bad Gateway หรือ 504 Gateway Timeout ทันทีKong Gateway แก้ปัญหานี้ด้วยการสร้าง In-Memory Upstream Ring Balancer โดยนำ Target ทั้งหมดมาร้อยเข้าในวงแหวนสล็อตเสมือน และวางระบบเฝ้าระวัง 2 ประตูคู่ขนาน ได้แก่ Active Probing (ส่งสัญญาณตรวจสุขภาพล่วงหน้า) ร่วมกับ Passive Circuit Breaking (ตัดไฟทันทีเมื่อลูกค้าจริงพบเจอ Error)
เพื่อความเข้าใจเชิงวิศวกรรมที่แม่นยำ ตารางด้านล่างนี้นิยามกลไกและตัวแปรหลักตาม Kong Upstream Balancing Specification:
| Entity / กลไก | นิยามทางการ (Formal Definition) | หน้าที่หลัก (Primary Purpose) | ขอบเขตและความแตกต่าง (Distinction) |
|---|---|---|---|
| Ring Balancer | โครงสร้างวงแหวนเสมือน (Virtual Slots Ring) ในหน่วยความจำ OpenResty (ปกติ 10,000 slots) | คำนวณและกระจาย Request ไปยัง Target ตามสัดส่วนน้ำหนัก (Weight) | ทำงานในหน่วยความจำระดับ Process เร็วกว่า DNS Round-Robin ภายนอก และเปลี่ยนสถานะ Target ได้ทันทีโดยไม่ต้อง Restart |
| Weighted Round-Robin | อัลกอริทึมกระจายโหลดแบบวนสลับลำดับถ่วงน้ำหนักตามค่า weight ของ Target |
จัดสรรให้เซิร์ฟเวอร์ที่มีสเปกเครื่องสูงกว่ารับสัดส่วนคำขอมากกว่า | ไม่สนใจข้อมูล Session หรือ Context ของผู้ใช้ กระจายสลับรอบเป็นหลัก |
| Consistent Hashing | อัลกอริทึมจัดสรร Request ลงบนวงแหวนแฮชโดยคำนวณจาก Key ที่กำหนด (IP, Header, Cookie) | รักษา Sticky Session และดัน Cache Hit Ratio ของ Backend ให้สูงสุด | Request ที่มีคีย์เหมือนกัน (เช่น User คนเดิม) จะตกไปที่ Target ตัวเดิมเสมอ เว้นแต่ Target นั้นจะล้มเหลวจึงจะ Failover ไปตัวถัดไป |
| Active Health Check | กลไก Background Worker ที่ Kong ส่งคำขอ HTTP Probe ไปยัง http_path ของแต่ละ Target ตามรอบเวลา |
ตรวจสอบว่าเครื่องเป้าหมายยังมีชีวิตอยู่หรือไม่ และดึง Target ที่ฟื้นคืนชีพกลับเข้ามา Healthy | มี Network Overhead จากการยิง Probe เสริม และมีความล่าช้าในการตรวจจับตามค่า interval |
| Passive Health Check | กลไก Circuit Breaker ที่ดักสังเกตผลลัพธ์ของ Live Traffic จริงที่ส่งไปหา Target โดยไม่สร้าง Probe เสริม | ตัด Target ที่พังทลายออกจาก Ring ทันทีในระดับมิลลิวินาทีเมื่อคำขอจริงของลูกค้าเริ่มเจอ HTTP 5xx หรือ Timeout | ไม่สามารถดึงโหนดกลับคืนมาเป็น Healthy ได้เอง เมื่อโหนดถูกตัดแล้วจะไม่มี Live Traffic วิ่งไปอีก ต้องพึ่ง Active Probe หรือ Admin API มาช่วยฟื้นฟู |
| Zero-Downtime Drainage | กระบวนการระบาย Connection ออกจาก Target โดยการปรับค่า weight: 0 |
หยุดรับ Request ใหม่ 100% แต่ยอมให้ In-flight Requests เดิมทำงานจนจบ | ปลอดภัยกว่าการลบ Target ทิ้งทันที เพราะไม่ทำให้คำขอที่อยู่ระหว่างประมวลผลถูกตัดสายกลางคัน |
จุดเริ่มต้นของวิกฤต:
ทีมงานมีระบบ Backend Service รับผิดชอบ Transaction Processing รันอยู่หลัง Kong Gateway โดยคอนฟิก Upstream ไว้แบบพื้นฐานที่สุด คือมีเฉพาะ algorithm: round-robin และไม่ได้เปิดใช้งาน healthchecks เลย เนื่องจากทีมงานคิดว่า Kubernetes Pod Readiness Probe ภายนอกทำงานได้ดีพออยู่แล้ว
วินาทีที่ระบบพังทลาย:
ในคืนวันศุกร์ ทีม DevOps สั่ง Deploy อัปเดตโค้ดเวอร์ชันใหม่แบบ Rolling Update เมื่อ Pods ทยอยรับสัญญาณ SIGTERM และปิด Connection:
HTTP 502 Bad Gateway ทันที
การกู้ภัยด้วย Kong Passive & Active Resilience:
ทีมตัดสินใจเปิดใช้งาน **Passive Health Checks (Circuit Breaker)** ทันที โดยตั้งค่า http_failures: 2 และ tcp_failures: 1 พร้อมเปิด **Active Health Checks** คู่ขนานกัน
ผลลัพธ์คือ เมื่อ Pod ใดเริ่มปิดตัวหรือตอบ Connection Error แม้เพียงครั้งเดียว วงแหวนของ Kong จะ **Circuit Break** ตัด Pod นั้นออกจาก Ring ทันทีในเสี้ยววินาที และย้ายคำขอไปยัง Healthy Target ตัวอื่นอย่างไร้รอยต่อ ปรากฏการณ์ 502 Bad Gateway หายไปกลายเป็น 0% อย่างถาวร
เปรียบเทียบการทำงานของ Kong Upstream และ Health Checks กับการจัดการทีมแพทย์ในโรงพยาบาล:
_format_version: "3.0"
upstreams:
- name: payment-pool # [1] ชื่อเสมือนที่ Service จะนำไปใช้เป็น host
algorithm: round-robin # [2] อัลกอริทึม: round-robin หรือ consistent-hashing
slots: 10000 # [3] จำนวนจุดพิกัดเสมือนบนวงแหวนแฮช
healthchecks:
active:
type: http
http_path: /healthz # [4] Path สำหรับ Background Probing
timeout: 1 # [5] เวลารอรับสัญญาณ Probe สูงสุด (วินาที)
interval: 2 # [6] ยิงทดสอบทุกๆ กี่วินาที
healthy:
successes: 2 # ตอบ 200 ติดกัน 2 ครั้ง จึงจะนับว่าฟื้น
http_statuses: [200, 204]
unhealthy:
http_failures: 2 # เจอรหัส Error 2 ครั้งติดกัน ถือว่าพัง
http_statuses: [500, 502, 503]
passive:
type: http # [7] Circuit Breaker ดักจับจากคำขอของลูกค้าจริง
unhealthy:
http_failures: 3 # ลูกค้าจริงเจอ 5xx ครบ 3 ครั้ง ตัดโหนดทิ้งทันที
http_statuses: [500, 503]
การกระจายโหลดตามสัดส่วนความสามารถของเครื่อง (เช่น เครื่องใหม่แรงกว่ารับ 80% เครื่องเก่ารับ 20%):
_format_version: "3.0"
upstreams:
- name: order-upstream-pool
algorithm: round-robin
targets:
- upstream: order-upstream-pool
target: 10.0.1.100:8080
weight: 80 # รับ 80% ของคำขอ
- upstream: order-upstream-pool
target: 10.0.1.101:8080
weight: 20 # รับ 20% ของคำขอ
services:
- name: order-service
host: order-upstream-pool # ชี้ host เชิงตรรกะมาที่ชื่อ Upstream
port: 8080
protocol: http
routes:
- name: order-route
paths:
- /v1/orders
การบังคับให้คำขอของผู้ใช้คนเดิมวิ่งไปลงที่โหนดเดิมเสมอ เพื่อให้ใช้ Local In-Memory Cache ได้เต็มประสิทธิภาพ:
_format_version: "3.0"
upstreams:
- name: user-profile-pool
algorithm: consistent-hashing
hash_on: header
hash_on_header: X-Consumer-ID # Hash จาก Header ของลูกค้า
hash_fallback: ip # หากไม่มี Header ให้ Fallback ใช้ Client IP แทน
targets:
- upstream: user-profile-pool
target: 10.0.2.10:8080
weight: 100
- upstream: user-profile-pool
target: 10.0.2.11:8080
weight: 100
Anti-Pattern ที่พบบ่อย: ใช้เฉพาะ Active Health Check โดยไม่เปิด Passive ทำให้ช่วงเวลาที่เครื่องดับฉับพลัน ลูกค้ายังคงหลุดไปเจอ 502 เป็นเวลาหลายวินาทีกว่าจะครบ interval
Production Solution: รวมพลัง Active Probing + Passive Circuit Breaker เข้าด้วยกัน พร้อมการทำ Zero-downtime Drain ด้วย weight: 0:
_format_version: "3.0"
upstreams:
- name: checkout-critical-pool
algorithm: round-robin
healthchecks:
active:
type: http
http_path: /actuator/health
interval: 2
timeout: 1
healthy:
interval: 2
successes: 2
http_statuses: [200]
unhealthy:
interval: 1
http_failures: 2
tcp_failures: 2
timeouts: 3
http_statuses: [500, 502, 503, 504]
passive:
type: http
unhealthy:
http_failures: 3
tcp_failures: 2
timeouts: 2
http_statuses: [500, 502, 503, 504]
targets:
- upstream: checkout-critical-pool
target: 10.0.3.10:8080
weight: 100
- upstream: checkout-critical-pool
target: 10.0.3.11:8080
weight: 100
# โหนดที่กำลังจะเตรียมซ่อมบำรุง ให้เซ็ต weight: 0 เพื่อทำ Drainage
- upstream: checkout-critical-pool
target: 10.0.3.12:8080
weight: 0 # Draining Mode: ปฏิเสธคำขอใหม่ แต่ปล่อยให้คำขอเดิมทำงานจนจบ
ทดลองปรับตัวแปรด้านล่าง เพื่อคำนวณเปรียบเทียบระยะเวลาที่ระบบใช้ตรวจจับโหนดที่ตาย (Detection Window) และจำนวนคำขอของลูกค้าที่จะพังเสียหาย ระหว่างการใช้ Active Health Check เพียงอย่างเดียว เทียบกับการเปิดใช้งาน Passive Circuit Breaker ร่วมด้วย:
แบบทดสอบจำนวน 20 ข้อ แบ่งเป็น: ข้อ 1-15 เจาะลึก Load Balancing & Health Checks ของ Day 03 และ ข้อ 16-20 คือ Spaced Repetition Review ดึงทบทวนแกนความรู้จาก Day 01 (Core Entities) และ Day 02 (Routing Mechanics) เพื่อตอกย้ำความจำระยะยาว