SRE RunbookMÃ: 012-LINUX-SYSTEMD-SERVICE⏱️ Đọc ~7 phút

Systemd và journalctl: runbook xử lý service khởi động thất bại

Service Linux liên tục chết sau khi restart? Runbook này giúp giữ bằng chứng, đọc journal theo đúng mốc thời gian, kiểm tra unit và drop-in, rồi mới thay đổi cấu hình. Phạm vi là...

Biên soạn: Vibe DevOps · Đăng ngày

Bản đầy đủ · Poster fanpage

Khổ: 1080×1350 (4:5)Phóng to hoặc tải để tra cứu

Service Linux liên tục chết sau khi restart? Runbook này giúp giữ bằng chứng, đọc journal theo đúng mốc thời gian, kiểm tra unit và drop-in, rồi mới thay đổi cấu hình. Phạm vi là máy Linux dùng systemd ở cấp hệ thống; các lệnh là mẫu tham khảo, chưa được chạy thử trên service production của bạn.

1. Khoanh vùng và giữ bằng chứng

Đầu tiên xác định đúng máy, đúng service và thời điểm bắt đầu sự cố. Đừng restart ngay: thao tác này có thể làm mất thông tin trạng thái quan trọng và tăng ảnh hưởng tới người dùng. Thay nginx.service bằng unit thực tế, rồi giữ các lệnh trong cùng phiên shell để biến u có giá trị.

u=nginx.service
systemctl status "$u" --no-pager -l
systemctl is-enabled "$u"
systemctl show "$u" -p ActiveState -p SubState -p Result -p ExecMainStatus

status tóm tắt trạng thái cùng vài dòng log gần nhất; -l tránh rút gọn dòng. is-enabled chỉ mô tả cách unit được kích hoạt, không chứng minh tiến trình đang chạy. Unit static cũng không nhất thiết bị cấu hình sai: nó có thể được unit khác kéo lên. active không bảo đảm endpoint ứng dụng khỏe hoặc truy cập database thành công.

Ghi lại thời gian, phiên bản triển khai, thay đổi gần nhất và phạm vi ảnh hưởng. Nếu xuất log để chia sẻ, loại thông tin cá nhân, URL có credential và secret trước khi gửi. Không đăng nguyên environment hoặc nội dung unit chứa khóa truy cập lên kênh công khai.

2. Đọc journal theo boot và mốc thời gian

journalctl -u "$u" -b --no-pager
journalctl -u "$u" -n 80 --no-pager
journalctl -u "$u" --since "30 minutes ago" --no-pager
journalctl -u "$u" -f
journalctl --list-boots

-u lọc theo unit, -b giới hạn lần boot hiện tại, -n chọn số dòng cuối và -f theo dõi liên tục. Nhấn Ctrl+C để thoát chế độ theo dõi. Hai lệnh không có -b có thể bao gồm log từ lần boot khác nếu journal còn lưu. Nếu sự cố xảy ra trước reboot, dùng -b -1 sau khi xác nhận có bản ghi trong --list-boots.

Tìm lỗi đầu tiên trước chuỗi restart, không chỉ dòng cuối “Failed to start”. Thiếu quyền đọc journal không có nghĩa service không sinh log; dùng quyền quản trị theo quy định của hệ thống. Journal dạng volatile có thể mất sau reboot, và một số ứng dụng ghi vào file riêng thay vì stdout/stderr.

3. Đối chiếu file unit với cấu hình thực sự được nạp

systemctl cat "$u"
systemctl show "$u" -p FragmentPath -p DropInPaths
systemctl show "$u" -p User -p Group -p WorkingDirectory -p ExecStart

cat hiển thị file gốc và các drop-in trên đĩa. show cho biết thuộc tính mà manager đang nạp; nếu vừa sửa file mà chưa reload, hai nguồn có thể khác nhau. Không sửa trực tiếp file vendor trong /usr/lib/systemd/system hoặc /lib/systemd/system, vì cập nhật gói có thể ghi đè.

Với 203/EXEC, kiểm tra executable tồn tại, đường dẫn tuyệt đối, quyền thực thi, shebang của script, quyền truy cập thư mục cha và mount noexec. Lỗi này không chỉ có một nguyên nhân. Với 217/USER, kiểm tra tài khoản, nhóm, NSS và cấu hình thông tin người dùng. ExecStart không tự diễn giải pipe, chuyển hướng hay && như shell; nếu cần shell phải khai báo tường minh và xem lại rủi ro quoting.

getent passwd www-data
namei -l /usr/sbin/nginx

