การดู : 0

12/04/2026 18:16น.

Golang The Series EP 129: High Availability & Failover – ออกแบบระบบให้ "อึด ถึก ทน"

Golang The Series EP 129: High Availability & Failover – ออกแบบระบบให้ "อึด ถึก ทน"

#High Availability

#Go

#Golang

#Failover

ยินดีต้อนรับชาว Gopher ทุกท่านครับ! เคยสงสัยไหมว่าทำไมแอปพลิเคชันระดับโลกอย่าง Facebook หรือ Netflix ถึงแทบไม่เคยล่มเลย? หรือต่อให้เกิดเหตุการณ์ระบบขัดข้อง เขาก็ยังประคองตัวให้ใช้งานได้ต่อราวกับไม่มีอะไรเกิดขึ้น?

 

คำตอบไม่ใช่ "การเขียนโค้ดให้ไม่มีบั๊ก" (เพราะในโลกวิศวกรรมซอฟต์แวร์ No Bug is a Myth) แต่คำตอบที่แท้จริงคือการออกแบบสถาปัตยกรรมให้มี High Availability (HA) หรือความพร้อมใช้งานสูงนั่นเองครับ

 

1. High Availability (HA) และมนต์ขลังของเลข "9"

 

ในวงการ SRE (Site Reliability Engineering) เราวัดความอึดของระบบด้วย "The Nines" หรือจำนวนเลข 9 ของเปอร์เซ็นต์ Uptime ยิ่งเลข 9 เยอะ ระบบยิ่งต้องเทพ:

 

Percent Uptime

Downtime ต่อปี

ระดับความคาดหวัง

99.9% (Three Nines)

~8.77 ชั่วโมง

มาตรฐานทั่วไป (SLA พื้นฐาน)

99.99% (Four Nines)

~52.56 นาที

ระบบ Enterprise / การเงิน

99.999% (Five Nines)

~5.26 นาที

ระบบวิกฤต (Critical Infrastructure)

 

การจะไปให้ถึง 99.99% ได้ เราต้องกำจัด SPOF (Single Point of Failure) หรือ "จุดตายเดี่ยว" ออกไปให้หมด กฎเหล็กคือ: ทุกองค์ประกอบของระบบต้องมีอย่างน้อยสอง (Redundancy) และต้องอยู่คนละที่กันเสมอ

 

2. กลยุทธ์ Redundancy: การมี "ตัวสำรอง" ที่ชาญฉลาด

 

เราแบ่งกลยุทธ์การสำรองระบบออกเป็น 2 รูปแบบหลัก:

  • Active-Active: รัน Server หลายตัวพร้อมกัน และใช้ Load Balancer กระจาย Request ไปทุกตัว ข้อดีคือช่วยขยายขีดความสามารถ (Scaling) และถ้าตัวหนึ่งพัง ตัวที่เหลือก็รับงานต่อได้ทันที
    • Pro Tip: การเลือก Algorithm ของ Load Balancer เช่น Round Robin (วนไปทีละเครื่อง) หรือ Least Connections (ส่งไปเครื่องที่งานน้อยที่สุด) มีผลอย่างมากต่อเสถียรภาพของระบบภายใต้ Load หนักๆ
  • Active-Passive (Failover): มีเครื่องหลัก (Primary) ทำงาน และมีเครื่องสำรอง (Standby) รอนิ่งๆ เมื่อเครื่องหลักตาย ระบบตรวจสอบจะดีดตัวสำรองขึ้นมาทำงานแทน มักใช้ในระบบที่ยากต่อการรันพร้อมกัน เช่น ระบบจัดการไฟล์หรือ Database บางประเภท

 

3. Failover Design ในระดับ Database: เมื่อข้อมูลคือหัวใจ

 

Server พังเราปั้นใหม่ได้ในวินาทีด้วย Container แต่ถ้า Database พัง ข้อมูลสูญหายคือหายนะ เราจึงต้องมี:

  1. Replication: การทำสำเนาข้อมูลจาก Leader ไปยัง Follower แบบ Real-time
  2. Read Replicas: แยกเครื่องสำหรับ "อ่าน" ออกมาเพื่อลดภาระเครื่องหลัก และหาก Leader พัง เราสามารถ Promote หนึ่งใน Follower ขึ้นมาเป็น Leader ตัวใหม่ได้ (Leader Election)
  3. Quorum Awareness: ในระบบ Distributed ขั้นสูง เรามักใช้จำนวนเครื่องเลขคี่ (เช่น 3, 5 ตัว) เพื่อให้ระบบทำการ "โหวต" เลือกเครื่องหลักใหม่ได้โดยไม่เกิดปัญหา Split-brain (อาการที่ต่างคนต่างคิดว่าตัวเองเป็นหัวหน้าจนข้อมูลขัดแย้งกัน)

 

