กลับไปยังบทความ

DevOps

Multi-Layer Load Balancing: กระจายโหลดตั้งแต่ DNS, Anycast ถึง Ingress

เข้าใจ multi-layer load balancing ตั้งแต่ DNS ถึง pod ทั้งการ scale ingress controller, จุดอ่อนของ DNS round-robin, GSLB และ geo routing, Anycast กับ BGP, Cloudflare pool และ externalTrafficPolicy

24 กันยายน 256910 นาทีในการอ่าน

multi layer load balancing dns anycast ingress

Multi-layer load balancing คือการที่ request หนึ่งตัวถูกกระจายโหลดหลายครั้งระหว่างทางไปถึง pod เริ่มจาก DNS หรือ global load balancer เลือก region, network load balancer เลือก node, ingress controller เลือก Service และ Service เลือก pod แต่ละชั้นแก้ปัญหาคนละเรื่อง และคำถามแนว "ตรงนี้เป็นคอขวดไหม" ส่วนใหญ่จะตอบได้เองเมื่อรู้ว่าชั้นไหนทำหน้าที่อะไร

บทความนี้เริ่มจากความกังวลที่เจอบ่อย ว่า NGINX ingress ตัวเดียวที่อยู่หน้าทุก service ต้องเป็นคอขวดแน่ ๆ แล้วไล่ออกไปทีละชั้น ได้แก่การ scale ingress, DNS round-robin, GSLB, Anycast, Cloudflare Load Balancing และ externalTrafficPolicy

Ingress controller ตัวเดียวเป็นคอขวดไหม

เป็นได้ แต่ไม่ใช่แบบที่เห็นตอนแรก

Ingress controller เป็นส่วนประกอบเดียวในเชิง logical ไม่ได้แปลว่ามี pod เดียว มันรันเป็น Deployment ที่ scale ได้หลาย replica หรือเป็น DaemonSet ที่มี pod บนทุก node และ external load balancer ที่อยู่ข้างหน้าก็กระจาย connection ไปยัง pod เหล่านั้นอยู่แล้ว ก่อน request จะถึงกฎ routing จึงผ่านการกระจายโหลดมาแล้วสองชั้น

Ingress ยังรับ traffic น้อยกว่าที่หลายคนคิด มันดูแลเฉพาะ traffic แบบ north-south คือจากนอก cluster เข้ามา ส่วน traffic แบบ east-west เช่น service หนึ่งเรียกอีก service หรือ backend คุยกับ database จะวิ่งผ่าน ClusterIP Service โดยตรง ไม่ผ่าน ingress เลย ในระบบ microservice traffic กลุ่มนี้มักเป็นส่วนใหญ่

ถ้า ingress รับโหลดหนักจริง มีสามทางให้เลือก:

  1. Scale ตัว controller เพิ่ม replica หรือใส่ HPA ตาม CPU pod ของ ingress ถือ connection ที่อยู่ยาว จึงควร scale down ช้า ๆ
  2. รันหลาย controller แยกด้วย IngressClass เช่น controller หนึ่งสำหรับ traffic สาธารณะ อีกตัวสำหรับ traffic ภายใน หรือแยก controller ให้ domain ที่โหลดหนักโดยเฉพาะ แต่ละชุดมี load balancer ของตัวเอง
  3. อย่าให้การเรียกภายในวิ่งผ่าน ingress service ควรเรียกกันผ่าน DNS ภายใน cluster ไม่ใช่ผ่าน hostname สาธารณะ
yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ingress-controller
  namespace: ingress
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ingress-controller
  minReplicas: 3
  maxReplicas: 12
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 65
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 600

รายละเอียดพฤติกรรมของ HPA อยู่ใน Kubernetes autoscaling ข้อสังเกตสำหรับคนที่ใช้ controller ingress-nginx ของ community: โปรเจกต์นี้หยุดการดูแลไปตั้งแต่มีนาคม 2026 ทุกอย่างในบทความนี้ใช้ได้เหมือนกันกับ ingress controller ตัวอื่นและ implementation ของ Gateway API

ทำไม DNS round-robin อย่างเดียวถึงไม่พอ

