K8s 完整手冊從零到全技術 · 繁中
🎓 把你當完全新手 · 同時是查得到的完整手冊

Kubernetes 完整教學手冊
從零開始,把所有技術都講給你聽

不假設你懂任何東西。先用生活比喻帶你入門,再一路涵蓋 K8s 全部核心技術:工作負載、網路、儲存、設定、排程、安全、自動擴展、擴充機制。每個概念都有白話+官方 YAML。

🧩 大量生活化比喻 📋 內容查證自 kubernetes.io ⌨️ 可複製的官方 YAML 🗂️ 37 章 / 10 大主題 🌗 深淺色閱讀
關於內容正確性

本手冊所有技術描述與 YAML 範例皆參考並查證自 Kubernetes 官方文件(kubernetes.io),非憑印象撰寫。API 欄位與行為會隨版本演進,實作前請以你所用版本的官方文件為準。

Part A

入門基礎

完全沒接觸過?從這裡開始,建立直覺。

0怎麼讀這份手冊

這份手冊有兩個身分:新手教科書(從第 0 章循序漸進)+技術查詢手冊(左側目錄隨點隨查)。建議第一次照順序看 Part A~B,之後當字典用。

🧠
一句話結論

Kubernetes(簡稱 K8s)是一個「自動幫你管理一大堆容器化程式」的系統。你只描述「我想要的最終狀態」,它就 24 小時不睡覺地幫你達成並維持。

💡
為什麼叫 K8s?

Kubernetes 開頭 K、結尾 s,中間剛好 8 個字母,所以縮寫成 K8s。這個字源自希臘文「舵手 / 領航員」,所以你常看到船舵圖示。

閱讀約定:灰底字是指令或專有名詞;🧩紫框是比喻、💡藍框是小撇步、⚠️黃框是注意、✅綠框是重點總結。


1為什麼需要 K8s?先理解它解決的痛

學工具前先懂「沒有它有多痛」。我們來演一齣戲:你是「阿明小舖」的唯一工程師,把購物網站放在一台租來的伺服器上。

🎬
情境劇:阿明的崩潰三個月

第 1 個月|凌晨 3:17,你的手機瘋狂震動——網站掛了。你睡眼惺忪爬起來,SSH 連進機器,發現程式因為記憶體不足被系統殺掉。你手動重開,回去睡。第二天同樣的事又來一次。

第 2 個月|雙十一前夜,行銷說要大促。你緊張地多租了 4 台機器,半夜手動把程式一台台裝起來、改設定、調流量分配。活動結束,你又要記得把多的機器關掉,不然帳單爆炸。

第 3 個月|要上新版,你把舊程式關掉、裝新版——結果這 30 秒內,正在結帳的客人全部看到錯誤頁,客服電話被打爆。而且新版有 bug,你還得手忙腳亂地裝回舊版。

阿明遇到的,正是所有線上服務都會碰到的四個經典痛點:

  • 🔥 會掛、掛了要有人救:程式半夜當掉,你卻在睡覺。
  • 📈 流量會暴衝暴跌:一台扛不住要加機器,閒下來又要關掉省錢。
  • 🔄 更新不能中斷服務:換版本時客人不該看到錯誤。
  • 🖥️ 機器一多就管不動:哪個程式在哪台、哪台壞了,一團亂。

這四件事如果全部靠人工,阿明遲早累垮、出包。而這四件事,正好就是 K8s 自動幫你做的事——它就是那個「永遠醒著、不會累、不會忘」的代理人。

🏢
比喻:K8s 是大樓的物業總管

你不會自己一間間檢查水電、修燈、客滿時開新房間,而是請一個物業總管。你只說「永遠保持 3 間房有人住,壞了就修」,剩下他全包。K8s 就是這個總管。

K8s 核心能力白話
自我修復 Self-healing程式當掉自動重開新的,不用你半夜爬起來。
自動擴展 Scaling客人多就自動加副本分擔,少了就收掉省錢。
滾動更新 Rolling Update更新一個一個換,全程不中斷,出錯可一鍵還原。
負載平衡 Load Balancing把流量平均分給每份程式。
服務探索 Service Discovery程式之間用固定名稱互找,不怕 IP 變動。
核心精神:聲明式(Declarative)

你只說「我想要的最終狀態」(例如 3 份、永遠健康),不用說「怎麼一步步做」。K8s 透過不斷比對「現況 vs 期望」的控制迴圈(Control Loop),自動把現況拉向你要的狀態。這是理解 K8s 一切行為的關鍵。

🌡️
比喻:聲明式 = 冷氣的恆溫設定

你不會對冷氣下「指令式」命令:「現在啟動壓縮機 5 分鐘、然後停 2 分鐘…」。你只設定「我要 26 度」(期望狀態),冷氣自己不斷量測現在幾度、和 26 度比、太熱就吹冷,無限循環。

K8s 一模一樣:你說「我要 3 個 Pod」,它持續數現在有幾個、少了就補、多了就收。這也是為什麼你刪掉一個 Pod,它會「自己長回來」——不是有人盯著,而是那個恆溫迴圈在運作。把這個畫面記在腦中,後面所有「為什麼 K8s 會自動做某件事」都能用它解釋。


2先搞懂「容器」與 Docker

K8s 是用來管理「容器」的,所以得先懂容器。這是最多人卡關、但其實一旦想通就超簡單的概念。

😱
情境:工程師界最著名的一句話

你寫好程式,在自己筆電跑得好好的。興沖沖交給同事,他一跑——爆炸。你脫口而出:「可是…在我電腦上明明可以跑啊!

原因是:你的筆電裝了 Python 3.11、某個函式庫 2.0 版、某個環境變數;同事的電腦是 Python 3.8、函式庫 1.5 版、少了那個環境變數。程式沒變,但它腳下的「地基」變了,所以垮了。

📦
比喻:容器 = 海運貨櫃

幾十年前運貨是場惡夢:每種貨物形狀不同,搬上船要各想各的辦法,換個港口又要重來。直到有人發明標準貨櫃——不管裡面裝家具還是香蕉,外觀尺寸都一樣,任何船、港口、吊車、卡車都能無痛處理。全球貿易因此起飛。

軟體容器就是這個革命的軟體版:把程式 + 它需要的一切(直譯器、函式庫、設定、環境變數)通通打包進一個標準盒子。這個盒子搬到任何電腦——你的筆電、同事的電腦、雲端伺服器——跑出來都一模一樣。地基被一起打包帶走了,「換台電腦就壞」從此絕跡。

容器 vs 虛擬機(VM)

虛擬機 (VM)容器 (Container)
比喻獨棟透天(連地基水電自己一套)公寓套房(共用大樓結構)
包含整套作業系統,肥(幾 GB)只包程式本身,輕(幾十 MB)
啟動慢,要開機快,幾秒起來
密度一台放得少一台放得多
🔑
Docker 與 K8s 的關係(必懂)

Docker:負責「做出容器」並在一台機器上跑。Kubernetes:在一大堆機器上管理成千上萬個容器。
👉 Docker 管一個,K8s 管一群。

⚠️
映像檔 vs 容器(最容易混淆,務必分清)

映像檔(Image)是食譜,容器(Container)是照食譜現做出來的那盤菜。同一份食譜可以同時做出 100 盤一模一樣的菜(100 個容器)。食譜放在「倉庫」(registry,如 Docker Hub)裡供人下載。

YAML 裡的 image: nginx:1.14.2 就是在說:「去倉庫拿 nginx 這份食譜的 1.14.2 版本來做菜」。冒號後面的 1.14.2標籤(tag)= 版本號。⚠️ 實務踩雷:別用 latest 標籤上正式環境——因為「最新」會一直變,今天和明天拿到的食譜可能不同,導致難以重現的問題。

