Bỏ qua tới nội dung

Kỹ thuật

Ping URL cho shell job: những dòng lệnh một dòng báo sai trạng thái

Mình chạy mười cách ping heartbeat phổ biến từ shell job trong bash, dash và /bin/sh mà Ubuntu 24.04 cài sẵn. Ba cách báo thành công cho job đã lỗi, một cách khiến job không bao giờ chạy.

Andrew Babo7 phút đọc

Chia sẻ
Trong bài này
Ping URL cho shell job: những dòng lệnh một dòng báo sai trạng thái

Hãy gửi mã thoát của job, đừng ping trơn: job; curl URL/$?. Trong mười dòng lệnh mình thử, job; curl URL báo thành công cho một job đã lỗi, pipe sang tee hay gzip cũng vậy, còn set -o pipefail, cách sửa quen thuộc cho pipe, lại làm dừng cả dòng lệnh trong /bin/sh mà Ubuntu 24.04 cài sẵn.

Lệnh ping còn có thể làm hỏng chính job nó theo dõi. Trong script có set -e, một ping URL trả 503 đã kết thúc script trước khi job kịp chạy. Mình phát hiện đúng lỗi này trong công thức GitHub Actions của chính Belvador và đã sửa ngay trong ngày.

Thí nghiệm#

Ngày 26/10/2026, mình chạy từng kiểu như một dòng lệnh, đúng cách cron chạy một dòng crontab, với một job thoát 0 và cùng job đó thoát 3. Ba shell: bash 5.3.3, dash 0.5.13.1, và dash 0.5.12, đúng file chạy mà Ubuntu 24.04 cài làm /bin/sh. Một collector cục bộ đóng vai endpoint ping và ghi lại mọi request. Mỗi ca chạy 3 lần, lần nào kết quả cũng giống nhau.

Mỗi dòng lệnh đã gửi gì
Chạy ngày 26/10/2026 · bash 5.3.3, dash 0.5.13.1, dash 0.5.12 của Ubuntu 24.04 · mỗi ca 3 lần
Dòng lệnhJob thoát 0Job thoát 3
job; curl URLOKOK
job && curl URLOKkhông có gì cho tới khi quá hạn
job; curl URL/$?/0/3
job | tee -a log; curl URL/$?/0/0
set -o pipefail; job | tee -a log; curl URL/$?/0/3, nhưng sh của Ubuntu: job không bao giờ chạy
dump | gzip > out.gz; curl URL/$? (dump lỗi)/0
if job; then curl URL; else curl URL/$?; fiOK/3
out=$(job); code=$?; curl URL/$code/0/3
timeout 3 hang; curl URL/$?/124
script có set -e và trap 'curl URL/$?' EXIT/0/3

Ba lần báo thành công nhầm#

job; curl URL ping sau job dù chuyện gì đã xảy ra. Lần ping chỉ chứng minh dòng lệnh đã chạy. Dùng để hỏi "bộ hẹn giờ còn chạy không" thì được, còn để hỏi "backup có thành công không" thì sai.

Pipe che mất trạng thái của job. Trong một pipeline, shell trả về mã thoát của lệnh cuối cùng, như tài liệu bash ghi, nên job | tee -a log báo trạng thái của tee, mà tee thì thành công. Dạng pg_dump | gzip > backup.gz là nguy hiểm nhất: bản dump giả của mình ghi một dòng, lỗi với mã 2, gzip vẫn vui vẻ nén phần dữ liệu dở dang, và lệnh ping báo /0. Bạn có một file .gz nhỏ, hợp lệ, và một monitor màu xanh.

Cách sửa không chạy được ở mọi nơi#

Cách sửa quen thuộc là set -o pipefail, khiến pipeline thất bại khi bất kỳ lệnh nào trong đó thất bại. Trong bash và dash 0.5.13.1, nó làm đúng như vậy: /3. Trong /bin/sh của Ubuntu 24.04, nó làm điều tệ hơn:

Text
$ /bin/sh -c 'set -o pipefail; job | tee -a log; curl …/$?'/bin/sh: 1: set: Illegal option -o pipefail$ echo $?2

Shell dừng ngay ở tuỳ chọn nó không biết. Job không chạy, không có lần ping nào, cả khi job thành công lẫn khi job lỗi. Cron chạy các dòng crontab bằng /bin/sh nếu bạn không đặt SHELL= ở đầu crontab, nên cùng một dòng lệnh chạy ngon trong terminal (bash) lại lặng lẽ làm job dừng trên production. Heartbeat rồi sẽ báo "không có tín hiệu", và đó là cách duy nhất để bạn biết.

Các cách thay thế chạy được ở mọi nơi, cả ba shell đều cho /3:

