Bạn có muốn nhận thông báo về nội dung mới không?
Khi thiết kế các dịch vụ, chúng ta tích hợp đủ mọi loại tính năng để tăng độ ổn định và khả năng phục hồi. Nhưng trong thực tế, để vận hành ổn định thì các dịch vụ cũng phải xử lý được những sự cố có thể lường trước. Tại Amazon, chúng tôi xây dựng các dịch vụ theo kiểu có thể mở rộng và dự phòng theo chiều ngang, do phần cứng dù thiết kế như thế nào thì rốt cuộc cũng sẽ gặp lỗi. Mọi ổ cứng đều có tuổi thọ tối đa lường trước được, và mỗi phần mềm đều có khả năng bị hỏng ở một thời điểm nào đó. Có vẻ như tình trạng của máy chủ là một biến nhị phân: một là hoạt động tốt, hai là không hề hoạt động và trở nên vô dụng. Đáng tiếc là không phải vậy. Chúng tôi nhận thấy rằng thay vì chỉ ngừng hoạt động, các máy chủ gặp lỗi có thể gây nên những tác hại không thể lường trước và đôi khi rất nghiêm trọng đối với hệ thống. Các bài kiểm tra tình trạng sẽ tự động phát hiện và ứng phó với những loại sự cố này.
Bài viết này trình bày cách chúng tôi sử dụng kiểm tra tình trạng để phát hiện và xử lý các sự cố đơn máy chủ, những điều xảy ra khi không tiến hành kiểm tra tình trạng, và các hệ thống khi phản ứng thái quá với các lỗi kiểm tra tình trạng sẽ có thể biến vấn đề nhỏ thành thảm họa lớn như thế nào. Chúng tôi cũng cung cấp thông tin chuyên sâu từ kinh nghiệm của Amazon về việc cân bằng sự đánh đổi giữa các phương pháp triển khai kiểm tra tình trạng.
Sự cố nhỏ, ảnh hưởng lớn
Những báo động khác bắt đầu kích hoạt, bởi vì hoạt động theo dõi liên quan đến việc đo lường tỷ lệ lỗi và độ trễ tại nhiều điểm khác nhau trong hệ thống. Dù các hệ thống giám sát và quá trình vận hành kiểu như vậy có thể đóng vai trò như một chốt chặn để hạn chế vấn đề, nhưng các bước kiểm tra tình trạng phù hợp có thể giảm thiểu đáng kể tác động của toàn bộ lớp lỗi này bằng cách nhanh chóng phát hiện và ứng phó với các sự cố.
Sự đánh đổi khi kiểm tra tình trạng
Kiểm tra tình trạng là một cách để hỏi xem liệu dịch vụ trên máy chủ cụ thể có khả năng thực hiện công việc một cách thành công hay không. Theo định kỳ, bộ cân bằng tải đặt ra câu hỏi này cho từng máy chủ để xác định xem máy chủ nào an toàn để chuyển lưu lượng truy cập đến đó. Một dịch vụ khi thăm dò tin nhắn từ hàng đợi có thể tự hỏi liệu nó có ở trong tình trạng tốt hay không, trước khi quyết định thăm dò công việc khác từ hàng đợi. Các tác nhân giám sát—chạy trên từng máy chủ hoặc trên nhóm theo dõi bên ngoài—có thể hỏi xem liệu các máy chủ có ở trong tình trạng tốt không, qua đó, các tác nhân này có thể đưa ra cảnh báo hoặc tự động xử lý các máy chủ gặp sự cố.
Như chúng ta đã thấy trong ví dụ về lỗi trên trang web của tôi, khi một máy chủ có tình trạng xấu ở trong dịch vụ, máy chủ đó có thể làm giảm đáng kể khả năng cung cấp của tổng thể dịch vụ. Với nhóm gồm 10 máy chủ, 1 máy chủ kém sẽ khiến cho khả năng cung cấp của nhóm chỉ còn 90% hoặc ít hơn. Tệ hơn nữa là một số thuật toán cân bằng tải (chẳng hạn như “yêu cầu ít nhất”) sẽ giao nhiều việc hơn cho máy chủ nhanh nhất. Khi gặp lỗi, máy chủ thường bắt đầu không xử lý được các yêu cầu, tạo nên “hố đen” trong nhóm dịch vụ bằng việc thu hút nhiều lượt yêu cầu hơn so với các máy chủ khỏe. Trong một số trường hợp, chúng ta thêm biện pháp bảo vệ bổ sung để ngăn chặn hố đen bằng cách làm chậm tốc độ của các yêu cầu thất bại để khớp với độ trễ trung bình của các yêu cầu thành công. Tuy nhiên, trong một số tình huống khác, chẳng hạn như với trường hợp của trình thăm dò hàng đợi, sự cố này khó xử lý hơn. Ví dụ: nếu trình thăm dò hàng đợi thăm dò tin nhắn nhanh ngang với tốc độ nhận, thì máy chủ thất bại cũng sẽ trở thành hố đen. Với một tập hợp môi trường đa dạng như vậy dành cho quá trình phân bổ công việc, tùy theo hệ thống mà cách chúng ta cân nhắc về biện pháp bảo vệ máy chủ gặp lỗi một phần sẽ khác nhau.
Chúng tôi nhận thấy rằng các máy chủ gặp lỗi một cách độc lập vì nhiều lý do, trong đó có: đĩa không ghi được khiến cho các yêu cầu thất bại ngay lập tức, đồng hồ đột nhiên lệch khiến cho lệnh gọi đến tác nhân độc lập không được xác thực thành công, máy chủ không truy xuất được tài nguyên mật mã sau cập nhật khiến cho việc mã hóa và giải mã thất bại, các quy trình hỗ trợ quan trọng bị hỏng do tự dính lỗi, rò rỉ bộ nhớ và khóa chết làm đông cứng quá trình xử lý.
Các máy chủ cũng có thể gặp lỗi do nhiều nguyên nhân tương quan với nhau, khiến cho nhiều hoặc tất cả máy chủ trong nhóm đồng loạt gặp lỗi. Các nguyên nhân tương quan bao gồm việc cắt đứt mối quan hệ phụ thuộc chung và các sự cố mạng diện rộng. Nếu hoàn toàn thành công thì việc kiểm tra tình trạng sẽ đánh giá được mọi khía cạnh về tình trạng của ứng dụng và máy chủ, thậm chí xác minh rằng các quy trình hỗ trợ không quan trọng đang chạy. Tuy nhiên, rắc rối xảy đến khi các bài kiểm tra tình trạng thất bại vì lý do không quan trọng, và khi thất bại đó làm liên lụy đến nhiều máy chủ. Nếu quy trình tự động hóa loại bỏ các máy chủ khỏi dịch vụ trong khi các máy chủ đó vẫn có thể thực hiện những công việc có ích, thì quy trình tự động hóa sẽ có hại hơn là có lợi.
Trở ngại trong việc kiểm tra tình trạng, đó là sự đánh đổi giữa một bên là lợi ích của việc kiểm tra tình trạng toàn diện để nhanh chóng giảm thiểu các lỗi đơn máy chủ, và một bên là tác hại của lỗi giả đối với toàn bộ nhóm. Do đó, nếu muốn xây dựng thành công các bài kiểm tra tình trạng thì một trong những thách thức cần đối mặt là làm sao tránh được các lỗi giả. Nhìn chung, điều này nghĩa là quy trình tự động hóa xung quanh các đợt kiểm tra tình trạng nên ngừng chuyển lưu lượng truy cập đến một máy chủ xấu, nhưng vẫn cho phép lưu lượng truy cập nếu toàn bộ nhóm có dấu hiệu gặp vấn đề.
Các cách kiểm tra tình trạng
Trên một máy chủ, có nhiều thứ có thể bị hỏng và có nhiều nơi trong hệ thống mà chúng ta cần đánh giá tình trạng máy chủ. Một số bài kiểm tra tình trạng có thể báo cáo chính xác rằng máy chủ bị hỏng một cách độc lập, nhưng một số bài kiểm tra khác lại cho kết quả mơ hồ hơn và báo lỗi giả trong trường hợp các lỗi tương quan với nhau. Một số bài kiểm tra tình trạng rất khó để triển khai. Một số khác được triển khai qua các dịch vụ như Amazon Elastic Compute Cloud (Amazon EC2) và Elastic Load Balancing. Mỗi loại kiểm tra tình trạng đều có những điểm mạnh riêng.
Kiểm tra sự sống sẽ kiểm thử khả năng kết nối cơ bản đến dịch vụ và sự hiện diện của một tiến trình trên máy chủ. Loại kiểm tra này thường do bộ cân bằng tải hoặc tác nhân theo dõi bên ngoài thực hiện, và không quan tâm đến chi tiết về cách ứng dụng hoạt động. Kiểm tra sự sống thường đi liền với dịch vụ và không cần tác giả ứng dụng triển khai bất cứ công đoạn nào. Một vài ví dụ về các bài kiểm tra sự sống mà chúng tôi sử dụng tại Amazon gồm có:
• Bài kiểm tra để xác nhận rằng máy chủ đang lắng nghe trên cổng mong muốn và chấp nhận các kết nối TCP mới.
• Bài kiểm tra xử lý các yêu cầu HTTP cơ bản và đảm bảo rằng máy chủ phản hồi 200 mã trạng thái.
• Bài kiểm tra trạng thái dành cho Amazon EC2 để kiểm tra những yếu tố cơ bản cần cho quá trình vận hành của mọi hệ thống, chẳng hạn như khả năng tiếp cận mạng.
Kiểm tra tình trạng cục bộ sẽ chuyên sâu hơn kiểm tra sự sống, có tác dụng xác nhận rằng ứng dụng có khả năng hoạt động hay không. Những bài kiểm tra tình trạng này sẽ kiểm thử các tài nguyên không được chia sẻ với các máy chủ ngang hàng. Do đó, loại kiểm tra này ít có khả năng thất bại đồng thời trên nhiều máy chủ trong nhóm. Loại kiểm tra này kiểm thử những lỗi sau:
• Mất khả năng ghi hoặc đọc đĩa—Nhiều người cho rằng dịch vụ vô trạng thì không cần phải có đĩa ghi được. Tuy nhiên, tại Amazon, dịch vụ của chúng tôi thường sử dụng đĩa để theo dõi, ghi nhật ký và xuất bản dữ liệu đo lường không đồng bộ.
• Lỗi hoặc hỏng các quá trình quan trọng—Một số dịch vụ tiếp nhận các yêu cầu thông qua một proxy trên máy chủ (tương tự như NGINX) và đưa ra logic nghiệp vụ trong một tiến trình máy chủ khác. Bài kiểm tra sự sống có thể chỉ kiểm thử liệu tiến trình proxy có đang chạy hay không. Quá trình kiểm tra tình trạng cục bộ có thể chuyền từ proxy sang ứng dụng để xác nhận rằng cả 2 đang chạy và đáp lại các yêu cầu một cách chính xác. Thú vị thay, ở ví dụ về trang web nêu ở đầu bài viết, phương pháp kiểm tra tình trạng có sẵn đủ chuyên sâu để đảm bảo rằng quá trình kết xuất đang chạy và phản hồi, nhưng lại không đủ chuyên sâu để đảm bảo rằng quá trình đó phản hồi một cách chính xác.
• Thiếu các tiến trình hỗ trợ—Máy chủ thiếu trình nền theo dõi có thể khiến các toán tử trở nên mơ hồ và phải tự "lần mò" về tình trạng của dịch vụ. Các tiến trình hỗ trợ khác đẩy bản ghi mức sử dụng đo lường và thanh toán hoặc nhận các bản cập nhật định danh. Các máy chủ có tiến trình hỗ trợ bị lỗi sẽ gây rủi ro cho khả năng hoạt động theo cách đột ngột, khó phát hiện.
Kiểm tra tình trạng phụ thuộc là quá trình đánh giá toàn diện khả năng tương tác của một ứng dụng với các hệ thống liền kề với ứng dụng đó. Theo lý tưởng, những bài kiểm tra này sẽ phát hiện được các vấn đề gần với máy chủ (chẳng hạn như thông tin đăng nhập hết hạn), cản trở máy chủ tương tác với một tác nhân phụ thuộc. Tuy nhiên, những bài kiểm tra này cũng có thể báo lỗi giả khi chính tác nhân phụ thuộc xảy ra vấn đề. Vì những lỗi giả này, chúng ta phải cẩn trọng khi xử lý các lỗi kiểm tra tình trạng phụ thuộc. Kiểm tra tình trạng phụ thuộc có thể kiểm thử những yếu tố sau:
• Cấu hình kém hoặc siêu dữ liệu cũ—Nếu một tiến trình tìm kiếm một cách không đồng bộ các bản cập nhật cho siêu dữ liệu hoặc cấu hình nhưng cơ chế cập nhật trên máy chủ lại bị hỏng, thì máy chủ đó có thể trở nên không đồng bộ một cách rõ rệt với các máy chủ ngang hàng với nó và hoạt động sai theo cách khó dự đoán và kiểm tra. Tuy nhiên, khi một máy chủ không thấy bản cập nhật trong một thời gian dài, nó sẽ không biết là cơ chế cập nhật bị hỏng hay là hệ thống cập nhật trung tâm ngừng xuất bản các bản cập nhật đối với mọi máy chủ.
• Mất khả năng giao tiếp với máy chủ ngang hàng hoặc tác nhân phụ thuộc—Chúng ta đã biết rằng hành vi lạ của mạng sẽ ảnh hưởng đến khả năng giao tiếp với tác nhân độc lập của một tập con các máy chủ trong nhóm, nhưng lại không ảnh hưởng đến khả năng chuyển lưu lượng truy cập đến máy chủ đó. Các sự cố phần mềm, chẳng hạn như khóa chết hoặc lỗi trong bể kết nối, cũng có thể cản trở hoạt động giao tiếp mạng.
• Lỗi phần mềm bất thường khác cần giới hạn tiến trình — Khóa chết, rò rỉ bộ nhớ hoặc lỗi hỏng trạng thái có thể là những nguyên nhân khiến máy chủ báo lỗi.
Phương pháp phát hiện sự bất thường sẽ kiểm tra mọi máy chủ trong một nhóm để xác định xem có máy chủ nào có hành vi khác lạ so với các máy chủ ngang hàng khác không. Bằng cách tổng hợp dữ liệu theo dõi trên mỗi máy chủ, chúng ta có thể liên tục so sánh tỷ lệ lỗi, dữ liệu về độ trễ, hoặc các thuộc tính khác để tìm các máy chủ bất thường và tự động loại bỏ những máy chủ đó khỏi dịch vụ. Phương pháp phát hiện sự bất thường có thể tìm ra sự sai lệch trong nhóm, điều mà máy chủ không thể tự phát hiện, chẳng hạn như:
• Đồng hồ lệch—Đặc biệt là khi máy chủ chịu tải cao, đồng hồ trên máy chủ đó có thể bị lệch đột ngột và đáng kể. Các biện pháp bảo mật (chẳng hạn như biện pháp dùng để đánh giá các yêu cầu có chữ ký đến AWS) đòi hỏi rằng thời gian trên đồng hồ của máy khách không lệch quá 5 phút so với thời gian thực. Nếu không, yêu cầu đến Dịch vụ AWS sẽ thất bại.
• Mã cũ—Nếu máy chủ bị ngắt kết nối khỏi mạng hoặc tắt nguồn trong thời gian dài, sau đó hoạt động trở lại, thì mã chạy trên máy chủ đó có thể trở nên quá cũ và không tương thích với các máy chủ còn lại trong nhóm. Điều này rất nguy hiểm.
• Kiểu lỗi khó lường bất kỳ—Đôi khi, máy chủ gặp lỗi theo cách mà chúng trả về các lỗi nhưng cho rằng lỗi đó là của máy khách chứ không phải của chúng (HTTP 400 thay vì 500). Máy chủ có thể chậm đi chứ không bị lỗi, hoặc có thể phản hồi nhanh hơn so với các máy chủ ngang hàng. Đây là dấu hiệu cho thấy chúng trả về các phản hồi sai cho người gọi. Phát hiện sự bất thường là một phương pháp tuyệt vời để tìm ra hết mọi kiểu lỗi khó lường.
Để phương pháp phát hiện sự bất thường đạt hiệu quả thực tiễn, cần đảm bảo một số điều sau:
• Các máy chủ nên có chức năng gần giống nhau—Trong trường hợp chúng ta định tuyến rõ ràng các loại lưu lượng khác nhau đến các loại máy chủ khác nhau, thì các máy chủ có thể không hoạt động đủ giống nhau để phát hiện được những điểm bất thường. Tuy nhiên, khi chúng ta sử dụng cân bằng tải để chuyển lưu lượng truy cập đến máy chủ, thì các máy chủ có khả năng cao sẽ phản hồi theo những cách giống nhau.
• Các nhóm máy chủ phải tương đối đồng nhất—Trong các nhóm gồm nhiều loại phiên bản khác nhau, một số phiên bản có thể chậm hơn so với phần còn lại. Điều này có thể khiến kích hoạt sai tính năng phát hiện máy chủ xấu bị động. Để xử lý tình huống này, chúng tôi đối chiếu số liệu theo loại phiên bản.
• Lỗi hoặc sự khác biệt khi hoạt động phải được báo cáo—Chúng ta kỳ vọng rằng máy chủ sẽ tự báo cáo khi có lỗi, vậy nếu hệ thống theo dõi của máy chủ bị hỏng thì sao? May mắn thay, máy khách của dịch vụ là một nơi tuyệt vời để thêm công cụ đo lường. Các bộ cân bằng tải, chẳng hạn như Cân bằng tải ứng dụng, sẽ xuất bản nhật ký truy cập, cho biết máy chủ backend nào được liên lạc trên mỗi yêu cầu, thời gian phản hồi và yêu cầu đó thành công hay thất bại.
Phản ứng một cách an toàn đối với những trường hợp không đạt bài kiểm tra tình trạng
Khi một máy chủ xác định rằng nó đang ở trong tình trạng xấu, có 2 loại hành động mà máy chủ này có thể thực hiện. Trong trường hợp cực đoan nhất, máy chủ này có thể quyết định một cách cục bộ rằng nó không nên nhận bất kỳ công việc nào và tự loại nó ra khỏi dịch vụ, bằng cách không đạt bài kiểm tra tình trạng của bộ cân bằng tải hoặc bằng cách dừng thăm dò hàng đợi. Máy chủ này cũng có thể phản ứng theo một cách khác, đó là báo cho trung tâm rằng nó gặp vấn đề và để hệ thống trung tâm quyết định cách xử lý. Hệ thống trung tâm có thể giải quyết vấn đề một cách an toàn mà không để cho quy trình tự động hóa làm tê liệt toàn bộ nhóm máy chủ.
Có nhiều cách để triển khai và phản hồi các bài kiểm tra tình trạng. Phần này trình bày một số cách quen thuộc mà chúng tôi sử dụng tại Amazon.
Một số bộ cân bằng tải có thể đóng vai trò như một cơ quan trung tâm thông minh. Khi một máy chủ đơn lẻ không vượt qua được bài kiểm tra tình trạng, bộ cân bằng tải sẽ ngừng chuyển lưu lượng truy cập đến máy chủ đó. Tuy nhiên, khi tất cả máy chủ cùng lúc không vượt qua được bài kiểm tra tình trạng, thì bộ cân bằng tải sẽ mở lỗi, cho phép lưu lượng truy cập đến mọi máy chủ. Chúng ta có thể sử dụng các bộ cân bằng tải để hỗ trợ quá trình triển khai an toàn bài kiểm tra tình trạng phụ thuộc, có thể bao gồm cả bộ cân bằng tải chịu trách nhiệm truy vấn cơ sở dữ liệu của nó và kiểm tra để đảm bảo rằng các quá trình hỗ trợ không quan trọng của nó đang chạy.
Ví dụ: AWS Network Load Balancer mở lỗi nếu không có máy chủ nào báo cáo là đang ở trong tình trạng tốt. Nó cũng rời bỏ Vùng sẵn sàng có tình trạng xấu nếu mọi máy chủ trong một Vùng an toàn đều báo cáo là ở trong tình trạng xấu. (Để biết hêm thông tin về cách sử dụng Network Load Balancers cho các bài kiểm tra tình trạng, hãy xem tài liệu về Elastic Load Balancing.) Dịch vụ Cân bằng tải ứng dụng của chúng tôi cũng hỗ trợ mở lỗi, giống như Amazon Route 53. (Để biết thêm thông tin về cách đặt cấu hình các bài kiểm tra tình trạng bằng Route 53, hãy xem tài liệu về Route 53.)
Khi dựa vào hành vi mở lỗi, chúng ta phải đảm bảo kiểm thử các kiểu lỗi của bài kiểm tra tình trạng phụ thuộc. Ví dụ: Xét trường hợp một dịch vụ có các máy chủ kết nối với kho dữ liệu chung. Nếu kho dữ liệu đó trở nên chậm hoặc phản hồi với tỷ lệ lỗi thấp, thì các máy chủ thỉnh thoảng có thể không đạt bài kiểm tra tình trạng phụ thuộc. Tình trạng này khiến máy chủ chập chờn trong và ngoài dịch vụ, nhưng lại không kích hoạt ngưỡng mở lỗi. Tìm hiểu lý do và kiểm thử những tác nhân phụ thuộc gặp lỗi một phần với các bài kiểm tra này là một việc quan trọng cần làm để tránh trường hợp lỗi có thể khiến cho các bài kiểm tra tình trạng chuyên sâu làm trầm trọng thêm vấn đề.
Dù mở lỗi là một hành vi hữu ích, nhưng tại Amazon, chúng tôi thường cẩn trọng với những thứ mà mình không hiểu tường tận hoặc không kiểm thử được mọi tình huống. Chúng tôi vẫn chưa thể đưa ra bằng chứng rằng mở lỗi sẽ kích hoạt như kỳ vọng đối với mọi loại quá tải, lỗi một phần hoặc lỗi xám trong hệ thống hoặc trong các tác nhân phụ thuộc của hệ thống. Vì hạn chế này, các đội ngũ tại Amazon có xu hướng giới hạn kiểm tra tình trạng của cân bằng tải phản ứng nhanh thành kiểm tra tình trạng cục bộ, và trông đợi các hệ thống tại trung tâm phản ứng một cách cẩn trọng đối với các bài kiểm tra tình trạng phụ thuộc chuyên sâu hơn. Điều này không nói lên được rằng chúng tôi không sử dụng hành vi mở lỗi, hay chứng minh rằng hành vi này có hiệu quả trong những trường hợp cụ thể. Nhưng khi logic có thể hành động nhanh chóng trên nhiều máy chủ, thì chúng tôi cực kỳ dè chừng logic đó.
Cho phép máy chủ tự phản ứng lại vấn đề của chính nó dường như là cách nhanh và đơn giản nhất để khôi phục. Tuy nhiên, đó cũng là cách tiềm ẩn nhiều nguy cơ nhất nếu máy chủ đánh giá sai tình trạng của nó, hoặc không nhìn được tổng thể những gì đang xảy ra trên nhóm máy chủ. Khi mọi máy chủ trên nhóm cùng đồng thời đưa ra quyết định sai, nó có thể gây ra thất bại nối tiếp trên những dịch vụ liền kề nhau. Nguy cơ này cho chúng ta thấy sự đánh đổi. Nếu có lỗ hổng giữa hoạt động kiểm tra tình trạng và theo dõi, thì máy chủ có thể giảm khả năng cung cấp của dịch vụ cho đến khi sự cố được phát hiện. Tuy nhiên, trường hợp này sẽ tránh được việc cắt đứt hoàn toàn dịch vụ do hành vi không mong muốn sau khi kiểm tra tình trạng trên toàn bộ nhóm máy chủ.
Sau đây là những cách làm tốt nhất mà chúng tôi sử dụng để triển khai các bài kiểm tra tình trạng khi không có bộ ngắt mạch tích hợp sẵn:
• Đặt cấu hình trình sản xuất công việc (bộ cân bằng tải, luồng thăm dò hàng đợi) để thực hiện các bài kiểm tra tình trạng cục bộ và sự sống. Các máy chủ sẽ tự động được bộ cân bằng tải đưa ra khỏi dịch vụ chỉ khi chúng gặp vấn đề mang tính cục bộ hoàn toàn, chẳng hạn như đĩa kém.
• Đặt cấu hình các hệ thống theo dõi bên ngoài khác để kiểm tra tình trạng phụ thuộc và phát hiện sự bất thường. Những hệ thống này có thể tìm cách tự động chấm dứt phiên bản hoặc đưa ra báo động hoặc nhờ sự can thiệp của một toán tử.
Khi xây dựng hệ thống để tự động phản ứng lại những trường hợp không đạt bài kiểm tra tình trạng phụ thuộc, chúng tôi phải tích hợp ngưỡng ở mức hợp lý để ngăn không cho hệ thống tự động thực hiện hành động quyết liệt không mong muốn. Khi thay thế máy chủ, các đội ngũ tại Amazon vận hành máy chủ hữu trạng như Amazon DynamoDB, Amazon S3, và Amazon Relational Database Service (Amazon RDS) phải tuân theo những yêu cầu quan trọng về độ bền. Họ cũng xây dựng tỉ mỉ các vòng lặp phản hồi kiểm soát và giới hạn tỷ lệ, để dừng quy trình tự động và nhờ sự can thiệp của con người khi xảy ra trường hợp vượt ngưỡng. Khi xây dựng quy trình tự động hóa như vậy, chúng tôi phải đảm bảo rằng mình biết được khi nào máy chủ thất bại trong bài kiểm tra tình trạng phụ thuộc. Đối với một vài số liệu, chúng tôi trông cậy máy chủ tự báo cáo tình trạng của chúng với hệ thống theo dõi trung tâm. Để đề phòng trường hợp máy chủ hỏng nên không báo cáo được tình trạng của nó, chúng tôi cũng chủ động tìm đến máy chủ để kiểm tra tình trạng của chúng.
Máy chủ cần ưu tiên kiểm tra tình trạng hơn là công việc thường ngày, nhất là trong trường hợp quá tải. Trong trường hợp đó, việc không đạt hoặc phản hồi chậm bài kiểm tra tình trạng có thể khiến tình trạng sụt giảm trở nên tồi tệ hơn nữa.
Khi máy chủ không vượt qua được bài kiểm tra tình trạng của bộ cân bằng tải, nó sẽ yêu cầu bộ cân bằng tải đó loại nó khỏi dịch vụ ngay lập tức trong khoảng thời gian tương đối dài. Khi một máy chủ thất bại, đó không phải là vấn đề, nhưng trong trường hợp tăng vọt lưu lượng truy cập đến dịch vụ, hẳn chúng ta sẽ không muốn thu hẹp quy mô dịch vụ. Việc loại máy chủ ra khỏi dịch vụ trong khi quá tải có thể khiến mọi thứ đi theo chiều hướng xấu. Việc ép các máy chủ còn lại tiếp nhận thêm lưu lượng truy cập sẽ khiến chúng dễ bị quá tải, cũng như không vượt qua được bài kiểm tra tình trạng và làm thu hẹp hơn nữa nhóm máy chủ.
Vấn đề không phải là các máy chủ trả về lỗi khi chúng bị quá tải. Vấn đề nằm ở chỗ các máy chủ không kịp phản hồi yêu cầu ping từ bộ cân bằng tải. Sau cùng, những bài kiểm tra tình trạng của bộ cân băng tải được đặt cấu hình với các khoảng thời gian chờ, cũng giống như bất kỳ lệnh gọi dịch vụ từ xa nào khác. Các máy chủ bị sụt giảm sẽ phản ứng chậm do nhiều nguyên nhân, trong đó có tranh chấp CPU, chu kỳ gom rác dài, hoặc chỉ đơn giản là hết luồng thợ. Các dịch vụ phải được đặt cấu hình để tạm thời gạt các tài nguyên sang một bên, qua đó phản hồi kịp thời các bài kiểm tra tình trạng, thay vì ôm đồm thêm quá nhiều yêu cầu.
Thật may là chúng tôi thực hiện theo những cách đặt cấu hình tốt nhất để ngăn chặn chiều hướng xấu này. Những công cụ như iptables và cả một số bộ cân bằng tải, sẽ hỗ trợ ý tưởng “kết nối tối đa”. Trong trường hợp này, hệ điều hành (hoặc bộ cân bằng tải) giới hạn số lượng kết nối đến máy chủ để máy chủ không bị ngập trong đống yêu cầu đồng thời có thể khiến nó chậm đi.
Khi một dịch vụ được đi trước bởi proxy hoặc bộ cân bằng tải có hỗ trợ kết nối tối đa, thì việc khớp số luồng thợ trên máy chủ HTTP với số kết nối tối đa trong proxy có vẻ hợp logic. Tuy nhiên, cấu hình này sẽ khiến dịch vụ đi theo chiều hướng xấu trong thời gian sụt giảm. Những bài kiểm tra tình trạng proxy cũng cần các đường kết nối, vậy nên điều quan trọng cần lưu ý là làm cho biể thợ của máy chủ đủ rộng để chứa các yêu cầu kiểm tra tình trạng bổ sung. Thợ nhàn rỗi thì khá rẻ, nên chúng ta có xu hướng đặt cấu hình thêm: bất cứ đâu từ rất nhiều thợ sẵn có để gấp đôi số kết nối tối đa của proxy đa đặt cấu hình.
Một chiến lược khác để ưu tiên kiểm tra tình trạng, đó là để máy chủ tự triển khai việc thực thi các yêu cầu đồng thời tối đa của chúng. Trong trường hợp này, những bài kiểm tra tình trạng của bộ cân bằng tải sẽ luôn được phép, nhưng các yêu cầu thông thường sẽ bị từ chối nếu máy chủ đang xử lý cùng một ngưỡng. Các phương pháp triển khai xung quanh Amazon rất đa dạng, từ đơn giản như semaphore trong Java cho đến phức tạp hơn như phân tích xu hướng sử dụng CPU.
Một cách khác để đảm bảo rằng các dịch vụ phản hồi kịp thời yêu cầu ping kiểm tra tình trạng, đó là thực hiện logic kiểm tra tình trạng phụ thuộc trong luồng nền và cập nhật cờ isHealthy mà logic ping kiểm tra. Trong trường hợp này, máy chủ phản hồi nhanh chóng các bài kiểm tra tình trạng, và quá trình kiểm tra tình trạng phụ thuộc tạo ra mức tải dự đoán được trên hệ thống bên ngoài mà quá trình này tương tác. Khi thực hiện việc này, các đội ngũ cẩn trọng hơn trong việc phát hiện thất bại của luồng kiểm tra tình trạng. Nếu luồng nền đó thoát, máy chủ không phát hiện được nếu có thất bại máy chủ trong tương lai (hoặc khôi phục!).
Cân bằng kiểm tra tình trạng phụ thuộc và phạm vi ảnh hưởng
Kiểm tra tình trạng phụ thuộc là một phương pháp hữu hiệu vì kiểm thử được toàn diện tình trạng của máy chủ. Tuy nhiên, phương pháp này đôi khi lại rất nguy hiểm, vì mối quan hệ phụ thuộc có thể gây ra thất bại nối tiếp trên toàn hệ thống.
Chúng ta có thể phác họa một số thông tin chi tiết về việc xử lý các tác nhân phụ thuộc kiểm tra tình trạng bằng cách xem xét cấu trúc thiên về dịch vụ tại Amazon. Mỗi dịch vụ tại Amazon được thiết kế để làm những việc nhất định, không có dịch vụ đơn khối nào làm hết được mọi việc. Chúng tôi quyết định xây dựng dịch vụ theo cách này vì nhiều lý do, trong đó có đẩy mạnh sáng tạo trong đội ngũ ít người, cũng như giảm thiểu phạm vi ảnh hưởng nếu xảy ra sự cố với một dịch vụ. Cách thiết kế theo cấu trúc này cũng có thể áp dụng với kiểm tra tình trạng.
Khi dịch vụ này gọi dịch vụ kia nghĩa là nó tiếp nhận quan hệ phụ thuộc trên dịch vụ đó. Nếu một dịch vụ chỉ thỉnh thoảng gọi tác nhân phụ thuộc, chúng ta có thể xem đó là “tác nhân phụ thuộc mềm”, bởi vì dịch vụ vẫn có thể thực hiện một số loại công việc ngay cả khi không giao tiếp được với tác nhân phụ thuộc. Khi không có biện pháp bảo vệ mở lỗi, việc triển khai kiểm tra tình trạng để kiểm thử tác nhân phụ thuộc sẽ biến tác nhân đó thành “tác nhân phụ thuộc cứng”. Khi tác nhân phụ thuộc ngừng hoạt động, dịch vụ cũng ngừng theo, gây ra thất bại nối tiếp với phạm vi ảnh hưởng lớn hơn.
Mặc dù chúng ta tách chức năng thành các dịch vụ khác nhau, mỗi dịch vụ lại thường phân phối nhiều API. Đôi khi, API trên dịch vụ có các tác nhân phụ thuộc riêng. Nếu một API bị ảnh hưởng, chúng tôi muốn dịch vụ tiếp tục phân phối các API khác. Ví dụ: một dịch vụ có thể vừa là control plane (chẳng hạn như API CRUD gọi không thường xuyên trên tài nguyên dài hạn) và vừa là data plane (API nghiệp vụ tối quan trọng thông lượng cao). Chúng ta sẽ muốn API data plane tiếp tục vận hành kể cả khi các API control plane gặp vấn đề giao tiếp với các tác nhân độc lập.
Tương tự, API đơn có thể có hành vi khác biệt tùy theo đầu vào và trạng thái của dữ liệu. Một khuôn mẫu thường thấy là Read API truy vấn cơ sở dữ liệu nhưng lưu bộ nhớ tạm các phản hồi một các cục bộ trong một khoảng thời gian. Nếu cơ sở dữ liệu ngừng hoạt động, dịch vụ vẫn có thể phân phối các bản đọc đã lưu vào bộ nhớ tạm cho đến khi cơ sở dữ liệu trở lại hoạt động. Việc không đạt bài kiểm tra tình trạng chỉ khi một đường lập trình có tình trạng xấu sẽ làm tăng phạm vi ảnh hưởng của vấn đề giao tiếp với tác nhân phụ thuộc.
Việc chọn tác nhân phụ thuộc nào để kiểm tra tình trạng làm nảy sinh một câu hỏi khá thú vị về sự đánh đổi giữa vi dịch vụ và các dịch vụ đơn khối tương đương. Rất hiếm khi có quy luật rõ ràng để xác định xem nên phân chia dịch vụ thành bao nhiêu đơn vị hoặc điểm cuối, nhưng những câu hỏi như “nên kiểm tra tình trạng của các tác nhân độc lập nào” và “liệu một thất bại có làm tăng phạm vi ảnh hưởng không” là góc nhìn thú vị mà chúng ta có thể sử dụng để xác định mức độ vi mô hoặc vĩ mô khi thực hiện dịch vụ.
Những sai lầm trong thực tế khi kiểm tra tình trạng
Tất cả những điều đã trình bày có vẻ hợp lý về mặt lý thuyết, nhưng điều gì sẽ xảy ra với hệ thống trong thực tiễn nếu chúng không được kiểm tra tình trạng một cách phù hợp? Chúng tôi đã tìm ra những điểm chung trong câu chuyện của khách hàng AWS cũng như từ chính nội bộ Amazon để minh họa bức tranh toàn cảnh. Chúng tôi cũng xem xét các yếu tố bù trừ – đại loại là theo kiểu “cẩn tắc vô ưu” mà các đội ngũ áp dụng để ngăn điểm yếu trong bài kiểm tra tình trạng gây ra sự cố diện rộng.
Một trong những vấn đề thường gặp khi kiểm tra tình trạng liên quan đến hoạt động triển khai. Các hệ thống triển khai, chẳng hạn như AWS CodeDeploy, cùng lúc đẩy mã mới sang một tập con của nhóm máy chủ, đợi một đợt triển khai hoàn tất trước khi chuyển sang đợt mới. Quá trình này trông cậy các máy chủ báo cáo ngược lại cho hệ thống triển khai sau khi các máy chủ đó sẵn sàng đưa mã mới vào sử dụng. Nếu chúng không báo cáo ngược lại, hệ thống triển khai sẽ nhận thấy mã mới có gì đó không ổn và hoàn tác quá trình triển khai.
Tập lệnh triển khai khởi động dịch vụ cơ bản nhất sẽ làm phân nhánh quá trình máy chủ và ngay lập tức phản hồi với hệ thống triển khai là “triển khai xong”. Tuy nhiên, điều này rất nguy hiểm vì mã mới có thể có nhiều vấn đề, chẳng hạn như: mã mới có thể hỏng ngay sau khi chạy, bị treo hoặc không thể bắt đầu nghe trên socket máy chủ, không tải được cấu hình cần thiết để xử lý thành công các yêu cầu, hoặc gặp phải lỗi. Khi một hệ thống triển khai không được đặt cấu hình để kiểm tra tình trạng phụ thuộc, hệ thống này sẽ không nhận ra rằng nó đang đẩy đi một quá trình triển khai xấu. Nó sẽ bắt đầu làm hỏng từ máy chủ này đến máy chủ khác.
Thật may là trong thực tế, các đội ngũ tại Amazon đã triển khai nhiều hệ thống di chuyển nhằm ngăn chặn không cho tình huống này làm tê liệt toàn bộ nhóm máy chủ. Một phương pháp di chuyển như vậy, đó là đặt cấu hình báo động, kích hoạt mỗi khi tổng quy mô nhóm quả nhỏ hoặc chạy ở mức tải cao, hoặc khi có độ trễ hoặc tỷ lệ lỗi cao. Nếu bất kỳ báo động nào trong số này kích hoạt, hệ thống triển khai sẽ dừng quá trình triển khai và hoàn tác quá trình.
Một phương pháp di chuyển khác là sử dụng quá trình triển khai theo giai đoạn. Thay vì triển khai cả nhóm trong một lần, dịch vụ có thể được cấu hình để triển khai từng tập con, có thể là một Vùng sẵn sàng, trước khi tạm dừng và chạy các thử nghiệm tích hợp trọn bộ cho vùng đó. Phép căn chỉnh triển khai trên mỗi Vùng sẵn sàng này rất tiện lợi vì các dịch vụ được thiết kế để có thể tiếp tục vận hành ngay cả khi có vấn đề xảy ra với một Vùng dịch vụ.
Và tất nhiên trước khi triển khai thành sản phẩm, đội ngũ Amazon đã đưa những thay đổi đó vào môi trường thử nghiệm và chạy các thử nghiệm tích hợp tự động mà sẽ phát hiện loại thất bại này. Tuy nhiên, những khác biệt nhỏ và khó tránh giữa môi trường thử nghiệm và sản xuất có thể tồn tại, vậy nên điều quan trọng là phải kết hợp nhiều lớp triển khai an toàn để phát hiện mọi loại vấn đề trước khi chúng ảnh hưởng đến sản xuất. Dù kiểm tra tình trạng là biện pháp quan trọng để bảo vệ các dịch vụ khỏi những quá trình triển khai kém, nhưng chúng tôi đảm bảo rằng sẽ không dừng lại ở đó. Chúng tôi luôn tiếp cận theo lối “cẩn tắc vô ưu”, coi đó là chốt chặn để bảo vệ các nhóm máy chủ khỏi mọi sai lầm.
Một khuôn mẫu thất bại khác xảy ra xoay quanh việc xử lý không đồng bộ tin nhắn, chẳng hạn như khi dịch vụ nhận công việc bằng cách thăm dò hàng đợi SQS Queue hoặc Amazon Kinesis Stream. Không giống như trong các hệ thống tiếp nhận yêu cầu từ bộ cân bằng tải, không có quy trình nào tự động thực hiện kiểm tra tình trạng để loại bỏ các máy chủ khỏi dịch vụ.
Khi các dịch vụ không được kiểm tra tình trạng một cách đủ chuyên sâu, các máy chủ thợ hàng đợi riêng rẽ có thể gặp lỗi chẳng hạn như đĩa đầy hoặc hết số đặc tả tệp. Sự cố này sẽ không ngăn máy chủ nhận công việc từ hàng đợi, nhưng sẽ ngăn máy chủ xử lý thành công tin nhắn. Sự cố này làm trì hoãn quá trình xử lý tin nhắn, khi mà máy chủ xấu nhanh chóng nhận công việc từ hàng đợi nhưng lại không xử lý được công việc đó.
Trong những tình huống dạng như thế này, có một số yếu tố bù trừ để giúp hạn chế mức độ ảnh hưởng. Ví dụ: Nếu một máy chủ không xử lý được tin nhắn lấy từ SQS, thì SQS sẽ gửi lại tin nhắn cho một máy chủ khác sau khi hết thời gian chờ hiển thị tin nhắn đã đặt cấu hình. Độ trễ end-to-end tăng lên, nhưng tin nhắn không bị bỏ lại. Một yếu tố bù trừ khác là báo động kích hoạt khi có quá nhiều lỗi xử lý tin nhắn, báo cho một toán tử để kiểm tra.
Một lớp lỗi khác là khi đĩa trên máy chủ đầy, khiến cho quá trình xử lý và ghi nhật ký thất bại. Lỗi này tạo ra một lỗ hổng khi giám sát khả năng hiển thị, do máy chủ có thể không báo cáo được lỗi của nó cho hệ thống theo dõi.
Một lần nữa, các biện pháp kiểm soát di chuyển sẽ ngăn không dịch vụ “lần mò” mà thay vào đó, nhanh chóng giảm thiểu mức độ ảnh hưởng. Các hệ thống đi trước bởi một proxy chẳng hạn như Cân bằng tải ứng dụng hoặc API Gateway sẽ có số liệu về độ trễ và tỷ lệ lỗi do proxy đó tạo ra. Trong trường hợp này, báo động sẽ kích hoạt kể cả khi máy chủ không báo cáo. Đối với các hệ thống dựa trên hàng đợi, những dịch vụ như Amazon Simple Queue Service (Amazon SQS) sẽ báo cáo các số liệu cho thấy rằng quá trình xử lý một số tin nhắn đang bị chậm trễ.
Điểm chung của những giải pháp này là có nhiều lớp theo dõi. Máy chủ tự báo cáo lỗi, nhưng hệ thống bên ngoài cũng vậy. Nguyên tắc này rất quan trọng đối với kiểm tra tình trạng. Hệ thống bên ngoài có thể kiểm tra tình trạng của một hệ thống nhất định chính xác hơn so với khi nó tự kiểm tra. Đó là lý do tại sao đối với AWS Auto Scaling, các đội ngũ lại đặt cấu hình cho bộ cân bằng tải thực hiện kiểm tra tình trạng ping bên ngoài.
Các đội ngũ cũng viết hệ thống kiểm tra tình trạng của riêng họ, để thường xuyên hỏi từng máy chủ xem tình trạng của nó có tốt không và báo cáo với AWS Auto Scaling khi tình trạng của máy chủ không tốt. Một phương pháp triển khai thường thấy của hệ thống này liên quan đến chức năng Lambda chạy mỗi phút, kiểm tra tình trạng của mọi máy chủ. Những bài kiểm tra tình trạng này còn có thể lưu trạng thái của chúng giữa mỗi lần chạy trong một số dịch vụ như DynamoDB để không vô ý cùng lúc đánh dấu quá nhiều máy chủ là có tình trạng xấu.
Một vấn đề khác thường gặp là máy chủ xác sống. Máy chủ nhiều khi có thể bị ngắt kết nối khỏi mạng nhưng vẫn chạy, hoặc có thể tắt nguồn trong khoảng thời gian kéo dài và sau đó khởi động trở lại.
Khi máy chủ xác sống sống dậy, chúng có thể trở nên lạc lõng đáng kể so với phần còn lại của nhóm máy chủ, gây nên các vấn đề nghiêm trọng. Ví dụ: Nếu một máy chủ xác sống chạy phiên bản phần mềm quá cũ và không tương thích, nó có thể gây ra lỗi khi cố tương tác với cơ sở dữ liệu bằng giao thức khác biệt, hoặc sử dụng cấu hình sai.
Để xử lý những máy chủ xác sống này, hệ thống thường đáp lại những bài kiểm tra tình trạng bằng phiên bản phần mềm hiện đang chạy. Sau đó, một tác nhân theo dõi tại trung tâm sẽ so sánh các phản hồi trên nhóm máy chủ để tìm ra những máy chủ đang chạy phiên bản lỗi thời đáng ngờ và ngăn không cho những máy chủ đó trở lại dịch vụ.
Kết luận
Máy chủ và phần mềm chạy trên máy chủ đó thất bại vì nhiều lý do khác thường. Phần cứng cuối cùng rồi cũng sẽ hỏng. Là nhà phát triển phần mềm, sẽ có lúc chúng ta viết ra một vài lỗi như của tôi ở trên, khiến phần mềm rơi vào trạng thái hỏng. Các bài kiểm tra nhiều lớp, từ nhẹ nhàng như kiểm tra sự sống cho đến nặng đô hơn là theo dõi bị động các số liệu trên mỗi máy chủ, là rất cần thiết để tóm gọn mọi kiểu lỗi khó lường.
Khi những lỗi này xảy ra, điều quan trọng cần làm là phải nhanh chóng phát hiện và đưa các máy chủ bị ảnh hưởng ra khỏi dịch vụ. Tuy nhiên, đối với quy trình tự động bất kỳ của nhóm máy chủ, chúng ta thêm giới hạn tỷ lệ, ngưỡng và bộ ngắt mạch để tắt quy trình tự động và nhờ sự can thiệp của con người trong những tình huống không chắc chắn hoặc nghiêm trọng. Mở lỗi và xây dựng tác nhân hành động tập trung là các chiến lược để tận dụng lợi ích của hoạt động kiểm tra tình trạng chuyên sâu với sự an toàn của quy trình tự động giới hạn tỷ lệ.
Phòng thực hành tại chỗ
Thử một vài nguyên tắc mà bạn đã học ở đây với phòng thực hành tại chỗ.
Về tác giả
David Yanacek là Kỹ sư chính cấp cao làm việc với AWS Lambda. David là nhà phát triển phần mềm tại Amazon từ năm 2006, trước đây đã làm việc với Amazon DynamoDB và AWS IoT, cũng như các khung dịch vụ web nội bộ và các hệ thống tự động hóa nghiệp vụ nhóm. Một trong những hoạt động yêu thích của David tại nơi làm việc là thực hiện phân tích nhật ký và sàng lọc các số liệu hoạt động để tìm cách làm cho hệ thống vận hành ngày càng trơn tru hơn.