🧅
深入:容器其實是「假裝獨立」的程序

容器並不是真的虛擬出一台電腦。它本質上是一個普通的程序(process),只是 Linux 核心用兩個機制把它「關進小房間」:namespace(隔離視野——讓它以為自己看到的檔案、網路、程序清單是獨立的)和 cgroups(限制資源——只能用這麼多 CPU 和記憶體)。

所以容器才會「啟動快、超省資源」:它沒有開機過程,就是直接跑一個被框起來的程序而已。理解這點,你就懂為什麼一台機器能塞下幾十個容器,卻塞不下幾十台虛擬機。

🧩
CRI:K8s 其實不綁 Docker

K8s 透過標準介面 CRI(Container Runtime Interface)呼叫容器執行引擎,常見的是 containerdCRI-O。K8s 1.24 起已移除內建的 Docker 支援(dockershim),但你用 Docker 做出來的映像檔仍完全通用(都是 OCI 標準格式)。


3K8s 到底是什麼

🎼
比喻:K8s 是管弦樂團指揮

幾十位樂手(容器)各司其職,沒指揮會亂。指揮(K8s)不親自演奏,但決定誰何時進場、誰大聲小聲、有人吹錯馬上補救。所以 K8s 又叫「容器編排(Container Orchestration)」——orchestration 本意就是管弦樂編排。

技術定義:Kubernetes 是開源的容器編排系統,最早由 Google 根據自家內部系統(Borg)的經驗開發,2014 年開源,現由 CNCF(雲端原生運算基金會)維護,自動化容器的部署、擴展與管理

🎻
深入:為什麼「編排」這個詞很傳神

單獨一個容器,用 Docker 就能跑——就像一個人哼歌不需要指揮。但當你有幾十、幾百個容器,彼此要溝通、要在對的時間啟動、有人倒下要補位、流量要分配——這就從「哼歌」變成「交響樂」了。

K8s 做的事,和指揮對樂團做的事驚人地像:決定誰上場(排程 Pod 到節點)、誰接誰(服務探索與網路)、有人吹錯立刻補救(自我修復)、整體音量隨場合調整(自動擴展)。你寫的 YAML,就是給樂團的「樂譜」。

🏗️
比喻:叢集 = 一個工地團隊

你不會只用一台電腦跑 K8s,而是一群電腦合作,這群電腦叫「叢集(Cluster)」。裡面有工頭(動腦指揮、不做粗活)和一群工人(實際跑你的容器)。


4架構與全部元件

一個叢集分兩種角色:控制平面(工頭)工作節點(工人)

⬡ Kubernetes 叢集 (Cluster)
🧠 控制平面 Control Plane(工頭)

動腦、發號施令,管理叢集狀態。

  • kube-apiserver:唯一入口(總機),所有指令都經過它。
  • etcd:鍵值資料庫,叢集的「大腦記憶」,存所有狀態。
  • kube-scheduler:調度員,決定新 Pod 放到哪個工人。
  • kube-controller-manager:監工,跑各種控制迴圈維持期望狀態。
  • cloud-controller-manager:對接雲端商(選用)。
💪 工作節點 Worker Node(工人)×N

真正跑你程式(容器)的機器。

  • kubelet:節點上的組長,確保 Pod 裡容器有照規格跑。
  • kube-proxy:處理 Service 的網路轉發規則。
  • Container Runtime:實際執行容器(containerd / CRI-O)。
📦 Pod📦 Pod📦 Pod
🍔
比喻:叢集 = 連鎖速食店

控制平面 = 店長辦公室:接單(apiserver)、看報表(etcd)、排班(scheduler)、盯場(controller)。工作節點 = 廚房:領班(kubelet)照單做餐(Pod),送餐動線(kube-proxy)。你在櫃台點餐就好,不用管廚房。

💡
控制迴圈(Controller)是 K8s 的靈魂

controller-manager 裡跑著一堆「控制器」,每個都在做同一件事:看現況 → 比對期望 → 有差距就修正,無限循環。例如 Deployment 控制器發現少了一個 Pod 就補一個。理解這點,你就懂 K8s 為什麼能「自我修復」。

📨
走一遍:你打一個指令,背後發生什麼事?

當你執行 kubectl apply -f deployment.yaml(要 3 個 Pod),這趟旅程是:
kubectl 把 YAML 送到 API Server(唯一大門)。
API Server 驗證後,把「期望狀態」寫進 etcd(記事本)。
Deployment 控制器發現「期望 3 個、現況 0 個」,於是建立 3 個 Pod 物件。
Scheduler看到這 3 個 Pod 還沒分配,幫它們各挑一台合適的工人節點。
對應節點上的 kubelet 收到通知,叫本機的容器執行引擎把容器拉起來。
kubelet 持續回報狀態給 API Server,寫回 etcd。

✨ 注意:沒有人直接命令誰。每個元件只是各自盯著 etcd 裡的狀態、做自己該做的修正。這種「鬆耦合 + 各自跑迴圈」的設計,就是 K8s 既強韌又能擴展的祕密。

⚠️
高可用(HA)

正式環境的控制平面通常會跑多份(多個 apiserver、etcd 組成叢集),避免單點故障。學習階段用單節點即可。


5API、物件與 YAML

你跟 K8s 互動的方式,本質是對 Kubernetes API 建立 / 修改 / 刪除「物件(Object)」。物件就是你寫在 YAML 裡的那些東西(Pod、Deployment、Service…)。

📝
關鍵:spec 是你的願望,status 是現實

每個 K8s 物件都有兩面:spec(spec = specification,你寫的「我想要」)status(K8s 回填的「現在實際長怎樣」)

想像你在咖啡廳點餐:spec 是你的點單「一杯熱拿鐵、少糖」;status 是店員的進度板「製作中 / 已完成」。你只負責寫 spec,status 由 K8s 不斷更新、你不要去改它。控制迴圈做的事,就是讓 status 一路追上 spec。

下指令 kubectl get pod xxx -o yaml,你會親眼看到這兩段並存——這是真正讀懂 K8s 的分水嶺。

每個 K8s YAML 都有的四大欄位

欄位意思
apiVersion用哪個 API 群組與版本(如 v1apps/v1)。
kind要建立什麼(Pod?Deployment?Service?)。
metadata基本資料:name 名字、labels 標籤、namespace
spec你想要的樣子(期望狀態)。K8s 會回填 status 表示現況。

YAML 三大規則

  1. 縮排表層級:靠空格多寡決定誰屬於誰。只能空格、不能用 Tab!(最常見錯誤)
  2. 冒號表「鍵: 值」:冒號後要空一格。
  3. 減號表「清單一項」- name: x
🏷️
標籤(Labels)是 K8s 的黏著劑

幾乎所有「A 物件找到一群 B 物件」都靠標籤。例如 Service 用 selector 撈標籤是 app=nginx 的 Pod、Deployment 用標籤管理它的 Pod。Annotations(註解)則放不用來篩選、給工具看的中繼資料。

⚠️
不知道欄位怎麼填?查官方說明

kubectl explain pod.spec 會列出該欄位底下能填什麼,不用上網找。

namespace 命名空間

Namespace 把叢集切成互不干擾的隔間(如 dev/test/prod),名字可重複。多數資源是「namespace 範圍」的;少數(如 Node、PV、StorageClass、ClusterRole)是「叢集範圍」的,不屬於任何 namespace。

Part B

工作負載 Workloads

你的程式怎麼跑起來、跑幾份、誰來管。這是 K8s 最核心的一塊。

6Pod 深入

🫛
比喻:Pod = 一個豌豆莢

豆莢裡裝一顆或幾顆豆子(容器)。Pod 是 K8s 能建立與管理的最小單位,K8s 不直接管容器,而是管 Pod。同一 Pod 內的容器共用網路(同一 IP)和儲存,像同房室友。

