Blog #26

Deploy with downtime is zero (Part 2)

# Reason

Dạo gần đây và cách đây vài ngày cho đến ngay lúc tôi đang viết blog này thì website học tiếng Anh của tôi có một số lượng lớn users mới. Điều này khiến tôi phải đau đầu khi mỗi lần deploy, có hôm phải thức tới 2h sáng hoặc dậy sớm lúc 4h để deploy.

Vậy thì sao? Không phải là trước đây tôi đã tạo ra 1 blog nói về vấn đề này rồi sao???  

Đúng là như vậy, tôi đã có một cách khắc phục ở Part 1. Nhưng nó không tối ưu, nó chỉ giúp cho server API của tôi không trả về lỗi khi response request thôi. Còn website học tiếng Anh của tôi là một web có giao diện người dùng. Họ sẽ thấy trang web tệ khi bắt họ delay khoảng thời gian hơn 10s. Nên tôi rất ngại mỗi khi deploy khi có user đang sử dụng. 

Khi một request được gửi tới server thì Nginx sẽ hứng request đó và gửi đến puma.sock. Vậy khi request gửi trong khi Puma restart thì sao? Thì error 502 chứ sao  

Nó làm gián đoạn server và người dùng sẽ bị ức chế. Kiểu như wtf sao tao đang chạy mà bị lỗi, debug các kiểu xong mới phát hiện ra à là do cái thằng API kia bị lỗi. Vậy thì sẽ giảm chất lượng dịch vụ của bạn đúng không? Rồi dần dần đ*o ai thèm dùng dịch vụ của bạn luôn @@

Trước đây khi chưa biết cơ chế này mình rất ngại deploy, phải kiếm khung giờ mà ít người dùng nhất và cầu trời đừng có request nào gửi tới khi deploy , kiểu như lén lút deploy vậy :D. Thôi bắt đầu cùng mình giải quyết vấn đề nào.  

# Getting started

Đầu tiên hãy cùng mình phân tích cơ chế hoạt động của puma:reload trong Capistrano:

  1. Gửi tín hiệu USR2 đến master process của Puma:

    USR2 là một tín hiệu Unix cho phép Puma thực hiện "hot restart" — tức là nạp lại toàn bộ ứng dụng (code, gem, config) trong một tiến trình mới, nhưng không gián đoạn kết nối đang mở.
    Capistrano gửi tín hiệu này đến PID (Process ID) của Puma, thường lưu tại shared/tmp/pids/puma.pid.
     
  2. Puma fork tiến trình mới:

    Puma tạo một tiến trình worker mới với code mới, trong khi tiến trình cũ vẫn tiếp tục phục vụ request.
    Sau khi tiến trình mới được sẵn sàng, Puma sẽ tự động dừng tiến trình cũ.
     
  3. Không cần restart hoàn toàn:

    Vì tiến trình mới được sinh ra song song, người dùng gần như không cảm thấy downtime.

Thêm cấu hình deploy.rb như sau:

set :puma_phased_restart, true

Vậy là xong rồi nha :D. Giờ đây mình không cần phải lén lút deploy nữa mà chỉ cần lén lút fix bugs rồi deploy mà không một ai biết