ไทย

สำรวจบทบาทสำคัญของ Health Check ใน Service Discovery สำหรับสถาปัตยกรรมไมโครเซอร์วิสที่ยืดหยุ่นและขยายขนาดได้ เรียนรู้เกี่ยวกับประเภทต่างๆ กลยุทธ์การนำไปใช้ และแนวทางปฏิบัติที่ดีที่สุด

Service Discovery: เจาะลึกกลไกการตรวจสอบสถานะ (Health Check)

ในโลกของไมโครเซอร์วิสและระบบแบบกระจาย (distributed systems) service discovery คือองค์ประกอบสำคัญที่ช่วยให้แอปพลิเคชันสามารถค้นหาและสื่อสารระหว่างกันได้ อย่างไรก็ตาม การรู้แค่ตำแหน่งของเซอร์วิสอย่างเดียวนั้นไม่เพียงพอ เรายังต้องแน่ใจว่าเซอร์วิสนั้นมีสถานะปกติ (healthy) และพร้อมที่จะจัดการกับคำขอ (request) ได้ นี่คือจุดที่ health checks เข้ามามีบทบาท

Service Discovery คืออะไร?

Service discovery คือกระบวนการตรวจจับและค้นหาตำแหน่งของเซอร์วิสต่างๆ โดยอัตโนมัติภายในสภาพแวดล้อมที่มีการเปลี่ยนแปลงอยู่เสมอ ในแอปพลิเคชันแบบ monolithic แบบดั้งเดิม เซอร์วิสมักจะอยู่บนเซิร์ฟเวอร์เดียวกันและตำแหน่งของมันก็เป็นที่ทราบล่วงหน้า ในทางกลับกัน ไมโครเซอร์วิสมักถูกนำไปใช้งานบนเซิร์ฟเวอร์หลายเครื่องและตำแหน่งของมันสามารถเปลี่ยนแปลงได้บ่อยครั้งเนื่องจากการขยายขนาด (scaling) การอัปเดต (deployment) และความล้มเหลว (failure) Service discovery แก้ปัญหานี้โดยการจัดเตรียมทะเบียนกลาง (central registry) ที่เซอร์วิสสามารถลงทะเบียนตัวเองได้ และไคลเอนต์สามารถเข้ามาสอบถามเพื่อค้นหาเซอร์วิสที่พร้อมใช้งานได้

เครื่องมือ service discovery ที่เป็นที่นิยม ได้แก่:

ความสำคัญของ Health Check

ในขณะที่ service discovery เป็นกลไกในการค้นหาตำแหน่งของเซอร์วิส แต่มันไม่ได้รับประกันว่าเซอร์วิสเหล่านั้นจะมีสถานะปกติ เซอร์วิสอาจจะลงทะเบียนอยู่ใน service registry แต่อาจกำลังประสบปัญหา เช่น การใช้งาน CPU สูง, memory leak หรือปัญหาการเชื่อมต่อฐานข้อมูล หากไม่มี health check ไคลเอนต์อาจส่งคำขอไปยังเซอร์วิสที่ผิดปกติโดยไม่ได้ตั้งใจ ซึ่งนำไปสู่ประสิทธิภาพที่ต่ำ ข้อผิดพลาด หรือแม้กระทั่งแอปพลิเคชันล่มได้ Health check เป็นวิธีการตรวจสอบสถานะของเซอร์วิสอย่างต่อเนื่อง และนำเซอร์วิสที่ผิดปกติออกจาก service registry โดยอัตโนมัติ ซึ่งช่วยให้แน่ใจว่าไคลเอนต์จะสื่อสารกับเซอร์วิสที่ปกติและตอบสนองได้ดีเท่านั้น

ลองพิจารณาสถานการณ์ที่แอปพลิเคชันอีคอมเมิร์ซต้องพึ่งพาเซอร์วิสแยกต่างหากในการประมวลผลการชำระเงิน หากเซอร์วิสชำระเงินทำงานหนักเกินไปหรือพบข้อผิดพลาดของฐานข้อมูล เซอร์วิสนั้นอาจยังคงลงทะเบียนอยู่ใน service registry หากไม่มี health check แอปพลิเคชันอีคอมเมิร์ซจะยังคงส่งคำขอชำระเงินไปยังเซอร์วิสที่ล้มเหลว ส่งผลให้การทำธุรกรรมล้มเหลวและสร้างประสบการณ์ที่ไม่ดีให้กับลูกค้า แต่เมื่อมี health check เซอร์วิสชำระเงินที่ล้มเหลวจะถูกนำออกจาก service registry โดยอัตโนมัติ และแอปพลิเคชันอีคอมเมิร์ซสามารถเปลี่ยนเส้นทางคำขอไปยังเซอร์วิสที่ปกติหรือจัดการข้อผิดพลาดได้อย่างเหมาะสม

