Vòng chờ của chúng tôi đã chờ chính nó suốt 35 giờ: pgrep -f và pkill -f qua SSH
Vận hành
Vào ngày 31 tháng 10, chúng tôi khởi động một vòng lặp nhỏ qua SSH: chờ cho đến khi một công việc dịch thuật trên máy khác hoàn tất, rồi chạy một công việc tiếp theo. Công việc dịch thuật đã hoàn tất vào đêm đó. Khi chúng tôi kiểm tra lại hai ngày sau, vòng lặp đã chờ suốt 35 giờ, và công việc tiếp theo chưa bao giờ được chạy.
Vòng lặp đó là gì
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f so khớp một mẫu với toàn bộ dòng lệnh của mọi tiến trình. SSH chạy lệnh được bọc trong dấu nháy dưới dạng bash -c 'while pgrep -f "i18n_job.py example.com" …', và dòng lệnh đó chứa mẫu cần so khớp. pgrep loại trừ chính tiến trình của nó khỏi kết quả, nhưng không loại trừ shell đã khởi chạy nó. Vì vậy, chừng nào vòng lặp còn sống, nó sẽ tìm thấy một kết quả khớp: chính nó.
Anh em sinh đôi của nó: pkill -f giết chính shell mà nó đang chạy trong đó
Điều tương tự cũng xảy ra với pkill -f, nhưng với hậu quả tệ hơn:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
Dòng lệnh của chính shell chứa mẫu cần so khớp, nên pkill giết shell đó. Worker bị dừng và không bao giờ được khởi động lại, còn SSH thoát với mã 255 mà không có thông báo lỗi. Nó trông giống như một vấn đề về dấu nháy, và đó là điều chúng tôi đã nghĩ lần đầu tiên.
Giữa tháng 9 và cuối tháng 10, chúng tôi đã rơi vào trường hợp này hoặc trường hợp kia ít nhất một tá lần: các lần khởi động lại không bao giờ xảy ra, một vòng lặp giữ một bước kiểm tra suốt ba tiếng rưỡi, một vòng lặp khác giữ hàng đợi dịch thuật suốt 50 phút, và trường hợp này.
Chúng tôi làm gì bây giờ
- Đặt dấu ngoặc vuông trong mẫu:
pgrep -f "[i]18n_job.py example.com". Dưới dạng biểu thức chính quy,[i]khớp với ký tự i, nên công việc vẫn khớp; dòng lệnh của shell chứa các ký tự[i], mà biểu thức không khớp. Cách này chỉ hiệu quả nếu mẫu nguyên bản không xuất hiện ở bất kỳ đâu khác trên dòng đó: một lệnh khởi động lại sau đó trên cùng dòng sẽ lại khớp. - Tìm, dừng và khởi động trong các lệnh gọi SSH riêng biệt. Một lệnh gọi liệt kê các tiến trình bằng
pgrep -af, lệnh tiếp theo giết các PID mà nó đã in ra, lệnh thứ ba khởi động tiến trình mới. - Chờ trên một PID, không phải một mẫu:
tail --pid=1234 -f /dev/null, hoặcwhile kill -0 1234; do sleep 30; done. - Tốt hơn nữa, hãy để systemd quản lý công việc. Chạy nó như một unit và dùng lệnh
systemctl is-active, hoặc khởi động bằngsystemd-run --wait. Không có mẫu cấu hình nào dễ gây nhầm lẫn. - Mỗi lần chờ đều có thời hạn (
timeout 3h) và cho biết khi nào thì từ bỏ. Một vòng lặp không có thời hạn sẽ thất bại âm thầm.
Factory Floor, bộ công cụ vận hành các máy của chúng tôi, hoạt động thông qua các unit systemd: nó không bao giờ tìm kiếm tiến trình dựa trên dòng lệnh. Những vòng lặp khiến chúng tôi gặp sự cố chính là những vòng lặp chúng tôi gõ thủ công.