แยกลิงก์พังออกจาก ISP ล่ม ก่อนสั่งรีสตาร์ตอะไรบนเครื่อง
watchdog ที่เตะทุกครั้งที่ ping ไม่ผ่าน คือ watchdog ที่จะเตะตอนที่ผู้ให้บริการล่ม ซึ่งเป็นเวลาที่ไม่ควรแตะอะไรที่สุด สิ่งแรกที่ต้องเขียนไม่ใช่คำสั่งแก้ แต่คือเงื่อนไขที่จะไม่แก้
พอดีผมต้องดูแลเครื่องตัวหนึ่งที่ทำหน้าที่เป็นเครื่องกลางของงานอีกโปรเจกต์ ตั้งอยู่ที่ออฟฟิศของเจ้าของงาน ต่อ WiFi ไม่ได้ต่อสาย และไม่มีใครนั่งอยู่ข้างๆ อาการที่เจอคือกลางดึกมันหายไปเฉยๆ ping ไม่ตอบ SSH เข้าไม่ได้ พอเช้ามีคนไปกดหน้าเครื่องก็กลับมาเอง ตอนแรกผมคิดว่ามันหลับ แต่ log บอกว่าเครื่องตื่นอยู่ตลอด ที่หายไปคือ WiFi ล้วนๆ
เจ้าของงานสั่งไว้ประโยคเดียวว่า รีบูตบ่อยๆ ไม่ใช่ทางออก ให้ใช้การจัดการอุปกรณ์ ผมเลยเขียน watchdog ที่ไต่จากเบาไปหนัก และที่สำคัญกว่านั้นคือ มีเงื่อนไขว่าเมื่อไหร่ที่จะไม่ทำอะไรเลย
เลข IP ชื่อ SSID ชื่อเครื่องและรหัสในบทความเป็นค่าตัวอย่าง ส่วนคำสั่ง ชื่อโมดูล และลำดับที่ใช้คือของจริงจากเครื่องที่ผมดูแลเอง
watchdog ที่เขียนกันทั่วไป ผิดครึ่งหนึ่งของเวลา
รูปแบบที่เจอในอินเทอร์เน็ตแทบทุกอันหน้าตาแบบนี้ ping ไปที่อะไรสักอย่างข้างนอก ถ้าไม่ผ่านก็ restart บริการเน็ตเวิร์ก จบ
ปัญหาคือ ping ไม่ผ่านมีสาเหตุได้หลายอย่างมาก และมีอยู่อย่างหนึ่งที่พบบ่อยที่สุด คือ ผู้ให้บริการล่ม ซึ่งเป็นเวลาที่การไป restart อะไรก็ตามที่เครื่องเรา ไม่ได้ช่วยอะไรเลย มีแต่จะทำให้แย่ลง เพราะระหว่าง restart ถ้าเน็ตกลับมาพอดี เราจะพลาดจังหวะนั้นไป แล้ววนใหม่ ยิ่งบางเครื่องที่ไต่ถึงขั้นรีบูตอัตโนมัติ ก็จะได้เครื่องที่รีบูตตัวเองทั้งคืนระหว่างที่ผู้ให้บริการซ่อมสายอยู่
ลิงก์ของเราพังจริง
การ์ดไม่ associate กับ AP หรือ associate แล้วแต่ไม่ได้ IP หรือได้ IP แล้วแต่ยิงหา gateway ไม่ตอบ ทั้งสามอย่างนี้คือปัญหาที่อยู่ในบ้านเรา แก้ที่เครื่องเราแล้วได้ผลจริง
ปลายทางล่ม
การ์ด associate อยู่ ได้ IP ปกติ gateway ตอบ ping ดี แต่ยิงออกอินเทอร์เน็ตไม่ได้ อันนี้คือปัญหาอยู่นอกบ้าน สิ่งที่ควรทำคือจดลง log แล้วรอ ไม่ต้องเตะอะไรทั้งนั้น
กับดักแรก เครื่องไม่มี iw ทำให้เช็กผิดตลอด
ตัวอย่างที่หาเจอส่วนใหญ่ใช้คำสั่ง iw เช็กว่าการ์ด associate กับ AP อยู่ไหม ผมเขียนตามนั้นแล้วเจอว่า watchdog รายงานว่าลิงก์พังทุกสองนาที ทั้งที่เน็ตใช้ได้ปกติ
สาเหตุคือเครื่องนี้ ไม่มี iw ติดตั้งมา และตัวสคริปต์รุ่นแรกของผมเช็กผลลัพธ์แบบไม่แยกว่าคำสั่งไม่มีอยู่ กับคำสั่งรันแล้วบอกว่าไม่ associate ทั้งสองกรณีให้ผลว่า "ไม่ associate" เหมือนกันหมด
บั๊กแบบนี้อันตรายกว่าบั๊กที่ทำให้โปรแกรมพัง เพราะสคริปต์ยังรันได้ ไม่มี error และรายงานผลอย่างมั่นใจผลที่ผิดทุกครั้ง ทางแก้ของผมคือใช้ nmcli ซึ่งมีอยู่แน่เพราะเครื่องนี้ใช้ NetworkManager คุมอยู่แล้ว และเขียนให้ถ้าหาเครื่องมือที่ต้องใช้ไม่เจอ ให้ออกทันทีพร้อมเขียน log ว่าออกเพราะอะไร ไม่ใช่เดินต่อแล้วเดาผล
# ผิด: ถ้าไม่มี iw จะได้ค่าว่างเสมอ แล้วสรุปว่าไม่ associate ทุกครั้ง
iw dev "$IFACE" link | grep -q "Connected to" && echo up
# ถูก: ใช้ของที่มีแน่ และแยกกรณีคำสั่งหายออกมาก่อน
command -v nmcli >/dev/null || { log "ไม่มี nmcli ออก"; exit 0; }
STATE=$(nmcli -t -f GENERAL.STATE device show "$IFACE" 2>/dev/null | cut -d: -f2)
case "$STATE" in
*connected*) ASSOC=1 ;;
*) ASSOC=0 ;;
esac
แยกสามสถานะออกจากกันให้ชัด
หัวใจของสคริปต์อยู่ตรงนี้ ถามสามคำถามเรียงกัน แล้วสรุปว่าเป็นเคสไหน สังเกตว่าเคสที่ผมเช็กก่อนเป็นอันดับแรกไม่ใช่ "เน็ตออกไหม" แต่เป็น "ลิงก์เราดีไหม"
#!/bin/bash
# /usr/local/bin/net-watchdog.sh
set -u
IFACE=${IFACE:-wlan0}
GW=$(ip route | awk '/^default/ {print $3; exit}')
STATE_FILE=/var/lib/net-watchdog/fails
LOG=/var/log/net-watchdog.log
log() { echo "$(date '+%F %T') $*" >> "$LOG"; }
# 1. associate อยู่ไหม
STATE=$(nmcli -t -f GENERAL.STATE device show "$IFACE" 2>/dev/null | cut -d: -f2)
case "$STATE" in *connected*) ASSOC=1 ;; *) ASSOC=0 ;; esac
# 2. มี IP ไหม
HAS_IP=0
ip -4 addr show dev "$IFACE" | grep -q 'inet ' && HAS_IP=1
# 3. gateway ตอบไหม 4. ออกเน็ตได้ไหม
GW_OK=0; NET_OK=0
[ -n "${GW:-}" ] && ping -c2 -W2 -I "$IFACE" "$GW" >/dev/null 2>&1 && GW_OK=1
ping -c2 -W3 -I "$IFACE" 1.1.1.1 >/dev/null 2>&1 && NET_OK=1
if [ "$NET_OK" = 1 ]; then
echo 0 > "$STATE_FILE"; exit 0 # ปกติ ไม่ทำอะไร
fi
if [ "$ASSOC" = 1 ] && [ "$HAS_IP" = 1 ] && [ "$GW_OK" = 1 ]; then
log "ลิงก์ปกติ gateway ตอบ แต่ออกเน็ตไม่ได้ = ปลายทางล่ม ไม่แตะอะไร"
echo 0 > "$STATE_FILE"; exit 0 # ISP ล่ม ปล่อยไว้
fi
FAILS=$(( $(cat "$STATE_FILE" 2>/dev/null || echo 0) + 1 ))
echo "$FAILS" > "$STATE_FILE"
log "ลิงก์พัง รอบที่ $FAILS (assoc=$ASSOC ip=$HAS_IP gw=$GW_OK)"
จุดที่ต้องเน้นคือบรรทัดที่รีเซ็ตตัวนับกลับเป็นศูนย์ในเคส ISP ล่ม ไม่ใช่แค่ไม่ทำอะไร แต่ต้องไม่นับด้วย ไม่งั้นพอผู้ให้บริการล่มไปสองชั่วโมง ตัวนับจะไต่ขึ้นไปจนถึงขั้นที่หนักที่สุด แล้วพอเน็ตกลับมา watchdog ก็จะไปถอดการ์ดออกพอดี
บันไดไต่ระดับ จากเบาไปหนัก
ผมไม่ให้มันกระโดดไปขั้นแรงตั้งแต่รอบแรก เพราะอาการหลุดส่วนใหญ่หายเองภายในรอบสองรอบ ตัวนับที่เก็บไว้ในไฟล์เลยเป็นตัวกำหนดว่ารอบนี้จะทำอะไร
- รอบที่ 1-2 ไม่ทำอะไรเลย แค่จด log อาการหลุดสั้นๆ หายเองบ่อยกว่าที่คิด
- รอบที่ 3 สั่ง nmcli ให้ปิดแล้วเปิดการเชื่อมต่อใหม่ เบาที่สุดและได้ผลบ่อยที่สุด
- รอบที่ 6 restart NetworkManager ทั้งตัว ใช้เมื่อ nmcli เองก็เริ่มตอบแปลกๆ
- รอบที่ 10 ถอดโมดูลไดรเวอร์ออกแล้วโหลดกลับ ใช้เมื่อสงสัยว่าเฟิร์มแวร์ในการ์ดค้าง
- รอบที่ 15 ขึ้นไป ถอดอุปกรณ์ออกจาก PCI แล้วสั่งสแกนใหม่ วนซ้ำในขั้นนี้ไปเรื่อยๆ ไม่ไต่ไปถึงการรีบูต
IFACE_CONN=$(nmcli -t -f NAME,DEVICE connection show --active \
| awk -F: -v d="$IFACE" '$2==d {print $1; exit}')
if [ "$FAILS" -ge 15 ]; then
log "ขั้น 4: PCI remove + rescan"
SLOT=$(basename "$(readlink -f /sys/class/net/$IFACE/device)")
echo 1 > "/sys/bus/pci/devices/$SLOT/remove"
sleep 5
echo 1 > /sys/bus/pci/rescan
elif [ "$FAILS" -ge 10 ]; then
log "ขั้น 3: reload โมดูลไดรเวอร์"
modprobe -r rtw89_8852bte && sleep 3 && modprobe rtw89_8852bte
elif [ "$FAILS" -ge 6 ]; then
log "ขั้น 2: restart NetworkManager"
systemctl restart NetworkManager
elif [ "$FAILS" -ge 3 ]; then
log "ขั้น 1: nmcli ปิดเปิดการเชื่อมต่อ"
nmcli connection down "$IFACE_CONN"; sleep 3
nmcli connection up "$IFACE_CONN"
fi
พูดตรงๆ ครับ เขียนไว้แล้วแต่ยังไม่เคยไต่ไปถึงขั้นนั้นสักครั้ง เพราะขั้นที่เบากว่าแก้ได้หมด และผมก็ไม่กล้าทดสอบด้วยการยิงคำสั่งนี้ตรงๆ กับเครื่องที่อยู่ต่างที่ เพราะถ้า rescan แล้วการ์ดไม่กลับมา เครื่องนั้นจะไม่มีเน็ตจนกว่าจะมีคนเดินไปกดหน้าเครื่อง ซึ่งอาจจะข้ามคืนหรือข้ามวันหยุด ใครจะเอาไปใช้ ทดสอบกับเครื่องที่เอื้อมมือถึงก่อน หรือตัดขั้นนี้ออกไปเลยแล้วให้วนอยู่ที่ขั้นโหลดโมดูลแทน ก็ปลอดภัยกว่ามาก
ตั้งเป็น timer ไม่ใช่ cron
ผมใช้ systemd timer แทน cron เพราะได้ log รวมอยู่ที่เดียวกับทุกอย่าง เห็นได้จาก journalctl และตั้ง Persistent ได้ ที่สำคัญคือมันไม่ทับกันเองถ้ารอบก่อนยังไม่จบ
# /etc/systemd/system/net-watchdog.service
[Unit]
Description=Network link watchdog
After=network.target
[Service]
Type=oneshot
Environment=IFACE=wlan0
ExecStart=/usr/local/bin/net-watchdog.sh
# /etc/systemd/system/net-watchdog.timer
[Unit]
Description=Run network watchdog every 2 minutes
[Timer]
OnBootSec=3min
OnUnitActiveSec=2min
AccuracySec=10s
[Install]
WantedBy=timers.target
install -d -m 755 /var/lib/net-watchdog
systemctl daemon-reload
systemctl enable --now net-watchdog.timer
systemctl list-timers net-watchdog # ดูว่ารอบหน้าเมื่อไหร่
journalctl -u net-watchdog.service -n 30 --no-pager
tail -f /var/log/net-watchdog.log
ก่อนเขียน watchdog ให้ปิดสาเหตุที่รู้อยู่แล้วก่อน
อันนี้สำคัญกว่าตัวสคริปต์ทั้งหมดรวมกัน watchdog คือตาข่ายรองรับ ไม่ใช่ทางแก้ ถ้าเรารู้อยู่แล้วว่าอะไรทำให้หลุด ต้องไปปิดตรงนั้นก่อน
ชิป WiFi ที่ผมเจอในเครื่องนี้เป็นตระกูล Realtek ที่ใช้ไดรเวอร์ rtw89 ซึ่งมีอาการชัดเจนมากคือ ตอนที่ไม่มีทราฟฟิกมันจะเข้าโหมดประหยัดไฟแล้วหลุด อาการที่เห็นจากข้างนอกเหมือนเครื่องหลับ ทั้งที่เครื่องตื่นอยู่ ทางแก้คือปิดโหมดประหยัดไฟของ WiFi ไปเลย
# /etc/NetworkManager/conf.d/wifi-powersave-off.conf
[connection]
wifi.powersave = 2
ค่า 2 คือปิด ส่วนค่า 3 คือเปิด อ่านแล้วสับสนแต่มันเป็นแบบนั้น หลังจากตั้งค่านี้แล้ว restart NetworkManager หนึ่งครั้ง แล้วเช็กว่าติดจริง
systemctl restart NetworkManager
nmcli -f 802-11-wireless.powersave connection show "$IFACE_CONN"
# ถ้ายังหลุดอีก ค่อยไปดูเรื่อง ASPM ของ PCIe ต่อ อย่าเพิ่งข้ามไปแตะตรงนั้นก่อน
ปิดการหลับของตัวเครื่องด้วย ไม่งั้นแยกอาการไม่ออก
อีกอย่างที่ต้องปิดคือการหลับของตัวเครื่องเอง เครื่องที่ทำหน้าที่เป็นเซิร์ฟเวอร์ต้องตื่นตลอด ต่อให้ปิดฝาก็ตาม ที่ต้องทำเรื่องนี้ให้จบก่อนไม่ใช่แค่เพราะเน็ตหลุด แต่เพราะถ้าเครื่องหลับจริง อาการที่เห็นจากข้างนอกจะเหมือนกันเป๊ะกับ WiFi หลุด แล้วเราจะแยกไม่ออกว่ากำลังไล่อะไรอยู่
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
# แล้วแก้ /etc/systemd/logind.conf ให้
# HandleLidSwitch=ignore
# IdleAction=ignore
systemctl restart systemd-logind
ส่วนตัวผมคิดว่าเครื่องที่ทำหน้าที่สำคัญไม่ควรต่อ WiFi ตั้งแต่แรกครับ ลากสายไปให้มันเถอะ ถูกกว่าเวลาที่เสียไปกับการไล่หาอาการแบบนี้เยอะ แต่หน้างานจริงมันมีข้อจำกัดของมัน ห้องที่วางเครื่องไม่มีจุดต่อสาย เจ้าของตึกไม่ให้เจาะผนัง อะไรก็ว่าไป ถ้าเลี่ยงไม่ได้จริงๆ อย่างน้อยก็ปิดโหมดประหยัดไฟ แล้วมี watchdog ที่รู้จักอยู่เฉยตอนที่ควรอยู่เฉย ใครจะเสียบ USB-Ethernet เพิ่มเป็นขาสำรองก็เข้าท่ากว่าอีก ตามสะดวกครับ
ที่ยังค้างในแผนของผมคือทำให้มันส่งแจ้งเตือนออกไปตอนไต่ถึงขั้นที่สาม เพื่อจะได้รู้ก่อนที่มันจะไปถึงขั้นสุดท้าย กับหาโอกาสเอาเครื่องที่เอื้อมมือถึงมาทดสอบขั้น PCI ให้จบสักที ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
อยากให้เราทำให้ไหม?
ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย