<?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/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Kubernetes｜林鼎淵 Dean Lin</title>
    <link>https://deanlin.net/tags/kubernetes/</link>
    <image><url>https://deanlin.net/images/author_hu_aa256fc934e9a932.jpg</url><title>Kubernetes｜林鼎淵 Dean Lin</title><link>https://deanlin.net/tags/kubernetes/</link><width>144</width><height>144</height></image>
    <description>林鼎淵（Dean Lin）的部落格：生成式 AI 企業內訓講師、外商資安公司軟體專家、7 本書作者。400 多篇文章用白話分享生成式 AI 實戰、Claude Code 與 Vibe Coding 教學、團隊合作與職場成長。</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <copyright>© 2020–2026 林鼎淵 Dean Lin</copyright>
    <lastBuildDate>Fri, 21 Jan 2022 12:27:18 +0800</lastBuildDate>
    <atom:link href="https://deanlin.net/tags/kubernetes/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>模擬 K8s Auto Scaling（可在 Local 端測試）</title>
      <link>https://deanlin.net/blog/202201/kubernetes-autoscaling-local/</link>
      <pubDate>Fri, 21 Jan 2022 12:27:18 +0800</pubDate>
      <guid>https://deanlin.net/blog/202201/kubernetes-autoscaling-local/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>K8s 最吸引人的其中一個功能應該就是用 Auto Scaling 來應付高併發(High concurrency)情境，今天就用一個水平自動擴展(HPA)的範例來讓大家了解他的實際運作模式。</description>
      <media:content url="https://deanlin.net/blog/202201/kubernetes-autoscaling-local/images/kubernetes-autoscaling-local-og_hu_7b901ad632188507.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>K8s 最吸引人的其中一個功能應該就是用 Auto Scaling 來應付高併發(High concurrency)情境，今天就用一個水平自動擴展(HPA)的範例來讓大家了解他的實際運作模式。</p>
<p><img src="https://deanlin.net/blog/202201/kubernetes-autoscaling-local/images/kubernetes-autoscaling-local-01.webp" width="1400" height="740" alt="模擬 K8s Auto Scaling（可在 Local 端測試）（圖 1）" fetchpriority="high" decoding="async"></p>
<pre tabindex="0"><code>文章大綱
一、撰寫 Auto Scaling 的 yaml 檔
  ➤ HorizontalPodAutoscaler（水平自動擴展）參數說明
  ➤ 建立「nodetest-k8s-auto.yaml」來做 Auto Scaling 測試
二、調整 Service、Deployment 參數並加入 yaml 檔
  ➤ 調整 Service 參數
  ➤ 調整 Deployment 參數
  ➤ 將 Service、Deployment 設定合併到 nodetest-k8s-auto.yaml 中
三、部署＆測試 pod 是否會依據流量的增加自動擴展
  ➤ 開啟流量監控、部署 K8s
  ➤ 取得 Deployment 的外部 ip，然後瘋狂存取來測試 Auto Scaling
  ➤ 瘋狂存取後後，確認 maxReplicas 設定是否生效
  ➤ 關閉存取後，確認 minReplicas 設定是否生效
  ➤ 測試完成後將建立的 K8s 服務刪除、關閉 minikube