ไอเดียถัดมาที่มักคิดกันคือรัน ingress หลายชุด แต่ละชุดมี public IP ของตัวเอง แล้วผูก IP ทั้งหมดไว้ใต้ hostname เดียว ระบบใหญ่ใช้ DNS เป็นชั้นแรกจริง แต่ถ้าใช้ DNS อย่างเดียวจะมีจุดอ่อนสามข้อ:

  1. ไม่มี health check ถ้าชุดที่อยู่หลัง IP หนึ่งล่ม DNS ก็ยังแจก IP นั้นต่อไป client ที่ได้ IP นั้นจะเข้าไม่ได้จนกว่าจะมีคนแก้ record และ cache หมดอายุ
  2. Cache ทำให้กระจายไม่สม่ำเสมอ resolver และ client cache คำตอบไว้ตาม TTL และบางตัวไม่เคารพ TTL ที่สั้นมาก ผู้ใช้จำนวนมากที่อยู่หลัง resolver เดียวกันอาจไปลง IP เดียวกันหมด
  3. ไม่รู้โหลดจริง round-robin วน record ไปเรื่อย ๆ และยังส่งคนไปยังจุดที่แน่นอยู่แล้ว

เทคนิคในหัวข้อถัดไปแต่ละตัวแก้จุดอ่อนเหล่านี้ได้อย่างน้อยหนึ่งข้อ

GSLB และ geo-based routing

Global Server Load Balancing (GSLB) คือ DNS ที่ตัดสินใจได้ ผู้ให้บริการจะ health-check endpoint ของคุณอยู่ตลอด แล้วตอบแต่ละ query ตามผลที่ได้:

  • Endpoint ที่ health check ไม่ผ่านจะถูกเอาออกจากคำตอบอัตโนมัติ
  • คำตอบขึ้นกับตำแหน่งของผู้ใช้ได้ ผู้ใช้ในไทยได้ region เอเชีย ผู้ใช้ในยุโรปได้ region ยุโรป traffic จึงไม่ต้องวิ่งข้ามโลกโดยไม่จำเป็น
  • ใช้ weight หรือค่า latency ที่วัดได้ในการย้าย traffic ระหว่าง region ได้

Amazon Route 53, Cloudflare และ NS1 มีความสามารถนี้ ซึ่งแก้จุดอ่อนข้อแรกและข้อสามได้ แต่ข้อสองยังอยู่ เพราะ failover ยังต้องรอ cache หมดอายุ record ที่อาจต้อง failover จึงควรตั้ง TTL สั้น

Anycast และ BGP

Anycast ใช้แนวทางต่างออกไป แทนที่จะแจก IP หลายตัว คุณประกาศ IP เดียวกัน จากหลายที่ผ่าน BGP แล้ว routing ของอินเทอร์เน็ตจะพา client แต่ละคนไปยังจุดที่ใกล้ที่สุดในเชิง network ถ้าจุดหนึ่งล่ม มันจะถอน route ออก และ traffic จะไหลไปจุดที่ใกล้รองลงมา client ยังใช้ IP เดิม จึงไม่ต้องรอ DNS cache

CDN และผู้ให้บริการ DNS รายใหญ่ทำงานแบบนี้ ต้องมี IP space และ BGP peering ของตัวเอง หรือใช้ผู้ให้บริการที่มี anycast network ให้ แนวคิดเดียวกันนี้ยังโผล่ใน data center ด้วย MetalLB ในโหมด BGP จะประกาศ Service IP จาก node ใน cluster ไปยัง router แล้ว router กระจาย traffic ไปหลาย node ด้วย ECMP

ข้อควรระวังคือ ถ้า routing เปลี่ยนระหว่างที่ TCP connection ยาว ๆ ยังเปิดอยู่ packet อาจไปโผล่อีกจุดและ connection จะหลุด Anycast จึงเหมาะกับ connection สั้น หรือวางไว้หน้าชั้น proxy ที่ terminate connection

เทคนิครู้สถานะ healthความเร็ว failoverรู้ตำแหน่งผู้ใช้สิ่งที่ต้องมี
DNS round-robinไม่แก้มือ บวกรอ TTLไม่DNS ทั่วไป
GSLB / smart DNSใช่ขึ้นกับ TTLใช่Managed DNS ที่มี health check
Anycast + BGPใช่ ผ่านการถอน routeตาม routing convergence ไม่ต้องรอ DNSใช่ ตามระยะทาง networkIP space และ BGP หรือผู้ให้บริการ

ตั้งค่า Cloudflare Load Balancing

Cloudflare Load Balancing รวมแนวคิดข้างต้นไว้ในสามส่วน:

  1. Monitor กำหนดวิธีเช็ค health เช่น HTTPS GET /healthz ที่ต้องได้ 200
  2. Pool กลุ่มของ endpoint มักจัดหนึ่ง pool ต่อ region และถูกตรวจด้วย monitor endpoint ที่ไม่ผ่านจะถูกเอาออกจาก pool อัตโนมัติ
  3. Load balancer ผูกกับ hostname ตัดสินว่าแต่ละ request จะไป pool ไหน และ failover ตามลำดับอย่างไร