Terminal
# Giữ log mà không cần pipejob >> log 2>&1; curl -fsS -m 10 --retry 5 -o /dev/null "$URL/$?" || true# Hoặc giữ output lại, rồi gửi trạng thái cùng output một lầnout=$(job 2>&1); code=$?printf '%s\n' "$out" | tail -c 60000 | curl -fsS -m 10 --retry 5 -o /dev/null --data-binary @- "$URL/$code" || true

Hoặc đặt SHELL=/bin/bash trong crontab rồi giữ pipefail.

Khi chính lệnh ping lỗi#

Mình cho lệnh ping mà Belvador hướng dẫn, curl -fsS -m 10 --retry 5 -o /dev/null, gọi tới các endpoint trục trặc:

Một lần ping mất bao lâu khi endpoint trục trặc
Chạy ngày 26/10/2026 · curl 8.18.0 · collector cục bộ, không phải endpoint thật
  • 503 kèm Retry-After: 1: sáu lần thử cách nhau một giây, rồi thoát mã 22 sau 5 giây. 429 kèm Retry-After: 2 mất 10 giây. curl tôn trọng header này.
  • 404: một lần thử, mã 22. Không có -f thì mã 0: một ping URL đã bị thu hồi trông vẫn bình thường với script của bạn.
  • Từ chối kết nối: không thử lại lần nào, thoát mã 7 ngay, trừ khi thêm --retry-connrefused (31 giây backoff).
  • Máy chủ nhận kết nối nhưng không bao giờ trả lời: không có -m, curl vẫn đang chờ khi mình dừng nó ở giây thứ 45. Với các cờ trong tài liệu, nó bỏ cuộc sau 91 giây; thêm --retry-max-time 30 thì còn 33 giây.

Những chuyện này lẽ ra không ảnh hưởng tới job. Với một dòng crontab bình thường thì đúng là không: lệnh ping lỗi in ra một dòng báo lỗi, cron gửi mail cho bạn nếu đã cấu hình mail. Nhưng trong script có set -e thì có. Mình chạy set -e; curl …/start; job với endpoint trả 503: script kết thúc bằng mã 22 của curl sau 5 giây và job không bao giờ chạy, ba lần như một. Thêm || true sau lệnh ping thì job chạy.

Tài liệu cú pháp workflow của GitHub cho thấy vì sao chuyện này không chỉ dừng ở script: một step run: không khai báo shell: sẽ chạy bằng bash -e {0}, và một step lỗi khiến các step sau bị bỏ qua. Công thức GitHub Actions của chính Belvador ping /start trong một step riêng mà không có || true, nên nếu Belvador gián đoạn, job chạy đêm của bạn sẽ bị bỏ qua. Giờ mọi công thức shell của Belvador đều kết thúc mỗi lệnh ping bằng || true, và có test sẽ fail nếu công thức nào thiếu.

Nên dùng dòng lệnh nào#

  1. Gửi trạng thái: job; curl -fsS -m 10 --retry 5 -o /dev/null "$URL/$?" || true.
  2. Không dùng pipe trước $?, trừ khi dòng lệnh chạy trong bash có pipefail. Thay vào đó, chuyển output vào file log.
  3. Bọc job có thể treo bằng timeout: /124 là một lỗi mà monitor hiển thị được.
  4. Giữ -f và -m: thiếu -f thì URL chết trông vẫn khoẻ, thiếu -m thì một endpoint im lặng có thể giữ job của bạn mãi.
  5. Kết thúc mọi lệnh ping bằng || true, để monitor không bao giờ là lý do job dừng.
  6. Ping từ dòng lệnh khởi động job, đừng chỉ ping từ bên trong: bài cron job bị treo nói về trường hợp chính shell bị dừng.

Belvador nằm ở đâu#

Monitor ping URL của Belvador nhận trạng thái qua phần đuôi URL (/0 tới /255, /fail, /start), báo ngay khi có lỗi, và báo đúng hạn khi các lần ping dừng lại, kể cả khi node của bạn đang offline. Công thức trên trang monitor chính là những dòng ở trên. Với job nằm trong mã của bạn thay vì crontab, hướng dẫn tác vụ nền chỉ cách dùng SDK, và trang giám sát cron đặt hai cách cạnh nhau.

Đọc tiếp#

Andrew Babo

Nhà sáng lập

Thuộc bài hướng dẫn: Giám sát AI agent chạy trên serverless function

Mọi bài viết

Hướng dẫn

Giám sát AI agent chạy trên serverless function

Một lần chạy agent trên serverless là một chuỗi lần gọi ngắn, mỗi lần có giới hạn thời gian riêng. Bài này chỉ ra cần theo dõi gì ở từng mắt xích, kèm ba thí nghiệm nhỏ cho thấy tín hiệu bị mất ở đâu.

Andrew Babo8 phút đọc

Đọc tiếp trong tài liệu

Xem lỗi production của chính bạn như thế này

Belvador chạy trên máy chủ Hetzner của chính bạn: lỗi, cron, uptime và trace — dữ liệu vẫn là của bạn.