ประเภทของ Health Check

มี health check หลายประเภทที่สามารถใช้ในการตรวจสอบสถานะของเซอร์วิสได้ ประเภทที่พบบ่อยที่สุด ได้แก่:

การตรวจสอบสถานะแบบ HTTP (HTTP Health Checks)

การตรวจสอบสถานะแบบ HTTP เกี่ยวข้องกับการส่งคำขอ HTTP ไปยัง endpoint ที่ระบุบนเซอร์วิสและตรวจสอบรหัสสถานะการตอบกลับ (response status code) โดยปกติแล้วรหัสสถานะ 200 (OK) หมายถึงเซอร์วิสมีสถานะปกติ ในขณะที่รหัสสถานะอื่นๆ (เช่น 500 Internal Server Error) บ่งชี้ว่ามีปัญหา การตรวจสอบสถานะแบบ HTTP นั้นง่ายต่อการนำไปใช้และสามารถใช้เพื่อตรวจสอบการทำงานพื้นฐานของเซอร์วิสได้ ตัวอย่างเช่น health check อาจตรวจสอบ endpoint `/health` ของเซอร์วิส ในแอปพลิเคชัน Node.js ที่ใช้ Express อาจทำได้ง่ายๆ ดังนี้:

app.get('/health', (req, res) => {
  res.status(200).send('OK');
});

ตัวอย่างการกำหนดค่า:

Consul

{
  "service": {
    "name": "payment-service",
    "port": 8080,
    "check": {
      "http": "http://localhost:8080/health",
      "interval": "10s",
      "timeout": "5s"
    }
  }
}

Kubernetes

apiVersion: v1
kind: Pod
metadata:
  name: payment-service
spec:
  containers:
  - name: payment-service-container
    image: payment-service:latest
    ports:
    - containerPort: 8080
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 3
      periodSeconds: 10

การตรวจสอบสถานะแบบ TCP (TCP Health Checks)

การตรวจสอบสถานะแบบ TCP เกี่ยวข้องกับการพยายามสร้างการเชื่อมต่อ TCP ไปยังพอร์ตที่ระบุบนเซอร์วิส หากการเชื่อมต่อสำเร็จ จะถือว่าเซอร์วิสมีสถานะปกติ การตรวจสอบสถานะแบบ TCP มีประโยชน์สำหรับการตรวจสอบว่าเซอร์วิสกำลังรอรับการเชื่อมต่อที่พอร์ตที่ถูกต้องหรือไม่ ซึ่งง่ายกว่าการตรวจสอบแบบ HTTP เนื่องจากไม่ได้ตรวจสอบในระดับแอปพลิเคชัน (application layer) การตรวจสอบพื้นฐานจะยืนยันแค่ว่าพอร์ตนั้นสามารถเข้าถึงได้

ตัวอย่างการกำหนดค่า:

Consul

{
  "service": {
    "name": "database-service",
    "port": 5432,
    "check": {
      "tcp": "localhost:5432",
      "interval": "10s",
      "timeout": "5s"
    }
  }
}

Kubernetes

apiVersion: v1
kind: Pod
metadata:
  name: database-service
spec:
  containers:
  - name: database-service-container
    image: database-service:latest
    ports:
    - containerPort: 5432
    livenessProbe:
      tcpSocket:
        port: 5432
      initialDelaySeconds: 15
      periodSeconds: 20

การตรวจสอบสถานะด้วยการรันคำสั่ง (Command Execution Health Checks)

การตรวจสอบสถานะด้วยการรันคำสั่งเกี่ยวข้องกับการรันคำสั่งบนโฮสต์ของเซอร์วิสและตรวจสอบ exit code โดยปกติแล้ว exit code 0 หมายถึงเซอร์วิสมีสถานะปกติ ในขณะที่ exit code อื่นๆ บ่งชี้ว่ามีปัญหา การตรวจสอบสถานะด้วยการรันคำสั่งเป็นประเภทที่ยืดหยุ่นที่สุด เนื่องจากสามารถใช้ตรวจสอบได้หลากหลาย เช่น การตรวจสอบพื้นที่ดิสก์, การใช้หน่วยความจำ หรือสถานะของ dependency ภายนอก ตัวอย่างเช่น คุณสามารถรันสคริปต์ที่ตรวจสอบว่าการเชื่อมต่อฐานข้อมูลยังปกติดีอยู่หรือไม่

ตัวอย่างการกำหนดค่า:

Consul

{
  "service": {
    "name": "monitoring-service",
    "port": 80,
    "check": {
      "args": ["/usr/local/bin/check_disk_space.sh"],
      "interval": "30s",
      "timeout": "10s"
    }
  }
}

