Tại Honeybadger, chúng tôi phụ thuộc rất nhiều vào Sidekiq trong quy trình xử lý của mình. Gần như mọi thứ chúng tôi làm đều chạy qua hàng đợi vào một thời điểm nào đó. Do đó, tôi muốn đảm bảo rằng chúng tôi đang chạy Sidekiq tốt. Với việc chuyển sang EC2 gần đây, chúng tôi đã thay đổi từ việc có một tập hợp các máy chủ tồn tại lâu dài ổn định thành một tập hợp các phiên bản luôn thay đổi để chạy công việc của chúng tôi. Điều này đã khiến tôi phải xem lại cách chúng tôi khởi động Sidekiq tại thời điểm khởi động, vì điều đó bây giờ quan trọng hơn nhiều so với trước đây.
Trong quá khứ, chúng tôi phụ thuộc vào chúa để quay tất cả các quy trình của worker, vì chúng tôi muốn nó giám sát chúng và chấm dứt những quy trình sử dụng quá nhiều bộ nhớ. Cấu hình thần thánh của chúng tôi đã được điều chỉnh thủ công cho các máy chủ mà chúng tôi đã triển khai để chạy các công nhân đó. Thật không may, tôi nhận thấy rằng khi khởi động cấu hình thần thánh cũ của chúng tôi trên các phiên bản mới bằng cách sử dụng systemd, đôi khi chúng tôi sẽ nhận được nhiều nhân viên chạy hơn cấu hình của chúng tôi đã chỉ định. Điều này sẽ dẫn đến áp lực về trí nhớ đối với các trường hợp, đó là một tin xấu. Sau khi tìm hiểu nó một thời gian và nhận ra rằng chúng tôi không sử dụng nhiều chức năng của nó, tôi quyết định đã đến lúc ngừng sử dụng thần để quản lý các quy trình.
Một lệnh đơn giản để chạy nhiều quy trình Sidekiq
Những gì tôi thực sự muốn là một tập lệnh mà tôi có thể chạy như một dịch vụ systemd sẽ tạo ra một quy trình Sidekiq trên mỗi lõi và điều đó sẽ khởi động lại các quy trình tiêu tốn quá nhiều bộ nhớ. Tập lệnh cần thiết để có thể hoạt động với các phiên bản EC2 có nhiều kích thước khác nhau, vì số lượng lõi và dung lượng RAM có thể khác nhau khi tôi thử các kích thước phiên bản khác nhau để xem cái nào sẽ hoạt động tốt nhất cho khối lượng công việc của chúng tôi. Vì tôi không thể tìm thấy một tập lệnh nào có thể làm chính xác điều đó, nên tôi đã viết một tập lệnh:
Tất nhiên, có một chút mã mượn trong tập lệnh đó. :) Mã trong process_count
phương thức có thể đến từ Stack Overflow (tôi đã sử dụng nó trong nhiều năm trong các cấu hình kỳ lân của chúng tôi) và fork_child
về cơ bản phương thức là sidekiq
bin từ đá quý sidekiq.
Tập lệnh này tạo ra một số quy trình con dựa trên số lượng lõi CPU hiện có, với mỗi quy trình con giống như khi bạn chạy sidekiq
chỉ huy trực tiếp. Kết quả là, bất kỳ tùy chọn dòng lệnh nào bạn chuyển cho tập lệnh này sẽ được chuyển đến (và được phân tích cú pháp bởi) mã Sidekiq CLI. Nói cách khác, bạn có thể chuyển bất kỳ tùy chọn nào cho tập lệnh này mà bạn có thể chuyển tới sidekiq
khi chạy trực tiếp, mặc dù một số tùy chọn (như tùy chọn tệp pid) không hợp lý khi sử dụng. Một chuỗi cũng được tạo ra để kiểm tra định kỳ việc sử dụng bộ nhớ của mỗi tiến trình con. Nếu bất kỳ quy trình con nào vượt qua ngưỡng sử dụng, quy trình đó sẽ bị giết và một quy trình mới sẽ được tạo ra để thay thế nó.
Làm việc với cụm
Mặc dù số lượng quy trình được mặc định bằng số lõi (giả sử bạn đang dành một thể hiện để chạy các quy trình Sidekiq), bạn có thể ghi đè điều đó bằng cách đặt biến môi trường SK_PROCESS_COUNT trước khi chạy tập lệnh. Tương tự như vậy, ngưỡng bộ nhớ được đặt theo tỷ lệ phần trăm của tổng RAM của phiên bản (để trống một số RAM bằng cách thêm một bộ nhớ vào giá trị process_count trên dòng 58), nhưng bạn có thể đặt bất kỳ tỷ lệ phần trăm nào bạn muốn với biến môi trường SK_MEMORY_PCT_LIMIT. Chúng tôi sử dụng hai biến này để hạn chế số lượng quy trình Sidekiq và mức sử dụng bộ nhớ trên các phiên bản đang chạy ứng dụng Rails hỗ trợ giao diện người dùng của chúng tôi.
Như một phần thưởng bổ sung, tập lệnh này bắt các tín hiệu thường được sử dụng để quản lý các quy trình Sidekiq và chuyển các tín hiệu đó cho các quy trình con. Điều này cho phép chúng tôi sử dụng pkill -f -USR1 skcluster
để các quy trình con ngừng chấp nhận công việc mới (đây là nhiệm vụ ban đầu trong tập lệnh triển khai của chúng tôi) và chúng tôi có thể sử dụng pkill -f skcluster
để chấm dứt mọi thứ.
Định cấu hình systemd
Định nghĩa dịch vụ systemd rất đơn giản:
Vì chúng tôi sử dụng pgbouncer trên mọi trường hợp để kết nối proxy tới postgres, chúng tôi đảm bảo rằng dịch vụ pgbouncer đang chạy trước khi các quy trình sidekiq được khởi động. Nếu chúng tôi đang chạy redis trên cùng các phiên bản, chúng tôi cũng sẽ thêm redis-server.service vào các dòng Yêu cầu và Sau. EnvironmentFile sử dụng tiền tố - cho tên tệp để thông báo cho systemd rằng sẽ ổn nếu tệp không tồn tại. Điều này cho phép chúng tôi bỏ qua tệp đó khi chúng tôi muốn đi với số lượng tiến trình mặc định và giới hạn bộ nhớ. Chúng tôi sử dụng --require
tùy chọn để cho Sidekiq biết nơi cần đến để tải cấu hình và trình khởi tạo ứng dụng Rails của chúng tôi. Vì tập lệnh skcluster bắt được SIGTERM, chúng ta có thể sử dụng systemctl restart skcluster.service
để khởi động lại tất cả các công nhân (đây là tác vụ muộn trong tập lệnh triển khai của chúng tôi).
Chúng tôi đã sử dụng tập lệnh này được một thời gian và nó hoạt động như một nhà vô địch. Chúng tôi không gặp bất kỳ sự cố nào nữa với quá nhiều quy trình được tạo ra khi khởi động, chúng tôi không có thêm bất kỳ cảnh báo sử dụng bộ nhớ nào từ việc giám sát của chúng tôi và chúng tôi có thể khởi chạy bất kỳ loại phiên bản EC2 nào mà chúng tôi muốn. Hoạt động không có rắc rối là loại hoạt động mà tôi thích nhất. :)