</code></pre><blockquote>
<p>如果讀者尚未在本機安裝 K8s 環境，可以先閱讀：「<a href="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/">帶你將 Node.js Web App 給 Docker 化，並用 Kubernetes 部署的超簡單範例</a>」來設定基礎環境喔！</p>
</blockquote>
<h2 id="一撰寫-auto-scaling-的-yaml-檔">一、撰寫 Auto Scaling 的 yaml 檔<a class="h-anchor" href="#%e4%b8%80%e6%92%b0%e5%af%ab-auto-scaling-%e7%9a%84-yaml-%e6%aa%94" aria-label="這個段落的連結"></a></h2>
<blockquote>
<p>你也可以直接去<a href="https://github.com/dean9703111/nodejs-docker-k8s-auto-scaling-example">筆者的 github</a>，直接將專案 Clone 到本機來做測試。</p>
</blockquote>
<h3 id="-horizontalpodautoscaler水平自動擴展參數說明">HorizontalPodAutoscaler（水平自動擴展）參數說明<a class="h-anchor" href="#-horizontalpodautoscaler%e6%b0%b4%e5%b9%b3%e8%87%aa%e5%8b%95%e6%93%b4%e5%b1%95%e5%8f%83%e6%95%b8%e8%aa%aa%e6%98%8e" aria-label="這個段落的連結"></a></h3>
<ul>
<li><strong>scaleTargetRef</strong>：填寫你想要針對哪個 ReplicaSet 或 Deployment 或 Replication Controller 來做 Scaling。</li>
<li><strong>minReplicas、maxReplicas</strong>：Pod 擴展範圍。</li>
<li><strong>targetCPUUtilizationPercentage</strong>：Auto Scaling 是根據 CPU 用量來做擴展，判定的依據為 Deployment resources 的 requests 設定值（如果沒填寫基本上就不會動 XD）。</li>
</ul>
<h3 id="-建立nodetest-k8s-autoyaml來做-auto-scaling-測試">建立「nodetest-k8s-auto.yaml」來做 Auto Scaling 測試<a class="h-anchor" href="#-%e5%bb%ba%e7%ab%8bnodetest-k8s-autoyaml%e4%be%86%e5%81%9a-auto-scaling-%e6%b8%ac%e8%a9%a6" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">apiVersion</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">autoscaling/v1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">kind</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">HorizontalPodAutoscaler</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">metadata</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">spec</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">scaleTargetRef</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">apiVersion</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">apps/v1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">kind</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">Deployment</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">minReplicas</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">maxReplicas</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">5</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">targetCPUUtilizationPercentage</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">10</span><span style="color:#6e7681">
</span></span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/3626e36b2122a62142d94e740157f792" rel="noopener">nodetest-k8s-auto.yaml</a>（GitHub Gist）</p>
<hr>
<h2 id="二調整-servicedeployment-參數並加入-yaml-檔">二、調整 Service、Deployment 參數並加入 yaml 檔<a class="h-anchor" href="#%e4%ba%8c%e8%aa%bf%e6%95%b4-servicedeployment-%e5%8f%83%e6%95%b8%e4%b8%a6%e5%8a%a0%e5%85%a5-yaml-%e6%aa%94" aria-label="這個段落的連結"></a></h2>
<blockquote>
<p>這裡的 Service、Deployment 都是從<a href="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/">先前這篇文章</a>改良過來的喔。</p>
<p>Docker image 可以 pull 筆者的範例，或是使用自己的都可以。</p>
</blockquote>
<h3 id="-調整-service-參數">調整 Service 參數<a class="h-anchor" href="#-%e8%aa%bf%e6%95%b4-service-%e5%8f%83%e6%95%b8" aria-label="這個段落的連結"></a></h3>
<ul>
<li><strong>nodePort</strong>：這裡建議指定一個 port，如果沒有設定的話，最後對外 ip 的 port 為隨機產生。</li>
</ul>
<h3 id="-調整-deployment-參數">調整 Deployment 參數<a class="h-anchor" href="#-%e8%aa%bf%e6%95%b4-deployment-%e5%8f%83%e6%95%b8" aria-label="這個段落的連結"></a></h3>
<ul>
<li><strong>resources 的 requests</strong>：如果 cpu 設定為 <code>100m</code>，而 targetCPUUtilizationPercentage 設定為 <code>10</code>；當 Pod 的 CPU 使用率為 <code>1m</code>（100m x 10%） 的時候就要進行 Auto Scaling。</li>
<li><strong>replicas</strong>：特別設定為 1 方便觀察，原則上他的值要跟 minReplicas 相同。</li>
</ul>
<h3 id="-將-servicedeployment-設定合併到-nodetest-k8s-autoyaml-中">將 Service、Deployment 設定合併到 nodetest-k8s-auto.yaml 中<a class="h-anchor" href="#-%e5%b0%87-servicedeployment-%e8%a8%ad%e5%ae%9a%e5%90%88%e4%bd%b5%e5%88%b0-nodetest-k8s-autoyaml-%e4%b8%ad" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">apiVersion</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">v1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">kind</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">Service</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">metadata</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-app-service</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">spec</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">selector</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">app</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">ports</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>- <span style="color:#7ee787">protocol</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#34;TCP&#34;</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">port</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">8080</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">targetPort</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">8080</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">nodePort</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">30390</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">type</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">LoadBalancer</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">---</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">apiVersion</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">apps/v1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">kind</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">Deployment</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">metadata</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">spec</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">selector</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">matchLabels</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">app</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">replicas</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">template</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">metadata</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">labels</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#7ee787">app</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">spec</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">containers</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>- <span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">          </span><span style="color:#7ee787">image</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">babydragon9703111/node-web-app:latest</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">          </span><span style="color:#7ee787">imagePullPolicy</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">Always</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">          </span><span style="color:#7ee787">ports</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>- <span style="color:#7ee787">containerPort</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">8080</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">          </span><span style="color:#7ee787">resources</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#7ee787">requests</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span><span style="color:#7ee787">cpu</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">100m</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span><span style="color:#7ee787">memory</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">50Mi</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span><span style="color:#7ee787">limits</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span><span style="color:#7ee787">cpu</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">200m</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">              </span><span style="color:#7ee787">memory</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">100Mi</span><span style="color:#6e7681">
</span></span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/18162c15146ad0bd116b07fa51bfa784" rel="noopener">nodetest-k8s-auto-extend.yaml</a>（GitHub Gist）</p>
<hr>
<h2 id="三部署測試-pod-是否會依據流量的增加自動擴展">三、部署＆測試 pod 是否會依據流量的增加自動擴展<a class="h-anchor" href="#%e4%b8%89%e9%83%a8%e7%bd%b2%e6%b8%ac%e8%a9%a6-pod-%e6%98%af%e5%90%a6%e6%9c%83%e4%be%9d%e6%93%9a%e6%b5%81%e9%87%8f%e7%9a%84%e5%a2%9e%e5%8a%a0%e8%87%aa%e5%8b%95%e6%93%b4%e5%b1%95" aria-label="這個段落的連結"></a></h2>
<h3 id="-開啟流量監控部署-k8s">開啟流量監控、部署 K8s<a class="h-anchor" href="#-%e9%96%8b%e5%95%9f%e6%b5%81%e9%87%8f%e7%9b%a3%e6%8e%a7%e9%83%a8%e7%bd%b2-k8s" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 將 minikube 監控流量的 addons 打開</span>
</span></span><span style="display:flex;"><span>minikube addons enable metrics-server
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 部署剛剛建立的 nodetest-k8s-auto.yaml</span>
</span></span><span style="display:flex;"><span>kubectl apply -f nodetest-k8s-auto.yaml
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 查看 Auto Scaler 狀態</span>
</span></span><span style="display:flex;"><span>kubectl get hpa
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202201/kubernetes-autoscaling-local/images/kubernetes-autoscaling-local-02.webp" width="1400" height="360" alt="開啟流量監控、部署 K8s（圖 2）" loading="lazy" decoding="async"></p>
<blockquote>
<p>一開始 TARGETS 會顯示 <code>&lt;unknown&gt;</code>，這是因為還在判斷流量，過一陣子就會顯示百分比了</p>
</blockquote>
<h3 id="-取得-deployment-的外部-ip然後瘋狂存取來測試-auto-scaling">取得 Deployment 的外部 ip，然後瘋狂存取來測試 Auto Scaling<a class="h-anchor" href="#-%e5%8f%96%e5%be%97-deployment-%e7%9a%84%e5%a4%96%e9%83%a8-ip%e7%84%b6%e5%be%8c%e7%98%8b%e7%8b%82%e5%ad%98%e5%8f%96%e4%be%86%e6%b8%ac%e8%a9%a6-auto-scaling" aria-label="這個段落的連結"></a></h3>
<pre tabindex="0"><code># 取得 Deployment 的外部 ip（此時會看到 port 與設定的 nodePort 相同）
minikube service node-app-service --url