Kubernetes

apiVersion: v1
kind: Pod
metadata:
  name: monitoring-service
spec:
  containers:
  - name: monitoring-service-container
    image: monitoring-service:latest
    command: ["/usr/local/bin/check_disk_space.sh"]
    livenessProbe:
      exec:
        command: ["/usr/local/bin/check_disk_space.sh"]
      initialDelaySeconds: 60
      periodSeconds: 30

การตรวจสอบสถานะแบบกำหนดเอง (Custom Health Checks)

สำหรับสถานการณ์ที่ซับซ้อนยิ่งขึ้น คุณสามารถสร้าง health check แบบกำหนดเองที่ตรวจสอบตรรกะเฉพาะของแอปพลิเคชันได้ ซึ่งอาจรวมถึงการตรวจสอบสถานะของคิวภายใน, การตรวจสอบความพร้อมใช้งานของทรัพยากรภายนอก หรือการวัดประสิทธิภาพที่ซับซ้อนยิ่งขึ้น การตรวจสอบสถานะแบบกำหนดเองให้การควบคุมกระบวนการตรวจสอบสถานะได้อย่างละเอียดที่สุด

ตัวอย่างเช่น health check แบบกำหนดเองสำหรับ consumer ของ message queue อาจตรวจสอบว่าความลึกของคิว (queue depth) ต่ำกว่าเกณฑ์ที่กำหนดและข้อความกำลังถูกประมวลผลในอัตราที่เหมาะสม หรือเซอร์วิสที่ต้องสื่อสารกับ API ของบุคคลที่สามอาจตรวจสอบเวลาตอบสนองและอัตราข้อผิดพลาดของ API นั้น

การนำ Health Check ไปใช้งาน

การนำ health check ไปใช้งานโดยทั่วไปมีขั้นตอนดังต่อไปนี้:

  1. กำหนดเกณฑ์สถานะปกติ (Health Criteria): กำหนดว่าอะไรคือเซอร์วิสที่มีสถานะปกติ ซึ่งอาจรวมถึงเวลาตอบสนอง, การใช้งาน CPU, การใช้งานหน่วยความจำ, สถานะการเชื่อมต่อฐานข้อมูล และความพร้อมใช้งานของทรัพยากรภายนอก
  2. สร้าง Endpoints หรือสคริปต์สำหรับ Health Check: สร้าง endpoints (เช่น `/health`) หรือสคริปต์ที่ทำการตรวจสอบสถานะและส่งคืนรหัสสถานะหรือ exit code ที่เหมาะสม
  3. กำหนดค่าเครื่องมือ Service Discovery: กำหนดค่าเครื่องมือ service discovery ของคุณ (เช่น Consul, Etcd, Kubernetes) ให้ทำการตรวจสอบสถานะเป็นระยะๆ และอัปเดต service registry ตามนั้น
  4. ตรวจสอบผลลัพธ์ของ Health Check: ติดตามผลลัพธ์ของ health check เพื่อระบุปัญหาที่อาจเกิดขึ้นและดำเนินการแก้ไข

สิ่งสำคัญคือ health check ต้องมีน้ำหนักเบาและไม่ใช้ทรัพยากรมากเกินไป หลีกเลี่ยงการดำเนินการที่ซับซ้อนหรือการเข้าถึงฐานข้อมูลภายนอกโดยตรงจาก health check endpoint แต่ให้เน้นการตรวจสอบฟังก์ชันการทำงานพื้นฐานของเซอร์วิสและใช้เครื่องมือตรวจสอบอื่นๆ สำหรับการวิเคราะห์ที่ลึกซึ้งยิ่งขึ้น

แนวทางปฏิบัติที่ดีที่สุดสำหรับ Health Check

นี่คือแนวทางปฏิบัติที่ดีที่สุดสำหรับการนำ health check ไปใช้งาน:

ตัวอย่างการใช้งานในเทคโนโลยีต่างๆ

เรามาดูตัวอย่างการนำ health check ไปใช้ในเทคโนโลยีต่างๆ กัน:

Java (Spring Boot)

@RestController
public class HealthController {

