systemd service ที่ไม่ยอมสตาร์ตแทบไม่เคยลึกลับเลย รัน systemctl status <unit> แล้วอ่าน 50 บรรทัดสุดท้ายของ journal หน่วยนั้นด้วย journalctl -u <unit> -n 50 --no-pager — ระหว่างสองคำสั่งนี้ ปกติจะมีหนึ่งในห้าสาเหตุถูกเรียกชื่อออกมาตรง ๆ: พาธพัง, ไบนารีหาย, สิทธิ์ผิด, การปฏิเสธจาก SELinux/AppArmor หรือไวยากรณ์ไฟล์ unit ผิด เหตุผลการล้มอยู่ใน log วิธีแก้ด้านล่างเป็นแค่การจับคู่รูปแบบกับมัน
ดูได้อย่างไรว่า systemd service ล้มเพราะอะไร?
สถานะก่อน แล้วค่อย journal:
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager status ให้สถานะ (inactive (dead), failed (exit-code), activating (auto-restart)) กับบรรทัด log สุดท้ายไม่กี่บรรทัด journal ให้เรื่องราวเต็ม: stdout, stderr และข้อร้องเรียนของ systemd เองที่มีต่อ unit
ถ้า unit เคยล้มก่อนหน้าและคุณต้องการบันทึกของรอบนั้น เพิ่ม -b สำหรับรอบบูตปัจจุบันหรือ --since today:
journalctl -u myapp.service -b --no-pager ชุดเครื่องมือเต็ม — รอบบูต ระดับความรุนแรง การติดตาม output สด — อยู่ที่สรุปคำสั่ง journalctl นี่คือความจำกล้ามเนื้อชุดเดิม
อีกหนึ่งคู่ที่ควรรู้:
systemctl list-units --failed
systemctl reset-failed myapp.service อันแรกแสดงทุก unit สีแดงในเครื่อง อันที่สองเคลียร์สถานะ failed หลังคุณแก้เสร็จ — เป็นเรื่องความสวยงาม แต่มันทำให้หน้า status ไม่ร้องห่มร้องไห้โดยไม่มีไฟ
สาเหตุที่พบบ่อยที่สุดคืออะไร?
หลัง log เรียกอาการแล้ว สาเหตุแทบจะเป็นหนึ่งในห้านี้:
| log พูดว่า | สาเหตุที่น่าจะเป็น | วิธีแก้ |
|---|---|---|
status=203/EXEC | พาธ ExecStart= ผิดหรือ interpreter หาย | พาธแบบ absolute, chmod +x, เช็ก shebang |
status=203/EXEC กับสคริปต์ | สคริปต์จบบรรทัดแบบ CRLF หรือ shebang เพี้ยน | dos2unix script.sh แก้บรรทัดแรก |
status=1/FAILURE ไม่มี log จากแอป | ขาด working directory หรือตัวแปรสภาพแวดล้อม | ตั้ง WorkingDirectory= เพิ่ม Environment= |
Permission denied | user อ่านไฟล์หรือ bind พอร์ตไม่ได้ | แก้ ownership; พอร์ตต่ำกว่า 1024 ต้อง AmbientCapabilities=CAP_NET_BIND_SERVICE หรือ root |
Unit is masked | มีคนรัน systemctl mask | systemctl unmask myapp.service |
| แก้ไฟล์ unit “แล้วไม่เกิดอะไรขึ้น” | daemon ยังไม่ได้ reload | systemctl daemon-reload |
ตระกูล 203/EXEC สมควรได้รับการกล่าวถึงเป็นพิเศษเพราะมันคือตัวกินบ่ายทั้งบ่าย systemd ไม่ใช้เชลล์ของคุณสั่งรัน ExecStart= แปลว่า:
# ผิด — ไม่มีเชลล์ ~ ไม่เคย expand ไม่มีการหาใน PATH
ExecStart=~/app/run.sh
# ถูก
ExecStart=/opt/app/run.sh และสคริปต์เองต้อง executable และขึ้นด้วย shebang จริง (#!/bin/bash หรือ #!/usr/bin/env bash) สคริปต์ที่รันจากเทอร์มินัลคุณลื่นไหลแต่ใต้ systemd ล้มด้วย 203 เกือบทุกครั้งเป็นหนึ่งใน: ไม่ executable, จบบรรทัดแบบ CRLF, shebang ชี้ไปไหนไม่เป็น หรือพาธแบบ relative
ทำไมสตาร์ตมือได้แต่สตาร์ตตอนบูตไม่ได้?
บั๊กลำดับคลาสสิก ถ้า log โชว์ความล้มเหลวหลังบูตทันทีแต่ unit สตาร์ตได้ปกติเมื่อคุณสั่ง systemctl start ด้วยมือ service ของคุณกำลังแพ้การแข่งขัน — มันวิ่งไปหาเน็ต ดิสก์ที่ mount หรือฐานข้อมูลก่อนสิ่งเหล่านั้นจะมีตัวตน
วิธีแก้คือประกาศ dependency แทนการเดา:
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml After= จัดลำดับการเริ่ม Wants= ทำให้ systemd ดึง dependency ขึ้นมาจริง ๆ network-online.target ทำงานได้ต่อเมื่อเซอร์วิสรอเน็ตถูกเปิดใช้ใน distro ของคุณ เช็กด้วย systemctl is-enabled NetworkManager-wait-online.service (หรือตัวเทียบของ systemd-networkd) และราว ExecStartPre= คือวิธีให้มันล้มดัง ๆ พร้อมข้อความอ่านได้ แทน stack trace
อีกรูปแบบหนึ่ง: เซอร์วิสสตาร์ตตอนบูตแต่ตายทันที หาสิ่งที่สภาพแวดล้อมแบบโต้ตอบของคุณมีแต่ตอนบูตไม่มี — PATH ต่างกัน virtualenv หรือ HOME ตั้งที่ต้องใช้อย่างชัดเจน:
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app ทำไม service ผมไม่เขียน log ลง journal?
ถ้า journalctl -u ไม่เห็นอะไรเลย เช็กสามอย่างตามลำดับ:
StandardOutput=กับStandardError=ใน unit — ต้องเป็นjournal(ค่าเริ่มต้น) หรือjournal+consoleอาจมีคนตั้งเป็นnull- แอปเขียนลงไฟล์แทน stdout systemd จับเฉพาะ stdout/stderr ผู้เขียน log ไฟล์จะ bypass journal ทั้งหมด ชี้แอปไปที่ stdout หรือไม่ก็ไปอ่านไฟล์
- ขีดจำกัดการเก็บทำรายการเก่าหาย:
journalctl --disk-usageและSystemMaxUse=ใน/etc/systemd/journald.confถ้า journal กำลังขัดขวางอยู่
สำหรับ debug การสตาร์ตเอง ไม่มีอะไรชนะการโยนเชลล์ลงไปในบริบทช่วงบูต:
[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1' หรือกับความลึกลับของแท้ รันคำสั่ง ExecStart= ตัวเป๊ะด้วย user ของ unit ในเชลล์ — ความต่างของสภาพแวดล้อมส่วนใหญ่ปรากฏตัวภายในสิบวินาทีแรก
ทำให้มัน restart เองหลังพังอย่างไร?
นโยบายดีฟอลต์คือ Restart=no: เซอร์วิสที่พังจะนอนตายไป แล้วคุณจะรู้ตัวจากผู้ใช้ แก้เป็นรายเซอร์วิส:
[Service]
Restart=on-failure
RestartSec=5 | การตั้งค่า | restart เมื่อ |
|---|---|
no (ดีฟอลต์) | ไม่เคย |
on-failure | exit ไม่ใช่ศูนย์ สัญญาณ หมดเวลา |
always | exit อะไรก็ตาม แม้แต่แบบสะอาด |
on-watchdog | เฉพาะหมดเวลาของ watchdog |
จับคู่ Restart=on-failure กับตัวกำกับอัตราการสตาร์ต เพื่อกันลูปพังไม่ให้ตอกเครื่อง: StartLimitIntervalSec= กับ StartLimitBurst= ในส่วน [Unit] ล้มห้าครั้งในหกสิบวินาทีควรเรียกคนมาดู ไม่ใช่หมุน CPU
ถ้าคุณกำลังต่องานนี้เป็นงานตามตารางแทน daemon ชั่งน้ำหนักcron vs systemd timerก่อน — timer ให้ทั้ง log ลง journal และการเรียง dependency ฟรี ซึ่งเป็นสิ่งที่บทความนี้กำลังไขว่คว้ามาตลอด
เช็กลิสต์ 60 วินาที
systemctl status <unit>— อ่านสถานะกับบรรทัดท้ายjournalctl -u <unit> -n 50 --no-pager— หา error ตัวจริง203/EXEC? แก้พาธ shebang สิทธิ์Permission denied? แก้ user กับ ownership ของไฟล์- ล้มเฉพาะตอนบูต? เพิ่ม
After=/Wants=network-online.targetและราวExecStartPre= - แก้ไฟล์ unit แล้ว?
systemctl daemon-reload && systemctl restart <unit> - เพิ่ม
Restart=on-failureให้การพังครั้งถัดไปประกาศตัวเองแทนการซ่อน
ช่วงเวลา “systemd ซับซ้อน” ส่วนใหญ่ย่อเหลือประโยคเดียวคือ error อยู่ใน journal มาตลอด อ่านมันก่อนแก้ไฟล์ unit ไม่ใช่หลังจากนั้น
— mrsaynothing
— mrsaynothing
บันทึกหน้างานเรื่อง AI, Linux และ self-hosted
คุยต่อโพสต์นี้บน dev.to dev.to ↗
รับวิธีแก้ฉบับถัดไปทางอีเมล
อีเมลหนึ่งฉบับต่อหนึ่งโพสต์ แก้เสร็จแล้วไปต่อ
นี่คืออะไร?Quantization ของ GGUF: ควรเลือกระดับไหน
ถ้าอ่านแล้วชอบ — ผมสร้างงานแบบนี้เป็นอาชีพ จ้างผม