<?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>Endpoint on Tarragon</title><link>https://tarrragon.github.io/blog/tags/endpoint/</link><description>Recent content in Endpoint 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/endpoint/index.xml" rel="self" type="application/rss+xml"/><item><title>Health check endpoint 設計</title><link>https://tarrragon.github.io/blog/devops/04-service-health/health-check-endpoint/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/devops/04-service-health/health-check-endpoint/</guid><description>&lt;p>Health check endpoint 的責任是讓外部用一個標準請求就問到服務的權威健康狀態，取代肉眼登入機器猜死活。它是這個模組所有自動恢復機制的共同輸入：重啟靠它判斷該不該重啟、負載平衡靠它決定要不要送流量、告警靠它知道什麼時候該叫人。端點回什麼、探多深，是每個服務要自己設計的決策，不是框架給的預設值。&lt;/p>
&lt;p>設計的核心問題只有一個：「這個端點回 200 的時候，到底保證了什麼」。答得越含糊，下游依賴這個訊號做的每個決策就越不可靠。&lt;/p>
&lt;h2 id="淺檢查只證明-http-handler-活著">淺檢查只證明 HTTP handler 活著&lt;/h2>
&lt;p>最簡單的 health endpoint 是收到請求就回 200。這種檢查探到的深度只到「HTTP server 還在接受連線、路由還在運作」，不保證服務真的能做事。進程活著不等於內部子系統活著——一個服務可以進程好端端跑著、&lt;code>pgrep&lt;/code> 找得到、CPU 不高，但它內部某個關鍵子系統已經 wedged，這種狀態下淺檢查照樣回 200。這正是 &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;/p>
&lt;p>一個資料寫入服務的 handler 執行緒還在監聽、但它背後的資料庫連線池已經全部逾時，淺檢查會回 200，負載平衡繼續把寫入請求送進來，每一筆都在下游卡死。訊號說健康、實際在掉資料——淺檢查的盲點就在這裡：它證明的比它看起來保證的少很多。&lt;/p>
&lt;h2 id="深檢查探到服務真正依賴的子系統">深檢查探到服務真正依賴的子系統&lt;/h2>
&lt;p>深檢查在回答健康之前，先確認服務賴以運作的關鍵子系統確實可用。一個寫入服務的深檢查會實際碰一下儲存層——取一個連線、確認 pool 沒枯竭就夠，不必完整寫一筆；一個查詢服務會確認索引載入完成、快取連得上。深檢查回 200 的保證比淺檢查強：不只 handler 活著，服務真正做事需要的那條路徑也通。&lt;/p>
&lt;p>深檢查的成本是它要花時間、也可能引入自己的失敗。檢查本身若把每個下游依賴都同步 ping 一遍，就製造了依賴放大：一個非關鍵的下游慢一點，health check 跟著逾時，服務被判成不健康、被摘掉流量或被重啟，但服務本身其實還能服務核心請求。所以深檢查的設計要先切開「關鍵依賴」跟「非關鍵依賴」——關鍵依賴掛了服務確實無法運作，值得檢查；非關鍵依賴（一個可以降級的推薦引擎、一個非同步的分析上報）掛了不該讓整個服務被判死。這條切分沒有通則，取決於這個服務缺了哪個子系統就真的做不了事。&lt;/p>
&lt;h2 id="帶診斷資訊的回應把活著跟有沒有在做事分開">帶診斷資訊的回應把「活著」跟「有沒有在做事」分開&lt;/h2>
&lt;p>health endpoint 回的不只有狀態碼，還可以帶回讓外部判讀健康程度的欄位。本站 collector 的 &lt;code>/health&lt;/code> 回 200 的同時附帶「最後一次成功寫入的時間」——這個時間戳把兩件事分開了：狀態碼 200 說進程活著，最後寫入時間說它有沒有在做事。進程活著但最後寫入停在十分鐘前，是「活著但卡住」的定案訊號，比單看狀態碼可靠。&lt;/p>
&lt;p>更細的健康程度來自處理管線的內部計數。collector 在 endpoint 暴露 &lt;code>channel.depth&lt;/code>（待寫入事件的積壓數）跟 &lt;code>storage.errors&lt;/code>（寫入錯誤累計）這類計數器；積壓持續變高、錯誤持續累加，代表服務還活著、但已經降級——處理速度跟不上進來的量。這是二元的「掛了沒」之外的中間態訊號。&lt;/p>
&lt;p>中間態不一定要暴露給最終呈現層。同一套 collector 的 DevOps 儀表板服務狀態卡刻意設計成純二元——綠色正常、紅色異常，不讓看板的人去解讀積壓數字。中間態的量化欄位存在於 endpoint、供機器判讀與告警規則收斂用；呈現層則收斂成二元，讓人一眼就懂。健康程度探得多細，跟最終要呈現多細，是兩個獨立的決策。&lt;/p>
&lt;h2 id="啟動期的健康是另一種狀態">啟動期的健康是另一種狀態&lt;/h2>
&lt;p>服務剛起來、還在載入索引或重建狀態時，它既不是掛了、也還不能服務——這是啟動自檢要覆蓋的一段。collector 啟動時會檢查儲存完整性、重建索引，這段期間服務進程活著，但還沒準備好接流量。淺檢查在這時就會回 200，若外部據此開始送流量，請求會打在還沒就緒的服務上。啟動期的健康狀態要跟「就緒可服務」分開回報——啟動中、就緒、存活是三種不同狀態，對應 &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 與 startup 三種 probe&lt;/a> 要分開回答的問題。&lt;/p>
&lt;h2 id="誰來戳這個端點">誰來戳這個端點&lt;/h2>
&lt;p>設計好回什麼之後，還要決定誰來讀它。服務自己不會主動回報，要有外部的探測者定期戳。最輕量的做法是一個定時器對 endpoint 發健康請求並設逾時，戳不動就讓那次檢查失敗、走既有的告警鏈——&lt;a href="https://tarrragon.github.io/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道&lt;/a> 的外部探針段有單機 systemd 上的完整實作（curl &lt;code>/health&lt;/code> 設 5 秒逾時、逾時就 failed）。跑在編排平台上時，這個角色由平台的 probe 機制接手，探測的語意、失敗後的動作，是 &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;/p>
&lt;p>外部探測比服務自我回報可靠的地方在於：服務內部卡死時，它可能連「我不健康」都報不出來——是外部戳它、發現戳不動，才抓得到這種失效。自我回報只能覆蓋服務還有能力回應的那些失敗。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>活著、準備好接流量、啟動中是三種健康、對應三種 probe → &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 定時探測並告警 → &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/systemd-watchdog-restart/" data-link-title="systemd watchdog 與自動重啟" data-link-desc="在單機 systemd 上做服務自動恢復時，釐清 watchdog 主動報活與 restart policy 被動拉起是兩套機制、以及為什麼要先重啟幾次才放棄">systemd watchdog 與自動重啟&lt;/a>&lt;/li>
&lt;li>health signal 怎麼收斂成 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;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Health check endpoint 的責任是讓外部用一個標準請求就問到服務的權威健康狀態，取代肉眼登入機器猜死活。它是這個模組所有自動恢復機制的共同輸入：重啟靠它判斷該不該重啟、負載平衡靠它決定要不要送流量、告警靠它知道什麼時候該叫人。端點回什麼、探多深，是每個服務要自己設計的決策，不是框架給的預設值。</p>
<p>設計的核心問題只有一個：「這個端點回 200 的時候，到底保證了什麼」。答得越含糊，下游依賴這個訊號做的每個決策就越不可靠。</p>
<h2 id="淺檢查只證明-http-handler-活著">淺檢查只證明 HTTP handler 活著</h2>
<p>最簡單的 health endpoint 是收到請求就回 200。這種檢查探到的深度只到「HTTP server 還在接受連線、路由還在運作」，不保證服務真的能做事。進程活著不等於內部子系統活著——一個服務可以進程好端端跑著、<code>pgrep</code> 找得到、CPU 不高，但它內部某個關鍵子系統已經 wedged，這種狀態下淺檢查照樣回 200。這正是 <a href="/blog/linux/debug/process-service-state-diagnosis/" data-link-title="程序、服務與狀態怎麼判" data-link-desc="要判斷一個程式活著沒、某個系統服務現在由誰提供、桌面 session 有沒有被鎖、或終端機多工器的 session 還在不在時，用對的權威來源而不是靠畫面或猜的名字">程序、服務與狀態怎麼判</a> 裡「進程活著 ≠ 在運作」那條，搬到服務自我回報的場景。</p>
<p>一個資料寫入服務的 handler 執行緒還在監聽、但它背後的資料庫連線池已經全部逾時，淺檢查會回 200，負載平衡繼續把寫入請求送進來，每一筆都在下游卡死。訊號說健康、實際在掉資料——淺檢查的盲點就在這裡：它證明的比它看起來保證的少很多。</p>
<h2 id="深檢查探到服務真正依賴的子系統">深檢查探到服務真正依賴的子系統</h2>
<p>深檢查在回答健康之前，先確認服務賴以運作的關鍵子系統確實可用。一個寫入服務的深檢查會實際碰一下儲存層——取一個連線、確認 pool 沒枯竭就夠，不必完整寫一筆；一個查詢服務會確認索引載入完成、快取連得上。深檢查回 200 的保證比淺檢查強：不只 handler 活著，服務真正做事需要的那條路徑也通。</p>
<p>深檢查的成本是它要花時間、也可能引入自己的失敗。檢查本身若把每個下游依賴都同步 ping 一遍，就製造了依賴放大：一個非關鍵的下游慢一點，health check 跟著逾時，服務被判成不健康、被摘掉流量或被重啟，但服務本身其實還能服務核心請求。所以深檢查的設計要先切開「關鍵依賴」跟「非關鍵依賴」——關鍵依賴掛了服務確實無法運作，值得檢查；非關鍵依賴（一個可以降級的推薦引擎、一個非同步的分析上報）掛了不該讓整個服務被判死。這條切分沒有通則，取決於這個服務缺了哪個子系統就真的做不了事。</p>
<h2 id="帶診斷資訊的回應把活著跟有沒有在做事分開">帶診斷資訊的回應把「活著」跟「有沒有在做事」分開</h2>
<p>health endpoint 回的不只有狀態碼，還可以帶回讓外部判讀健康程度的欄位。本站 collector 的 <code>/health</code> 回 200 的同時附帶「最後一次成功寫入的時間」——這個時間戳把兩件事分開了：狀態碼 200 說進程活著，最後寫入時間說它有沒有在做事。進程活著但最後寫入停在十分鐘前，是「活著但卡住」的定案訊號，比單看狀態碼可靠。</p>
<p>更細的健康程度來自處理管線的內部計數。collector 在 endpoint 暴露 <code>channel.depth</code>（待寫入事件的積壓數）跟 <code>storage.errors</code>（寫入錯誤累計）這類計數器；積壓持續變高、錯誤持續累加，代表服務還活著、但已經降級——處理速度跟不上進來的量。這是二元的「掛了沒」之外的中間態訊號。</p>
<p>中間態不一定要暴露給最終呈現層。同一套 collector 的 DevOps 儀表板服務狀態卡刻意設計成純二元——綠色正常、紅色異常，不讓看板的人去解讀積壓數字。中間態的量化欄位存在於 endpoint、供機器判讀與告警規則收斂用；呈現層則收斂成二元，讓人一眼就懂。健康程度探得多細，跟最終要呈現多細，是兩個獨立的決策。</p>
<h2 id="啟動期的健康是另一種狀態">啟動期的健康是另一種狀態</h2>
<p>服務剛起來、還在載入索引或重建狀態時，它既不是掛了、也還不能服務——這是啟動自檢要覆蓋的一段。collector 啟動時會檢查儲存完整性、重建索引，這段期間服務進程活著，但還沒準備好接流量。淺檢查在這時就會回 200，若外部據此開始送流量，請求會打在還沒就緒的服務上。啟動期的健康狀態要跟「就緒可服務」分開回報——啟動中、就緒、存活是三種不同狀態，對應 <a href="/blog/devops/04-service-health/liveness-vs-readiness/" data-link-title="Liveness 與 Readiness" data-link-desc="分不清該用哪種探針、或探針失敗後平台重啟了不該重啟的服務時，回來釐清 liveness、readiness、startup 三種探針各自宣告什麼、失敗後平台做什麼">liveness、readiness 與 startup 三種 probe</a> 要分開回答的問題。</p>
<h2 id="誰來戳這個端點">誰來戳這個端點</h2>
<p>設計好回什麼之後，還要決定誰來讀它。服務自己不會主動回報，要有外部的探測者定期戳。最輕量的做法是一個定時器對 endpoint 發健康請求並設逾時，戳不動就讓那次檢查失敗、走既有的告警鏈——<a href="/blog/linux/debug/service-failure-monitoring/" data-link-title="服務掛了怎麼自動知道：從肉眼盯到主動告警" data-link-desc="不想每次都手動 systemctl 檢查服務死活、想讓機器在 service 掛掉時主動推播通知、或擔心整台機器當掉沒人知道時回來讀">服務掛了怎麼自動知道</a> 的外部探針段有單機 systemd 上的完整實作（curl <code>/health</code> 設 5 秒逾時、逾時就 failed）。跑在編排平台上時，這個角色由平台的 probe 機制接手，探測的語意、失敗後的動作，是 <a href="/blog/devops/04-service-health/liveness-vs-readiness/" data-link-title="Liveness 與 Readiness" data-link-desc="分不清該用哪種探針、或探針失敗後平台重啟了不該重啟的服務時，回來釐清 liveness、readiness、startup 三種探針各自宣告什麼、失敗後平台做什麼">liveness 與 readiness</a> 的主題。</p>
<p>外部探測比服務自我回報可靠的地方在於：服務內部卡死時，它可能連「我不健康」都報不出來——是外部戳它、發現戳不動，才抓得到這種失效。自我回報只能覆蓋服務還有能力回應的那些失敗。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>活著、準備好接流量、啟動中是三種健康、對應三種 probe → <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 定時探測並告警 → <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/systemd-watchdog-restart/" data-link-title="systemd watchdog 與自動重啟" data-link-desc="在單機 systemd 上做服務自動恢復時，釐清 watchdog 主動報活與 restart policy 被動拉起是兩套機制、以及為什麼要先重啟幾次才放棄">systemd watchdog 與自動重啟</a></li>
<li>health signal 怎麼收斂成 DevOps 儀表板的服務狀態卡 → <a href="/blog/monitoring/04-collector/dashboard-devops/" data-link-title="DevOps Dashboard 設計" data-link-desc="Collector 和 SDK 是否健康 — 日常監控的服務狀態卡、吞吐量曲線、儲存用量，以及告警觸發後的排障視圖">Monitoring DevOps 儀表板</a></li>
</ul>
]]></content:encoded></item></channel></rss>