🏠
深入:同一個 Pod 裡的容器到底「共用」什麼?

把 Pod 想成一間合租公寓,裡面的容器是室友:
共用門牌與電話(網路):整個 Pod 只有一個 IP。室友之間用 localhost 就能互通,像在家裡喊一聲。
可共用儲藏室(Volume):掛同一個 volume,A 寫的檔案 B 看得到。
同生共死:Pod 被排到哪台機器,所有室友一起去;Pod 被刪,全部一起走。

這也解釋了「為什麼大多數情況一個 Pod 只放一個容器」:只有當兩個容器關係緊密到必須住一起、同進同出(如主程式 + 它的日誌收集 sidecar)才放同一個 Pod。否則就該各自獨立成不同 Pod,方便個別擴展。

pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-demo
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

Pod 生命週期(Phase)

Phase意思
Pending已接受,但容器還沒全部跑起來(如還在下載映像檔、等排程)。
Running已綁定到節點,至少一個容器在跑。
Succeeded所有容器正常結束(exit 0),不會再重啟。
Failed所有容器都結束,至少一個是失敗的。
Unknown無法取得 Pod 狀態(通常節點失聯)。

restartPolicy 控制容器結束後是否重啟:Always(預設,Deployment 用)、OnFailureNever

🔁
實務踩雷:CrashLoopBackOff 是什麼?

新手最常看到的紅字狀態就是 CrashLoopBackOff。它的意思是:「容器一直啟動、一直當掉,我(K8s)一直重開,但開太多次了,所以我每次重開前刻意拖長等待時間」(backoff = 退避,重試間隔越拉越長:10 秒、20 秒、40 秒…最多 5 分鐘)。

這不是 K8s 壞掉,是你的程式本身一啟動就出錯。正確反應不是一直刪 Pod 重建,而是去看日誌找真正死因:kubectl logs <pod>kubectl describe pod <pod>

Init 容器(初始化容器)

在主容器啟動,依序跑完的容器,常用來「等待依賴就緒」或「做前置準備」。全部成功後主容器才開始。

init-containers.yaml(官方範例)
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:               # 主容器啟動前先跑這些
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice; do echo waiting; sleep 2; done"]

Sidecar 容器(邊車)

🏍️
比喻:機車的邊車

Sidecar 是和主程式同生共死、提供輔助功能的容器(如收集日誌、代理流量)。在 K8s 中,sidecar 寫在 initContainers 裡並設 restartPolicy: Always,它會在整個 Pod 生命週期持續運作。

sidecar 片段(官方範例)
spec:
  containers:
    - name: myapp
      image: alpine:latest
  initContainers:
    - name: logshipper
      image: alpine:latest
      restartPolicy: Always   # 👈 設成 Always 就變成 sidecar
      command: ['sh', '-c', 'tail -F /opt/logs.txt']

臨時容器(Ephemeral Containers)

專為除錯而生:臨時注入一個容器到正在跑的 Pod 裡看狀況(用 kubectl debug)。不能用一般 YAML 預先宣告,沒有重啟保證。

⚠️
Pod 是「用過即丟」的

Pod 暫時又脆弱,隨時可能被刪、被搬。你幾乎不該自己直接建 Pod,而是用下面的工作負載控制器(Deployment 等)來管理它們。把 Pod 想成免洗餐具——壞了換新,不修。


7ReplicaSet 與 Deployment

👔
比喻:Deployment = 副本管理員

你說「我要永遠有 3 個一樣的 Pod」,它就幫你建立、補救(掛了補一個)、更新(一個一個換)。實務上你 90% 都建 Deployment,而不是 Pod。

ReplicaSet 是真正負責「維持副本數量」的底層元件;Deployment 是它的上層,多了「版本管理、滾動更新、回滾」的能力。你通常只碰 Deployment,ReplicaSet 由它自動管理。

deployment.yaml(官方範例)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3            # 👈 我要 3 個副本,K8s 幫我維持
  selector:
    matchLabels:
      app: nginx
  template:               # 👇 每個 Pod 長什麼樣
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

滾動更新與回滾

🔄
比喻:換輪胎不熄火 / 接力賽交棒

更新版本時,K8s 預設用 RollingUpdate 策略:像賽車進站一個一個換輪胎,車子始終在動——先起一個新版 Pod、等它健康(通過就緒探針)、再關掉一個舊版,如此反覆直到全部換新。全程都有 Pod 在服務,使用者無感。另一種策略 Recreate 則是「先全部關掉再全部開新的」,中間會有空窗、會中斷,只在特殊情況用。

🎛️
深入:兩個旋鈕控制「換多快、能多虛」

滾動更新的節奏由兩個參數控制(皆可調):
maxSurge:更新時最多可「超出」期望數量幾個(暫時多開幾個新的,換得快但吃更多資源)。
maxUnavailable:更新時最多可「」幾個可用(容忍同時幾個不在線上)。

例如 3 個副本、maxSurge: 1maxUnavailable: 0,代表「永遠至少 3 個在線、最多暫時開到 4 個」——零中斷但稍慢。這就是為什麼正式環境敢在上班時間更新。

更新與回滾指令
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
$ kubectl rollout status deployment/nginx-deployment   # 看更新進度
$ kubectl rollout undo deployment/nginx-deployment     # 退回上一版
$ kubectl rollout history deployment/nginx-deployment  # 看版本歷史
💡
replicas 就是擴展

replicas: 3 改成 10,或 kubectl scale deployment nginx-deployment --replicas=10,立刻變 10 份。


8StatefulSet(有狀態應用)

🏷️
比喻:有編號座位 vs 自由入座

Deployment 的 Pod 是「自由入座」——每個都一樣、可互換、隨機命名。但資料庫這類需要「固定身分 + 自己的硬碟」的程式,需要「有編號座位」:每個 Pod 有固定名字(web-0、web-1…)、固定的專屬儲存按順序啟動/關閉。這就是 StatefulSet。

StatefulSet 適合有狀態(stateful)應用:資料庫(MySQL、PostgreSQL)、訊息佇列(Kafka)等。它搭配 Headless ServiceclusterIP: None)給每個 Pod 穩定的 DNS 名稱,並用 volumeClaimTemplates 自動為每個 Pod 建立專屬 PVC。

🗃️
情境:為什麼資料庫不能用 Deployment?

想像你跑一個 3 節點的資料庫叢集,規則是「第一台是主庫(可寫),另兩台是從庫(只讀,複製主庫資料)」。

如果用 Deployment:Pod 名字是隨機亂碼、開機順序不固定、每個 Pod 搶到的硬碟也隨機——從庫根本不知道「主庫是哪一個」,重啟後還可能拿到別人的舊資料。整個資料庫會錯亂。

StatefulSet 解決這一切:Pod 叫 db-0db-1db-2固定名字),按順序 0→1→2 啟動、反向關閉(固定順序),每個 Pod 永遠綁定自己那顆硬碟db-0 重生後還是接回原本的資料)。於是程式可以安心約定「db-0 永遠是主庫」。

statefulset.yaml(官方範例,節錄)
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  clusterIP: None          # Headless Service:給每個 Pod 穩定 DNS
  selector:
    app: nginx
  ports:
  - port: 80
    name: web
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 3             # 會建立 web-0, web-1, web-2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.24
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:        # 👈 每個 Pod 自動獲得專屬硬碟
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

9DaemonSet(每台機器跑一份)

🧯
比喻:每層樓都要有的滅火器

大樓規定「每一層樓都必須掛一支滅火器」——不是越多越好,而是「每層剛好一支」。新蓋一層樓,就自動補一支;拆掉一層,那支也跟著移除。