    @GetMapping("/health")
    public ResponseEntity<String> health() {
        // ทำการตรวจสอบที่นี่ เช่น การเชื่อมต่อฐานข้อมูล
        boolean isHealthy = true; // แทนที่ด้วยการตรวจสอบจริง

        if (isHealthy) {
            return new ResponseEntity<>("OK", HttpStatus.OK);
        } else {
            return new ResponseEntity<>("Error", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

Python (Flask)

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/health')
def health_check():
    # ทำการตรวจสอบที่นี่
    is_healthy = True  # แทนที่ด้วยการตรวจสอบจริง

    if is_healthy:
        return jsonify({'status': 'OK'}), 200
    else:
        return jsonify({'status': 'Error'}), 500

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

Go

package main

import (
    "fmt"
    "net/http"
)

func healthHandler(w http.ResponseWriter, r *http.Request) {
    // ทำการตรวจสอบที่นี่
    isHealthy := true // แทนที่ด้วยการตรวจสอบจริง

    if isHealthy {
        w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "OK")
    } else {
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprint(w, "Error")
    }
}

func main() {
    http.HandleFunc("/health", healthHandler)
    fmt.Println("Server listening on port 8080")
    http.ListenAndServe(":8080", nil)
}

Health Check และ Load Balancing

Health check มักถูกนำไปรวมกับโซลูชัน load balancing เพื่อให้แน่ใจว่าทราฟฟิกจะถูกส่งไปยังเซอร์วิสที่ปกติเท่านั้น Load balancer ใช้ผลลัพธ์จาก health check เพื่อตัดสินว่าเซอร์วิสใดพร้อมที่จะรับทราฟฟิก เมื่อเซอร์วิสไม่ผ่านการตรวจสอบสถานะ load balancer จะนำเซอร์วิสนั้นออกจากกลุ่มเซอร์วิสที่พร้อมใช้งานโดยอัตโนมัติ ซึ่งจะป้องกันไม่ให้ไคลเอนต์ส่งคำขอไปยังเซอร์วิสที่ผิดปกติและช่วยเพิ่มความน่าเชื่อถือโดยรวมของแอปพลิเคชัน

ตัวอย่างของ load balancer ที่ทำงานร่วมกับ health check ได้แก่:

การตรวจสอบและการแจ้งเตือน (Monitoring and Alerting)

นอกเหนือจากการนำเซอร์วิสที่ไม่ปกติออกจาก service registry โดยอัตโนมัติแล้ว health check ยังสามารถใช้เพื่อส่งการแจ้งเตือนได้อีกด้วย เมื่อเซอร์วิสไม่ผ่านการตรวจสอบสถานะ ระบบตรวจสอบสามารถส่งการแจ้งเตือนไปยังทีมปฏิบัติการ (operations team) เพื่อแจ้งให้พวกเขาทราบถึงปัญหาที่อาจเกิดขึ้น ซึ่งช่วยให้พวกเขาสามารถตรวจสอบปัญหาและดำเนินการแก้ไขก่อนที่จะส่งผลกระทบต่อผู้ใช้

เครื่องมือตรวจสอบที่เป็นที่นิยมซึ่งทำงานร่วมกับ health check ได้แก่:

สรุป

Health check เป็นองค์ประกอบสำคัญของ service discovery ในสถาปัตยกรรมไมโครเซอร์วิส โดยเป็นวิธีการตรวจสอบสถานะของเซอร์วิสอย่างต่อเนื่องและนำเซอร์วิสที่ไม่ปกติออกจาก service registry โดยอัตโนมัติ การสร้างกลไก health check ที่แข็งแกร่งจะช่วยให้คุณมั่นใจได้ว่าแอปพลิเคชันของคุณมีความยืดหยุ่น (resilient) ขยายขนาดได้ (scalable) และเชื่อถือได้ (reliable) การเลือกประเภทของ health check ที่เหมาะสม การกำหนดค่าอย่างถูกต้อง และการผสานรวมเข้ากับระบบตรวจสอบและการแจ้งเตือนเป็นกุญแจสำคัญในการสร้างสภาพแวดล้อมไมโครเซอร์วิสที่แข็งแรงและมีเสถียรภาพ

ใช้แนวทางเชิงรุกในการตรวจสอบสถานะ อย่ารอให้ผู้ใช้รายงานปัญหา สร้าง health check ที่ครอบคลุมซึ่งคอยตรวจสอบสถานะของเซอร์วิสของคุณอย่างต่อเนื่องและดำเนินการแก้ไขโดยอัตโนมัติเมื่อเกิดปัญหาขึ้น ซึ่งจะช่วยให้คุณสร้างสถาปัตยกรรมไมโครเซอร์วิสที่ยืดหยุ่นและเชื่อถือได้ ซึ่งสามารถทนต่อความท้าทายของสภาพแวดล้อมที่มีการเปลี่ยนแปลงและกระจายตัวได้ ทบทวนและอัปเดต health check ของคุณเป็นประจำเพื่อปรับให้เข้ากับความต้องการและ dependency ของแอปพลิเคชันที่เปลี่ยนแปลงไป

ท้ายที่สุดแล้ว การลงทุนในกลไก health check ที่แข็งแกร่งคือการลงทุนในเสถียรภาพ ความพร้อมใช้งาน และความสำเร็จโดยรวมของแอปพลิเคชันที่ใช้ไมโครเซอร์วิสของคุณ