การดู : 0

12/04/2026 18:16น.

Golang The Series EP 135: CI/CD สำหรับ WebSocket Deploy เนียนกริบ ไม่ต้องกลัวคนหลุด

Golang The Series EP 135: CI/CD สำหรับ WebSocket Deploy เนียนกริบ ไม่ต้องกลัวคนหลุด

#Golang

#CI/CD

#WebSocket

#Docker

#Deploy

เดินทางมาถึง EP 135 กันแล้วครับ! หลังจากที่เราถล่มระบบจนรู้จุดแตกหักในตอนที่แล้ว (Load Testing) ตอนนี้เราจะมาคุยเรื่องที่ทำให้ชีวิตการเป็น Developer สบายขึ้นและเสี่ยงน้อยลง นั่นคือการทำ Continuous Integration & Continuous Deployment (CI/CD)

 

แต่เดี๋ยวก่อน! การทำ CI/CD ให้ WebSocket Server นั้น "ท้าทาย" กว่า REST API ทั่วไปหลายเท่า เพราะ WebSocket มีความเป็น Stateful (มีการเปิดท่อค้างไว้) หากคุณสั่ง Deploy แบบสุ่มสี่สุมห้า User ทั้งหมดจะถูกตัดการเชื่อมต่อทันที และรุมกลับเข้ามาใหม่จนระบบล่ม (Thundering Herd Problem) วันนี้เราจะมาดูวิธีสร้าง Pipeline ที่เนียนกริบและปลอดภัยที่สุดกันครับ

 

1. CI/CD Pipeline สำหรับ Go: ความเร็วและความปลอดภัย

 

เนื่องจาก Go คอมไพล์ได้เร็วมาก เราควรใช้ประโยชน์จากจุดนี้สร้าง Pipeline ที่เข้มข้นแต่ไม่เสียเวลาครับ

 

Continuous Integration (CI)

  • Linting: ใช้ golangci-lint เพื่อเช็คคุณภาพโค้ดและมาตรฐานการเขียน
  • Unit Testing: รัน go test -v -race เพื่อหา Race Condition (สำคัญมากสำหรับระบบ Concurrent)
  • Security Scan: ใช้ govulncheck (เครื่องมือ Official จากทีม Go) เพื่อหาช่องโหว่ใน Library ที่เราดึงมาใช้

 

Continuous Deployment (CD)

  • Build & Push: สร้าง Docker Image (แนะนำเป็น Multi-stage build เพื่อลดขนาด) และส่งไปเก็บที่ Private Registry
  • Orchestration: สั่ง Update Image ใน Kubernetes หรือ Docker Swarm โดยใช้กลยุทธ์ที่เหมาะสม

 

2. กลยุทธ์การ Deploy: เลือกท่าไหนให้ User ไม่ด่า?

 

นี่คือจุดวัดกึ๋นของคนทำระบบ Real-time ครับ เรามาเทียบกันชัดๆ ว่าท่าไหนเหมาะกับเรา:

 

กลยุทธ์

การทำงาน

ข้อดี

ข้อเสีย

Rolling Update

ค่อยๆ ปิดตัวเก่า เปิดตัวใหม่ทีละตัว

ประหยัดทรัพยากร, มาตรฐาน K8s

User บางส่วนจะถูกตัดการเชื่อมต่อเป็นระลอก

Blue-Green

รันเวอร์ชันใหม่รอไว้ แล้วสลับ Traffic ทั้งหมด

ปลอดภัย Rollback ได้ทันที

เปลือง RAM/CPU สองเท่าช่วงสลับ

Canary

ปล่อยเวอร์ชันใหม่ให้ User แค่ 5-10% ลองใช้

ตรวจสอบบั๊กได้ก่อนพังทั้งระบบ

จัดการเรื่อง Routing ยากขึ้น

 

