<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Auto-Restart on Tarragon</title><link>https://tarrragon.github.io/blog/tags/auto-restart/</link><description>Recent content in Auto-Restart on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 03 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/auto-restart/index.xml" rel="self" type="application/rss+xml"/><item><title>systemd watchdog 與自動重啟</title><link>https://tarrragon.github.io/blog/devops/04-service-health/systemd-watchdog-restart/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/devops/04-service-health/systemd-watchdog-restart/</guid><description>&lt;p>單機 systemd 上的自動恢復由兩套獨立機制組成：watchdog 讓服務定期主動報活、超時沒報就被 systemd 重建；restart policy 在進程退出後把它重新拉起。前者對應 &lt;a href="https://tarrragon.github.io/blog/devops/04-service-health/liveness-vs-readiness/" data-link-title="Liveness 與 Readiness" data-link-desc="分不清該用哪種探針、或探針失敗後平台重啟了不該重啟的服務時，回來釐清 liveness、readiness、startup 三種探針各自宣告什麼、失敗後平台做什麼">liveness 探針&lt;/a> 的語意——服務宣告自己還在運作；後者對應進程層的崩潰恢復。兩者觸發條件不同、覆蓋的失效模式也不同，一個服務常常兩套都要。&lt;/p>
&lt;p>在編排平台上，這兩件事由 probe 機制表達；在單機部署，systemd 靠 &lt;code>sd_notify&lt;/code> 協議讓服務明確宣告狀態，反而比 probe 更直接——服務主動說「我還活著」「我準備好了」，不必外部反覆猜測。&lt;/p>
&lt;h2 id="watchdog服務主動報活超時就被重建">Watchdog：服務主動報活，超時就被重建&lt;/h2>
&lt;p>Watchdog 是主動式的 liveness：服務端定期呼叫 &lt;code>sd_notify(WATCHDOG=1)&lt;/code> 告訴 systemd 「我還在正常運作」，systemd 設定一個 &lt;code>WatchdogSec=&lt;/code> 逾時；服務在時限內沒報，systemd 判定它卡死、自動 kill 加 restart。本站 collector 用的是 &lt;code>WatchdogSec=30s&lt;/code>——服務要在 30 秒內報一次，逾時就被重啟。&lt;/p>
&lt;p>Watchdog 抓的正是 &lt;a href="https://tarrragon.github.io/blog/linux/debug/process-service-state-diagnosis/" data-link-title="程序、服務與狀態怎麼判" data-link-desc="要判斷一個程式活著沒、某個系統服務現在由誰提供、桌面 session 有沒有被鎖、或終端機多工器的 session 還在不在時，用對的權威來源而不是靠畫面或猜的名字">進程活著但子系統死掉&lt;/a> 那類失效：進程還在、&lt;code>systemctl is-active&lt;/code> 還顯示 active，但內部某個關鍵迴圈已經 hung。這種狀態下崩潰恢復幫不上忙（進程根本沒退出），但 watchdog 抓得到——因為卡死的服務報不出那一次心跳。前提是服務改得動：&lt;code>sd_notify(WATCHDOG=1)&lt;/code> 要寫進服務自己的碼，報活的位置要放在「真的有在做事才會經過」的路徑上，才不會變成一個進程活著就自動報活的假訊號。&lt;/p>
&lt;p>服務改不動的情況（閉源程式、別人的服務），watchdog 這條用不上，改從外部主動戳它——一個定時器對服務發健康請求並設逾時，戳不動就讓那次檢查失敗。這個外部探針的完整單機實作在 &lt;a href="https://tarrragon.github.io/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道&lt;/a> 的外部健康探針段。Watchdog 跟外部探針的分界就是「控不控制得了那個服務」：控制得了用 watchdog（零額外依賴、服務自己報），控制不了用外部探針（從體外戳）。&lt;/p>
&lt;h2 id="restart-policy進程退出後被拉起">Restart policy：進程退出後被拉起&lt;/h2>
&lt;p>Restart policy 覆蓋的是進程真的退出的情況——崩潰、被 kill、非正常結束。&lt;code>Restart=on-failure&lt;/code> 讓 systemd 在服務以失敗狀態退出時自動重啟，&lt;code>RestartSec=5&lt;/code> 設定重啟前等幾秒。這是最基本的崩潰恢復：多數暫時性失敗（一次連線抖動、一個 race）重試一下就好，不值得驚動人。&lt;/p>
&lt;p>但無限重啟會掩蓋真正壞掉的服務——一個因為配置錯誤永遠起不來的服務，會被 restart policy 反覆拉起、反覆失敗。所以要配重試上限：&lt;code>StartLimitBurst=3&lt;/code> 加 &lt;code>StartLimitIntervalSec=60&lt;/code> 表示 60 秒內失敗 3 次才真的進 failed 狀態、停止重試。本站 collector 用的門檻是 10 分鐘內重啟 3 次以上就停止自動重啟、改發告警要求人工介入——這條門檻是「機器自動恢復」跟「人工恢復」的交界：撞上限之前交給機器重試，撞上限之後承認自動恢復無效、升級成人的問題。&lt;/p>
&lt;h2 id="先自動重啟放棄了才告警">先自動重啟，放棄了才告警&lt;/h2>
&lt;p>自動恢復跟告警是兩段，要分開。理由是多數失敗自己重試就會過，每次瞬斷都吵人會把告警洗到沒人看。正確的分段是：restart policy 負責「重試幾次」，告警只在「重試上限撞到、真的放棄」時才發。&lt;/p>
&lt;p>這裡有個實測踩到、跟直覺相反的意外：systemd 的 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/systemd-onfailure/" data-link-title="systemd OnFailure（失敗觸發鉤子）" data-link-desc="想讓某個服務進 failed 時自動觸發告警或修復動作、或發現 OnFailure 每次重啟中途都觸發把告警洗爆時讀">&lt;code>OnFailure&lt;/code>&lt;/a> 鉤子不是「放棄才觸發」，而是每一次失敗都觸發——包含 &lt;code>Restart=on-failure&lt;/code> 的每次 auto-restart 中途。&lt;a href="https://tarrragon.github.io/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道&lt;/a> 實測一個反覆崩潰、重試 3 次後放棄的服務，&lt;code>OnFailure&lt;/code> 觸發了 4 次（3 次 auto-restart 加 1 次最終放棄）。這個觸發次數是特定 systemd 版本的實測，&lt;code>OnFailure&lt;/code> 與 &lt;code>Restart=&lt;/code> 的互動跨版本調整過，換一個版本可能量到不同次數，但「每次失敗都觸發、不是只在放棄時」這個機制本身跨版本成立。所以只靠 restart 加 start-limit 的 config，每次瞬斷都會發告警。真正做到「只在放棄才吵」，要在告警處理器裡加一道狀態閘門——auto-restart 中途服務的 &lt;code>ActiveState&lt;/code> 是 &lt;code>activating&lt;/code>、撞上限進 failed 才是 &lt;code>failed&lt;/code>，處理器只在 &lt;code>failed&lt;/code> 才送，加上這道閘門後同一個崩潰測試從 4 則告警降到 1 則。config 管重試次數、handler 的閘門管只在終局告警，兩段合起來才是完整的「先重啟、放棄才吵」。這道告警鏈的完整設定（&lt;code>OnFailure&lt;/code> 鉤子、送出腳本、遞迴陷阱）在那篇有實測驗證過的完整版，本模組不重述。&lt;/p>
&lt;h2 id="恢復閉環要回饋到狀態呈現">恢復閉環要回饋到狀態呈現&lt;/h2>
&lt;p>自動恢復做完之後，狀態呈現要跟著收斂。collector 在自動恢復成功後送一個 &lt;code>collector.started&lt;/code> 事件，讓 DevOps 儀表板的服務狀態卡從紅轉綠；撞到重啟上限、升級成人工告警時，狀態卡維持紅色等人處理。恢復機制跟呈現層的這個回饋迴路，讓「服務掉了又自己回來」這件事在看板上留下可追溯的痕跡，而不是靜默地掉了又靜默地回來。收斂成儀表板的細節見 &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/dashboard-devops/" data-link-title="DevOps Dashboard 設計" data-link-desc="Collector 和 SDK 是否健康 — 日常監控的服務狀態卡、吞吐量曲線、儲存用量，以及告警觸發後的排障視圖">Monitoring DevOps 儀表板&lt;/a>。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Watchdog 對應的概念層 liveness、restart 對應的重啟代價 → &lt;a href="https://tarrragon.github.io/blog/devops/04-service-health/liveness-vs-readiness/" data-link-title="Liveness 與 Readiness" data-link-desc="分不清該用哪種探針、或探針失敗後平台重啟了不該重啟的服務時，回來釐清 liveness、readiness、startup 三種探針各自宣告什麼、失敗後平台做什麼">Liveness 與 Readiness&lt;/a>&lt;/li>
&lt;li>單機 systemd 之外，supervisord、Docker、Kubernetes 怎麼表達重啟與重建 → &lt;a href="https://tarrragon.github.io/blog/devops/04-service-health/process-supervisor-selection/" data-link-title="Process supervisor 選型" data-link-desc="在 systemd、supervisord、Docker restart policy、Kubernetes 之間選服務監管方式時，用平台能不能分開表達 startup、readiness、liveness、drain 當判準">Process supervisor 選型&lt;/a>&lt;/li>
&lt;li>告警鏈的完整單機實作（OnFailure、送出腳本、心跳、canary）→ &lt;a href="https://tarrragon.github.io/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道&lt;/a>&lt;/li>
&lt;li>重啟前先讓服務有序收束在途工作 → &lt;a href="https://tarrragon.github.io/blog/devops/04-service-health/graceful-shutdown/" data-link-title="Graceful shutdown" data-link-desc="設計服務收到停止信號後的收束流程時，釐清 SIGTERM 到 SIGKILL 的 grace period、退場的固定順序、以及不同 workload 的 drain 窗口要留多長">Graceful shutdown&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>單機 systemd 上的自動恢復由兩套獨立機制組成：watchdog 讓服務定期主動報活、超時沒報就被 systemd 重建；restart policy 在進程退出後把它重新拉起。前者對應 <a href="/blog/devops/04-service-health/liveness-vs-readiness/" data-link-title="Liveness 與 Readiness" data-link-desc="分不清該用哪種探針、或探針失敗後平台重啟了不該重啟的服務時，回來釐清 liveness、readiness、startup 三種探針各自宣告什麼、失敗後平台做什麼">liveness 探針</a> 的語意——服務宣告自己還在運作；後者對應進程層的崩潰恢復。兩者觸發條件不同、覆蓋的失效模式也不同，一個服務常常兩套都要。</p>
<p>在編排平台上，這兩件事由 probe 機制表達；在單機部署，systemd 靠 <code>sd_notify</code> 協議讓服務明確宣告狀態，反而比 probe 更直接——服務主動說「我還活著」「我準備好了」，不必外部反覆猜測。</p>
<h2 id="watchdog服務主動報活超時就被重建">Watchdog：服務主動報活，超時就被重建</h2>
<p>Watchdog 是主動式的 liveness：服務端定期呼叫 <code>sd_notify(WATCHDOG=1)</code> 告訴 systemd 「我還在正常運作」，systemd 設定一個 <code>WatchdogSec=</code> 逾時；服務在時限內沒報，systemd 判定它卡死、自動 kill 加 restart。本站 collector 用的是 <code>WatchdogSec=30s</code>——服務要在 30 秒內報一次，逾時就被重啟。</p>
<p>Watchdog 抓的正是 <a href="/blog/linux/debug/process-service-state-diagnosis/" data-link-title="程序、服務與狀態怎麼判" data-link-desc="要判斷一個程式活著沒、某個系統服務現在由誰提供、桌面 session 有沒有被鎖、或終端機多工器的 session 還在不在時，用對的權威來源而不是靠畫面或猜的名字">進程活著但子系統死掉</a> 那類失效：進程還在、<code>systemctl is-active</code> 還顯示 active，但內部某個關鍵迴圈已經 hung。這種狀態下崩潰恢復幫不上忙（進程根本沒退出），但 watchdog 抓得到——因為卡死的服務報不出那一次心跳。前提是服務改得動：<code>sd_notify(WATCHDOG=1)</code> 要寫進服務自己的碼，報活的位置要放在「真的有在做事才會經過」的路徑上，才不會變成一個進程活著就自動報活的假訊號。</p>
<p>服務改不動的情況（閉源程式、別人的服務），watchdog 這條用不上，改從外部主動戳它——一個定時器對服務發健康請求並設逾時，戳不動就讓那次檢查失敗。這個外部探針的完整單機實作在 <a href="/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道</a> 的外部健康探針段。Watchdog 跟外部探針的分界就是「控不控制得了那個服務」：控制得了用 watchdog（零額外依賴、服務自己報），控制不了用外部探針（從體外戳）。</p>
<h2 id="restart-policy進程退出後被拉起">Restart policy：進程退出後被拉起</h2>
<p>Restart policy 覆蓋的是進程真的退出的情況——崩潰、被 kill、非正常結束。<code>Restart=on-failure</code> 讓 systemd 在服務以失敗狀態退出時自動重啟，<code>RestartSec=5</code> 設定重啟前等幾秒。這是最基本的崩潰恢復：多數暫時性失敗（一次連線抖動、一個 race）重試一下就好，不值得驚動人。</p>
<p>但無限重啟會掩蓋真正壞掉的服務——一個因為配置錯誤永遠起不來的服務，會被 restart policy 反覆拉起、反覆失敗。所以要配重試上限：<code>StartLimitBurst=3</code> 加 <code>StartLimitIntervalSec=60</code> 表示 60 秒內失敗 3 次才真的進 failed 狀態、停止重試。本站 collector 用的門檻是 10 分鐘內重啟 3 次以上就停止自動重啟、改發告警要求人工介入——這條門檻是「機器自動恢復」跟「人工恢復」的交界：撞上限之前交給機器重試，撞上限之後承認自動恢復無效、升級成人的問題。</p>
<h2 id="先自動重啟放棄了才告警">先自動重啟，放棄了才告警</h2>
<p>自動恢復跟告警是兩段，要分開。理由是多數失敗自己重試就會過，每次瞬斷都吵人會把告警洗到沒人看。正確的分段是：restart policy 負責「重試幾次」，告警只在「重試上限撞到、真的放棄」時才發。</p>
<p>這裡有個實測踩到、跟直覺相反的意外：systemd 的 <a href="/blog/linux/dotfile/knowledge-cards/systemd-onfailure/" data-link-title="systemd OnFailure（失敗觸發鉤子）" data-link-desc="想讓某個服務進 failed 時自動觸發告警或修復動作、或發現 OnFailure 每次重啟中途都觸發把告警洗爆時讀"><code>OnFailure</code></a> 鉤子不是「放棄才觸發」，而是每一次失敗都觸發——包含 <code>Restart=on-failure</code> 的每次 auto-restart 中途。<a href="/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道</a> 實測一個反覆崩潰、重試 3 次後放棄的服務，<code>OnFailure</code> 觸發了 4 次（3 次 auto-restart 加 1 次最終放棄）。這個觸發次數是特定 systemd 版本的實測，<code>OnFailure</code> 與 <code>Restart=</code> 的互動跨版本調整過，換一個版本可能量到不同次數，但「每次失敗都觸發、不是只在放棄時」這個機制本身跨版本成立。所以只靠 restart 加 start-limit 的 config，每次瞬斷都會發告警。真正做到「只在放棄才吵」，要在告警處理器裡加一道狀態閘門——auto-restart 中途服務的 <code>ActiveState</code> 是 <code>activating</code>、撞上限進 failed 才是 <code>failed</code>，處理器只在 <code>failed</code> 才送，加上這道閘門後同一個崩潰測試從 4 則告警降到 1 則。config 管重試次數、handler 的閘門管只在終局告警，兩段合起來才是完整的「先重啟、放棄才吵」。這道告警鏈的完整設定（<code>OnFailure</code> 鉤子、送出腳本、遞迴陷阱）在那篇有實測驗證過的完整版，本模組不重述。</p>
<h2 id="恢復閉環要回饋到狀態呈現">恢復閉環要回饋到狀態呈現</h2>
<p>自動恢復做完之後，狀態呈現要跟著收斂。collector 在自動恢復成功後送一個 <code>collector.started</code> 事件，讓 DevOps 儀表板的服務狀態卡從紅轉綠；撞到重啟上限、升級成人工告警時，狀態卡維持紅色等人處理。恢復機制跟呈現層的這個回饋迴路，讓「服務掉了又自己回來」這件事在看板上留下可追溯的痕跡，而不是靜默地掉了又靜默地回來。收斂成儀表板的細節見 <a href="/blog/monitoring/04-collector/dashboard-devops/" data-link-title="DevOps Dashboard 設計" data-link-desc="Collector 和 SDK 是否健康 — 日常監控的服務狀態卡、吞吐量曲線、儲存用量，以及告警觸發後的排障視圖">Monitoring DevOps 儀表板</a>。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Watchdog 對應的概念層 liveness、restart 對應的重啟代價 → <a href="/blog/devops/04-service-health/liveness-vs-readiness/" data-link-title="Liveness 與 Readiness" data-link-desc="分不清該用哪種探針、或探針失敗後平台重啟了不該重啟的服務時，回來釐清 liveness、readiness、startup 三種探針各自宣告什麼、失敗後平台做什麼">Liveness 與 Readiness</a></li>
<li>單機 systemd 之外，supervisord、Docker、Kubernetes 怎麼表達重啟與重建 → <a href="/blog/devops/04-service-health/process-supervisor-selection/" data-link-title="Process supervisor 選型" data-link-desc="在 systemd、supervisord、Docker restart policy、Kubernetes 之間選服務監管方式時，用平台能不能分開表達 startup、readiness、liveness、drain 當判準">Process supervisor 選型</a></li>
<li>告警鏈的完整單機實作（OnFailure、送出腳本、心跳、canary）→ <a href="/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道</a></li>
<li>重啟前先讓服務有序收束在途工作 → <a href="/blog/devops/04-service-health/graceful-shutdown/" data-link-title="Graceful shutdown" data-link-desc="設計服務收到停止信號後的收束流程時，釐清 SIGTERM 到 SIGKILL 的 grace period、退場的固定順序、以及不同 workload 的 drain 窗口要留多長">Graceful shutdown</a></li>
</ul>
]]></content:encoded></item></channel></rss>