DaemonSet 就是這個規則的執行者:確保符合條件的每個節點剛好跑一份該 Pod。最典型的用途是「每台機器都需要的基礎設施」:日誌收集器(收集該機所有容器的日誌)、監控代理(回報該機 CPU/記憶體)、網路外掛、儲存外掛。叢集自動加一台新節點時,DaemonSet 會自動在新節點補上一份,完全不用你操心。

daemonset.yaml(官方範例,節錄)
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v5.0.1
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      volumes:
      - name: varlog
        hostPath:
          path: /var/log

10Job 與 CronJob(一次性與排程任務)

📋
比喻:Deployment 是常駐員工,Job 是約聘工讀

前面的工作負載都是「長期顧店、一直跑」的服務——關掉就是出事。但有些任務本質是「做完這件事就該下班」:把一批檔案匯入資料庫、跑一次月報運算、寄一波電子報。

這種任務若用 Deployment 會很荒謬——程式做完正常結束(exit 0),Deployment 卻以為「它怎麼停了?快重開!」於是無限重跑。Job 才是對的工具:它知道「跑到成功完成就算數,完成後就不該再起來」。CronJob 則是 Job 上面加一個鬧鐘,按時間表定期生出 Job(如每天凌晨 2 點備份)。

Job:控制完成數與並行數

  • completions:要成功完成幾次。
  • parallelism:同時可跑幾個 Pod。
  • completionMode: Indexed:每個 Pod 有固定編號(適合分散式運算)。
job.yaml(官方範例,節錄)
apiVersion: batch/v1
kind: Job
metadata:
  name: distributed-training
spec:
  parallelism: 8
  completions: 8
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: trainer
        image: training-image:latest

CronJob:時間表

schedule 用標準 cron 格式(分 時 日 月 星期)。例如 "0 2 * * *" = 每天凌晨 2 點。

cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "0 2 * * *"      # 每天 02:00 觸發
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
          - name: backup
            image: backup-tool:1.0
🗄️
其他工作負載

ReplicationController 是 ReplicaSet 的舊版前身,現已不建議使用(用 Deployment 取代)。知道有這東西即可。

Part C

網路 Networking

Pod 怎麼被找到、怎麼互通、怎麼對外開放、怎麼管控流量。

11Service 全類型

📞
比喻:Service = 公司總機電話

Pod 隨時重生、IP 一直變(像員工手機常換號)。Service 提供一個永遠不變的總機號碼,外面打總機,它自動轉接給目前還活著的某個 Pod,還會平均分配(負載平衡)。靠標籤 selector 決定納入哪些 Pod。

類型用途(白話)
ClusterIP預設。只給叢集內部用,外面連不到。內部程式互相溝通。
NodePort在每台節點開一個固定埠號,讓外面能連進來(測試常用)。
LoadBalancer跟雲端商要一個對外負載平衡器,正式對外服務常用。
ExternalName把 Service 對應到外部網域名稱(CNAME),用來代理叢集外的服務。

另外,clusterIP: NoneHeadless Service(無頭服務)不做負載平衡,而是讓你直接拿到每個 Pod 的位址(StatefulSet 用)。

🔎
深入:Service 怎麼「一直知道」哪些 Pod 還活著?

祕密還是控制迴圈。Service 用 selector 標籤條件,背後有個控制器持續維護一份「目前符合條件、而且健康(通過就緒探針)的 Pod IP 清單」(存在 EndpointSlice 物件裡,見第 14 章)。

Pod 死掉 → 自動從清單移除(流量不再導給死人);新 Pod 起來且就緒 → 自動加入清單。所以你滾動更新、擴縮容、Pod 重生,Service 那個「總機號碼」永遠不變,名單卻在背後即時更新。這就是「服務探索」的本質。

🧱
三種對外類型是「層層疊加」的

它們不是平行選項,而是一層蓋一層:ClusterIP(最底層,給內部)→ NodePort(在 ClusterIP 之上,多開每台節點的固定埠讓外面進得來)→ LoadBalancer(在 NodePort 之上,再叫雲端商架一台對外負載平衡器,給你一個漂亮的對外 IP)。理解這個堆疊,你就懂為什麼建 LoadBalancer 時,它其實也順便有了 NodePort 和 ClusterIP 的能力。

service.yaml(LoadBalancer,官方範例)
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp   # 撈這個標籤的 Pod
  ports:
    - protocol: TCP
      port: 80         # Service 對外的埠
      targetPort: 9376 # 轉接到 Pod 的埠
  type: LoadBalancer

12Ingress 與 Gateway API

🏨
情境:為什麼需要 Ingress?

假設你有 3 個服務:官網(前端)、API(後端)、後台管理。如果每個都開一個 LoadBalancer,你就要跟雲端商租 3 個對外 IP(每個都要錢),使用者還要記 3 個不同網址,太亂。

Ingress 像飯店大廳的「櫃台接待 + 樓層指引牌」:所有人從同一個大門進來(一個 IP / 一個網域),櫃台再依你要去哪裡指路——www.站.com/ 帶你去前端、www.站.com/api 帶你去後端、admin.站.com 帶你去後台。順便還能在大門口統一檢查證件(處理 HTTPS/TLS 憑證)。一個入口,聰明分流。

⚠️
Ingress 需要 Ingress Controller

Ingress 物件只是「規則」,要有 Ingress Controller(如 ingress-nginx、Traefik)實際執行它才有效。記得用 ingressClassName 指定用哪個 Controller。

ingress.yaml(官方範例)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

加上 TLS(HTTPS)只要補 spec.tls 指向一個含憑證的 Secret。

🌐
Gateway API:下一代入口

Gateway API 是比 Ingress 更新、更有彈性的官方標準(角色分離、支援更多協定)。新專案可考慮,但 Ingress 仍最普及。先學 Ingress 即可。


13NetworkPolicy(網路防火牆)

🚧
比喻:Pod 之間的門禁

預設情況下,叢集內所有 Pod 都能互相連線(門全開)。NetworkPolicy 像門禁規則:限制「誰能進(ingress)、能出到哪(egress)」,用標籤、命名空間、IP 區段來描述。

🔓
情境:為什麼「全開」很危險?

預設「全開」意味著:如果前端網頁不小心被駭客入侵,駭客就能從那個 Pod 直接連到資料庫 Pod、內部金流 Pod……整個叢集任他橫著走(業界叫「東西向移動 / lateral movement」)。

NetworkPolicy 像在每個房間裝門禁:規定「資料庫房只接受『後端』標籤的 Pod 進入,其他一律擋」。這樣就算前端被攻陷,駭客也碰不到資料庫。正式環境的安全基本功。常見做法是先設一條「預設全部拒絕」,再逐一開放必要連線(白名單思維)。

⚠️
需要支援的網路外掛(CNI)

NetworkPolicy 要由支援它的 CNI 外掛(如 Calico、Cilium)執行才生效。有些預設網路外掛不支援,規則寫了也不會擋。

networkpolicy.yaml(官方範例,節錄)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
spec:
  podSelector:
    matchLabels:
      role: db          # 這條規則套用在 role=db 的 Pod
  policyTypes:
  - Ingress
  - Egress
  ingress:                # 誰可以連進來
  - from:
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:                 # 可以連出去哪
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24

14DNS、kube-proxy、EndpointSlice

叢集 DNS(服務探索)

📇
比喻:叢集內部的電話簿

還記得 Service 是「永不變的總機號碼」嗎?但 IP 還是一串難記的數字。叢集 DNS(通常是 CoreDNS)就是內部電話簿:把好記的名字對應到 Service 的 IP。於是你的程式要連資料庫,不用寫死 IP,直接寫 mysql 這個名字就好——就算 Service IP 哪天變了,名字照樣通。

建立 Service 後會自動有一個 DNS 名稱,Pod 之間用名字就能互找,不用記 IP。格式:

