Go + systemd Zero-Downtime Deploy Kit

Release service Go trên một VPS mà không rớt request, chỉ cần systemd, Caddy và bash. Không pm2, không container.

Mua trên Polar: $29 Mua một lần · v0.1.0 · tải ngay qua Polar sau khi thanh toán

Ảnh bìa Go + systemd Zero-Downtime Deploy Kit
Kết quả của một lần chạy test thật, không phải ảnh dựng.

Dành cho ai

Bạn đưa một binary Go lên một server và chạy nó dưới systemd. Hiện giờ deploy là scp, systemctl restart, rồi liếc qua log. Cách đó ổn cho tới khi một lần restart rơi vào đúng phút đông khách và Caddy trả 502 trong vài giây, hoặc khi bạn đẩy lên một bản build khởi động bình thường nhưng request nào cũng lỗi, và không có gì ngăn nó nhận traffic.

Bộ kit này thay cách đó bằng một script deploy: chạy release mới cạnh bản cũ, kiểm tra nó trước khi nó nhận traffic, chuyển sang, và tự lùi lại nếu release mới hỏng. Đây là bản cho Go và systemd của bộ kit Node và pm2 mà chúng tôi đã bán, dành cho người không dùng pm2 và không muốn dùng.

Trong bộ này có gì

  • deploy.sh: thư mục release với symlink current đổi nguyên tử, deploy binary bạn build trong CI hoặc build ngay trên server, health check (N lần trả lời liên tiếp), chuyển traffic, drain, tự rollback, deploy.sh rollback, và một cái lock để hai lần deploy không chồng lên nhau.
  • Các systemd unit đã siết bảo mật: Type=notify, stop êm, chính sách restart, user không đặc quyền, secret nằm trong file environment quyền 600 mà user của service không đọc được.
  • Một script cài đặt dựng sẵn user, thư mục, unit và file environment.
  • Một app Go mẫu nhỏ (chỉ dùng thư viện chuẩn) có graceful shutdown, kèm một helper đã test cho socket activation và sd_notify.
  • Cảnh báo qua Telegram hoặc webhook khi deploy lỗi. Deploy thành công thì không báo gì.
  • Một bộ test harness và kết quả của lần chạy gần nhất.

Hai cách chuyển traffic

Blue/green (mặc định). Hai instance, app@a và app@b, đứng sau Caddy. Release mới khởi động trên port đang rảnh và phải trả lời health check nhiều lần liên tiếp thì Caddy mới được báo. Sau đó Caddy reload êm, instance cũ drain rồi dừng. Nếu release mới hỏng sau khi đã chuyển, script đặt mọi thứ về như cũ. Rollback là cùng thao tác đó chạy ngược, và với KEEP_PREVIOUS=1 thì instance cũ vẫn chạy sẵn nên rollback chỉ là đổi Caddy.

Socket activation. systemd giữ socket đang lắng nghe trong lúc service restart, nên kết nối được xếp hàng chứ không bị từ chối. Release mới trước hết chạy thử như một canary trên port khác, nên bản build hỏng không bao giờ tới được bước restart.

Blue/green là mặc định vì nó giữ request chậm nhất ở gần độ dài của chính request đó. Chế độ socket dành cho những trường hợp bạn không chạy được hai instance: một port cố định không đổi được, hoặc một tài nguyên mà chỉ một process được giữ. Các số đo bên dưới có cả hai chế độ.

Số đo

Cách đo: container Debian 12 với systemd 252 làm PID 1, Caddy 2.6.2, một app mẫu cần 1.5 s để khởi động, tải đều 20 request mỗi giây qua Caddy, mỗi kịch bản chạy ba lần. Request bị tính là lỗi nếu gặp bất kỳ lỗi kết nối, timeout hay status nào khác 200.

Kịch bảnĐã gửiLỗi
systemctl restart thường, một instance152 đến 16233 đến 42 (toàn HTTP 502)
Như trên, thêm lb_try_duration 5s của Caddy1620, nhưng request chậm nhất mất 2.3 s
Blue/green, deploy tốt293 đến 2950
Blue/green, release có /health trả 500458 đến 4600
Blue/green, release chết ngay khi khởi động1320
Blue/green, release hỏng sau khi đã chuyển381 đến 3830 (bản lỗi đã trả lời 182 hoặc 183 request trước khi rollback)
Blue/green, rollback293 đến 2950
Blue/green, rollback với KEEP_PREVIOUS=1222 đến 2230, traffic chuyển sau 0.1 s
Blue/green, deploy tốt khi có request 2 s đang xử lý2340, các request chậm vẫn xong, không bị cắt
Chế độ socket, deploy tốt268 đến 2690
Chế độ socket, deploy tốt khi có request 2 s đang xử lý307 đến 3100, request chậm nhất 5.65 s

Một lần deploy ở chế độ blue/green mất 8 đến 9 giây, và traffic sang release mới sau khoảng 3.6 giây. Đó là số đo cho app này trên máy này.

Một lần deploy diễn ra thế nào

README đi qua bảy bước. Tóm lại: lấy lock, tạo thư mục release, khởi động instance đang rảnh và chờ nó báo sẵn sàng, health check trực tiếp, ghi upstream mới và reload Caddy, đổi symlink, chờ, kiểm tra lại, và chỉ lúc đó mới dừng instance cũ. Mỗi bước đều có đường xử lý khi lỗi, và những đường quan trọng đã được test: release không khỏe, chết ngay khi khởi động, và release hỏng sau khi đã chuyển.

Yêu cầu

Linux có systemd, Caddy, curl và flock. Go và git chỉ cần nếu bạn build ngay trên server. Một service trên một host.

Câu hỏi hay gặp

Tôi dùng Node và pm2. Bộ này có hợp không?

Không, hãy xem VPS Deploy & Backup Kit. Bộ đó làm cùng việc này cho stack kia.

Có bắt buộc dùng Caddy không?

Blue/green cần Caddy để chuyển traffic. Cả hai chế độ đều được đo với Caddy đứng phía trước.

Chạy trên Ubuntu được không?

Ubuntu cũng dùng systemd, mà bộ này chỉ cần systemd và Caddy nên dùng được. Chúng tôi đã test trên Debian 12.

Lần deploy thứ hai thì sao khi một lần đang chạy?

Nó thoát ngay với code 75 và không gửi cảnh báo. Đó là việc của cái lock.

Giấy phép

Dùng trên server của bạn. Xin đừng bán lại các file.

Lịch sử thay đổi

  • 0.1.0: bản đầu tiên.

Bài liên quan

Mua sản phẩm này

Mua trên Polar: $29

Polar lo thanh toán, thuế và hóa đơn (họ là merchant of record). Thanh toán xong bạn nhận link tải ngay.