# 開始瘋狂存取剛剛輸出的 url 來確認 pod 是否會自動成長
while true
do curl &lt;Deployment 的外部 ip&gt;
done
</code></pre><p><img src="https://deanlin.net/blog/202201/kubernetes-autoscaling-local/images/kubernetes-autoscaling-local-03.webp" width="1400" height="275" alt="取得 Deployment 的外部 ip，然後瘋狂存取來測試 Auto Scaling（圖 3）" loading="lazy" decoding="async"></p>
<h3 id="-瘋狂存取後後確認-maxreplicas-設定是否生效">瘋狂存取後後，確認 maxReplicas 設定是否生效<a class="h-anchor" href="#-%e7%98%8b%e7%8b%82%e5%ad%98%e5%8f%96%e5%be%8c%e5%be%8c%e7%a2%ba%e8%aa%8d-maxreplicas-%e8%a8%ad%e5%ae%9a%e6%98%af%e5%90%a6%e7%94%9f%e6%95%88" aria-label="這個段落的連結"></a></h3>
<p>看 REPLICAS 這個欄位是否會依據流量的增加自動擴展到設定的 maxReplicas。</p>
<p><img src="https://deanlin.net/blog/202201/kubernetes-autoscaling-local/images/kubernetes-autoscaling-local-04.webp" width="1400" height="564" alt="瘋狂存取後後，確認 maxReplicas 設定是否生效（圖 4）" loading="lazy" decoding="async"></p>
<h3 id="-關閉存取後確認-minreplicas-設定是否生效">關閉存取後，確認 minReplicas 設定是否生效<a class="h-anchor" href="#-%e9%97%9c%e9%96%89%e5%ad%98%e5%8f%96%e5%be%8c%e7%a2%ba%e8%aa%8d-minreplicas-%e8%a8%ad%e5%ae%9a%e6%98%af%e5%90%a6%e7%94%9f%e6%95%88" aria-label="這個段落的連結"></a></h3>
<p>將上面的存取 Deployment 外部 ip 的 while 迴圈關掉後，pod 會自動減少到設定的 minReplicas。</p>
<p><img src="https://deanlin.net/blog/202201/kubernetes-autoscaling-local/images/kubernetes-autoscaling-local-05.webp" width="811" height="174" alt="關閉存取後，確認 minReplicas 設定是否生效（圖 5）" loading="lazy" decoding="async"></p>
<h3 id="-測試完成後將建立的-k8s-服務刪除關閉-minikube">測試完成後將建立的 K8s 服務刪除、關閉 minikube<a class="h-anchor" href="#-%e6%b8%ac%e8%a9%a6%e5%ae%8c%e6%88%90%e5%be%8c%e5%b0%87%e5%bb%ba%e7%ab%8b%e7%9a%84-k8s-%e6%9c%8d%e5%8b%99%e5%88%aa%e9%99%a4%e9%97%9c%e9%96%89-minikube" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl delete -f nodetest-k8s-auto.yaml
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 如果沒關閉的話，你會發現電腦燙燙的，CPU 居高不下</span>
</span></span><span style="display:flex;"><span>minikube delete
</span></span></code></pre></div><hr>
<blockquote class="pullquote"><p>以上就是模擬 K8s Auto Scaling 的測試方法啦！希望讀者可以透過文章了解這個技術的應用～</p>
</blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>帶你將 Node.js Web App 給 Docker 化，並用 Kubernetes 部署的超簡單範例</title>
      <link>https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/</link>
      <pubDate>Fri, 21 Jan 2022 10:28:04 +0800</pubDate>
      <guid>https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>公司很早就導入了 Kubernetes（K8s），主要是為了自動化部署以及管理、監測多台機器上的 Container，在遇到高併發情境時可以自動擴展。過去我都是以輔助者的角色跟 DevOps 說我希望有哪些功能，所以便藉著寫這篇文章的機會來初探這個已經成為業界潮流的技術。</description>
      <media:content url="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-og_hu_1aafeb649fb6e222.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>公司很早就導入了 Kubernetes（K8s），主要是為了自動化部署以及管理、監測多台機器上的 Container，在遇到高併發情境時可以自動擴展。過去我都是以輔助者的角色跟 DevOps 說我希望有哪些功能，所以便藉著寫這篇文章的機會來初探這個已經成為業界潮流的技術。</p>