Service DNS 格式
# 完整:<service>.<namespace>.svc.cluster.local
my-service.default.svc.cluster.local
# 同一個 namespace 內可直接用短名:
my-service

kube-proxy

跑在每個節點上,負責把「連到 Service 的流量」實際轉發到後端 Pod(透過 iptables / IPVS 規則)。它是 Service 能運作的幕後功臣。

EndpointSlice

記錄「某個 Service 目前對應到哪些 Pod 的 IP 與埠」。EndpointSlice 是較新、可擴展的設計(取代舊的 Endpoints),讓大規模叢集的網路資訊更有效率地同步。

Part D

儲存 Storage

容器重生資料就消失?這一部教你怎麼讓資料「活下來」。

15Volume 種類

👻
情境:消失的上傳檔案

你的網站讓使用者上傳大頭照,存在容器裡的 /uploads。一切正常——直到某天容器因故重生(當機、更新、被搬到別台),所有大頭照瞬間人間蒸發。為什麼?因為容器內部的檔案系統是「用完即丟」的:容器一重生,等於拿一份全新的食譜重做一盤菜,舊菜(包括寫進去的檔案)全部不見。

💾
比喻:給容器外接的隨身碟

Volume(卷)就是掛載到容器裡的「外接儲存」——像插一支隨身碟。程式把資料寫到這支「碟」上,而不是寫進容器自己的肚子裡。如此一來容器就算重生,只要把同一支碟再插回去,資料還在。Volume 也能讓同一個 Pod 裡的多個容器共用同一支碟。種類很多,先認識常見的幾種:

Volume 類型用途
emptyDirPod 存活期間的暫存空間,同 Pod 內容器可共用。Pod 刪掉就沒了。可用記憶體當儲存。
hostPath掛載節點主機上的目錄(有安全風險,少用)。
configMap / secret把設定 / 機密當成檔案掛進容器。
downwardAPI把 Pod 自身資訊(名字、標籤等)當檔案掛進去。
projected把多個來源(secret、configMap…)合併投影到同一目錄。
persistentVolumeClaim掛載一個「永久卷」(見下章),真正用於保存資料。

這些之中,emptyDirconfigMapsecretdownwardAPI 等屬於「臨時卷(ephemeral)」——生命週期跟著 Pod。要長久保存資料,需要下一章的 PV/PVC。


16PV / PVC / StorageClass(永久儲存)

🏬
比喻:倉儲出租(房東 vs 租客)

上一章的隨身碟適合暫存,但正式要「長久保存的重要資料」需要更正規的機制,而且要把「誰提供儲存」和「誰使用儲存」分開——因為通常開發者(用的人)不該也不想管底層硬碟細節。

PV(PersistentVolume)= 房東準備好的儲藏室:一塊實際的儲存空間,由叢集管理員 / 雲端提供,講明「我有 5GB、在哪、怎麼接」。
PVC(PersistentVolumeClaim)= 租客的租賃申請單:開發者只填「我要 8GB、要能讀寫」,完全不用知道硬碟是哪一顆、什麼牌子

你(Pod)把申請單(PVC)交出去,K8s 自動幫你媒合一間符合條件的儲藏室(PV)並「綁定」。關鍵好處:資料的生命週期和 Pod 脫鉤了——Pod 死了、重生了、搬家了,那間儲藏室和裡面的資料都還在,新 Pod 接回同一張申請單就能繼續用。

pvc.yaml(官方範例)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow

存取模式(Access Modes)

模式意思
ReadWriteOnce (RWO)可被單一節點掛載成讀寫。
ReadOnlyMany (ROX)可被多個節點掛載成唯讀。
ReadWriteMany (RWX)可被多個節點同時掛載成讀寫。
ReadWriteOncePod (RWOP)只能被單一 Pod 掛載成讀寫。

StorageClass 與動態供應

不用房東先蓋倉庫

有了 StorageClass,你不必事先手動建 PV。當 PVC 指定一個 StorageClass,K8s 會自動即時建立對應的 PV(向雲端 / 儲存系統要空間),這叫「動態供應(Dynamic Provisioning)」。

storageclass.yaml(官方範例,節錄)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: low-latency
provisioner: csi-driver.example-vendor.example
reclaimPolicy: Retain          # 預設是 Delete
allowVolumeExpansion: true      # 允許之後擴大容量
volumeBindingMode: WaitForFirstConsumer
♻️
回收策略(reclaimPolicy)

PVC 刪掉後 PV 怎麼處理:Delete(連同底層儲存一起刪,雲端常見預設)、Retain(保留資料,需人工處理)。正式環境放重要資料建議用 Retain 較安全。


17CSI、快照與臨時卷

CSI(容器儲存介面)

CSI(Container Storage Interface)是一套標準介面,讓各家儲存廠商寫「驅動程式(driver)」接到 K8s。有了 CSI,K8s 能支援幾乎任何儲存系統(雲端硬碟、NFS、Ceph…)而不用改 K8s 本體。

Volume 快照(Snapshot)

對 PVC 的資料做「時間點快照」,可用來備份或複製出新的卷。透過 VolumeSnapshotVolumeSnapshotClass 物件操作(需 CSI driver 支援)。

臨時卷(Ephemeral Volumes)

官方支援多種臨時卷:emptyDir(本機或 RAM)、configMap / secret / downwardAPI(注入 K8s 資料)、image(掛載容器映像檔內容)、CSI 臨時卷、以及「通用臨時卷(generic ephemeral)」——任何支援動態供應的儲存都能當臨時卷用,Pod 刪掉就跟著刪。

Part E

設定與資源

把設定與程式分離、控制每個程式能用多少資源、確認程式健康。

18ConfigMap 與 Secret

🔐
比喻:ConfigMap 是便利貼,Secret 是保險箱

程式需要設定值(資料庫位置、開關)和機密(密碼、金鑰),不該寫死在程式裡。ConfigMap 放一般設定(公開便利貼);Secret 放機密(上鎖保險箱)。好處:改設定不用重新打包映像檔。

⚠️
Secret 預設只是 Base64,不是加密!

Secret 的內容預設只用 Base64 編碼(人眼看不懂但很好還原),不等於加密。正式環境應啟用 etcd 靜態加密(Encryption at Rest)並用 RBAC 嚴格限制誰能讀 Secret。

兩種注入方式:當成環境變數,或當成檔案掛進容器。以下是用 Secret 當環境變數(官方範例):

secret 當環境變數(官方範例)
apiVersion: v1
kind: Pod
metadata:
  name: envvars-multiple-secrets
spec:
  containers:
  - name: envars-test-container
    image: nginx
    env:
    - name: BACKEND_USERNAME
      valueFrom:
        secretKeyRef:
          name: backend-user
          key: backend-username

19資源請求 / 限制與 QoS

🍽️
比喻:訂位人數 vs 包廂上限

requests(請求)= 訂位人數:保證至少給你的資源,排程器用它決定把 Pod 放哪台(哪台有空位才放得進去)。limits(限制)= 包廂人數上限:最多能用到這麼多。

兩者超過時的處理很不一樣,務必記住
CPU 超過 limit → 被「限速」(throttle),程式變慢但不會死。CPU 是可壓縮資源。
記憶體超過 limit → 直接被殺掉,狀態顯示 OOMKilled(Out Of Memory)。記憶體是不可壓縮資源,要不夠就只能殺。

💥
情境:半夜的 OOMKilled 連環爆

你沒設記憶體 limit,某個程式記憶體洩漏越吃越多,把整台節點的記憶體吃光。結果——不只它自己,連同一台機器上其他無辜的 Pod 都被波及驅逐,引發雪崩。

反過來,limit 設太小,程式正常運作就被 OOMKilled、不斷 CrashLoopBackOff。所以「合理設定 requests/limits」是 K8s 維運最重要的基本功之一——它同時保護你的程式、和它的鄰居。