4. Implementation ใน Go: หัวใจของ Cloud-Native App

 

ในฐานะ Go Developer เราต้องเตรียมแอปให้พร้อมสำหรับ HA ผ่าน 2 กลไกสำคัญที่ห้ามมองข้าม:

 

A. Advanced Health Checks (/livez/readyz)

 

ในมาตรฐานการจัดการ Container (เช่น Kubernetes) เราแยก Health Check เป็น 2 ประเภท:

  • Liveness (/livez): เช็คว่าแอปยัง "มีชีวิต" ไหม? ถ้าพัง (เช่น Deadlock) ให้ Restart เครื่องนี้ซะ
  • Readiness (/readyz): เช็คว่าแอป "พร้อมรับงาน" ไหม? เช่น ต่อ Database ติดหรือยัง? ถ้าไม่พร้อม ให้ Load Balancer หยุดส่ง Request มาที่นี่ชั่วคราว

 

Go
func main() {
    http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) {
        // ตรวจสอบ Dependency เช่น DB, Redis
        if err := checkDependencies(); err != nil {
            w.WriteHeader(http.StatusServiceUnavailable) // 503
            return
        }
        w.WriteHeader(http.StatusOK) // 200
        w.Write([]byte("Ready"))
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
}

 

B. Robust Graceful Shutdown

 

ห้ามปล่อยให้ Request ของ User "ค้าง" หรือ "พัง" เมื่อเราสั่งปิด Server เพื่ออัปเดตเวอร์ชัน เราต้องรอให้งานที่ค้างอยู่เสร็จสิ้นก่อนปิดตัวลง

 

Go
package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	server := &http.Server{Addr: ":8080"}

	// ดักจับ Signal จาก OS (เช่น SIGTERM จาก Kubernetes)
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

	go func() {
		if err := server.ListenAndServe(); err != http.ErrServerClosed {
			log.Fatalf("Listen error: %v", err)
		}
	}()

	<-stop // รอรับสัญญาณปิดเครื่อง
	log.Println("Shutting down server...")

	// ให้เวลาเคลียร์งานที่ค้างอยู่ 30 วินาที (Timeout)
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	if err := server.Shutdown(ctx); err != nil {
		log.Fatalf("Server forced to shutdown: %v", err)
	}

	log.Println("Server gracefully stopped")
}

 

5. Multi-Region: เมื่อโลกทั้งใบคือ Data Center

 

ระบบ HA ขั้นสุดยอดต้องวางแผนรับมือกับภัยพิบัติ (Disaster Recovery) โดยการกระจาย Server ไว้คนละ Availability Zone (AZ) หรือคนละ Region (เช่น สิงคโปร์ และ ญี่ปุ่น) เพื่อให้มั่นใจว่าต่อให้ทั้งภูมิภาคไฟดับ เว็บของคุณจะยังคงออนไลน์อยู่เสมอจากอีกซีกโลกหนึ่ง

 


 

สรุป

 

การออกแบบ High Availability & Failover ไม่ใช่แค่เรื่องของการจ่ายเงินซื้อ Cloud แพงๆ แต่มันคือ Mindset ของวิศวกรซอฟต์แวร์ที่มองว่า "ทุกอย่างมีวันพัง" หน้าที่ของเราคือสร้างระบบที่ล้มแล้วลุกไว (Resilience) และโปร่งใสต่อผู้ใช้งานมากที่สุดครับ

 

ในตอนหน้า (EP 130): เราจะนำแนวคิดเรื่อง HA มาประยุกต์ใช้กับโจทย์ที่ปราบเซียนที่สุดอย่าง Scalable Real-time WebSocket โดยใช้ Redis Pub/Sub เพื่อให้ระบบแชทขยายเครื่องได้ไม่จำกัด! ห้ามพลาดครับ