Monitor (POST /accounts/{account_id}/load_balancers/monitors) ที่ต้อง retry พลาดสองครั้งก่อนจะถือว่า endpoint ไม่ healthy:

json
{
  "type": "https",
  "method": "GET",
  "path": "/healthz",
  "expected_codes": "200",
  "interval": 60,
  "timeout": 5,
  "retries": 2
}

Pool ต่อ region (POST /accounts/{account_id}/load_balancers/pools) โดย endpoint แต่ละตัวคือ public IP ของ ingress หนึ่งชุด:

json
{
  "name": "asia-pool",
  "monitor": "<monitor_id>",
  "origins": [
    { "name": "sg-ingress-1", "address": "203.0.113.10", "enabled": true },
    { "name": "sg-ingress-2", "address": "203.0.113.11", "enabled": true }
  ]
}

Load balancer (POST /zones/{zone_id}/load_balancers) ที่ steer ตาม region และมี fallback pool:

json
{
  "name": "www.example.com",
  "proxied": true,
  "steering_policy": "geo",
  "region_pools": {
    "SEAS": ["<asia_pool_id>"],
    "WEU": ["<europe_pool_id>"]
  },
  "default_pools": ["<asia_pool_id>", "<europe_pool_id>"],
  "fallback_pool": "<asia_pool_id>"
}

เมื่อตั้ง proxied: true edge แบบ anycast ของ Cloudflare จะ terminate connection ของ client แล้วส่ง request ต่อไปยัง pool ที่เลือก failover จึงไม่ขึ้นกับ DNS cache ถ้าใช้โหมด DNS-only Cloudflare จะตอบ DNS ด้วย IP ของ endpoint ที่ healthy ทำงานแบบ GSLB รวมถึงมีข้อจำกัดเรื่อง TTL ด้วย

Steering policy กำหนดวิธีเลือก pool:

Steeringวิธีเลือก pool
Off (failover)ใช้ pool ตามลำดับที่ระบุ ย้ายไปตัวถัดไปเมื่อตัวก่อนหน้าไม่ healthy
Randomสุ่มแบบถ่วงน้ำหนัก เช่น 70% กับ 30%
Geomap region ของผู้ใช้ไปยัง pool
Dynamicเลือก round-trip time ต่ำสุด ซึ่งวัดจาก health check ที่ยิงจาก data center ของ Cloudflare
Proximitypool ที่ใกล้ที่สุดตามพิกัดที่กำหนดไว้
Least outstanding requestspool ที่มี request ค้างน้อยที่สุด

ภายใน pool เดียวกัน ยังเลือก endpoint steering ได้อีก เช่น random, แบบ hash, least outstanding requests หรือ least connections

Load balancing สี่ชั้น

ชั้นตัวอย่างระดับกระจายตามหน้าที่
1. GlobalCloudflare LB, Route 53DNS หรือ L7ตำแหน่งผู้ใช้, health ของ pool, latencyเลือก region หรือ data center
2. External LBNetwork LB ของ cloud, MetalLBL4health ของ node, connectionพา traffic เข้า cluster กระจายไปยัง node ที่รัน ingress
3. IngressNGINX, Traefik, EnvoyL7host, path, headerroute ไปยัง Service ที่ถูกต้อง, terminate TLS
4. Servicekube-proxy หรือ eBPF dataplaneL4endpoint ที่ Readyเลือก pod

ชั้นที่ 4 คือชั้นที่คนมักลืม Service ทุกตัวเลือกเฉพาะ pod ที่ readiness probe ผ่าน pod ที่ readiness probe ล้มจึงถูกถอดออกจากการรับ traffic ที่ชั้นนี้ อ่านกลไกเต็ม ๆ ได้ใน Kubernetes Service แต่ละประเภท

externalTrafficPolicy: Cluster vs Local

ระหว่างชั้นที่ 2 กับชั้นที่ 4 มีค่าหนึ่งของ Service ที่กำหนดว่า traffic จะเดินทางอย่างไรเมื่อถึง node