Các đường dẫn và tài khoản trên chỉ là ví dụ. Không kết luận quyền hợp lệ chỉ vì chạy lệnh bằng root thành công: cần đối chiếu người dùng service, sandbox và chính sách SELinux/AppArmor nếu có.

4. Phân biệt lỗi ứng dụng, cgroup và OOM

systemctl show "$u" -p MemoryCurrent -p MemoryMax
systemctl show "$u" -p CPUQuotaPerSecUSec -p NRestarts
journalctl -k -b --since "30 minutes ago" --no-pager

MemoryMax là giới hạn bộ nhớ cgroup; infinity nghĩa không đặt giới hạn tại thuộc tính này, không có nghĩa máy có RAM vô hạn. Giới hạn ở slice cha hoặc áp lực bộ nhớ toàn máy vẫn có thể ảnh hưởng. CPUQuotaPerSecUSec thể hiện ngân sách thời gian CPU mỗi giây, không phải trực tiếp phần trăm sử dụng CPU tức thời.

NRestarts đếm các lần tự khởi động lại theo cơ chế restart của service, không phải lịch sử mọi thao tác restart thủ công. Đối chiếu log kernel, log service và mốc thời gian trước khi kết luận OOM. Một exit code hoặc tín hiệu SIGKILL riêng lẻ không đủ chứng minh bị OOM killer. Không tăng RAM hay bỏ giới hạn vô điều kiện: cần xem tải, memory leak và giới hạn tài nguyên của cả máy.

5. Sửa bằng drop-in, tránh vòng restart vô tận

Trong cửa sổ thay đổi được phép, tạo override bằng:

sudo systemctl edit "$u"

Ví dụ cho daemon chạy lâu dài, không áp dụng máy móc cho mọi loại unit:

[Unit]
StartLimitIntervalSec=60
StartLimitBurst=5

[Service]
Restart=on-failure
RestartSec=5s

Giới hạn tần suất giúp tránh vòng restart quá nhanh nhưng không chữa nguyên nhân ứng dụng chết. Restart=on-failure cũng không biến chương trình batch thành service chạy mãi. Nếu thêm MemoryMax hoặc CPUQuota, chọn giá trị từ đo đạc và ngân sách host, không sao chép một ngưỡng tùy ý.

sudo systemctl daemon-reload
sudo systemctl restart "$u"

daemon-reload nạp lại định nghĩa unit, không restart ứng dụng. restart có thể gây gián đoạn; chuẩn bị rollback trước khi chạy. Khi thấy start-limit-hit, sửa nguyên nhân rồi mới dùng sudo systemctl reset-failed "$u" và start lại trong quy trình được phép. Không liên tục reset bộ đếm để che lỗi.

6. Xác minh, rollback và checklist bàn giao

systemctl is-active "$u"
journalctl -u "$u" --since "5 minutes ago" --no-pager
systemctl show "$u" -p NRestarts -p Result
systemctl --failed

Kiểm tra health endpoint thật của ứng dụng, một giao dịch mẫu và kết nối phụ thuộc. Theo dõi đủ lâu để thấy service không tiếp tục tăng bộ đếm restart; vài giây active chưa phải bằng chứng ổn định. Đối chiếu độ trễ, lỗi và tài nguyên với trước thay đổi.

Nếu phải rollback, hoàn nguyên đúng file drop-in vừa sửa từ bản sao lưu hoặc cấu hình quản lý, chạy daemon-reload rồi restart theo cửa sổ được phép. Tránh dùng systemctl revert một cách mù quáng vì có thể xóa cả override hợp lệ khác.

  • Đã lưu log và mốc thời gian trước restart.
  • Đã xác nhận đúng unit, tài khoản, đường dẫn và drop-in.
  • Đã phân biệt lỗi ứng dụng với giới hạn cgroup hoặc OOM.
  • Đã đánh giá gián đoạn và chuẩn bị rollback.
  • Đã kiểm tra endpoint, giao dịch mẫu và log sau thay đổi.

Tham khảo chính thức: systemctl, journalctl, systemd.service. Tùy phiên bản systemd và cấu hình journal, một số thuộc tính hoặc dữ liệu lịch sử có thể không có.

💡 Mẹo lưu trữ & sử dụng trong đội ngũ

Bạn có thể tải bản PDF vector về lưu trữ trong kho tài liệu nội bộ của team (Notion, Confluence) hoặc in khổ A4 dán tại bàn làm việc để tra cứu nhanh khi gặp sự cố lúc nửa đêm.