<p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-01.webp" width="1400" height="740" alt="帶你將 Node.js Web App 給 Docker 化，並用 Kubernetes 部署的超簡單範例（圖 1）" fetchpriority="high" decoding="async"></p>
<pre tabindex="0"><code>文章大綱
一、安裝 Docker、kubectl、minikube 等工具
  ➤ 安裝 Docker
  ➤ 安裝 kubectl
  ➤ 安裝 minikube
二、建立 Node.js Web App
  ➤ 建立「package.json」
  ➤ 建立「server.js」
  ➤ 運行 Node.js Web App
三、將 Node.js Web App 給 Docker 化
  ➤ 建立「Dockerfile」
  ➤ 建立「.dockerignore」
  ➤ 製作 Docker Image
  ➤ 印出建立的 Images
  ➤ 將建立好的 Docker Image 跑起來
  ➤ 確認 Docker Image 運行狀態，並進入網址確認畫面如預期
四、撰寫 K8s 使用的 yaml 檔＆部署 K8s
  ➤ K8s 的 yaml 檔基礎說明
  ➤ K8s 部署
  ➤ 查看部署後 pods 狀態
  ➤ 取得 deployment 的外部 ip，確認是否會顯示網頁
  ➤ 刪除執行中的 K8s
  ➤ 關閉 minikube