Cluster (ค่าเริ่มต้น) node ไหนก็รับ traffic ของ Service ได้ ถ้า node นั้นไม่มี pod ที่ตรง kube-proxy จะส่งต่อไปยัง pod บน node อื่น

  • โหลดกระจายสม่ำเสมอ และทุก node เป็นปลายทางได้
  • บาง request ต้องเสีย network hop เพิ่มหนึ่งทอด
  • เสีย source IP ของ client ไป เพราะ traffic ถูก SNAT เป็น address ของ node เพื่อให้คำตอบย้อนกลับทางเดิม pod จึงเห็น IP ของ node

Local node ส่ง traffic ให้เฉพาะ pod ที่รันอยู่บน node เดียวกันเท่านั้น

  • ไม่มี hop เพิ่ม
  • เก็บ source IP ของ client ไว้ได้
  • Node ที่ไม่มี pod ในเครื่องจะไม่มี endpoint สำหรับ Service แบบ LoadBalancer Kubernetes จะจัดสรร healthCheckNodePort ให้ cloud load balancer ใช้ตรวจ แล้วข้าม node เหล่านั้นไป
  • โหลดอาจไม่สม่ำเสมอถ้า pod กระจุกอยู่ไม่กี่ node เพราะ external LB กระจายตาม node ไม่ใช่ตาม pod

สำหรับ ingress controller นิยมใช้ Local เพราะมักต้องการ IP จริงของ client ไว้ใช้กับ log, rate limiting และ allow-list ควรใช้คู่กับ DaemonSet หรือ topology spread constraint เพื่อให้ pod ของ ingress กระจายทั่วทุก node

yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-controller
  namespace: ingress
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app.kubernetes.io/name: ingress-controller
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443

ถ้า Local ไม่เหมาะกับระบบของคุณ load balancer หลายตัวรองรับ PROXY protocol ซึ่งส่ง IP ของ client ไปให้ ingress controller ภายในตัว connection

คำถามที่พบบ่อย

NGINX ingress controller เป็น single point of failure ไหม

ไม่เป็น ถ้ารันหลาย replica กระจายหลาย node หลัง load balancer จะเป็นก็ต่อเมื่อรัน pod เดียว หรือทุก replica ไปกองอยู่บน node เดียวกัน

GSLB ต่างจาก load balancer ทั่วไปอย่างไร

GSLB ตัดสินว่าผู้ใช้จะไปลง region หรือ data center ไหน ส่วนใหญ่ทำผ่านคำตอบ DNS ส่วน load balancer ทั่วไปกระจาย connection ไปยัง server ภายใน location เดียว

Anycast ใช้แทน DNS load balancing ได้ไหม

ใช้แทนได้ในเรื่อง failover และการพาไปจุดที่ใกล้ที่สุด เพราะ client ใช้ IP เดียวตลอด แต่หลายระบบยังใช้ DNS ครอบอีกชั้นเพื่อถ่วงน้ำหนักหรือย้าย traffic ด้วยมือ

ควรใช้ externalTrafficPolicy แบบ Local เมื่อไหร่

เมื่อ pod ต้องการ IP จริงของ client หรืออยากตัด hop ที่เพิ่มมา ต้องแน่ใจว่า load balancer health-check node ได้ และกระจาย pod ให้ทั่ว

Checklist สำหรับ multi-layer load balancing

  • ชั้น global health-check ทุก region และมี fallback pool
  • Record ที่อาจต้อง failover ตั้ง TTL สั้น
  • Ingress controller รันหลาย replica กระจายหลาย node และมี HPA
  • Traffic ที่หนักหรือ traffic ภายในมี IngressClass และ controller ของตัวเอง
  • การเรียกระหว่าง service ใช้ DNS ภายใน cluster ไม่ผ่าน ingress
  • เลือก externalTrafficPolicy อย่างตั้งใจ และ IP ของ client ไปถึง log จริง
  • Readiness probe สะท้อนว่า pod พร้อมรับ traffic จริงหรือไม่

แต่ละชั้นควรทำงานของตัวเองให้ดีเพียงอย่างเดียว ชั้น global เลือก region, network LB พา traffic เข้า cluster, ingress ทำ routing และ Service เลือก pod ที่ healthy ถ้าอยากลงมือฝึกเรื่อง Service, Ingress และ autoscaling คอร์ส DevOps ฟรีของ Vectorkub สอนเรื่องเหล่านี้ทีละขั้น

พร้อมสร้างสิ่งที่ยิ่งใหญ่หรือยัง?

เล่าโปรเจคของคุณให้เราฟัง — เราจะพาคุณไปถึง

เลือกคุกกี้ที่เราใช้ได้ คุกกี้ที่จำเป็นช่วยให้เว็บไซต์ทำงานได้และเปิดใช้งานเสมอ