resources 片段(官方範例)
resources:
  requests:
    cpu: "100m"      # 100 milliCPU = 0.1 顆 CPU
    memory: "200Mi"
  limits:
    cpu: "2"
    memory: "200Mi"

QoS 服務品質等級

K8s 依你設定的 requests/limits 自動把 Pod 分三等。當節點資源不足要驅逐 Pod 時,等級低的先被犧牲

QoS 等級條件被驅逐順序
Guaranteed每個容器的 requests 和 limits 都設定且相等。最後(最受保護)
Burstable不符合 Guaranteed,但至少有設一個 request 或 limit。中間
BestEffort完全沒設 requests/limits。最先被犧牲
💡
CPU 單位

CPU 用 m(milliCPU),1000m = 1 顆 CPU。記憶體用 Mi/Gi(2 的次方)或 M/G(10 的次方)。


20健康探針 Probes

🩺
情境:明明「沒當掉」,卻不能用

有一種最惱人的故障:程式的程序還在(作業系統看它活著),但它其實內部卡死了——連線池滿了、陷入死迴圈、等一個永遠不會回來的回應。對 K8s 來說「程序在 = 沒事」,於是流量繼續導進這個「活死人」,使用者全部卡住。

探針(Probe)就是為了戳破這種「假活」。K8s 不只看程序在不在,還主動定期去敲門問「你真的還好嗎?」

探針作用失敗時
Liveness 存活探針程式是否還活著重啟容器
Readiness 就緒探針程式是否準備好接流量暫時把它從 Service 移除(不導流量),但不重啟
Startup 啟動探針程式是否「啟動完成」(給慢啟動程式用)啟動期間先不跑前兩種探針
probes 片段
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 5   # 啟動後等幾秒才開始檢查
  periodSeconds: 10       # 每 10 秒檢查一次
readinessProbe:
  httpGet:
    path: /ready
    port: 8080

檢查方式有三種:httpGet(打 HTTP)、tcpSocket(試連埠)、exec(在容器內跑指令看回傳碼)。


21ResourceQuota、LimitRange、Downward API

ResourceQuota(命名空間總量管制)

限制整個 namespace 能用的資源總量(CPU、記憶體總和、物件數量上限等)。避免某個團隊把整個叢集吃光。

LimitRange(單一物件的預設與上下限)

為 namespace 內每個 Pod/容器設定預設 requests/limits,以及最小 / 最大值。沒寫資源的 Pod 會自動套用預設。

Downward API

讓容器能讀到「關於自己的資訊」(Pod 名稱、namespace、標籤、所在節點、自己的資源設定等),透過環境變數或檔案注入。程式想知道自己是誰時很有用。

Part F

排程 Scheduling

Pod 該被放到哪一台節點?這一部講「分房間的學問」。

22排程器與 Affinity(親和性)

kube-scheduler 負責決定新 Pod 放到哪個節點:先過濾出「放得下」的節點,再評分挑「最適合」的。你可以用以下方式影響它的決定:

nodeSelector(最簡單)

只放到有特定標籤的節點,例如 nodeSelector: { disktype: ssd }

Node Affinity(節點親和性)

比 nodeSelector 更有彈性的「我想放在哪種節點」,分兩種強度:

  • required…(硬性):不滿足就不排程。
  • preferred…(軟性):盡量滿足,不行也接受。

Pod Affinity / Anti-Affinity(Pod 間親和 / 反親和)

🧲
比喻:想跟誰住一起 / 不想跟誰住一起

Pod Affinity(親和):把這個 Pod 和某些 Pod 放近一點(同節點 / 同區域),例如把 app 和它的快取放一起降低延遲。Pod Anti-Affinity(反親和):把它們刻意分開。

🥚
情境:別把雞蛋放同一個籃子

你設了 Deployment 跑 3 個副本,覺得很安全。但若 K8s 剛好把這 3 個副本全排到同一台節點上——那台機器一掛,你的「3 副本高可用」瞬間變成「0 個存活」,安全感是假的。

Pod Anti-Affinity 就是解法:規定「同一個服務的副本,盡量(或強制)分散到不同節點」。這樣任何單一台機器壞掉,都還有其他節點上的副本頂著。這是做「高可用」最常用的排程技巧。

Topology Spread Constraints(拓撲分散)

更精細地控制 Pod「平均分散」到不同區域 / 節點,避免擠在一起,提升可用性。


23Taints 與 Tolerations(汙點與容忍)

🚫
比喻:節點貼「閒人勿入」,Pod 帶「通行證」

Affinity 是「Pod 挑節點」;Taints/Tolerations 反過來是「節點排斥 Pod」。在節點上加 Taint(汙點)等於貼上「閒人勿入」,除非 Pod 帶著對應的 Toleration(容忍)通行證,否則不能進。常用於保留特殊節點(如 GPU 機、控制平面)。

Taint 的效果(effect)有三種:NoSchedule(不排新 Pod 進來)、PreferNoSchedule(盡量別)、NoExecute(連現有不容忍的 Pod 都趕走)。

tolerations 片段(官方範例)
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
在節點加 / 移除 taint
$ kubectl taint nodes node1 key1=value1:NoSchedule
$ kubectl taint nodes node1 key1=value1:NoSchedule-   # 結尾加 - 移除

24優先權、搶占與驅逐

Pod 優先權與搶占(Priority & Preemption)

🚑
比喻:救護車優先通行

PriorityClass 給 Pod 設優先權。當高優先權 Pod 找不到位置時,排程器會搶占(preempt)——驅逐優先權較低的 Pod 來騰出空間。只會驅逐「剛好夠用」的數量。

節點壓力驅逐(Node-pressure Eviction)

當節點資源(記憶體、磁碟)吃緊,kubelet 會主動驅逐部分 Pod 來自救,順序大致依 QoS:BestEffortBurstableGuaranteed。這也是為什麼設好 requests/limits 很重要。

API 發起的驅逐 & PodDisruptionBudget(PDB)

🛡️
比喻:值班最低人數規定

維運時(如節點要維護、清空 kubectl drain)會主動驅逐 Pod。PodDisruptionBudget 像「值班最低人數規定」:保證某服務在主動干擾期間至少保留 N 個(或最多只少 M 個)可用,避免一次全關造成服務中斷。

Part G

安全 Security

誰能對叢集做什麼、容器能拿到多大權限、怎麼守住底線。

25認證、授權與 RBAC

一個 API 請求進到 apiserver 要過三關:認證(Authentication,你是誰)→ 授權(Authorization,你能做什麼)→ 准入控制(Admission,再做檢查 / 修改)

ServiceAccount(給程式的身分)

使用者(人)用憑證 / token 認證;Pod 裡的程式則用 ServiceAccount 當身分去呼叫 API。每個 namespace 有預設 ServiceAccount,可自建並綁定權限。

RBAC(角色為基礎的存取控制)

🗝️
比喻:職務權限表 + 派任令

Role / ClusterRole = 職務權限表(這個角色能對哪些資源做哪些動作)。RoleBinding / ClusterRoleBinding = 派任令(把某角色指派給某個人 / 群組 / ServiceAccount)。Role 限定在單一 namespace;Cluster 版本是整個叢集範圍。

role + rolebinding(概念範例)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]   # 只能讀 Pod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
最小權限原則

只給剛好夠用的權限。別圖方便把 cluster-admin 到處亂綁。


26SecurityContext(容器權限設定)

🦹
情境:以 root 執行的容器有多危險?

容器預設常以 root(最高權限)身分跑程式。平常沒事,但若程式有漏洞被駭客攻破,駭客就在容器內拿到 root——再利用核心漏洞「逃逸」到容器外,就可能控制整台節點,進而威脅整個叢集。

