12/04/2026 18:16น.

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 ครับ เรามาเทียบกันชัดๆ ว่าท่าไหนเหมาะกับเรา:
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 ด้วยครับ:
- Exponential Backoff: เมื่อหลุด อย่ารุมต่อใหม่ทันที ให้รอ 1วิ, 2วิ, 4วิ... ไปเรื่อยๆ
- Jitter (การสุ่ม): อย่า Reconnect พร้อมกันเป๊ะๆ ให้บวก/ลบเวลาสุ่มเข้าไปเล็กน้อย (เช่น 1.2s, 0.9s) เพื่อกระจายโหลดไม่ให้ Server ตัวใหม่พังตั้งแต่วินาทีแรก
สรุป
การทำ CI/CD สำหรับ WebSocket คือการผสมผสานระหว่าง Automation (ความเร็ว) และ Empathy (ความเข้าใจพฤติกรรม User) การทำให้ Pipeline รันได้อัตโนมัติจะช่วยลด Human Error และทำให้ทีมกล้าปล่อยฟีเจอร์ใหม่ๆ ได้ทุกวันโดยไม่ต้องกังวลว่าระบบจะล่มตอนตี 2 ครับ
ในตอนหน้า (EP 136): เราจะมาคุยเรื่องที่ปวดตับไม่แพ้กัน คือ Versioning และ Backward Compatibility — จะอัปเกรดระบบอย่างไรให้ User ที่ยังใช้แอปเวอร์ชันเก่าไม่ค้าง และยังคุยกับเวอร์ชันใหม่รู้เรื่อง! ห้ามพลาดครับ