Pro Tip: สำหรับ WebSocket ผมแนะนำ Rolling Update ที่มาคู่กับ Graceful Shutdown และ Client-side Retry ครับ เป็นท่าที่สมดุลที่สุด

 

3. หัวใจสำคัญ: Graceful Shutdown

 

เพื่อให้การ Deploy นุ่มนวล Server ต้องไม่ "ปิดประตูใส่หน้า" User แต่ต้องค่อยๆ บอกลาครับ

 

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

    // Channel สำหรับรับสัญญาณจาก OS (SIGTERM คือสิ่งที่ K8s ส่งมาเวลาจะปิด Pod)
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

    go func() {
        if err := server.ListenAndServe(); err != http.ErrServerClosed {
            slog.Error("Server error", "err", err)
        }
    }()

    <-stop // รอสัญญาณปิดเครื่อง
    slog.Info("Shutting down gracefully...")

    // กำหนดเวลาให้ User เคลียร์งาน (เช่น 30 วินาที)
    // ในช่วงนี้ Server จะหยุดรับ Connection ใหม่ แต่ท่อเก่าจะยังทำงานต่อได้จนกว่าจะปิด
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        slog.Error("Forced shutdown", "err", err)
    }
    slog.Info("Server stopped")
}

 

4. ตัวอย่าง Pipeline (GitHub Actions 2026 Edition)

 

ไฟล์ .github/workflows/deploy.yml สำหรับโปรเจกต์ Go ที่รวมการเช็คความปลอดภัยไว้ด้วย:

 

YAML
name: Go WebSocket CI/CD

on:
  push:
    branches: [ main ]

jobs:
  quality-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.24' # สมมติว่าเป็นเวอร์ชันล่าสุดในปี 2026
      - name: Lint
        run: golangci-lint run
      - name: Security Scan
        run: go install golang.org/x/vuln/cmd/govulncheck@latest && govulncheck ./...
      - name: Test
        run: go test -v -race ./...

  deploy:
    needs: quality-gate
    runs-on: ubuntu-latest
    steps:
      - name: Build and Push Docker
        run: |
          docker build -t superdev/websocket-app:${{ github.sha }} .
          docker push superdev/websocket-app:${{ github.sha }}
      # ขั้นตอนสั่ง Update Kubernetes Deployment (เช่น kubectl set image)

 

5. การจัดการฝั่ง Client: อย่าปล่อยให้ลอยแพ

 

ระบบ CI/CD ที่ดีต้องรวมไปถึง "ความฉลาด" ของ Client ด้วยครับ:

  1. Exponential Backoff: เมื่อหลุด อย่ารุมต่อใหม่ทันที ให้รอ 1วิ, 2วิ, 4วิ... ไปเรื่อยๆ
  2. Jitter (การสุ่ม): อย่า Reconnect พร้อมกันเป๊ะๆ ให้บวก/ลบเวลาสุ่มเข้าไปเล็กน้อย (เช่น 1.2s, 0.9s) เพื่อกระจายโหลดไม่ให้ Server ตัวใหม่พังตั้งแต่วินาทีแรก

 


 

สรุป

 

การทำ CI/CD สำหรับ WebSocket คือการผสมผสานระหว่าง Automation (ความเร็ว) และ Empathy (ความเข้าใจพฤติกรรม User) การทำให้ Pipeline รันได้อัตโนมัติจะช่วยลด Human Error และทำให้ทีมกล้าปล่อยฟีเจอร์ใหม่ๆ ได้ทุกวันโดยไม่ต้องกังวลว่าระบบจะล่มตอนตี 2 ครับ

 

ในตอนหน้า (EP 136): เราจะมาคุยเรื่องที่ปวดตับไม่แพ้กัน คือ Versioning และ Backward Compatibility — จะอัปเกรดระบบอย่างไรให้ User ที่ยังใช้แอปเวอร์ชันเก่าไม่ค้าง และยังคุยกับเวอร์ชันใหม่รู้เรื่อง! ห้ามพลาดครับ