原則很簡單:給程式剛好夠用的權限就好(最小權限)。一個只是回應網頁的程式,根本不需要 root、不需要寫入根目錄、不需要任何特殊能力。SecurityContext 就是把這些通通鎖緊的工具,讓「就算被攻破,也翻不出多大浪」。

SecurityContext 控制容器 / Pod 的安全屬性,降低被入侵後的危害。常見設定(皆查證自官方 API):

欄位作用
runAsNonRoot強制不可用 root(UID 0)執行,是 root 就拒絕啟動。
runAsUser / runAsGroup指定執行的使用者 / 群組 ID。
readOnlyRootFilesystem根檔案系統設成唯讀,防止被植入檔案。
allowPrivilegeEscalation禁止程序取得比父程序更高的權限。
privileged特權模式(≈ 主機 root),危險,盡量別開
capabilities細緻地增減 Linux capabilities(建議 drop 不需要的)。
seccompProfile限制容器可呼叫的系統呼叫。
securityContext 片段
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop: ["ALL"]
💡
優先順序

同時在 Pod(PodSecurityContext)和容器(SecurityContext)設定時,容器層級優先。部分欄位在 Windows 節點無效(僅 Linux)。


27Pod Security Standards 與 Admission

光有 RBAC 不夠細,無法限制「Pod 規格本身」(例如禁止特權容器)。官方定義了三個Pod Security Standards(PSS)等級:

等級意思
privileged不設限,什麼都能做(給系統元件)。
baseline基本防護,擋掉已知的高風險設定。
restricted最嚴格,遵循強化最佳實務(建議一般應用用這個)。

透過內建的 Pod Security Admission(K8s 1.23 起預設啟用)執行,只要在 namespace 貼標籤即可。模式有 enforce(強制擋)、audit(記錄)、warn(警告)。

namespace 套用 PSS(官方範例,節錄)
apiVersion: v1
kind: Namespace
metadata:
  name: my-baseline-namespace
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted
🔌
准入控制器(Admission Controllers)

在「授權通過後、寫進 etcd 前」攔截請求做檢查或修改。內建很多個;你也可用 ValidatingAdmissionWebhook / MutatingAdmissionWebhook 接自訂邏輯(見第 30 章)。常見第三方策略引擎:OPA Gatekeeper、Kyverno。

Part H

自動擴展 Autoscaling

讓副本數、資源配額、節點數量都隨負載自動增減。

28HPA / VPA / Cluster Autoscaler

K8s 的自動擴展有三個層次,可搭配使用:

名稱調整什麼比喻
HPA 水平擴展Pod 的數量(多開幾份)店裡忙就多排幾個店員
VPA 垂直擴展單一 Pod 的資源大小(給更多 CPU/記憶體)把店員換成能力更強的
Cluster Autoscaler叢集的節點數量(加減機器)店面不夠就多租一間店

HorizontalPodAutoscaler(HPA)

🌊
情境:回到阿明小舖的雙十一

還記得第 1 章阿明半夜手動加機器的惡夢嗎?HPA 就是那一夜的救星。你只要先講好規則:「當平均 CPU 超過 50%,就自動多開 Pod,最多開到 10 個;流量退了就慢慢收回最少 1 個」。

於是雙十一流量湧入 → CPU 飆高 → HPA 自動從 2 個 Pod 擴到 8 個扛住人潮 → 活動結束流量退 → 自動縮回 2 個省錢。阿明整晚在睡覺,什麼都不用做。這就是「彈性(elasticity)」——雲端最迷人的能力。

最常用。根據 CPU 使用率(或自訂指標)自動增減副本數。需要 Metrics Server 提供指標。

hpa.yaml(官方範例)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50   # 平均 CPU 超過 50% 就擴展

快速指令版:kubectl autoscale deployment foo --min=2 --max=10 --cpu-percent=50

⚠️
VPA 要另外安裝

HPA 是內建 API;VPA 不是內建,需以附加元件安裝(用 CRD 定義),且同樣需要 Metrics Server。HPA 與 VPA 同時調整 CPU/記憶體時要小心衝突。

Part I

擴充 Kubernetes

K8s 本身可被擴充——你能教它認識全新的資源類型與自動化邏輯。

29CRD 與 Operator 模式

Custom Resource Definition(CRD)

🧬
比喻:教 K8s 認識新單字

K8s 內建 Pod、Service…等「單字」。CRD 讓你新增自己的單字——定義一種全新的資源類型(例如 kind: CronTabkind: Database),之後就能像內建資源一樣用 kubectl 操作它。

crd.yaml(官方範例,節錄)
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com   # 格式:<複數>.<群組>
spec:
  group: example.com
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames: [ct]
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host: { type: string }

Operator 模式

🤖
比喻:把資深維運的腦袋寫成程式

光有 CRD 只是「能記錄這種資源」。Operator = CRD + 自訂控制器(custom controller):寫一個程式跑控制迴圈,持續盯著你的自訂資源,自動完成複雜維運。等於把一位資深維運工程師的腦袋和雙手變成 24 小時不下班的程式。

🗄️
具體想像:一個 PostgreSQL Operator

沒有 Operator 時,架一套「高可用 + 自動備份 + 故障自動切換」的 PostgreSQL 叢集,是資深 DBA 好幾天的工作。

有了 PostgreSQL Operator,你只要寫短短幾行 YAML:kind: PostgresCluster, replicas: 3, backup: daily。Operator 看到這個資源,就自動幫你:部署主從架構、設定資料複製、每天備份、主庫掛掉時把從庫升為主庫……全部照「資深 DBA 會做的事」執行。這就是為什麼 Operator 被稱為「把營運知識封裝成軟體」,也是雲端原生生態最強大的擴充模式。


30Webhook、聚合層與外掛

除了 CRD,官方還提供多種擴充點:

  • Admission Webhooks:在資源被建立 / 修改時,呼叫你的外部服務做驗證(Validating)修改(Mutating)。例如「所有 Pod 強制加上某標籤」或「拒絕沒設 limits 的 Pod」。
  • API Aggregation Layer(聚合層):在主 apiserver 後面掛自己的 API server,提供 CRD 做不到的進階 API。
  • Device Plugins:讓節點上報特殊硬體(GPU、FPGA…)給 K8s 排程使用(YAML 裡會看到 nvidia.com/gpu: 1)。
  • CNI / CSI / CRI:網路、儲存、執行時的標準介面,各廠商以外掛接入。
  • Scheduler 擴充:自訂排程邏輯(scheduler framework / 多排程器)。
🧩
該用 CRD 還是聚合層?

大多數情況用 CRD 就夠(宣告式、由控制平面代管儲存)。只有需要 CRD 無法滿足的特殊行為(如自訂儲存、特殊 API 語意)才考慮聚合層。

Part J

實作與工具

動手把前面學的跑起來,並認識日常必備工具。

31安裝與第一個應用

📋
前置需求

先裝好 Docker Desktop(或其他容器執行環境)。學習用最推薦 minikube(本機迷你叢集)。

  1. 安裝 minikube 與 kubectl
    macOS / Windows
    # macOS
    $ brew install minikube kubectl
    # Windows
    > winget install Kubernetes.minikube
    > winget install Kubernetes.kubectl
  2. 啟動叢集
    terminal
    $ minikube start          # 第一次會下載元件,等幾分鐘
    $ kubectl get nodes        # 看到 minikube 為 Ready 就成功
  3. 部署 Deployment 並體驗自我修復
    terminal
    $ kubectl apply -f deployment.yaml      # 套用第 7 章的檔案
    $ kubectl get pods                       # 看到 3 個 Running
    $ kubectl delete pod <某個pod名>          # 故意刪一個
    $ kubectl get pods                       # 數量還是 3!K8s 自動補回 🪄
  4. 對外開放並從瀏覽器看到
    terminal
    $ kubectl expose deployment nginx-deployment --type=NodePort --port=80
    $ minikube service nginx-deployment      # 自動開瀏覽器
