Đầu năm nay chúng tôi đã di chuyển từ cơ sở lưu trữ máy chủ chuyên dụng sang AWS, và chúng tôi rất vui vì đã làm như vậy. Chúng tôi hạnh phúc có một loạt tự động hóa, vì chúng tôi dành ít thời gian hơn loay hoay với các máy chủ. :) Bài viết này mô tả những gì xảy ra trong cơ sở hạ tầng của chúng tôi khi có thay đổi về lượng thông báo lỗi mà chúng tôi đang xử lý.
Chúng Tôi Scale Như Thế Nào
Pipeline xử lý của chúng tôi phụ thuộc rất nhiều vào Sidekiq, thứ đã cực kỳ ổn định cho chúng tôi. Phần lớn công việc trong pipeline được xử lý bởi các instance thuộc một autoscaling group. Khi backlog Sidekiq tăng lên, một cảnh báo CloudWatch được kích hoạt, khiến các instance mới được khởi động trong group này. Khi lưu lượng giảm và backlog biến mất, các instance bị chấm dứt.
Scale Có Kiểu
Điều duy nhất gây rắc rối trong thiết lập này là chúng tôi muốn đảm bảo các worker Sidekiq hoàn thành tất cả công việc của họ trước khi một instance bị chấm dứt. Mặc dù Sidekiq hỗ trợ lên lịch lại các job bị chấm dứt trước khi hoàn thành, chúng tôi thích tránh điều đó khi có thể — tốt hơn là mất vài giây thêm để hoàn thành một job thay vì để nó được lên lịch lại và khởi động lại. May mắn thay, trong sự tuyệt vời của nó, Sidekiq có một phương thức để nói với các tiến trình worker ngừng chấp nhận công việc mới sau khi job hiện tại hoàn thành, giúp dễ dàng drain các worker để chuẩn bị cho việc chấm dứt. Tricky part là让那些 worker 知道他们需要开始 draining — và cho điều đó, chúng tôi sử dụng kết hợp Lifecycle Hooks, CloudWatch Events + Lambda, EC2 Simple Systems Manager, và một chút code.
Phần còn lại của bài viết sẽ chỉ cho bạn cách bạn có thể sao chép thiết lập của chúng tôi để có các instance "đi vào đêm ấy một cách nhẹ nhàng".
Điều Kiện Tiên Quyết
Tự nhiên, bạn sẽ cần một autoscaling group có chính sách scale chấm dứt instance dựa trên một số quy tắc bạn định nghĩa. Ngoài ra, bạn sẽ cần cấu hình instance chạy SSM Agent và chạy trong một role có quyền truy cập SSM. Các instance này cũng sẽ cần quyền tương tác với API autoscaling. Đây là tài liệu chính sách cho role instance:
Code
Khi những mục đó đã có sẵn, bạn có thể bắt đầu kết nối các mảnh ghép lại với nhau. Chúng tôi sẽ bắt đầu từ cuối quy trình — script của chúng tôi sẽ sống trên instance và xử lý việc drain Sidekiq — và làm việc ngược lại. Đầu tiên, code Ruby sẽ quiet các worker và chờ chúng hoàn thành công việc:
Chúng tôi lấy danh sách các tiến trình Sidekiq đang chạy trên instance hiện tại qua Sidekiq API, nói cho chúng ngừng chấp nhận công việc mới, sau đó chờ cho đến khi tất cả các tiến trình không còn job đang hoạt động.
Đây là shell script sẽ kích hoạt script đó khi đến lúc chấm dứt instance:
Có nhiều thiết lập phải làm, nhưng phần chính của script này bắt đầu từ dòng 8. Đầu tiên chúng tôi báo hiệu cho autoscaling group rằng chúng tôi đang làm việc trên lifecycle hook. Điều này có tác dụng phụ là một guard — nếu instance này chưa được lên lịch chấm dứt bởi autoscaling group, thì lệnh này sẽ thất bại, và script tắt worker sẽ không được gọi. Script này không nên được gọi trừ khi instance đã được lên lịch chấm dứt, nhưng không sao phải hơi phòng thủ. :) Giả sử chúng ta tiến triển, chúng ta tắt các worker, và sau đó chúng ta nói cho autoscaling group tiếp tục chấm dứt. Nếu chúng ta không thực hiện cuộc gọi API đó, thì instance sẽ tiếp tục chạy cho đến khi hết thời gian timeout của hook (chúng ta sẽ nói đến đó sau chút).
SSM Document
Bây giờ chúng ta cần cái gì đó sẽ kích hoạt shell script này. Đó là nơi SSM agent xuất hiện. Nó đang chạy vui vẻ trong nền, chờ một Document xuất hiện qua SendCommand để nói cho nó biết phải làm gì. Document của chúng tôi hướng dẫn agent chạy script chấm dứt:
Bạn có thể tạo Document của mình bằng cách nhấp vào liên kết Documents trong phần Systems Manager Shared Resources của EC2 console (gần cuối sidebar), nhấp vào nút Create Document, và thả JSON đó vào hộp Content.
Lambda Function
Với Document của bạn đã sẵn sàng, hãy tạo một Lambda function sẽ chạy nó trên instance mục tiêu khi đến lúc chấm dứt một instance. Đây là một số code cho việc đó:
Bạn sẽ cần thay thế PrepInstanceForTermination bằng bất kỳ tên nào bạn chọn cho Document của mình, nhưng ngoài ra code này chỉ cần copy-paste.
Lambda function này cần một vài quyền (ngoài các quyền Lambda thông thường), và đây là tài liệu chính sách IAM cho điều đó:
Lại 한번, bạn có thể cần thay thế tên Document.
Hook Và Event Rule
Kiêng nén nhé — chúng ta gần xong rồi! Bạn còn hai việc phải làm: tạo CloudWatch Event và Lifecycle Hook. Đầu tiên, Event:
Trong CloudWatch management console, nhấp vào Events ở sidebar, sau đó nhấp nút Create rule. Đây là thứ sẽ kích hoạt Lambda function bạn vừa tạo.

Bên trái bạn chọn sự kiện bạn muốn theo dõi và bất kỳ đặc điểm bổ sung nào về sự kiện đó (như giới hạn autoscaling group nào sẽ kích hoạt sự kiện này), và bên phải bạn chọn target của rule — trong trường hợp của chúng tôi, là Lambda function chúng ta tạo trước đó. Bạn không cần thay đổi bất kỳ mặc định nào cho target Lambda function.
Khi xong, quay lại EC2 management console và điều hướng đến autoscaling group có các instance bạn muốn quản lý. Sau khi nhấp vào tab Lifecycle Hooks, bạn sẽ có thể tạo một hook. Của chúng tôi trông như thế này:

Terminate là chuyển đổi chúng ta quan tâm, và chúng ta muốn việc chấm dứt tiếp tục (Default Result: CONTINUE) nếu vì lý do nào đó cuộc gọi API không xảy ra trong shell script của chúng ta sau 600 giây (Heartbeat Timeout) đã trôi qua. Điều này sẽ dẫn đến việc instance của chúng ta bị chấm dứt theo cách này hay cách khác, ngay cả khi worker Sidekiq chưa xong, sau 10 phút.
Kết Thúc
Bạn làm được rồi! Bây giờ bạn có một autoscaling group có thể chấm dứt instance Sidekiq của nó theo cách nhân văn. Enjoy!
P.S.: Credit goes to awslabs for a super-helpful tutorial on putting some of these pieces together.