</code></pre><blockquote>
<p>本篇文章是在 Mac 的作業系統下操作的</p>
</blockquote>
<h2 id="一安裝-dockerkubectlminikube-等工具">一、安裝 Docker、kubectl、minikube 等工具<a class="h-anchor" href="#%e4%b8%80%e5%ae%89%e8%a3%9d-dockerkubectlminikube-%e7%ad%89%e5%b7%a5%e5%85%b7" aria-label="這個段落的連結"></a></h2>
<h3 id="-安裝-docker">安裝 Docker<a class="h-anchor" href="#-%e5%ae%89%e8%a3%9d-docker" aria-label="這個段落的連結"></a></h3>
<p>前往<a href="https://docs.docker.com/desktop/mac/install/">官網下載 Docker 安裝檔</a>，安裝過程就是狂按下一步，這裡就不再多做介紹，下載後要把 App 打開依照步驟執行才算是完成安裝喔！</p>
<figure class="figure"><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-02.webp" width="1400" height="505" alt="這邊建議順便註冊一組 Docker 帳號，方便你日後的操作。" loading="lazy" decoding="async">
  <figcaption>這邊建議順便註冊一組 Docker 帳號，方便你日後的操作。</figcaption>
</figure>

<h3 id="-安裝-kubectl">安裝 kubectl<a class="h-anchor" href="#-%e5%ae%89%e8%a3%9d-kubectl" aria-label="這個段落的連結"></a></h3>
<p>安裝他才能操作 K8s 喔！</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 安裝 kubectl</span>
</span></span><span style="display:flex;"><span>brew install kubectl
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 確認是否安裝成功</span>
</span></span><span style="display:flex;"><span>kubectl version --client
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 查看目前 cluster 運行狀態</span>
</span></span><span style="display:flex;"><span>kubectl get all
</span></span></code></pre></div><h3 id="-安裝-minikube">安裝 minikube<a class="h-anchor" href="#-%e5%ae%89%e8%a3%9d-minikube" aria-label="這個段落的連結"></a></h3>
<p>這個工具可以讓你在本機(Local)架設 K8s 叢集(Cluster)喔！</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 安裝 minikube</span>
</span></span><span style="display:flex;"><span>brew install minikube
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 啟動 minikube，啟動 k8s cluster</span>
</span></span><span style="display:flex;"><span>minikube start
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 查看 minikube 狀態</span>
</span></span><span style="display:flex;"><span>minikube status
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 取得目前 minikube 的 IP address</span>
</span></span><span style="display:flex;"><span>minikube ip
</span></span></code></pre></div><hr>
<h2 id="二建立-nodejs-web-app">二、建立 Node.js Web App<a class="h-anchor" href="#%e4%ba%8c%e5%bb%ba%e7%ab%8b-nodejs-web-app" aria-label="這個段落的連結"></a></h2>
<blockquote>
<p>你也可以直接去<a href="https://github.com/dean9703111/nodejs-docker-k8s-auto-scaling-example">筆者的 github</a>，直接將專案 Clone 到本機來做測試。</p>
</blockquote>
<h3 id="-建立packagejson">建立「package.json」<a class="h-anchor" href="#-%e5%bb%ba%e7%ab%8bpackagejson" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#7ee787">&#34;name&#34;</span>: <span style="color:#a5d6ff">&#34;docker_web_app&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#7ee787">&#34;version&#34;</span>: <span style="color:#a5d6ff">&#34;1.0.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#7ee787">&#34;description&#34;</span>: <span style="color:#a5d6ff">&#34;Node.js on Docker&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#7ee787">&#34;author&#34;</span>: <span style="color:#a5d6ff">&#34;Dean Lin&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#7ee787">&#34;main&#34;</span>: <span style="color:#a5d6ff">&#34;server.js&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#7ee787">&#34;scripts&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#7ee787">&#34;start&#34;</span>: <span style="color:#a5d6ff">&#34;node server.js&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#7ee787">&#34;dependencies&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#7ee787">&#34;express&#34;</span>: <span style="color:#a5d6ff">&#34;^4.17.2&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/dba0f27584d5c0d7f778aa7cacb1d4b3" rel="noopener">node_web_app.json</a>（GitHub Gist）</p>
<h3 id="-建立serverjs">建立「server.js」<a class="h-anchor" href="#-%e5%bb%ba%e7%ab%8bserverjs" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#a5d6ff">&#34;use strict&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> express <span style="color:#ff7b72;font-weight:bold">=</span> require(<span style="color:#a5d6ff">&#34;express&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// Constants
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> PORT <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">8080</span>;
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> HOST <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#34;0.0.0.0&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// App
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> app <span style="color:#ff7b72;font-weight:bold">=</span> express();
</span></span><span style="display:flex;"><span>app.get(<span style="color:#a5d6ff">&#34;/&#34;</span>, (req, res) =&gt; {
</span></span><span style="display:flex;"><span>  res.send(<span style="color:#a5d6ff">&#34;Hello World&#34;</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>app.listen(PORT, HOST);
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">`Running on http://</span><span style="color:#a5d6ff">${</span>HOST<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">:</span><span style="color:#a5d6ff">${</span>PORT<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">`</span>);
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/0e82ccc37a3ae3cb68e78e047464c1ec" rel="noopener">node_web_app.js</a>（GitHub Gist）</p>
<h3 id="-運行-nodejs-web-app">運行 Node.js Web App<a class="h-anchor" href="#-%e9%81%8b%e8%a1%8c-nodejs-web-app" aria-label="這個段落的連結"></a></h3>
<p>在終端機先輸入<code>yarn</code>將套件安裝，再輸入<code>yarn start</code>確認專案可以運行。</p>
<figure class="figure"><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-03.webp" width="1292" height="390" alt="如果你是 Clone 筆者專案，記得要 cd 進入 docker_web_app 裡面再安裝＆執行喔！" loading="lazy" decoding="async">
  <figcaption>如果你是 Clone 筆者專案，記得要 cd 進入 docker_web_app 裡面再安裝＆執行喔！</figcaption>
</figure>

<figure class="figure"><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-04.webp" width="1334" height="796" alt="看到 Hello World 就恭喜完成第一步嚕" loading="lazy" decoding="async">
  <figcaption>看到 Hello World 就恭喜完成第一步嚕</figcaption>
</figure>

<hr>
<h2 id="三將-nodejs-web-app-給-docker-化">三、將 Node.js Web App 給 Docker 化<a class="h-anchor" href="#%e4%b8%89%e5%b0%87-nodejs-web-app-%e7%b5%a6-docker-%e5%8c%96" aria-label="這個段落的連結"></a></h2>
<h3 id="-建立dockerfile">建立「Dockerfile」<a class="h-anchor" href="#-%e5%bb%ba%e7%ab%8bdockerfile" aria-label="這個段落的連結"></a></h3>
<p>填寫製作 Docker Image 時需要參考的環境＆指令。</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#ff7b72">FROM</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">node:14</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Create app directory</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">WORKDIR</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">/usr/src/app</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Install app dependencies</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># A wildcard is used to ensure both package.json AND package-lock.json are copied</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># where available (npm@5+)</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">COPY</span> package*.json ./<span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">RUN</span> npm install<span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># If you are building your code for production</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># RUN npm ci --only=production</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Bundle app source</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">COPY</span> . .<span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">EXPOSE</span><span style="color:#6e7681"> </span><span style="color:#a5d6ff">8080</span><span style="color:#f85149">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">CMD</span> [ <span style="color:#a5d6ff">&#34;node&#34;</span>, <span style="color:#a5d6ff">&#34;server.js&#34;</span> ]<span style="color:#f85149">
</span></span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/b4ed238746412ea272addb3e7667c1ee" rel="noopener">Dockerfile</a>（GitHub Gist）</p>
<h3 id="-建立dockerignore">建立「.dockerignore」<a class="h-anchor" href="#-%e5%bb%ba%e7%ab%8bdockerignore" aria-label="這個段落的連結"></a></h3>
<p>填寫製作 Docker Image 時不想要複製的檔案。</p>
<pre tabindex="0"><code>node_modules
npm-debug.log
</code></pre><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/346e2e3876af9dbd6e8ee568b1b72bc5" rel="noopener">.dockerignore</a>（GitHub Gist）</p>
<h3 id="-製作-docker-image">製作 Docker Image<a class="h-anchor" href="#-%e8%a3%bd%e4%bd%9c-docker-image" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 登入 Docker</span>
</span></span><span style="display:flex;"><span>docker login
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># ！如果 Docker app沒有開啟就執行下面步驟會報錯！</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 建立 Docker Image</span>
</span></span><span style="display:flex;"><span>docker build . -t &lt;your username&gt;/node-web-app
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 筆者範例指令</span>
</span></span><span style="display:flex;"><span>docker build . -t babydragon9703111/node-web-app
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-05.webp" width="1400" height="336" alt="製作 Docker Image（圖 5）" loading="lazy" decoding="async"></p>
<h3 id="-印出建立的-images">印出建立的 Images<a class="h-anchor" href="#-%e5%8d%b0%e5%87%ba%e5%bb%ba%e7%ab%8b%e7%9a%84-images" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker images
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-06.webp" width="1400" height="199" alt="印出建立的 Images（圖 6）" loading="lazy" decoding="async"></p>
<h3 id="-將建立好的-docker-image-跑起來">將建立好的 Docker Image 跑起來<a class="h-anchor" href="#-%e5%b0%87%e5%bb%ba%e7%ab%8b%e5%a5%bd%e7%9a%84-docker-image-%e8%b7%91%e8%b5%b7%e4%be%86" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 裡面填寫的 port 是本地端要跑的（對外 port）</span>
</span></span><span style="display:flex;"><span>docker run -p <span style="color:#ff7b72;font-weight:bold">[</span>port<span style="color:#ff7b72;font-weight:bold">]</span>:8080 -d &lt;your username&gt;/node-web-app
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 筆者範例指令</span>
</span></span><span style="display:flex;"><span>docker run -p 8080:8080 -d babydragon9703111/node-web-app
</span></span></code></pre></div><h3 id="-確認-docker-image-運行狀態並進入網址確認畫面如預期">確認 Docker Image 運行狀態，並進入網址確認畫面如預期<a class="h-anchor" href="#-%e7%a2%ba%e8%aa%8d-docker-image-%e9%81%8b%e8%a1%8c%e7%8b%80%e6%85%8b%e4%b8%a6%e9%80%b2%e5%85%a5%e7%b6%b2%e5%9d%80%e7%a2%ba%e8%aa%8d%e7%95%ab%e9%9d%a2%e5%a6%82%e9%a0%90%e6%9c%9f" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Get container ID</span>
</span></span><span style="display:flex;"><span>docker ps
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Print app output</span>
</span></span><span style="display:flex;"><span>docker logs &lt;container id&gt;
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-07.webp" width="1400" height="264" alt="確認 Docker Image 運行狀態，並進入網址確認畫面如預期（圖 7）" loading="lazy" decoding="async"></p>
<figure class="figure"><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-08.webp" width="1334" height="796" alt="看到 Hello World 就距離成功非常接近嚕！" loading="lazy" decoding="async">
  <figcaption>看到 Hello World 就距離成功非常接近嚕！</figcaption>
</figure>

<hr>
<h2 id="四撰寫-k8s-使用的-yaml-檔部署-k8s">四、撰寫 K8s 使用的 yaml 檔＆部署 K8s<a class="h-anchor" href="#%e5%9b%9b%e6%92%b0%e5%af%ab-k8s-%e4%bd%bf%e7%94%a8%e7%9a%84-yaml-%e6%aa%94%e9%83%a8%e7%bd%b2-k8s" aria-label="這個段落的連結"></a></h2>
<h3 id="-k8s-的-yaml-檔基礎說明">K8s 的 yaml 檔基礎說明<a class="h-anchor" href="#-k8s-%e7%9a%84-yaml-%e6%aa%94%e5%9f%ba%e7%a4%8e%e8%aa%aa%e6%98%8e" aria-label="這個段落的連結"></a></h3>
<ul>
<li><strong>apiVersion</strong>：API 版本，需要依照 kind 的物件來決定 version。</li>
<li><strong>kind</strong>：建立的物件。</li>
<li><strong>metadata</strong>：描述物件及標籤。</li>
<li><strong>spec</strong>：物件要的功能。</li>
</ul>
<p>建立<code>nodetest-k8s.yaml</code>來做測試範例，裡面的 image 等相關資訊記得算成自己的</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">apiVersion</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">v1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">kind</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">Service</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">metadata</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-app-service</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">spec</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">selector</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">app</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">ports</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span>- <span style="color:#7ee787">protocol</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">&#34;TCP&#34;</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">port</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">8080</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">targetPort</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">8080</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">type</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">LoadBalancer</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">---</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">apiVersion</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">apps/v1</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">kind</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">Deployment</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">metadata</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">spec</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">selector</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">matchLabels</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">app</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">replicas</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">3</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">template</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">metadata</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">labels</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span><span style="color:#7ee787">app</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">spec</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span><span style="color:#7ee787">containers</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">        </span>- <span style="color:#7ee787">name</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">node-web-app</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">          </span><span style="color:#7ee787">image</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">babydragon9703111/node-web-app:latest</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">          </span><span style="color:#7ee787">imagePullPolicy</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">Always</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">          </span><span style="color:#7ee787">ports</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">            </span>- <span style="color:#7ee787">containerPort</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">8080</span><span style="color:#6e7681">
</span></span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/fbdb562caf70b78ee7801d377c546e12" rel="noopener">nodetest-k8s.yaml</a>（GitHub Gist）</p>
<h3 id="-k8s-部署">K8s 部署<a class="h-anchor" href="#-k8s-%e9%83%a8%e7%bd%b2" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 啟動 k8s cluster</span>
</span></span><span style="display:flex;"><span>minikube start
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 執行部署</span>
</span></span><span style="display:flex;"><span>kubectl apply -f nodetest-k8s.yaml
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-09.webp" width="1400" height="545" alt="K8s 部署（圖 9）" loading="lazy" decoding="async"></p>
<h3 id="-查看部署後-pods-狀態">查看部署後 pods 狀態<a class="h-anchor" href="#-%e6%9f%a5%e7%9c%8b%e9%83%a8%e7%bd%b2%e5%be%8c-pods-%e7%8b%80%e6%85%8b" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 此指令會查 default namespace 的資源</span>
</span></span><span style="display:flex;"><span>kubectl get pods
</span></span></code></pre></div><p>因為我們在 spec 的 replicas 的值設為 3，所以會產生 3 個 pods。</p>
<p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-10.webp" width="1400" height="231" alt="查看部署後 pods 狀態（圖 10）" loading="lazy" decoding="async"></p>
<h3 id="-取得-deployment-的外部-ip確認是否會顯示網頁">取得 deployment 的外部 ip，確認是否會顯示網頁<a class="h-anchor" href="#-%e5%8f%96%e5%be%97-deployment-%e7%9a%84%e5%a4%96%e9%83%a8-ip%e7%a2%ba%e8%aa%8d%e6%98%af%e5%90%a6%e6%9c%83%e9%a1%af%e7%a4%ba%e7%b6%b2%e9%a0%81" aria-label="這個段落的連結"></a></h3>
<pre tabindex="0"><code>minikube service node-app-service --url
</code></pre><figure class="figure"><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-11.webp" width="1400" height="649" alt="如果網頁有正確顯示就大功告成啦～" loading="lazy" decoding="async">
  <figcaption>如果網頁有正確顯示就大功告成啦～</figcaption>
</figure>

<h3 id="-刪除執行中的-k8s">刪除執行中的 K8s<a class="h-anchor" href="#-%e5%88%aa%e9%99%a4%e5%9f%b7%e8%a1%8c%e4%b8%ad%e7%9a%84-k8s" aria-label="這個段落的連結"></a></h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># 指定刪除剛剛建立的 K8s</span>
</span></span><span style="display:flex;"><span>kubectl delete -f nodetest-k8s.yaml
</span></span></code></pre></div><p>剛下達指令刪除時，在 STATUS 的部分會顯示 Terminating，過一段時間就會消失嚕～</p>
<p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-12.webp" width="1400" height="301" alt="刪除執行中的 K8s（圖 12）" loading="lazy" decoding="async"></p>
<h3 id="-關閉-minikube">關閉 minikube<a class="h-anchor" href="#-%e9%97%9c%e9%96%89-minikube" aria-label="這個段落的連結"></a></h3>
<pre tabindex="0"><code># 如果沒關閉的話，你會發現電腦燙燙的，CPU 居高不下
minikube delete
</code></pre><p><img src="https://deanlin.net/blog/202201/nodejs-docker-kubernetes-deploy/images/nodejs-docker-kubernetes-deploy-13.webp" width="769" height="116" alt="關閉 minikube（圖 13）" loading="lazy" decoding="async"></p>
<hr>
<blockquote class="pullquote"><p>以上就是 Node.js X Docker X Kubernetes 超簡單範例啦！希望讀者可以透過這篇文章對這些技術有基礎認識～</p>
</blockquote>
<blockquote class="pullquote"><p>如果想更近一步了解 K8s Auto Scaling 的技術，歡迎閱讀筆者寫的這篇文章：「<a href="https://deanlin.net/blog/202201/kubernetes-autoscaling-local/">模擬 K8s Auto Scaling（可在 Local 端測試）</a>」</p>
</blockquote>
]]></content:encoded>
    </item>
  </channel>
</rss>