🧹
收拾

kubectl delete deployment nginx-deployment 清資源;minikube stop 關叢集;minikube delete 徹底刪掉重來。


32kubectl 指令速查

指令多為「動詞 + 名詞」。以下是日常最常用的。

查看

kubectl get pods / deploy / svc / all列出資源(可加 -A 看所有 namespace、-o wide 看更多)
kubectl describe pod <名>看詳細資訊與事件(除錯第一步)
kubectl logs <pod> [-f]看日誌(-f 持續追蹤)
kubectl get events --sort-by=.lastTimestamp看叢集事件
kubectl explain <資源>.<欄位>查欄位說明

操作

kubectl apply -f 檔.yaml最常用,套用設定(建立 / 更新)
kubectl delete -f 檔.yaml刪除
kubectl scale deploy <名> --replicas=5調整副本數
kubectl rollout undo deploy <名>回滾到上一版
kubectl exec -it <pod> -- bash進入容器內(像 SSH)
kubectl port-forward <pod> 8080:80把本機埠轉發到 Pod(本機測試)
kubectl debug ...注入臨時容器除錯
小技巧

設定別名 alias k=kubectl;用 -n <namespace> 指定隔間;kubectl config get-contexts 切換叢集。


33Helm(套件管理)

📦
比喻:K8s 的 App Store

部署複雜應用常要一拖拉庫 YAML。Helm 把這些打包成「Chart」(可參數化的範本),一行指令就裝好整套(如資料庫、監控系統),還能版本管理與升級回滾。

helm 常用指令
$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm install my-db bitnami/mysql      # 一鍵安裝 MySQL
$ helm list                             # 看已安裝的 release
$ helm upgrade my-db bitnami/mysql      # 升級
$ helm uninstall my-db                  # 移除
ℹ️
生態系工具(非 K8s 本體)

Helm 屬於 CNCF 生態系,不是 K8s 核心 API。同類概念還有 Kustomize(K8s 內建、用疊加方式客製 YAML,kubectl apply -k)。兩者常見且互補。


34監控、日誌與維運概念

正式運作還需要「看得到叢集在幹嘛」。這些多半是生態系工具,列出讓你有方向:

  • Metrics Server:提供基礎資源指標(HPA、kubectl top 需要它)。
  • Prometheus + Grafana:最主流的指標監控與儀表板。
  • 日誌:容器日誌可用 kubectl logs 看;正式環境集中收集常用 Fluent Bit / Loki / Elasticsearch。
  • 追蹤(Tracing):OpenTelemetry、Jaeger 等做分散式追蹤。
  • GitOps:用 Git 當「唯一真實來源」自動同步到叢集,工具如 Argo CD、Flux。
你已經走完地圖

到這裡,你已經把 Kubernetes 從「為什麼存在」到「全部核心技術」走過一遍。剩下的是不斷動手實作把它變熟。

附錄

收尾與查詢

名詞表、常見問題、下一步。

35名詞對照速查表

名詞一句話白話
Container 容器把程式和依賴打包成的標準盒子。
Image 映像檔做容器的「食譜」,容器是成品。
CRI / CNI / CSI執行時 / 網路 / 儲存的標準介面,讓廠商以外掛接入。
Cluster 叢集一群合作跑 K8s 的機器。
Node 節點叢集裡的一台機器。
Control Plane 控制平面叢集的「大腦」(apiserver、etcd、scheduler、controller)。
Pod裝容器的最小管理單位(免洗)。
ReplicaSet維持指定數量 Pod 的底層元件。
Deployment管理無狀態 Pod:副本、更新、回滾。
StatefulSet管理有狀態 Pod:固定身分 + 專屬儲存。
DaemonSet每個節點各跑一份。
Job / CronJob一次性任務 / 定時任務。
Service給一群 Pod 固定門牌 + 負載平衡。
Ingress依網址 / 路徑分流的對外 HTTP(S) 入口。
NetworkPolicyPod 間的網路門禁。
Namespace叢集裡的隔間。
ConfigMap / Secret一般設定 / 機密資料。
Volume / PV / PVC儲存 / 永久卷 / 卷的申請單。
StorageClass動態自動建立 PV 的範本。
requests / limits資源的保證量 / 上限。
QoS服務品質等級,決定資源不足時誰先被驅逐。
Probe 探針健康檢查(liveness / readiness / startup)。
Affinity / Taint影響 Pod 該放哪個節點的規則。
RBAC角色為基礎的權限控制。
ServiceAccount給 Pod 內程式的身分。
HPA / VPA自動調整副本數 / 單一 Pod 資源。
CRD / Operator自訂資源類型 / 自訂資源 + 控制器自動化。
HelmK8s 套件管理(App Store)。
kubectl操作叢集的命令列遙控器。

36常見問題 FAQ

我一定要先很會 Docker 嗎?
不用很會,懂「什麼是容器、映像檔」並用過 docker run 跑過一個容器就夠開始。
K8s 是免費的嗎?
Kubernetes 本身開源免費。但跑它的機器要錢;雲端託管(EKS/GKE/AKS)另收管理與機器費用。
Pod 和 Container 差在哪?
Container 真正跑程式;Pod 是 K8s 包住一或多個 Container 的最小管理單位。K8s 管 Pod,不直接管 Container。
為什麼不直接建 Pod,要透過 Deployment?
單獨建的 Pod 壞掉不會自動重生。Deployment 會維持數量、自我修復、滾動更新,所以實務幾乎都用它(有狀態則用 StatefulSet)。
Deployment 和 StatefulSet 怎麼選?
無狀態、Pod 可互換 → Deployment。需要固定身分 + 各自專屬儲存(資料庫等)→ StatefulSet。
Service、Ingress 差在哪?
Service 給一群 Pod 固定入口與負載平衡(L4);Ingress 是更上層的 HTTP(S) 入口,依網址 / 路徑把流量分到不同 Service(L7)。
Secret 真的安全嗎?
預設只是 Base64 編碼,不是加密。正式環境要啟用 etcd 靜態加密並用 RBAC 限制存取。
正式環境也用 minikube 嗎?
不是,minikube 只用於本機學習 / 測試。正式環境用雲端託管或自架多節點叢集,但觀念與指令完全通用。
學 K8s 對找工作有幫助嗎?
非常有。K8s 是後端、DevOps、SRE、雲端領域的標準技能之一。

37學習路徑與下一步

  1. 把 Part J 的實作全跑一遍

    光看不練等於沒學。用 minikube 親手做 Deployment、Service、自我修復實驗。

  2. 練熟五大工作負載

    Deployment、StatefulSet、DaemonSet、Job、CronJob 各做一個範例。

  3. 打通網路與儲存

    Service → Ingress → NetworkPolicy;PVC → StorageClass 動態供應。

  4. 補上設定、排程、安全

    ConfigMap/Secret、probes、resources/QoS、affinity/taint、RBAC、SecurityContext。

  5. 學 Helm / Kustomize 與自動擴展

    用 Helm 裝一套現成應用;設定 HPA 觀察自動擴展。

  6. 挑戰雲端與進階

    在 GKE/EKS/AKS 開真叢集;研究 Operator、GitOps、可觀測性。

📚
官方權威資源(本手冊內容查證來源)

最權威是官方網站 kubernetes.io(含中文文件),以及免安裝的互動教學 Kubernetes Basics

🚀
最後一句

K8s 看似龐大,但核心只有一句:用 YAML 聲明你想要的狀態,K8s 透過控制迴圈自動幫你維持。本手冊講的每個物件,都是這個觀念的延伸。慢慢來,多動手,你一定學得會。加油!