<?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>Resource-Limit on Tarragon</title><link>https://tarrragon.github.io/blog/tags/resource-limit/</link><description>Recent content in Resource-Limit on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Sat, 20 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/resource-limit/index.xml" rel="self" type="application/rss+xml"/><item><title>容器化資源設計</title><link>https://tarrragon.github.io/blog/devops/05-capacity-planning/container-resource-design/</link><pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/devops/05-capacity-planning/container-resource-design/</guid><description>&lt;p>Container 的資源限制是容量規劃在容器化環境的落地。每個 container 設定 memory limit、CPU limit 和磁碟 I/O 控制，確保單一 container 不會吃光 host 資源影響其他服務。限制設太緊觸發 OOMKill 或 CPU throttle，設太鬆等於沒有限制。&lt;/p>
&lt;h2 id="memory-限制設計">Memory 限制設計&lt;/h2>
&lt;h3 id="觀察-baseline">觀察 baseline&lt;/h3>
&lt;p>在限制之前先觀察服務的真實記憶體使用。用 &lt;code>docker stats&lt;/code> 看 container 的 MEM USAGE，跑至少 24 小時涵蓋日常操作和定期 job（降採樣、清理）。&lt;/p>
&lt;p>Baseline 包含：&lt;/p>
&lt;ul>
&lt;li>應用程式本身的 heap + stack&lt;/li>
&lt;li>Runtime 開銷（Go 的 GC metadata、JVM 的 metaspace、Python 的 interpreter）&lt;/li>
&lt;li>內嵌資料庫的 page cache（如 SQLite 的 &lt;code>PRAGMA cache_size&lt;/code>）&lt;/li>
&lt;li>HTTP server 的連線 buffer&lt;/li>
&lt;/ul>
&lt;h3 id="設定-limit">設定 limit&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Memory limit = baseline peak × 1.5（安全係數）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安全係數 1.5 是經驗值 — 預留 burst 時的記憶體波動（如大 batch 的 JSON 反序列化、查詢結果集暫存）。安全係數太大浪費資源、太小在 burst 時 OOMKill。&lt;/p>
&lt;h3 id="oomkill-排查">OOMKill 排查&lt;/h3>
&lt;p>OOMKill 的症狀是 container 突然消失、沒有 application log。排查步驟：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">docker inspect &amp;lt;container&amp;gt; &lt;span class="p">|&lt;/span> jq &lt;span class="s1">&amp;#39;.[0].State.OOMKilled&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># true = 被 OOM killer 終止&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">dmesg &lt;span class="p">|&lt;/span> grep -i oom
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># kernel log 中的 OOM 記錄、包含被殺的 process 和當時的記憶體使用&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OOMKill 後的處理：提高 memory limit，或找出記憶體使用異常的原因（memory leak、unbounded cache、大結果集查詢）。&lt;/p>
&lt;h3 id="不同-runtime-的記憶體特性">不同 runtime 的記憶體特性&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Runtime&lt;/th>
 &lt;th>特性&lt;/th>
 &lt;th>注意事項&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Go&lt;/td>
 &lt;td>GC 自動管理、GOGC 控制觸發頻率&lt;/td>
 &lt;td>&lt;code>GOMEMLIMIT&lt;/code> 讓 Go runtime 感知 container 的 memory limit、避免 GC 不積極&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>JVM&lt;/td>
 &lt;td>heap + metaspace + native memory&lt;/td>
 &lt;td>設 &lt;code>-Xmx&lt;/code> 小於 container limit（留空間給 native memory）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Python&lt;/td>
 &lt;td>無 GC 上限、依賴 OS&lt;/td>
 &lt;td>大 DataFrame / 大 dict 可能瞬間超限&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Node.js&lt;/td>
 &lt;td>V8 heap limit 預設 ~1.5GB&lt;/td>
 &lt;td>設 &lt;code>--max-old-space-size&lt;/code> 配合 container limit&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="cpu-限制設計">CPU 限制設計&lt;/h2>
&lt;h3 id="--cpus-vs---cpu-shares">&lt;code>--cpus&lt;/code> vs &lt;code>--cpu-shares&lt;/code>&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>設定&lt;/th>
 &lt;th>行為&lt;/th>
 &lt;th>適用場景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>--cpus=0.5&lt;/code>&lt;/td>
 &lt;td>Hard limit — 最多用 0.5 個 CPU core&lt;/td>
 &lt;td>嚴格隔離、多 container 共用一台主機&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>--cpu-shares=512&lt;/code>&lt;/td>
 &lt;td>Relative weight — 和其他 container 按比例分 CPU&lt;/td>
 &lt;td>彈性分配、host 閒置時可用更多&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="cpu-throttle-症狀">CPU throttle 症狀&lt;/h3>
