<?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>Minikube on Tarragon</title><link>https://tarrragon.github.io/blog/tags/minikube/</link><description>Recent content in Minikube on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 05 Oct 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/minikube/index.xml" rel="self" type="application/rss+xml"/><item><title>單機練習用的 Kubernetes：kind、k3d 與 minikube 的節點模擬方式、預設元件、主機連線與單機上量得到和量不到的行為</title><link>https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/kubernetes/local-practice-clusters/</link><pubDate>Mon, 05 Oct 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/kubernetes/local-practice-clusters/</guid><description>&lt;p>這篇比較三個在一台電腦上起 Kubernetes 叢集的工具 kind、k3d 與 minikube：它們怎麼用容器模擬節點、預設附了哪些元件、主機怎麼連進叢集，以及節點共用同一台電腦這件事讓哪些 Kubernetes 行為量不到。本篇直接使用 Kubernetes 的元件與物件名詞：節點上的 kubelet 與 container runtime、控制平面、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/pod/" data-link-title="Pod" data-link-desc="在 Kubernetes 的輸出或設定裡看到 Pod，要知道它和容器的關係、為什麼它的 IP 與名稱會變、以及誰負責替補它時">Pod&lt;/a>、ReplicaSet、Deployment、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/kubernetes-service/" data-link-title="Kubernetes Service" data-link-desc="在 Kubernetes 裡要讓其他服務連到一組會換 IP 的 Pod、或排查請求為什麼沒有送到 Pod 時：Service 如何用標籤選 Pod、ClusterIP 與 DNS 名稱、EndpointSlice 與 ready 狀態">Service&lt;/a>（含 LoadBalancer 型別）、EndpointSlice、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/ingress/" data-link-title="Ingress" data-link-desc="要讓叢集外的 HTTP 請求依網域與路徑進到 Kubernetes 裡不同的服務、Ingress 建立了卻連不進去、或 ingress-nginx 結束維護後要換 controller 時：Ingress 物件與 Ingress controller 的分工、IngressClass，以及 Gateway API 的關係">Ingress&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness probe&lt;/a>，以及描述這些物件的 YAML 檔（manifest），它們的責任與彼此的關係在 &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/kubernetes/components-and-request-path/" data-link-title="Kubernetes 的元件與請求路徑：控制平面、節點、Pod、Deployment、Service 與 Ingress" data-link-desc="從 Docker Compose 的使用經驗出發，說明 Kubernetes 用期望狀態與控制迴圈管理容器的方式、控制平面與節點上各元件的責任、Deployment、ReplicaSet 與 Pod 三層工作負載、一個外部 HTTP 請求經過 Ingress 與 Service 抵達 Pod 的路徑和每一段出問題時的訊號，以及節點故障後的重建時間與這套模型在什麼規模下划算">Kubernetes 的元件與請求路徑&lt;/a> 說明。文中的輸出取自 macOS（Apple Silicon）上的 kind 0.33（Kubernetes v1.37）、k3d 5.9（k3s v1.35）與 minikube 1.39（Kubernetes v1.37）。&lt;/p>
&lt;h2 id="kindk3d-與-minikube-把叢集放在哪裡">kind、k3d 與 minikube 把叢集放在哪裡&lt;/h2>
&lt;p>三個工具的共同做法是&lt;strong>用容器扮演節點&lt;/strong>：每個 Kubernetes 節點是一個 Docker 容器，容器裡再跑 kubelet 與 container runtime，Pod 的容器在節點容器裡面再建一層。所以一台電腦上可以有多個節點，而它們都由同一個 Docker daemon 建立，共用這台電腦的 CPU 與記憶體。三個工具的差別在節點容器裡跑的是哪一種 Kubernetes、預設附了哪些元件：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>kind&lt;/th>
 &lt;th>k3d&lt;/th>
 &lt;th>minikube（Docker driver）&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>節點裡的 Kubernetes&lt;/td>
 &lt;td>上游版（Kubernetes 專案發行的原版），控制平面元件各自是一個 Pod&lt;/td>
 &lt;td>k3s（精簡發行版），控制平面元件併在同一個行程裡&lt;/td>
 &lt;td>上游版&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>建立叢集時的容器&lt;/td>
 &lt;td>每個節點一個&lt;/td>
 &lt;td>每個節點一個，加一個負責對應主機埠的代理容器&lt;/td>
 &lt;td>每個節點一個&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>節點容器的資源上限&lt;/td>
 &lt;td>不設，節點看得到 Docker 能用的全部資源&lt;/td>
 &lt;td>不設&lt;/td>
 &lt;td>設上限，用 &lt;code>--cpus&lt;/code>、&lt;code>--memory&lt;/code> 調整&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>預設附的 Ingress controller&lt;/td>
 &lt;td>沒有&lt;/td>
 &lt;td>Traefik&lt;/td>
 &lt;td>沒有，&lt;code>ingress&lt;/code> addon 裝的是 ingress-nginx&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>預設附的 metrics-server&lt;/td>
 &lt;td>沒有&lt;/td>
 &lt;td>有&lt;/td>
 &lt;td>沒有，&lt;code>metrics-server&lt;/code> addon 開啟&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>本機 build 的 image 送進叢集&lt;/td>
 &lt;td>&lt;code>kind load docker-image&lt;/code>&lt;/td>
 &lt;td>&lt;code>k3d image import&lt;/code>&lt;/td>
 &lt;td>&lt;code>minikube image load&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>多節點&lt;/td>
 &lt;td>設定檔列出各節點&lt;/td>
 &lt;td>&lt;code>--agents&lt;/code> 指定 worker 節點數，或寫在設定檔&lt;/td>
 &lt;td>&lt;code>--nodes&lt;/code> 指定節點數&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>表裡幾個名詞先說明：&lt;strong>Traefik&lt;/strong> 是一個反向代理，k3s 把它裝成叢集的 Ingress controller；&lt;strong>metrics-server&lt;/strong> 是收集各 Pod CPU 與記憶體用量的元件，HorizontalPodAutoscaler 與 &lt;code>kubectl top&lt;/code> 都讀它的資料；&lt;strong>addon&lt;/strong> 是 minikube 可以一鍵安裝的附加元件；&lt;strong>worker 節點&lt;/strong>是只跑工作負載、不跑控制平面元件的節點；minikube 的 &lt;strong>driver&lt;/strong> 決定節點用什麼建出來，Docker driver 用容器，另有幾種 driver 用虛擬機。本篇只比較 Docker driver，讓三者的條件相同。Docker Desktop 與 OrbStack 這類 Docker 桌面程式也內建 Kubernetes，在程式的設定裡開關，適合只想要一個現成叢集、不需要調整元件的情形。&lt;/p></description><content:encoded><![CDATA[<p>這篇比較三個在一台電腦上起 Kubernetes 叢集的工具 kind、k3d 與 minikube：它們怎麼用容器模擬節點、預設附了哪些元件、主機怎麼連進叢集，以及節點共用同一台電腦這件事讓哪些 Kubernetes 行為量不到。本篇直接使用 Kubernetes 的元件與物件名詞：節點上的 kubelet 與 container runtime、控制平面、<a href="/blog/backend/knowledge-cards/pod/" data-link-title="Pod" data-link-desc="在 Kubernetes 的輸出或設定裡看到 Pod，要知道它和容器的關係、為什麼它的 IP 與名稱會變、以及誰負責替補它時">Pod</a>、ReplicaSet、Deployment、<a href="/blog/backend/knowledge-cards/kubernetes-service/" data-link-title="Kubernetes Service" data-link-desc="在 Kubernetes 裡要讓其他服務連到一組會換 IP 的 Pod、或排查請求為什麼沒有送到 Pod 時：Service 如何用標籤選 Pod、ClusterIP 與 DNS 名稱、EndpointSlice 與 ready 狀態">Service</a>（含 LoadBalancer 型別）、EndpointSlice、<a href="/blog/backend/knowledge-cards/ingress/" data-link-title="Ingress" data-link-desc="要讓叢集外的 HTTP 請求依網域與路徑進到 Kubernetes 裡不同的服務、Ingress 建立了卻連不進去、或 ingress-nginx 結束維護後要換 controller 時：Ingress 物件與 Ingress controller 的分工、IngressClass，以及 Gateway API 的關係">Ingress</a> 與 <a href="/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness probe</a>，以及描述這些物件的 YAML 檔（manifest），它們的責任與彼此的關係在 <a href="/blog/backend/05-deployment-platform/vendors/kubernetes/components-and-request-path/" data-link-title="Kubernetes 的元件與請求路徑：控制平面、節點、Pod、Deployment、Service 與 Ingress" data-link-desc="從 Docker Compose 的使用經驗出發，說明 Kubernetes 用期望狀態與控制迴圈管理容器的方式、控制平面與節點上各元件的責任、Deployment、ReplicaSet 與 Pod 三層工作負載、一個外部 HTTP 請求經過 Ingress 與 Service 抵達 Pod 的路徑和每一段出問題時的訊號，以及節點故障後的重建時間與這套模型在什麼規模下划算">Kubernetes 的元件與請求路徑</a> 說明。文中的輸出取自 macOS（Apple Silicon）上的 kind 0.33（Kubernetes v1.37）、k3d 5.9（k3s v1.35）與 minikube 1.39（Kubernetes v1.37）。</p>
<h2 id="kindk3d-與-minikube-把叢集放在哪裡">kind、k3d 與 minikube 把叢集放在哪裡</h2>
<p>三個工具的共同做法是<strong>用容器扮演節點</strong>：每個 Kubernetes 節點是一個 Docker 容器，容器裡再跑 kubelet 與 container runtime，Pod 的容器在節點容器裡面再建一層。所以一台電腦上可以有多個節點，而它們都由同一個 Docker daemon 建立，共用這台電腦的 CPU 與記憶體。三個工具的差別在節點容器裡跑的是哪一種 Kubernetes、預設附了哪些元件：</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>kind</th>
          <th>k3d</th>
          <th>minikube（Docker driver）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>節點裡的 Kubernetes</td>
          <td>上游版（Kubernetes 專案發行的原版），控制平面元件各自是一個 Pod</td>
          <td>k3s（精簡發行版），控制平面元件併在同一個行程裡</td>
          <td>上游版</td>
      </tr>
      <tr>
          <td>建立叢集時的容器</td>
          <td>每個節點一個</td>
          <td>每個節點一個，加一個負責對應主機埠的代理容器</td>
          <td>每個節點一個</td>
      </tr>
      <tr>
          <td>節點容器的資源上限</td>
          <td>不設，節點看得到 Docker 能用的全部資源</td>
          <td>不設</td>
          <td>設上限，用 <code>--cpus</code>、<code>--memory</code> 調整</td>
      </tr>
      <tr>
          <td>預設附的 Ingress controller</td>
          <td>沒有</td>
          <td>Traefik</td>
          <td>沒有，<code>ingress</code> addon 裝的是 ingress-nginx</td>
      </tr>
      <tr>
          <td>預設附的 metrics-server</td>
          <td>沒有</td>
          <td>有</td>
          <td>沒有，<code>metrics-server</code> addon 開啟</td>
      </tr>
      <tr>
          <td>本機 build 的 image 送進叢集</td>
          <td><code>kind load docker-image</code></td>
          <td><code>k3d image import</code></td>
          <td><code>minikube image load</code></td>
      </tr>
      <tr>
          <td>多節點</td>
          <td>設定檔列出各節點</td>
          <td><code>--agents</code> 指定 worker 節點數，或寫在設定檔</td>
          <td><code>--nodes</code> 指定節點數</td>
      </tr>
  </tbody>
</table>
<p>表裡幾個名詞先說明：<strong>Traefik</strong> 是一個反向代理，k3s 把它裝成叢集的 Ingress controller；<strong>metrics-server</strong> 是收集各 Pod CPU 與記憶體用量的元件，HorizontalPodAutoscaler 與 <code>kubectl top</code> 都讀它的資料；<strong>addon</strong> 是 minikube 可以一鍵安裝的附加元件；<strong>worker 節點</strong>是只跑工作負載、不跑控制平面元件的節點；minikube 的 <strong>driver</strong> 決定節點用什麼建出來，Docker driver 用容器，另有幾種 driver 用虛擬機。本篇只比較 Docker driver，讓三者的條件相同。Docker Desktop 與 OrbStack 這類 Docker 桌面程式也內建 Kubernetes，在程式的設定裡開關，適合只想要一個現成叢集、不需要調整元件的情形。</p>
<h3 id="每個節點回報的容量">每個節點回報的容量</h3>
<p>節點容器沒有資源上限時，每個節點都回報 Docker 能用的全部資源。在 kind 建立的三節點叢集（一個控制平面、兩個 worker）上列出每個節點的容量：</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"><span class="c1"># 列出每個節點回報的 CPU 數與記憶體</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">kubectl get nodes -o custom-columns<span class="o">=</span>NAME:.metadata.name,CPU:.status.capacity.cpu,MEM:.status.capacity.memory</span></span></code></pre></div><p>三個節點回報的 CPU 數與記憶體完全相同，每一個都等於這台電腦給 Docker 的總量。排程器把三個節點的容量加起來當成叢集的容量，得到的是實際資源的三倍，所以它最多會排進資源需求合計達實際資源三倍的 Pod，而這些 Pod 搶的是同一批 CPU。回報的數字隨各自機器的 Docker 設定不同，在自己的叢集上跑一次這條指令就知道。</p>
<h3 id="主機與節點各自的-image-儲存">主機與節點各自的 image 儲存</h3>
<p>主機上的 Docker 與節點容器裡的 container runtime 各有自己的 image 儲存。在主機上 <code>docker build</code> 出 <code>lab/hello:dev</code> 之後直接部署，節點在自己的儲存裡找不到這個 image，於是去 Docker Hub 拉，而 Docker Hub 上沒有它，Pod 停在 <code>ImagePullBackOff</code>（拉取失敗、等待一段時間後重試的狀態）：</p>





<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">Failed to pull image &#34;lab/hello:dev&#34;: failed to resolve reference &#34;docker.io/lab/hello:dev&#34;:
</span></span><span class="line"><span class="ln">2</span><span class="cl">pull access denied, repository does not exist or may require authorization</span></span></code></pre></div><p>三個工具各有一條指令把 image 從主機複製進節點的儲存，對應比較表裡「本機 build 的 image 送進叢集」那一列：</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">kind load docker-image lab/hello:dev --name lab   <span class="c1"># --name 指定叢集名稱，複製到這個叢集的每個節點</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">k3d image import lab/hello:dev -c lab             <span class="c1"># -c 指定叢集名稱</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">minikube image load lab/hello:dev -p lab          <span class="c1"># -p 指定 profile，也就是 minikube 對叢集的稱呼</span></span></span></code></pre></div><p>複製進去之後還有一個條件：Kubernetes 對 <code>latest</code> 標籤（以及沒寫標籤）的預設拉取策略是 <code>Always</code>，每次建立容器都向 registry 拉，不看節點上有沒有，所以 <code>lab/hello:latest</code> 複製進去之後仍然拉不到。明確的版本標籤（例如 <code>dev</code>）的預設策略是 <code>IfNotPresent</code>，節點上有就直接用；用 <code>latest</code> 時要在 Pod 範本裡把 <code>imagePullPolicy</code> 設成 <code>IfNotPresent</code>。</p>
<h3 id="缺-ingress-controller-或-metrics-server-時的現形位置ingress-的-address-與-hpa-的-targets">缺 Ingress controller 或 metrics-server 時的現形位置：Ingress 的 ADDRESS 與 HPA 的 TARGETS</h3>
<p>比較表裡「預設附的 Ingress controller」與「預設附的 metrics-server」兩列，決定了同一份設定在三個工具上能不能動。缺元件時，物件照樣建立成功，只是沒有程式處理它，現形的位置在物件的狀態欄位。</p>
<p>在 kind 叢集套用 Ingress，<code>kubectl get ingress</code> 的 <code>CLASS</code> 是 <code>&lt;none&gt;</code>、<code>ADDRESS</code> 是空的，因為叢集裡沒有任何 Ingress controller。k3d 的同一份 Ingress 由 Traefik 處理、<code>ADDRESS</code> 有值；minikube 要先開啟 <code>ingress</code> addon。</p>
<p>在 kind 叢集建立 HorizontalPodAutoscaler（HPA，依指標自動調整副本數的物件），<code>TARGETS</code> 欄停在 <code>&lt;unknown&gt;</code>，副本數不會變：</p>





<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">NAME   REFERENCE        TARGETS              MINPODS   MAXPODS   REPLICAS
</span></span><span class="line"><span class="ln">2</span><span class="cl">web    Deployment/web   cpu: &lt;unknown&gt;/50%   2         6         3</span></span></code></pre></div><p><code>kubectl describe hpa web</code> 的事件寫著 <code>unable to fetch metrics from resource metrics API</code>：HPA 讀的 CPU 使用率來自 metrics-server，kind 沒有附這個元件，<code>kubectl top pods</code> 也回 <code>Metrics API not available</code>。k3d 預設附了 metrics-server，minikube 用 addon 開啟，kind 要另外安裝。HPA 另一個前提是 Pod 範本寫了 <code>resources.requests.cpu</code>，它的百分比是相對於這個請求量計算的。</p>
<p>minikube 的 <code>ingress</code> addon 安裝的是社群的 ingress-nginx controller，這個專案已經結束維護，維護狀態與替代方案見 <a href="/blog/backend/knowledge-cards/ingress/" data-link-title="Ingress" data-link-desc="要讓叢集外的 HTTP 請求依網域與路徑進到 Kubernetes 裡不同的服務、Ingress 建立了卻連不進去、或 ingress-nginx 結束維護後要換 controller 時：Ingress 物件與 Ingress controller 的分工、IngressClass，以及 Gateway API 的關係">Ingress</a>。在本機練習 Ingress 的寫法不受影響，正式環境的 controller 另外選擇。</p>
<h2 id="主機連進叢集的方式port-forwardk3d-的埠對應與-minikube-tunnel">主機連進叢集的方式：port-forward、k3d 的埠對應與 minikube tunnel</h2>
<p>macOS 與 Windows 上的 Docker 把容器放在一台虛擬機裡，主機通常連不到節點容器的 IP；Linux 上的 Docker 直接跑在主機上，節點容器的 IP 從主機連得到。所以在 macOS 與 Windows 上，叢集裡的服務要靠某種通道或埠對應才連得進去，三個工具各自用不同的方式補上這一段。部分 Docker 桌面程式會把容器 IP 路由到主機，那時直接連節點 IP 也通，但這個行為隨 Docker 的實作而不同，換一台機器不一定成立。</p>
<h3 id="kubectl-與-context">kubectl 與 context</h3>
<p>三個工具建立叢集時，都在 kubeconfig（kubectl 讀的設定檔，預設在 <code>~/.kube/config</code>）裡新增一個 <strong>context</strong>，並把它設成目前使用的那一個。context 記錄「連哪個叢集、用哪個身分」，所以之後的 <code>kubectl</code> 指令都作用在剛建立的叢集上；同時有多個叢集時，<code>kubectl config get-contexts</code> 列出全部、<code>kubectl config use-context &lt;名稱&gt;</code> 切換。三個工具建出來的都是標準的 Kubernetes API，所以同一份 manifest 不用改就能套上去：</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"><span class="c1"># web.yaml 是〈元件與請求路徑〉裡 web 的 Deployment 與 Service，web-ingress.yaml 是它的 Ingress</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">kubectl apply -f web.yaml -f web-ingress.yaml</span></span></code></pre></div><p>kubectl 本身是獨立於叢集的用戶端：它連哪個叢集由 context 決定，它是哪個版本由 <code>PATH</code> 選中哪一份執行檔決定，兩件事彼此無關。Docker 桌面程式、Homebrew 與各工具可能各自裝了一份 kubectl，<code>which -a kubectl</code> 會列出不只一個，排在 <code>PATH</code> 前面的那一份才是實際執行的。<code>kubectl version</code> 同時印出 kubectl 本身（Client Version）與叢集（Server Version）的版本，Kubernetes 支援的兩者差距是前後一個 minor 版本。</p>
<h3 id="kindkubectl-建立的通道">kind：kubectl 建立的通道</h3>





<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"><span class="c1"># 建立名為 lab 的單節點叢集；新增的 context 叫 kind-lab</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">kind create cluster --name lab
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 把主機的 8080 埠轉到叢集裡 web 這個 Service 的 80 埠；指令持續執行，Ctrl-C 結束</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">kubectl port-forward svc/web 8080:80</span></span></code></pre></div><p>kind 沒有附 Ingress controller，也不替叢集建立對外的埠對應，所以最直接的連法是 <code>kubectl port-forward</code>：kubectl 經由 API server 建一條通道，主機的 8080 埠收到的連線從這條通道送進去。指定 <code>svc/web</code> 時，kubectl 從 Service 選出的 Pod 裡挑一個建立通道，所有連線都送到那一個 Pod，不經過 Service 的分派，也不經過 Ingress，適合確認單一服務能不能回應。kind 官方文件建議要練 Ingress 時另外執行 <a href="https://github.com/kubernetes-sigs/cloud-provider-kind">cloud-provider-kind</a>：它在主機上替叢集提供 LoadBalancer 型別的 Service，從 0.9.0 版起直接支援 Ingress 與 Gateway API；在 macOS 與 WSL2 上要用 <code>sudo</code> 執行，因為它要在主機上建立網路對應。在自己的電腦上執行時照常輸入密碼；在 GitHub Actions 的 GitHub-hosted runner 上，sudo 預設不需要密碼，CI 裡可以直接執行。兩種環境對 sudo 的設計為什麼不同，見 <a href="/blog/linux/install/unattended-remote-work/" data-link-title="讓機器跑無人值守的長任務" data-link-desc="要讓一台遠端機器在你不盯著時自己跑完一個長任務或 agent、卻被 sudo 密碼 / 斷線就死 / 推不出結果擋住時讀">讓機器跑無人值守的長任務</a> 的〈兩種自動化環境的 sudo 設計：GitHub Actions runner 與 Ansible〉。</p>
<p>kind 的多節點叢集寫在設定檔裡，〈單機上量得到與量不到的行為〉裡的節點故障實驗用的就是這種叢集：</p>





<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="c"># kind-multi.yaml，用 kind create cluster --name multi --config kind-multi.yaml 建立</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">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Cluster</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">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">kind.x-k8s.io/v1alpha4</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">nodes</span><span class="p">:</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">role</span><span class="p">:</span><span class="w"> </span><span class="l">control-plane</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">role</span><span class="p">:</span><span class="w"> </span><span class="l">worker</span><span class="w"> </span><span class="c"># 每一列是一個節點容器，要幾個 worker 就列幾列</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">  </span>- <span class="nt">role</span><span class="p">:</span><span class="w"> </span><span class="l">worker</span></span></span></code></pre></div><p>節點容器的名稱是「叢集名稱-角色」，例如 <code>multi-control-plane</code>、<code>multi-worker</code>、<code>multi-worker2</code>。</p>
<h3 id="k3d代理容器對應的主機埠">k3d：代理容器對應的主機埠</h3>





<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"><span class="c1"># 建立名為 lab 的叢集，並把主機的 8080 埠對應到叢集入口的 80 埠</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">k3d cluster create lab -p <span class="s2">&#34;8080:80@loadbalancer&#34;</span></span></span></code></pre></div><p>k3d 除了節點容器，還多建一個代理容器。<code>-p</code> 的格式是「主機埠:容器埠@要對應的容器」，<code>@loadbalancer</code> 指的就是這個代理容器：Docker 把主機的 8080 埠接到它，它再把流量轉給叢集裡的 Traefik。所以主機直接連 <code>localhost:8080</code> 就進到 Ingress，請求的 Host 標頭決定符合哪一條規則：</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"><span class="c1"># Host 標頭是 web.localhost，符合 Ingress 規則，轉到 web 的 Pod</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">curl -H <span class="s1">&#39;Host: web.localhost&#39;</span> http://localhost:8080/
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># curl 預設送出 Host: localhost:8080，不符合任何規則，Traefik 回 404</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">curl http://localhost:8080/</span></span></code></pre></div><p>8080 是示範用的主機埠，主機上已經被別的程式佔用時，換一個沒在用的埠。</p>
<h3 id="minikubetunnel-補上的路由">minikube：tunnel 補上的路由</h3>





<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"><span class="c1"># 用 Docker driver 建立名為 lab 的叢集；profile 名稱也是 context 的名稱</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">minikube start --driver<span class="o">=</span>docker --profile lab
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 讓 LoadBalancer 型別的 Service 與 Ingress 從主機的 127.0.0.1 連得到；持續執行</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">minikube tunnel -p lab
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 只連單一 Service 時，印出一個從主機可連的 127.0.0.1 網址；終端也要保持開著</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">minikube service web -p lab --url</span></span></code></pre></div><p>minikube 的做法是在主機上執行一個持續運作的程式，補上主機到節點的路由。<code>minikube tunnel</code> 讓 LoadBalancer 型別的 Service 與 Ingress 從主機的 <code>127.0.0.1</code> 連得到，啟用 <code>ingress</code> addon 時 minikube 也會提示要跑它；<code>minikube service</code> 只開一條到單一 Service 的通道。兩者在 macOS 的 Docker driver 上都要讓終端保持開著。minikube 第一次啟動要下載節點 image 與一份預先打包的 Kubernetes 元件，耗時取決於網路，之後再建立叢集直接用快取。</p>
<h2 id="單機上量得到與量不到的行為">單機上量得到與量不到的行為</h2>
<p>單機叢集跑的是完整的 Kubernetes 元件，所以控制迴圈、排程、Service 的流量分派、probe 這些<strong>機制</strong>的行為與正式叢集相同。差別在節點不是獨立的機器：它們共用同一組 CPU、同一個作業系統核心、同一塊磁碟。判斷一個實驗在單機上有沒有意義，看那個實驗量的是機制，還是量容量與故障的獨立性。</p>
<p><strong>量得到的是機制</strong>：</p>
<ul>
<li>
<p><strong>自我修復</strong>：刪掉 Pod，ReplicaSet 在幾秒內補一個新的。</p>
</li>
<li>
<p><strong>readiness 摘除</strong>：讓一個 Pod 的健康檢查失敗，它在 EndpointSlice 裡被標成未就緒，請求不再送過去，Pod 也不會被重啟。</p>
</li>
<li>
<p><strong>滾動更新與回退</strong>：改 image 觀察新舊 ReplicaSet 交接；改成不存在的 image 標籤，觀察更新停住而舊版繼續服務，再用 <code>kubectl rollout undo</code> 回退。</p>
</li>
<li>
<p><strong>Ingress 路由</strong>：依 Host 與路徑分流到不同 Service，以及各種設定錯誤時 controller 回的狀態碼。</p>
</li>
<li>
<p><strong>節點故障的時間線</strong>：在 kind 的三節點叢集用 <code>docker stop multi-worker</code> 停掉一個 worker 節點容器，約一分鐘後節點變成 <code>NotReady</code>，它上面的 Pod 同時在 EndpointSlice 裡被標成未就緒；再約五分鐘，那些 Pod 才在另一個 worker 上重建。兩個時間點的機制見 <a href="/blog/backend/05-deployment-platform/vendors/kubernetes/components-and-request-path/" data-link-title="Kubernetes 的元件與請求路徑：控制平面、節點、Pod、Deployment、Service 與 Ingress" data-link-desc="從 Docker Compose 的使用經驗出發，說明 Kubernetes 用期望狀態與控制迴圈管理容器的方式、控制平面與節點上各元件的責任、Deployment、ReplicaSet 與 Pod 三層工作負載、一個外部 HTTP 請求經過 Ingress 與 Service 抵達 Pod 的路徑和每一段出問題時的訊號，以及節點故障後的重建時間與這套模型在什麼規模下划算">元件與請求路徑</a> 的〈節點故障的時間線：NotReady 與 Pod 重建〉。觀察服務有沒有中斷時，請求要從 Ingress 入口（kind 要先自行安裝 controller）或叢集裡的用戶端 Pod 送，<code>kubectl port-forward</code> 只連一個 Pod，那個 Pod 剛好在被停的節點上時會量出假中斷；停的節點也要避開 Ingress controller 所在的那一個：controller 只有一個副本時（例如 k3d 預設的 Traefik），它所在的節點停了，整個入口都會斷。</p>
</li>
<li>
<p><strong>HPA 的運作</strong>：在 k3d 叢集上設定 HPA，再從另一個 Pod 用 16 個迴圈持續送請求，HPA 依 CPU 使用率把副本加上去，概念見 <a href="/blog/backend/knowledge-cards/autoscaling/" data-link-title="Autoscaling" data-link-desc="說明系統如何依負載自動調整服務實例數量">Autoscaling</a>：</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"><span class="c1"># CPU 使用率（相對於 requests.cpu）超過 50% 就加副本，副本數維持在 2 到 6 之間</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 較舊版本的 kubectl 把 --cpu=50% 寫成 --cpu-percent=50</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">kubectl autoscale deployment web --cpu<span class="o">=</span>50% --min<span class="o">=</span><span class="m">2</span> --max<span class="o">=</span><span class="m">6</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 起一個名為 load 的 Pod，在裡面開 16 個迴圈持續請求 web；它沒有設 CPU 上限，會用掉這台電腦大量的 CPU</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">kubectl run load --image<span class="o">=</span>busybox:1.37 --restart<span class="o">=</span>Never -- <span class="se">\
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="se"></span>  sh -c <span class="s1">&#39;for j in $(seq 1 16); do (while true; do wget -q -O /dev/null http://web/; done) &amp; done; wait&#39;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 觀察使用率與副本數</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">kubectl get hpa web</span></span></code></pre></div><p>一分鐘內 <code>TARGETS</code> 顯示 <code>cpu: 335%/50%</code>，副本從 3 個加到上限 6 個。</p>
</li>
</ul>
<p><strong>量不到的是容量與獨立性</strong>：</p>
<ul>
<li><strong>擴充帶來的吞吐量上限</strong>：上面的 HPA 實驗證明了「負載上升時副本會被加上去」這個機制，量不到「加副本能讓服務多處理多少流量」。這個實驗裡每個 Pod 的 CPU limit 是 0.2 個 CPU，副本從 3 個加到 6 個，可用的 CPU 從 0.6 個變成 1.2 個，吞吐量確實會上升；但再往上加，總量最多就是這台電腦的 CPU，之後六個或十個 Pod 分的都是同一批 CPU。正式叢集加副本時，排程器會把新 Pod 放到別台還有空間的機器，必要時再加節點，總容量跟著機器數成長，這條路在單機上不存在。所以在單機叢集上壓測，量到的是這台電腦扣掉叢集元件開銷之後的上限。</li>
<li><strong>節點故障的獨立性</strong>：停掉一個節點容器只模擬了「kubelet 失聯」。真實的節點故障包含磁碟損壞、網路分區、整個可用區斷線，而單機的各個節點共用同一個作業系統核心與磁碟，主機出問題時全部一起受影響。</li>
<li><strong>跨機器的網路延遲</strong>：節點之間的網路是同一台電腦裡的虛擬網路，量不到跨機器、跨可用區的延遲實際的分布與抖動；在節點容器的網路介面上人工注入延遲，得到的是自己設定的值。</li>
</ul>
<p>所以單機叢集適合學 Kubernetes 的操作模型、驗證 manifest 與 probe 設定、在 CI 裡跑需要 Kubernetes 的整合測試。壓測結果裡可以帶到別的環境的，是單一 Pod 在固定 CPU limit 下的吞吐量，前提是量的時候這台電腦的 CPU 沒有用滿，而且目標環境的 CPU 架構與單核效能和這台電腦相近（CPU limit 是每段時間可用的 CPU 時間，不是固定的算力，換了 CPU 型號要在目標環境重量一次）；叢集的總吞吐量帶不走。在同一台電腦上比較兩個版本或兩種實作時，一次只跑一個受測服務、兩者設相同的 requests 與 limits，負載產生器若也在這台電腦上，它用掉的 CPU 要寫進結果的說明裡，否則量到的差距可能來自資源分配，而不是受測的那一項。</p>
<h2 id="kindk3d-與-minikube-的閒置開銷與適合練習的對象">kind、k3d 與 minikube 的閒置開銷與適合練習的對象</h2>
<p>在同一台電腦上量叢集剛建立、還沒部署服務時的記憶體，三者都在數百 MiB 到 1GiB 的量級：kind 與 k3d 的單節點相近（k3d 的數字已包含 Traefik 與 metrics-server），minikube 較高；多節點時，每個 worker 的開銷遠低於控制平面。實際數字隨版本與機器不同，用 <code>docker stats --no-stream</code> 在自己的機器上量。macOS 上叢集能用的總量是 Docker 虛擬機的記憶體設定，<code>docker stats</code> 量的是容器，不含虛擬機本身的開銷，記憶體小的電腦先確認這個上限夠不夠，不夠就調高；k3d 不練 Ingress 時，可以用 <code>k3d cluster create lite --k3s-arg &quot;--disable=traefik@server:*&quot;</code> 建立不含 Traefik 的叢集，閒置記憶體明顯較低。壓測時叢集元件與受測服務共用資源，這筆開銷要寫進結果的說明裡。</p>
<p>三個工具的機制相同，差別在預設元件與設定方式，選擇看要練習的對象：</p>
<ul>
<li>要貼近上游 Kubernetes 的元件組成、在 CI 裡起叢集跑測試：kind。它原本是 Kubernetes 專案用來測試自身的工具；代價是 Ingress controller 與 metrics-server 要自己補。</li>
<li>要開箱就有 Ingress 與 HPA 可以練：k3d。k3s 換掉了部分上游元件，行為細節與上游有差異時，以要部署的正式環境為準。</li>
<li>要用 addon 一鍵開啟元件、需要 dashboard、或要用虛擬機當節點：minikube。</li>
<li>要練叢集本身的建置、升級與 etcd 備份：三個工具都替使用者做完了這一層，要改用多台虛擬機，以 kubeadm 或 k3s 自己建叢集。每台虛擬機有自己的作業系統核心，一個節點的核心或 kubelet 出問題不會波及其他節點，主機的硬體與磁碟仍然共用。</li>
</ul>
<p>CI 裡跑需要 Ingress 的整合測試時，kind 與 k3d 兩條都成立，分岔在測試要驗的是誰的行為：要驗與正式環境同一個 Ingress controller 的行為（annotation、預設逾時、錯誤回應），用 kind，或用 k3d 加 <code>--disable=traefik@server:*</code> 之後裝那一個 controller；只驗 Ingress 規則與 Service 的串接，k3d 預設的 Traefik 就夠。</p>
<h2 id="延伸閱讀元件部署策略compose-與正式環境的平台">延伸閱讀：元件、部署策略、Compose 與正式環境的平台</h2>
<ul>
<li>在 kind 上裝 kube-prometheus-stack 的記憶體用量、序列數，以及主機記憶體不足時叢集的連鎖故障：<a href="/blog/backend/04-observability/vendors/prometheus/kubernetes-monitoring-entry/" data-link-title="在 Kubernetes 上接上監控：kube-prometheus-stack 的元件、ServiceMonitor 與 PrometheusRule 的選取、節點標籤、儀表板 sidecar、Alertmanager 的預設路由、控制平面的讀取與單節點叢集的資源用量" data-link-desc="在 Kubernetes 上以 kube-prometheus-stack 接上監控：chart 裝了哪些元件與 CRD，讀取對象與告警規則改以 ServiceMonitor、PrometheusRule 宣告之後由誰轉成 Prometheus 的設定，release 標籤決定哪些宣告會被選中，主機標籤改成節點標籤，儀表板經由 ConfigMap 與 sidecar 載入，Alertmanager 預設不送任何通知，以及控制平面元件只綁本機時讀不到的原因、單節點叢集上的資源用量與記憶體不足時的表現">在 Kubernetes 上接上監控</a> 的〈單節點叢集上的資源用量與記憶體不足時的表現〉。</li>
<li>本篇用到的 Pod、Service、EndpointSlice、Ingress 各自負責什麼，以及請求在它們之間的路徑：<a href="/blog/backend/05-deployment-platform/vendors/kubernetes/components-and-request-path/" data-link-title="Kubernetes 的元件與請求路徑：控制平面、節點、Pod、Deployment、Service 與 Ingress" data-link-desc="從 Docker Compose 的使用經驗出發，說明 Kubernetes 用期望狀態與控制迴圈管理容器的方式、控制平面與節點上各元件的責任、Deployment、ReplicaSet 與 Pod 三層工作負載、一個外部 HTTP 請求經過 Ingress 與 Service 抵達 Pod 的路徑和每一段出問題時的訊號，以及節點故障後的重建時間與這套模型在什麼規模下划算">Kubernetes 的元件與請求路徑</a>。</li>
<li>在單機叢集上練的滾動更新與 probe，要決定批次節奏、加上 liveness 與 startup probe 的判斷：<a href="/blog/backend/05-deployment-platform/kubernetes-deployment/" data-link-title="5.2 Kubernetes 部署策略" data-link-desc="整理 deployment、probe 與 rolling update">5.2 Kubernetes 部署策略</a>。</li>
<li>單機上只需要把幾個容器串起來、不需要 Kubernetes 的控制迴圈時，Compose 的寫法與它的適用範圍：<a href="/blog/backend/05-deployment-platform/vendors/docker/docker-compose/" data-link-title="Docker Compose：多 service dev 環境編排" data-link-desc="一個 app 要好幾個 container(DB / cache / web)、手動 docker run 串不起來、或 compose 起來後 app 連不到 DB 或 DB 還沒 ready 就被連時回來讀 — 多 service 怎麼宣告式編排">Docker Compose：多 service dev 環境編排</a>。</li>
<li>練習完要在雲端上選平台，託管 Kubernetes 與不用 Kubernetes 的託管容器服務怎麼取捨：<a href="/blog/devops/infra/05-core-services/compute-ecs-eks/" data-link-title="運算平台上 IaC — ECS 與 EKS" data-link-desc="容器運算平台的 IaC 描述：ECS 與 EKS 選型、task definition 與映像版本解耦、IAM task role 分離、auto-scaling 策略">運算平台上 IaC — ECS 與 EKS</a>。</li>
</ul>
]]></content:encoded></item></channel></rss>