Kubernetes 完整教學手冊
從零開始,把所有技術都講給你聽
不假設你懂任何東西。先用生活比喻帶你入門,再一路涵蓋 K8s 全部核心技術:工作負載、網路、儲存、設定、排程、安全、自動擴展、擴充機制。每個概念都有白話+官方 YAML。
本手冊所有技術描述與 YAML 範例皆參考並查證自 Kubernetes 官方文件(kubernetes.io),非憑印象撰寫。API 欄位與行為會隨版本演進,實作前請以你所用版本的官方文件為準。
入門基礎
完全沒接觸過?從這裡開始,建立直覺。
0怎麼讀這份手冊
這份手冊有兩個身分:新手教科書(從第 0 章循序漸進)+技術查詢手冊(左側目錄隨點隨查)。建議第一次照順序看 Part A~B,之後當字典用。
Kubernetes(簡稱 K8s)是一個「自動幫你管理一大堆容器化程式」的系統。你只描述「我想要的最終狀態」,它就 24 小時不睡覺地幫你達成並維持。
Kubernetes 開頭 K、結尾 s,中間剛好 8 個字母,所以縮寫成 K8s。這個字源自希臘文「舵手 / 領航員」,所以你常看到船舵圖示。
閱讀約定:灰底字是指令或專有名詞;🧩紫框是比喻、💡藍框是小撇步、⚠️黃框是注意、✅綠框是重點總結。
1為什麼需要 K8s?先理解它解決的痛
學工具前先懂「沒有它有多痛」。我們來演一齣戲:你是「阿明小舖」的唯一工程師,把購物網站放在一台租來的伺服器上。
第 1 個月|凌晨 3:17,你的手機瘋狂震動——網站掛了。你睡眼惺忪爬起來,SSH 連進機器,發現程式因為記憶體不足被系統殺掉。你手動重開,回去睡。第二天同樣的事又來一次。
第 2 個月|雙十一前夜,行銷說要大促。你緊張地多租了 4 台機器,半夜手動把程式一台台裝起來、改設定、調流量分配。活動結束,你又要記得把多的機器關掉,不然帳單爆炸。
第 3 個月|要上新版,你把舊程式關掉、裝新版——結果這 30 秒內,正在結帳的客人全部看到錯誤頁,客服電話被打爆。而且新版有 bug,你還得手忙腳亂地裝回舊版。
阿明遇到的,正是所有線上服務都會碰到的四個經典痛點:
- 🔥 會掛、掛了要有人救:程式半夜當掉,你卻在睡覺。
- 📈 流量會暴衝暴跌:一台扛不住要加機器,閒下來又要關掉省錢。
- 🔄 更新不能中斷服務:換版本時客人不該看到錯誤。
- 🖥️ 機器一多就管不動:哪個程式在哪台、哪台壞了,一團亂。
這四件事如果全部靠人工,阿明遲早累垮、出包。而這四件事,正好就是 K8s 自動幫你做的事——它就是那個「永遠醒著、不會累、不會忘」的代理人。
你不會自己一間間檢查水電、修燈、客滿時開新房間,而是請一個物業總管。你只說「永遠保持 3 間房有人住,壞了就修」,剩下他全包。K8s 就是這個總管。
| K8s 核心能力 | 白話 |
|---|---|
| 自我修復 Self-healing | 程式當掉自動重開新的,不用你半夜爬起來。 |
| 自動擴展 Scaling | 客人多就自動加副本分擔,少了就收掉省錢。 |
| 滾動更新 Rolling Update | 更新一個一個換,全程不中斷,出錯可一鍵還原。 |
| 負載平衡 Load Balancing | 把流量平均分給每份程式。 |
| 服務探索 Service Discovery | 程式之間用固定名稱互找,不怕 IP 變動。 |
你只說「我想要的最終狀態」(例如 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:負責「做出容器」並在一台機器上跑。Kubernetes:在一大堆機器上管理成千上萬個容器。
👉 Docker 管一個,K8s 管一群。
映像檔(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 和記憶體)。
所以容器才會「啟動快、超省資源」:它沒有開機過程,就是直接跑一個被框起來的程序而已。理解這點,你就懂為什麼一台機器能塞下幾十個容器,卻塞不下幾十台虛擬機。
K8s 透過標準介面 CRI(Container Runtime Interface)呼叫容器執行引擎,常見的是 containerd 或 CRI-O。K8s 1.24 起已移除內建的 Docker 支援(dockershim),但你用 Docker 做出來的映像檔仍完全通用(都是 OCI 標準格式)。
3K8s 到底是什麼
幾十位樂手(容器)各司其職,沒指揮會亂。指揮(K8s)不親自演奏,但決定誰何時進場、誰大聲小聲、有人吹錯馬上補救。所以 K8s 又叫「容器編排(Container Orchestration)」——orchestration 本意就是管弦樂編排。
技術定義:Kubernetes 是開源的容器編排系統,最早由 Google 根據自家內部系統(Borg)的經驗開發,2014 年開源,現由 CNCF(雲端原生運算基金會)維護,自動化容器的部署、擴展與管理。
單獨一個容器,用 Docker 就能跑——就像一個人哼歌不需要指揮。但當你有幾十、幾百個容器,彼此要溝通、要在對的時間啟動、有人倒下要補位、流量要分配——這就從「哼歌」變成「交響樂」了。
K8s 做的事,和指揮對樂團做的事驚人地像:決定誰上場(排程 Pod 到節點)、誰接誰(服務探索與網路)、有人吹錯立刻補救(自我修復)、整體音量隨場合調整(自動擴展)。你寫的 YAML,就是給樂團的「樂譜」。
你不會只用一台電腦跑 K8s,而是一群電腦合作,這群電腦叫「叢集(Cluster)」。裡面有工頭(動腦指揮、不做粗活)和一群工人(實際跑你的容器)。
4架構與全部元件
一個叢集分兩種角色:控制平面(工頭)與工作節點(工人)。
動腦、發號施令,管理叢集狀態。
- kube-apiserver:唯一入口(總機),所有指令都經過它。
- etcd:鍵值資料庫,叢集的「大腦記憶」,存所有狀態。
- kube-scheduler:調度員,決定新 Pod 放到哪個工人。
- kube-controller-manager:監工,跑各種控制迴圈維持期望狀態。
- cloud-controller-manager:對接雲端商(選用)。
真正跑你程式(容器)的機器。
- kubelet:節點上的組長,確保 Pod 裡容器有照規格跑。
- kube-proxy:處理 Service 的網路轉發規則。
- Container Runtime:實際執行容器(containerd / CRI-O)。
控制平面 = 店長辦公室:接單(apiserver)、看報表(etcd)、排班(scheduler)、盯場(controller)。工作節點 = 廚房:領班(kubelet)照單做餐(Pod),送餐動線(kube-proxy)。你在櫃台點餐就好,不用管廚房。
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 既強韌又能擴展的祕密。
正式環境的控制平面通常會跑多份(多個 apiserver、etcd 組成叢集),避免單點故障。學習階段用單節點即可。
5API、物件與 YAML
你跟 K8s 互動的方式,本質是對 Kubernetes API 建立 / 修改 / 刪除「物件(Object)」。物件就是你寫在 YAML 裡的那些東西(Pod、Deployment、Service…)。
每個 K8s 物件都有兩面:spec(spec = specification,你寫的「我想要」)和 status(K8s 回填的「現在實際長怎樣」)。
想像你在咖啡廳點餐:spec 是你的點單「一杯熱拿鐵、少糖」;status 是店員的進度板「製作中 / 已完成」。你只負責寫 spec,status 由 K8s 不斷更新、你不要去改它。控制迴圈做的事,就是讓 status 一路追上 spec。
下指令 kubectl get pod xxx -o yaml,你會親眼看到這兩段並存——這是真正讀懂 K8s 的分水嶺。
每個 K8s YAML 都有的四大欄位
| 欄位 | 意思 |
|---|---|
apiVersion | 用哪個 API 群組與版本(如 v1、apps/v1)。 |
kind | 要建立什麼(Pod?Deployment?Service?)。 |
metadata | 基本資料:name 名字、labels 標籤、namespace。 |
spec | 你想要的樣子(期望狀態)。K8s 會回填 status 表示現況。 |
YAML 三大規則
- 縮排表層級:靠空格多寡決定誰屬於誰。只能空格、不能用 Tab!(最常見錯誤)
- 冒號表「鍵: 值」:冒號後要空一格。
- 減號表「清單一項」:
- name: x。
幾乎所有「A 物件找到一群 B 物件」都靠標籤。例如 Service 用 selector 撈標籤是 app=nginx 的 Pod、Deployment 用標籤管理它的 Pod。Annotations(註解)則放不用來篩選、給工具看的中繼資料。
kubectl explain pod.spec 會列出該欄位底下能填什麼,不用上網找。
Namespace 把叢集切成互不干擾的隔間(如 dev/test/prod),名字可重複。多數資源是「namespace 範圍」的;少數(如 Node、PV、StorageClass、ClusterRole)是「叢集範圍」的,不屬於任何 namespace。
工作負載 Workloads
你的程式怎麼跑起來、跑幾份、誰來管。這是 K8s 最核心的一塊。
6Pod 深入
豆莢裡裝一顆或幾顆豆子(容器)。Pod 是 K8s 能建立與管理的最小單位,K8s 不直接管容器,而是管 Pod。同一 Pod 內的容器共用網路(同一 IP)和儲存,像同房室友。
把 Pod 想成一間合租公寓,裡面的容器是室友:
• 共用門牌與電話(網路):整個 Pod 只有一個 IP。室友之間用 localhost 就能互通,像在家裡喊一聲。
• 可共用儲藏室(Volume):掛同一個 volume,A 寫的檔案 B 看得到。
• 同生共死:Pod 被排到哪台機器,所有室友一起去;Pod 被刪,全部一起走。
這也解釋了「為什麼大多數情況一個 Pod 只放一個容器」:只有當兩個容器關係緊密到必須住一起、同進同出(如主程式 + 它的日誌收集 sidecar)才放同一個 Pod。否則就該各自獨立成不同 Pod,方便個別擴展。
apiVersion: v1
kind: Pod
metadata:
name: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80Pod 生命週期(Phase)
| Phase | 意思 |
|---|---|
Pending | 已接受,但容器還沒全部跑起來(如還在下載映像檔、等排程)。 |
Running | 已綁定到節點,至少一個容器在跑。 |
Succeeded | 所有容器正常結束(exit 0),不會再重啟。 |
Failed | 所有容器都結束,至少一個是失敗的。 |
Unknown | 無法取得 Pod 狀態(通常節點失聯)。 |
restartPolicy 控制容器結束後是否重啟:Always(預設,Deployment 用)、OnFailure、Never。
新手最常看到的紅字狀態就是 CrashLoopBackOff。它的意思是:「容器一直啟動、一直當掉,我(K8s)一直重開,但開太多次了,所以我每次重開前刻意拖長等待時間」(backoff = 退避,重試間隔越拉越長:10 秒、20 秒、40 秒…最多 5 分鐘)。
這不是 K8s 壞掉,是你的程式本身一啟動就出錯。正確反應不是一直刪 Pod 重建,而是去看日誌找真正死因:kubectl logs <pod> 和 kubectl describe pod <pod>。
Init 容器(初始化容器)
在主容器啟動前,依序跑完的容器,常用來「等待依賴就緒」或「做前置準備」。全部成功後主容器才開始。
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 生命週期持續運作。
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,而是用下面的工作負載控制器(Deployment 等)來管理它們。把 Pod 想成免洗餐具——壞了換新,不修。
7ReplicaSet 與 Deployment
你說「我要永遠有 3 個一樣的 Pod」,它就幫你建立、補救(掛了補一個)、更新(一個一個換)。實務上你 90% 都建 Deployment,而不是 Pod。
ReplicaSet 是真正負責「維持副本數量」的底層元件;Deployment 是它的上層,多了「版本管理、滾動更新、回滾」的能力。你通常只碰 Deployment,ReplicaSet 由它自動管理。
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: 1、maxUnavailable: 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: 3 改成 10,或 kubectl scale deployment nginx-deployment --replicas=10,立刻變 10 份。
8StatefulSet(有狀態應用)
Deployment 的 Pod 是「自由入座」——每個都一樣、可互換、隨機命名。但資料庫這類需要「固定身分 + 自己的硬碟」的程式,需要「有編號座位」:每個 Pod 有固定名字(web-0、web-1…)、固定的專屬儲存、按順序啟動/關閉。這就是 StatefulSet。
StatefulSet 適合有狀態(stateful)應用:資料庫(MySQL、PostgreSQL)、訊息佇列(Kafka)等。它搭配 Headless Service(clusterIP: None)給每個 Pod 穩定的 DNS 名稱,並用 volumeClaimTemplates 自動為每個 Pod 建立專屬 PVC。
想像你跑一個 3 節點的資料庫叢集,規則是「第一台是主庫(可寫),另兩台是從庫(只讀,複製主庫資料)」。
如果用 Deployment:Pod 名字是隨機亂碼、開機順序不固定、每個 Pod 搶到的硬碟也隨機——從庫根本不知道「主庫是哪一個」,重啟後還可能拿到別人的舊資料。整個資料庫會錯亂。
StatefulSet 解決這一切:Pod 叫 db-0、db-1、db-2(固定名字),按順序 0→1→2 啟動、反向關閉(固定順序),每個 Pod 永遠綁定自己那顆硬碟(db-0 重生後還是接回原本的資料)。於是程式可以安心約定「db-0 永遠是主庫」。
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: 1Gi9DaemonSet(每台機器跑一份)
大樓規定「每一層樓都必須掛一支滅火器」——不是越多越好,而是「每層剛好一支」。新蓋一層樓,就自動補一支;拆掉一層,那支也跟著移除。
DaemonSet 就是這個規則的執行者:確保符合條件的每個節點剛好跑一份該 Pod。最典型的用途是「每台機器都需要的基礎設施」:日誌收集器(收集該機所有容器的日誌)、監控代理(回報該機 CPU/記憶體)、網路外掛、儲存外掛。叢集自動加一台新節點時,DaemonSet 會自動在新節點補上一份,完全不用你操心。
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/log10Job 與 CronJob(一次性與排程任務)
前面的工作負載都是「長期顧店、一直跑」的服務——關掉就是出事。但有些任務本質是「做完這件事就該下班」:把一批檔案匯入資料庫、跑一次月報運算、寄一波電子報。
這種任務若用 Deployment 會很荒謬——程式做完正常結束(exit 0),Deployment 卻以為「它怎麼停了?快重開!」於是無限重跑。Job 才是對的工具:它知道「跑到成功完成就算數,完成後就不該再起來」。CronJob 則是 Job 上面加一個鬧鐘,按時間表定期生出 Job(如每天凌晨 2 點備份)。
Job:控制完成數與並行數
completions:要成功完成幾次。parallelism:同時可跑幾個 Pod。completionMode: Indexed:每個 Pod 有固定編號(適合分散式運算)。
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:latestCronJob:時間表
schedule 用標準 cron 格式(分 時 日 月 星期)。例如 "0 2 * * *" = 每天凌晨 2 點。
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.0ReplicationController 是 ReplicaSet 的舊版前身,現已不建議使用(用 Deployment 取代)。知道有這東西即可。
網路 Networking
Pod 怎麼被找到、怎麼互通、怎麼對外開放、怎麼管控流量。
11Service 全類型
Pod 隨時重生、IP 一直變(像員工手機常換號)。Service 提供一個永遠不變的總機號碼,外面打總機,它自動轉接給目前還活著的某個 Pod,還會平均分配(負載平衡)。靠標籤 selector 決定納入哪些 Pod。
| 類型 | 用途(白話) |
|---|---|
ClusterIP | 預設。只給叢集內部用,外面連不到。內部程式互相溝通。 |
NodePort | 在每台節點開一個固定埠號,讓外面能連進來(測試常用)。 |
LoadBalancer | 跟雲端商要一個對外負載平衡器,正式對外服務常用。 |
ExternalName | 把 Service 對應到外部網域名稱(CNAME),用來代理叢集外的服務。 |
另外,clusterIP: None 的 Headless Service(無頭服務)不做負載平衡,而是讓你直接拿到每個 Pod 的位址(StatefulSet 用)。
祕密還是控制迴圈。Service 用 selector 標籤條件,背後有個控制器持續維護一份「目前符合條件、而且健康(通過就緒探針)的 Pod IP 清單」(存在 EndpointSlice 物件裡,見第 14 章)。
Pod 死掉 → 自動從清單移除(流量不再導給死人);新 Pod 起來且就緒 → 自動加入清單。所以你滾動更新、擴縮容、Pod 重生,Service 那個「總機號碼」永遠不變,名單卻在背後即時更新。這就是「服務探索」的本質。
它們不是平行選項,而是一層蓋一層:ClusterIP(最底層,給內部)→ NodePort(在 ClusterIP 之上,多開每台節點的固定埠讓外面進得來)→ LoadBalancer(在 NodePort 之上,再叫雲端商架一台對外負載平衡器,給你一個漂亮的對外 IP)。理解這個堆疊,你就懂為什麼建 LoadBalancer 時,它其實也順便有了 NodePort 和 ClusterIP 的能力。
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: LoadBalancer12Ingress 與 Gateway API
假設你有 3 個服務:官網(前端)、API(後端)、後台管理。如果每個都開一個 LoadBalancer,你就要跟雲端商租 3 個對外 IP(每個都要錢),使用者還要記 3 個不同網址,太亂。
Ingress 像飯店大廳的「櫃台接待 + 樓層指引牌」:所有人從同一個大門進來(一個 IP / 一個網域),櫃台再依你要去哪裡指路——www.站.com/ 帶你去前端、www.站.com/api 帶你去後端、admin.站.com 帶你去後台。順便還能在大門口統一檢查證件(處理 HTTPS/TLS 憑證)。一個入口,聰明分流。
Ingress 物件只是「規則」,要有 Ingress Controller(如 ingress-nginx、Traefik)實際執行它才有效。記得用 ingressClassName 指定用哪個 Controller。
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 是比 Ingress 更新、更有彈性的官方標準(角色分離、支援更多協定)。新專案可考慮,但 Ingress 仍最普及。先學 Ingress 即可。
13NetworkPolicy(網路防火牆)
預設情況下,叢集內所有 Pod 都能互相連線(門全開)。NetworkPolicy 像門禁規則:限制「誰能進(ingress)、能出到哪(egress)」,用標籤、命名空間、IP 區段來描述。
預設「全開」意味著:如果前端網頁不小心被駭客入侵,駭客就能從那個 Pod 直接連到資料庫 Pod、內部金流 Pod……整個叢集任他橫著走(業界叫「東西向移動 / lateral movement」)。
NetworkPolicy 像在每個房間裝門禁:規定「資料庫房只接受『後端』標籤的 Pod 進入,其他一律擋」。這樣就算前端被攻陷,駭客也碰不到資料庫。正式環境的安全基本功。常見做法是先設一條「預設全部拒絕」,再逐一開放必要連線(白名單思維)。
NetworkPolicy 要由支援它的 CNI 外掛(如 Calico、Cilium)執行才生效。有些預設網路外掛不支援,規則寫了也不會擋。
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/2414DNS、kube-proxy、EndpointSlice
叢集 DNS(服務探索)
還記得 Service 是「永不變的總機號碼」嗎?但 IP 還是一串難記的數字。叢集 DNS(通常是 CoreDNS)就是內部電話簿:把好記的名字對應到 Service 的 IP。於是你的程式要連資料庫,不用寫死 IP,直接寫 mysql 這個名字就好——就算 Service IP 哪天變了,名字照樣通。
建立 Service 後會自動有一個 DNS 名稱,Pod 之間用名字就能互找,不用記 IP。格式:
# 完整:<service>.<namespace>.svc.cluster.local
my-service.default.svc.cluster.local
# 同一個 namespace 內可直接用短名:
my-servicekube-proxy
跑在每個節點上,負責把「連到 Service 的流量」實際轉發到後端 Pod(透過 iptables / IPVS 規則)。它是 Service 能運作的幕後功臣。
EndpointSlice
記錄「某個 Service 目前對應到哪些 Pod 的 IP 與埠」。EndpointSlice 是較新、可擴展的設計(取代舊的 Endpoints),讓大規模叢集的網路資訊更有效率地同步。
儲存 Storage
容器重生資料就消失?這一部教你怎麼讓資料「活下來」。
15Volume 種類
你的網站讓使用者上傳大頭照,存在容器裡的 /uploads。一切正常——直到某天容器因故重生(當機、更新、被搬到別台),所有大頭照瞬間人間蒸發。為什麼?因為容器內部的檔案系統是「用完即丟」的:容器一重生,等於拿一份全新的食譜重做一盤菜,舊菜(包括寫進去的檔案)全部不見。
Volume(卷)就是掛載到容器裡的「外接儲存」——像插一支隨身碟。程式把資料寫到這支「碟」上,而不是寫進容器自己的肚子裡。如此一來容器就算重生,只要把同一支碟再插回去,資料還在。Volume 也能讓同一個 Pod 裡的多個容器共用同一支碟。種類很多,先認識常見的幾種:
| Volume 類型 | 用途 |
|---|---|
emptyDir | Pod 存活期間的暫存空間,同 Pod 內容器可共用。Pod 刪掉就沒了。可用記憶體當儲存。 |
hostPath | 掛載節點主機上的目錄(有安全風險,少用)。 |
configMap / secret | 把設定 / 機密當成檔案掛進容器。 |
downwardAPI | 把 Pod 自身資訊(名字、標籤等)當檔案掛進去。 |
projected | 把多個來源(secret、configMap…)合併投影到同一目錄。 |
persistentVolumeClaim | 掛載一個「永久卷」(見下章),真正用於保存資料。 |
這些之中,emptyDir、configMap、secret、downwardAPI 等屬於「臨時卷(ephemeral)」——生命週期跟著 Pod。要長久保存資料,需要下一章的 PV/PVC。
16PV / PVC / StorageClass(永久儲存)
上一章的隨身碟適合暫存,但正式要「長久保存的重要資料」需要更正規的機制,而且要把「誰提供儲存」和「誰使用儲存」分開——因為通常開發者(用的人)不該也不想管底層硬碟細節。
• PV(PersistentVolume)= 房東準備好的儲藏室:一塊實際的儲存空間,由叢集管理員 / 雲端提供,講明「我有 5GB、在哪、怎麼接」。
• PVC(PersistentVolumeClaim)= 租客的租賃申請單:開發者只填「我要 8GB、要能讀寫」,完全不用知道硬碟是哪一顆、什麼牌子。
你(Pod)把申請單(PVC)交出去,K8s 自動幫你媒合一間符合條件的儲藏室(PV)並「綁定」。關鍵好處:資料的生命週期和 Pod 脫鉤了——Pod 死了、重生了、搬家了,那間儲藏室和裡面的資料都還在,新 Pod 接回同一張申請單就能繼續用。
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)」。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: low-latency
provisioner: csi-driver.example-vendor.example
reclaimPolicy: Retain # 預設是 Delete
allowVolumeExpansion: true # 允許之後擴大容量
volumeBindingMode: WaitForFirstConsumerPVC 刪掉後 PV 怎麼處理:Delete(連同底層儲存一起刪,雲端常見預設)、Retain(保留資料,需人工處理)。正式環境放重要資料建議用 Retain 較安全。
17CSI、快照與臨時卷
CSI(容器儲存介面)
CSI(Container Storage Interface)是一套標準介面,讓各家儲存廠商寫「驅動程式(driver)」接到 K8s。有了 CSI,K8s 能支援幾乎任何儲存系統(雲端硬碟、NFS、Ceph…)而不用改 K8s 本體。
Volume 快照(Snapshot)
對 PVC 的資料做「時間點快照」,可用來備份或複製出新的卷。透過 VolumeSnapshot 與 VolumeSnapshotClass 物件操作(需 CSI driver 支援)。
臨時卷(Ephemeral Volumes)
官方支援多種臨時卷:emptyDir(本機或 RAM)、configMap / secret / downwardAPI(注入 K8s 資料)、image(掛載容器映像檔內容)、CSI 臨時卷、以及「通用臨時卷(generic ephemeral)」——任何支援動態供應的儲存都能當臨時卷用,Pod 刪掉就跟著刪。
設定與資源
把設定與程式分離、控制每個程式能用多少資源、確認程式健康。
18ConfigMap 與 Secret
程式需要設定值(資料庫位置、開關)和機密(密碼、金鑰),不該寫死在程式裡。ConfigMap 放一般設定(公開便利貼);Secret 放機密(上鎖保險箱)。好處:改設定不用重新打包映像檔。
Secret 的內容預設只用 Base64 編碼(人眼看不懂但很好還原),不等於加密。正式環境應啟用 etcd 靜態加密(Encryption at Rest)並用 RBAC 嚴格限制誰能讀 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-username19資源請求 / 限制與 QoS
requests(請求)= 訂位人數:保證至少給你的資源,排程器用它決定把 Pod 放哪台(哪台有空位才放得進去)。limits(限制)= 包廂人數上限:最多能用到這麼多。
兩者超過時的處理很不一樣,務必記住:
• CPU 超過 limit → 被「限速」(throttle),程式變慢但不會死。CPU 是可壓縮資源。
• 記憶體超過 limit → 直接被殺掉,狀態顯示 OOMKilled(Out Of Memory)。記憶體是不可壓縮資源,要不夠就只能殺。
你沒設記憶體 limit,某個程式記憶體洩漏越吃越多,把整台節點的記憶體吃光。結果——不只它自己,連同一台機器上其他無辜的 Pod 都被波及驅逐,引發雪崩。
反過來,limit 設太小,程式正常運作就被 OOMKilled、不斷 CrashLoopBackOff。所以「合理設定 requests/limits」是 K8s 維運最重要的基本功之一——它同時保護你的程式、和它的鄰居。
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 用 m(milliCPU),1000m = 1 顆 CPU。記憶體用 Mi/Gi(2 的次方)或 M/G(10 的次方)。
20健康探針 Probes
有一種最惱人的故障:程式的程序還在(作業系統看它活著),但它其實內部卡死了——連線池滿了、陷入死迴圈、等一個永遠不會回來的回應。對 K8s 來說「程序在 = 沒事」,於是流量繼續導進這個「活死人」,使用者全部卡住。
探針(Probe)就是為了戳破這種「假活」。K8s 不只看程序在不在,還主動定期去敲門問「你真的還好嗎?」:
| 探針 | 作用 | 失敗時 |
|---|---|---|
| Liveness 存活探針 | 程式是否還活著 | 重啟容器 |
| Readiness 就緒探針 | 程式是否準備好接流量 | 暫時把它從 Service 移除(不導流量),但不重啟 |
| Startup 啟動探針 | 程式是否「啟動完成」(給慢啟動程式用) | 啟動期間先不跑前兩種探針 |
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、標籤、所在節點、自己的資源設定等),透過環境變數或檔案注入。程式想知道自己是誰時很有用。
排程 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(汙點與容忍)
Affinity 是「Pod 挑節點」;Taints/Tolerations 反過來是「節點排斥 Pod」。在節點上加 Taint(汙點)等於貼上「閒人勿入」,除非 Pod 帶著對應的 Toleration(容忍)通行證,否則不能進。常用於保留特殊節點(如 GPU 機、控制平面)。
Taint 的效果(effect)有三種:NoSchedule(不排新 Pod 進來)、PreferNoSchedule(盡量別)、NoExecute(連現有不容忍的 Pod 都趕走)。
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"$ 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:BestEffort → Burstable → Guaranteed。這也是為什麼設好 requests/limits 很重要。
API 發起的驅逐 & PodDisruptionBudget(PDB)
維運時(如節點要維護、清空 kubectl drain)會主動驅逐 Pod。PodDisruptionBudget 像「值班最低人數規定」:保證某服務在主動干擾期間至少保留 N 個(或最多只少 M 個)可用,避免一次全關造成服務中斷。
安全 Security
誰能對叢集做什麼、容器能拿到多大權限、怎麼守住底線。
25認證、授權與 RBAC
一個 API 請求進到 apiserver 要過三關:認證(Authentication,你是誰)→ 授權(Authorization,你能做什麼)→ 准入控制(Admission,再做檢查 / 修改)。
ServiceAccount(給程式的身分)
使用者(人)用憑證 / token 認證;Pod 裡的程式則用 ServiceAccount 當身分去呼叫 API。每個 namespace 有預設 ServiceAccount,可自建並綁定權限。
RBAC(角色為基礎的存取控制)
Role / ClusterRole = 職務權限表(這個角色能對哪些資源做哪些動作)。RoleBinding / ClusterRoleBinding = 派任令(把某角色指派給某個人 / 群組 / ServiceAccount)。Role 限定在單一 namespace;Cluster 版本是整個叢集範圍。
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、不需要寫入根目錄、不需要任何特殊能力。SecurityContext 就是把這些通通鎖緊的工具,讓「就算被攻破,也翻不出多大浪」。
SecurityContext 控制容器 / Pod 的安全屬性,降低被入侵後的危害。常見設定(皆查證自官方 API):
| 欄位 | 作用 |
|---|---|
runAsNonRoot | 強制不可用 root(UID 0)執行,是 root 就拒絕啟動。 |
runAsUser / runAsGroup | 指定執行的使用者 / 群組 ID。 |
readOnlyRootFilesystem | 根檔案系統設成唯讀,防止被植入檔案。 |
allowPrivilegeEscalation | 禁止程序取得比父程序更高的權限。 |
privileged | 特權模式(≈ 主機 root),危險,盡量別開。 |
capabilities | 細緻地增減 Linux capabilities(建議 drop 不需要的)。 |
seccompProfile | 限制容器可呼叫的系統呼叫。 |
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(警告)。
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在「授權通過後、寫進 etcd 前」攔截請求做檢查或修改。內建很多個;你也可用 ValidatingAdmissionWebhook / MutatingAdmissionWebhook 接自訂邏輯(見第 30 章)。常見第三方策略引擎:OPA Gatekeeper、Kyverno。
自動擴展 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 提供指標。
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
HPA 是內建 API;VPA 不是內建,需以附加元件安裝(用 CRD 定義),且同樣需要 Metrics Server。HPA 與 VPA 同時調整 CPU/記憶體時要小心衝突。
擴充 Kubernetes
K8s 本身可被擴充——你能教它認識全新的資源類型與自動化邏輯。
29CRD 與 Operator 模式
Custom Resource Definition(CRD)
K8s 內建 Pod、Service…等「單字」。CRD 讓你新增自己的單字——定義一種全新的資源類型(例如 kind: CronTab、kind: Database),之後就能像內建資源一樣用 kubectl 操作它。
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 小時不下班的程式。
沒有 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 無法滿足的特殊行為(如自訂儲存、特殊 API 語意)才考慮聚合層。
實作與工具
動手把前面學的跑起來,並認識日常必備工具。
31安裝與第一個應用
先裝好 Docker Desktop(或其他容器執行環境)。學習用最推薦 minikube(本機迷你叢集)。
- 安裝 minikube 與 kubectlmacOS / Windows
# macOS $ brew install minikube kubectl # Windows > winget install Kubernetes.minikube > winget install Kubernetes.kubectl - 啟動叢集terminal
$ minikube start # 第一次會下載元件,等幾分鐘 $ kubectl get nodes # 看到 minikube 為 Ready 就成功 - 部署 Deployment 並體驗自我修復terminal
$ kubectl apply -f deployment.yaml # 套用第 7 章的檔案 $ kubectl get pods # 看到 3 個 Running $ kubectl delete pod <某個pod名> # 故意刪一個 $ kubectl get pods # 數量還是 3!K8s 自動補回 🪄 - 對外開放並從瀏覽器看到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(套件管理)
部署複雜應用常要一拖拉庫 YAML。Helm 把這些打包成「Chart」(可參數化的範本),一行指令就裝好整套(如資料庫、監控系統),還能版本管理與升級回滾。
$ 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 # 移除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) 入口。 |
| NetworkPolicy | Pod 間的網路門禁。 |
| 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 | 自訂資源類型 / 自訂資源 + 控制器自動化。 |
| Helm | K8s 套件管理(App Store)。 |
| kubectl | 操作叢集的命令列遙控器。 |
36常見問題 FAQ
我一定要先很會 Docker 嗎?
docker run 跑過一個容器就夠開始。K8s 是免費的嗎?
Pod 和 Container 差在哪?
為什麼不直接建 Pod,要透過 Deployment?
Deployment 和 StatefulSet 怎麼選?
Service、Ingress 差在哪?
Secret 真的安全嗎?
正式環境也用 minikube 嗎?
學 K8s 對找工作有幫助嗎?
37學習路徑與下一步
- 把 Part J 的實作全跑一遍
光看不練等於沒學。用 minikube 親手做 Deployment、Service、自我修復實驗。
- 練熟五大工作負載
Deployment、StatefulSet、DaemonSet、Job、CronJob 各做一個範例。
- 打通網路與儲存
Service → Ingress → NetworkPolicy;PVC → StorageClass 動態供應。
- 補上設定、排程、安全
ConfigMap/Secret、probes、resources/QoS、affinity/taint、RBAC、SecurityContext。
- 學 Helm / Kustomize 與自動擴展
用 Helm 裝一套現成應用;設定 HPA 觀察自動擴展。
- 挑戰雲端與進階
在 GKE/EKS/AKS 開真叢集;研究 Operator、GitOps、可觀測性。
最權威是官方網站 kubernetes.io(含中文文件),以及免安裝的互動教學 Kubernetes Basics。
K8s 看似龐大,但核心只有一句:用 YAML 聲明你想要的狀態,K8s 透過控制迴圈自動幫你維持。本手冊講的每個物件,都是這個觀念的延伸。慢慢來,多動手,你一定學得會。加油!