&lt;p>CPU throttle 不會 crash（和 OOMKill 不同）。症狀是延遲上升 — request 處理時間從 10ms 變成 100ms，因為 container 的 CPU time 被 cgroup 暫停。&lt;/p></description><content:encoded><![CDATA[<p>Container 的資源限制是容量規劃在容器化環境的落地。每個 container 設定 memory limit、CPU limit 和磁碟 I/O 控制，確保單一 container 不會吃光 host 資源影響其他服務。限制設太緊觸發 OOMKill 或 CPU throttle，設太鬆等於沒有限制。</p>
<h2 id="memory-限制設計">Memory 限制設計</h2>
<h3 id="觀察-baseline">觀察 baseline</h3>
<p>在限制之前先觀察服務的真實記憶體使用。用 <code>docker stats</code> 看 container 的 MEM USAGE，跑至少 24 小時涵蓋日常操作和定期 job（降採樣、清理）。</p>
<p>Baseline 包含：</p>
<ul>
<li>應用程式本身的 heap + stack</li>
<li>Runtime 開銷（Go 的 GC metadata、JVM 的 metaspace、Python 的 interpreter）</li>
<li>內嵌資料庫的 page cache（如 SQLite 的 <code>PRAGMA cache_size</code>）</li>
<li>HTTP server 的連線 buffer</li>
</ul>
<h3 id="設定-limit">設定 limit</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Memory limit = baseline peak × 1.5（安全係數）</span></span></code></pre></div><p>安全係數 1.5 是經驗值 — 預留 burst 時的記憶體波動（如大 batch 的 JSON 反序列化、查詢結果集暫存）。安全係數太大浪費資源、太小在 burst 時 OOMKill。</p>
<h3 id="oomkill-排查">OOMKill 排查</h3>
<p>OOMKill 的症狀是 container 突然消失、沒有 application log。排查步驟：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker inspect &lt;container&gt; <span class="p">|</span> jq <span class="s1">&#39;.[0].State.OOMKilled&#39;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># true = 被 OOM killer 終止</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">dmesg <span class="p">|</span> grep -i oom
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># kernel log 中的 OOM 記錄、包含被殺的 process 和當時的記憶體使用</span></span></span></code></pre></div><p>OOMKill 後的處理：提高 memory limit，或找出記憶體使用異常的原因（memory leak、unbounded cache、大結果集查詢）。</p>
<h3 id="不同-runtime-的記憶體特性">不同 runtime 的記憶體特性</h3>
<table>
  <thead>
      <tr>
          <th>Runtime</th>
          <th>特性</th>
          <th>注意事項</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Go</td>
          <td>GC 自動管理、GOGC 控制觸發頻率</td>
          <td><code>GOMEMLIMIT</code> 讓 Go runtime 感知 container 的 memory limit、避免 GC 不積極</td>
      </tr>
      <tr>
          <td>JVM</td>
          <td>heap + metaspace + native memory</td>
          <td>設 <code>-Xmx</code> 小於 container limit（留空間給 native memory）</td>
      </tr>
      <tr>
          <td>Python</td>
          <td>無 GC 上限、依賴 OS</td>
          <td>大 DataFrame / 大 dict 可能瞬間超限</td>
      </tr>
      <tr>
          <td>Node.js</td>
          <td>V8 heap limit 預設 ~1.5GB</td>
          <td>設 <code>--max-old-space-size</code> 配合 container limit</td>
      </tr>
  </tbody>
</table>
<h2 id="cpu-限制設計">CPU 限制設計</h2>
<h3 id="--cpus-vs---cpu-shares"><code>--cpus</code> vs <code>--cpu-shares</code></h3>
<table>
  <thead>
      <tr>
          <th>設定</th>
          <th>行為</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>--cpus=0.5</code></td>
          <td>Hard limit — 最多用 0.5 個 CPU core</td>
          <td>嚴格隔離、多 container 共用一台主機</td>
      </tr>
      <tr>
          <td><code>--cpu-shares=512</code></td>
          <td>Relative weight — 和其他 container 按比例分 CPU</td>
          <td>彈性分配、host 閒置時可用更多</td>
      </tr>
  </tbody>
</table>
<h3 id="cpu-throttle-症狀">CPU throttle 症狀</h3>
<p>CPU throttle 不會 crash（和 OOMKill 不同）。症狀是延遲上升 — request 處理時間從 10ms 變成 100ms，因為 container 的 CPU time 被 cgroup 暫停。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cat /sys/fs/cgroup/cpu/cpu.stat
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># nr_throttled: 被限制的次數</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># throttled_time: 累計被暫停的時間（奈秒）</span></span></span></code></pre></div><p>I/O bound 的服務（如監控 collector — 主要時間花在 SQLite 寫入和 HTTP 收發）通常不需要嚴格 CPU 限制。CPU 只在查詢處理（JSON 反序列化、聚合計算）時短暫使用。</p>
<h2 id="磁碟-io-考量">磁碟 I/O 考量</h2>
<h3 id="overlay-filesystem-的寫入放大">Overlay filesystem 的寫入放大</h3>
<p>Docker 的 overlay2 storage driver 把 container 的寫入操作分層管理。每次寫入新檔案或修改檔案，overlay 在上層（upper layer）建立副本再修改（copy-on-write）。對 SQLite 這類頻繁 fsync 的嵌入式資料庫，overlay 層增加 20-40% 的寫入延遲。</p>
<h3 id="volume-mount-繞過-overlay">Volume mount 繞過 overlay</h3>
<p>把需要高 I/O 效能的目錄掛載為 host volume（<code>-v /host/path:/container/path</code>），寫入直接到 host 檔案系統、繞過 overlay。</p>
<p>適用 volume mount 的場景：</p>
<ul>
<li>嵌入式資料庫的資料目錄（SQLite、BoltDB）</li>
<li>需要持久化的 log 檔案</li>
<li>大量小檔案寫入（cache 目錄）</li>
</ul>
<p>不適用 volume mount 的場景（用 overlay 即可）：</p>
<ul>
<li>暫存檔（處理完就刪）</li>
<li>只讀的設定檔（<code>-v config:/config:ro</code>，overlay 讀取開銷小）</li>
</ul>
<h3 id="tmpfs-mount">tmpfs mount</h3>
<p>記憶體中的暫存目錄，不寫磁碟。適合不需要持久化的高頻寫入（如 SDK 的離線 buffer、session 暫存）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker run --tmpfs /tmp:size<span class="o">=</span>64m ...</span></span></code></pre></div><h2 id="health-check-設計">Health Check 設計</h2>
<p>Container 的 health check 告訴 orchestrator「這個 container 是否正常運作」。Process 活著但 HTTP 不回應的場景（deadlock、資源耗盡）只靠 process 監控抓不到。</p>
<h3 id="dockerfile-healthcheck">Dockerfile HEALTHCHECK</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">HEALTHCHECK</span> --interval<span class="o">=</span>30s --timeout<span class="o">=</span>5s --retries<span class="o">=</span><span class="m">3</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  <span class="k">CMD</span> wget -q --spider http://localhost:8080/health <span class="o">||</span> <span class="nb">exit</span> <span class="m">1</span></span></span></code></pre></div><h3 id="docker-compose-healthcheck">Docker Compose healthcheck</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">healthcheck</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;CMD&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;wget&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;-q&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;--spider&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;http://localhost:8080/health&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="l">30s</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">  </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="l">5s</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="nt">retries</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="nt">start_period</span><span class="p">:</span><span class="w"> </span><span class="l">10s</span></span></span></code></pre></div><p><code>start_period</code> 是啟動寬限期 — container 啟動後前 10 秒的 health check 失敗不算。避免服務還在初始化時就被標記 unhealthy。</p>
<h3 id="kubernetes-probe-對應">Kubernetes probe 對應</h3>
<table>
  <thead>
      <tr>
          <th>Docker</th>
          <th>Kubernetes</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>HEALTHCHECK</td>
          <td>livenessProbe</td>
          <td>container 是否活著（失敗 → 重啟）</td>
      </tr>
      <tr>
          <td>—</td>
          <td>readinessProbe</td>
          <td>container 是否準備好接流量（失敗 → 從 service 移除）</td>
      </tr>
      <tr>
          <td>—</td>
          <td>startupProbe</td>
          <td>container 是否完成啟動（失敗 → 重啟、比 liveness 寬容）</td>
      </tr>
  </tbody>
</table>
<p>Docker 的 HEALTHCHECK 只有一種、等同 Kubernetes 的 livenessProbe。Kubernetes 的 readinessProbe 和 startupProbe 在 Docker 單機環境沒有對應物 — 它們是多 pod 場景下的流量控制機制。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>監控 collector 的 container 部署實例 → <a href="/blog/monitoring/04-collector/container-deployment/" data-link-title="Container 部署設計" data-link-desc="Docker 部署 collector 的設計 — SQLite 在 overlay filesystem 的 I/O 考量、volume mount、graceful shutdown、資源限制">Container 部署設計</a></li>
<li>服務探活與自動恢復 → <a href="/blog/devops/04-service-health/" data-link-title="模組四：服務探活與自動恢復" data-link-desc="服務掛了怎麼自動發現和恢復 — health check 設計、liveness vs readiness、systemd watchdog、process supervisor">DevOps 服務探活</a></li>
<li>負載平衡設計 → <a href="/blog/devops/01-load-balancing/" data-link-title="模組一：負載平衡與反向代理" data-link-desc="流量進來怎麼分給多個服務實例 — nginx / HAProxy / DNS round-robin 的選型和健康檢查路由設計">DevOps 負載平衡</a></li>
</ul>
]]></content:encoded></item><item><title>Resource Limit</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/resource-limit/</link><pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/resource-limit/</guid><description>&lt;p>Resource Limit 的核心概念是「限制一個服務實例可使用多少 CPU、memory 或其他運行資源」。它會直接影響啟動、排程、延遲、穩定性與故障型態，當成單純的部署參數會低估其影響面。 可先對照 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/retention/" data-link-title="Retention" data-link-desc="說明資料或事件保留多久，以及保留期限如何影響重放與成本">Retention&lt;/a>。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Resource Limit 位在 container、runtime、deployment platform 與 scheduler 之間。它決定服務在資源不足時是被 throttling、被拒絕排程，還是因記憶體超限而被終止。 可先對照 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/retention/" data-link-title="Retention" data-link-desc="說明資料或事件保留多久，以及保留期限如何影響重放與成本">Retention&lt;/a>。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>系統需要 resource limit 的訊號是：&lt;/p>
&lt;ul>
&lt;li>多個 instance 需要共享固定主機資源&lt;/li>
&lt;li>單一服務可能因記憶體成長或 CPU 尖峰影響其他服務&lt;/li>
&lt;li>平台需要用上限保護整體節點穩定性&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實網路服務的例子">接近真實網路服務的例子&lt;/h2>
&lt;p>Kubernetes container limit、單機 systemd service 的 cgroup 限制、worker pool 的 CPU 上限或 memory cap，都屬於 resource limit 的問題。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>設計時要區分 request 與 limit、理解 throttling 與 OOM 的差異，並把上限調整和實際流量、cache、啟動成本與重試行為一起看。Resource Limit 的目標是保護系統穩定，而不是只追求把數字填滿。&lt;/p></description><content:encoded><![CDATA[<p>Resource Limit 的核心概念是「限制一個服務實例可使用多少 CPU、memory 或其他運行資源」。它會直接影響啟動、排程、延遲、穩定性與故障型態，當成單純的部署參數會低估其影響面。 可先對照 <a href="/blog/backend/knowledge-cards/retention/" data-link-title="Retention" data-link-desc="說明資料或事件保留多久，以及保留期限如何影響重放與成本">Retention</a>。</p>
<h2 id="概念位置">概念位置</h2>
<p>Resource Limit 位在 container、runtime、deployment platform 與 scheduler 之間。它決定服務在資源不足時是被 throttling、被拒絕排程，還是因記憶體超限而被終止。 可先對照 <a href="/blog/backend/knowledge-cards/retention/" data-link-title="Retention" data-link-desc="說明資料或事件保留多久，以及保留期限如何影響重放與成本">Retention</a>。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>系統需要 resource limit 的訊號是：</p>
<ul>
<li>多個 instance 需要共享固定主機資源</li>
<li>單一服務可能因記憶體成長或 CPU 尖峰影響其他服務</li>
<li>平台需要用上限保護整體節點穩定性</li>
</ul>
<h2 id="接近真實網路服務的例子">接近真實網路服務的例子</h2>
<p>Kubernetes container limit、單機 systemd service 的 cgroup 限制、worker pool 的 CPU 上限或 memory cap，都屬於 resource limit 的問題。</p>
<h2 id="設計責任">設計責任</h2>
<p>設計時要區分 request 與 limit、理解 throttling 與 OOM 的差異，並把上限調整和實際流量、cache、啟動成本與重試行為一起看。Resource Limit 的目標是保護系統穩定，而不是只追求把數字填滿。</p>
]]></content:encoded></item></channel></rss>