前面四天的觀念講完了,今天正式開始動手。
這篇的目標其實很小,就是把一個叢集開起來,然後說明為什麼是這樣開的。但是「為什麼」的部分比指令重要,網路上教你打 kind create cluster 的文章有幾百篇,我想寫的是那些設定值背後的取捨,像是為什麼要多開兩個節點、為什麼這個系列從頭到尾都用 port-forward。
昨天列過理由了,這裡補一個比較實際的:kind 開出來的叢集,本體就是幾個 Docker 容器。
這代表我們可以直接用 docker ps 看到自己的「節點」,也代表砍掉重建只要幾十秒。而這 30 天我會砍掉重建非常多次,使用 kind 在這方面可以方便許多。
# macOS
brew install kind kubectl helm
# Windows,用 PowerShell,Win10 / Win11 內建 winget
winget install Kubernetes.kind
winget install Kubernetes.kubectl
winget install Helm.Helm
Windows 的話要先把 Docker Desktop 裝好、而且 WSL2 後端要開著,kind 是靠它才跑得起來的。
裝完之後確認版本,然後把版本抄進自己的環境筆記:
kind version
kubectl version --client
docker version --format '{{.Server.Version}}'
以下是我撰寫文章時所採用的版本,這 30 天都會用這組版本:
| 工具 | 版本 |
|---|---|
| kind | v0.33.0(darwin/arm64) |
| Kubernetes(節點映像檔) | kindest/node:v1.37.0 |
| kubectl | v1.37.0(Kustomize v5.8.1) |
| Helm | v4.2.4 |
| Docker | 28.4.0 |
最簡單的做法是直接下 kind create cluster ,會得到一個單節點的叢集。我這裡要設定不只一個節點,所以自己寫一個設定檔,也方便日後管理。
在專案根目錄新增一個檔案,取名 kind-config.yaml,檔名其實隨便取都可以,等一下用 --config 指定它就好:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: obs
nodes:
- role: control-plane
- role: worker
- role: worker
一行一行解釋這個檔案:
| 這一行 | 意思 |
|---|---|
kind: Cluster |
這裡的 kind 不是工具名稱,是「資源類型」的意思,我們要描述的東西是一個 Cluster。剛好跟工具同名,很容易誤會 |
apiVersion |
這份設定檔是用哪個格式版本寫的,v1alpha4 是目前 kind 的設定檔格式 |
name: obs |
叢集的名字。這個名字後面會一直用到,kubectl 的連線設定會叫 kind-obs,明天載入映像檔的時候要打 --name obs。取了就不要再改 |
nodes |
底下列出要開幾個節點、每個是什麼角色 |
role: control-plane |
控制平面節點,負責排程和維護整個叢集的狀態,後面幾天會拆解裡面到底跑了哪些東西 |
role: worker 兩個 |
工作節點,我們部署的 Pod 會被排到這裡 |
單節點也能跑,但是我刻意多開兩個 worker,理由有三個:
代價就是記憶體。三個節點的 kind 叢集本身大概吃 1.5 到 2 GB,之後裝完整套觀測 stack 還會再加 5 到 6 GB。如果你的機器只有 8 GB,把 worker 減成一個,後面的內容都還是跑得動。
kind create cluster --config kind-config.yaml
kubectl cluster-info --context kind-obs
kubectl get nodes -o wide
第一次跑會花比較久,因為要下載節點的映像檔,之後重建就快很多了。

三個節點都是 Ready,obs-control-plane、obs-worker、obs-worker2,名字前面的 obs 就是設定檔裡取的叢集名字。
下面那半是 docker ps,可以看到這三個節點在 Docker 眼裡就是三個容器,映像檔都是 kindest/node:v1.37.0。開頭說「kind 的叢集本體就是幾個 Docker 容器」,指的就是這件事。
上面那張版本表其實不是我一開始的狀態。叢集開起來之後我又跑了一次 kubectl version,這次沒有加 --client,結果多了一行警告:
Client Version: v1.34.1
Kustomize Version: v5.7.1
Server Version: v1.37.0
Warning: version difference between client (1.34) and server (1.37) exceeds the supported minor version skew of +/-1
我的 kubectl 是 v1.34.1,但是 kind 開出來的叢集是 v1.37.0,差了三個版號。Kubernetes 官方只保證 kubectl 跟 API server 相差一個 minor 版本以內,超過就不保證,可能哪天某個指令就怪怪的。
原因是我的 kubectl 根本不是自己裝的。macOS 上只要裝了 Docker Desktop,它會附一份 kubectl 丟在 /usr/local/bin,版本跟著 Docker Desktop 走,跟我們用 kind 開的叢集完全沒關係。所以我另外裝了一份:
brew install kubernetes-cli
Homebrew 裝在 /opt/homebrew/bin,這個路徑在 PATH 裡面排在 /usr/local/bin 前面,所以裝完就會用到新的那份,Docker Desktop 那份還在但被蓋掉了。再跑一次 kubectl version,警告就消失了。
在 kind 裡面建立 type: LoadBalancer 的 Service,EXTERNAL-IP 會永遠停在 <pending>,因為沒有雲端供應商幫我們生一個 IP 出來。這件事等我們裝完 Grafana、想打開它的畫面時就會第一次遇到。
所以現在先把做法定下來,這個系列一律用 port-forward:
kubectl port-forward svc/grafana 3000:80
這行拆開來看是這樣:
port-forward 是叫 kubectl 在我們自己電腦上開一個埠,然後把送到這個埠的流量,透過 Kubernetes 的 API server 轉進叢集裡面。它不需要 LoadBalancer、也不需要 ingress,因為它根本不是從叢集外面連進去的,而是借用 kubectl 本來就有的那條連線。svc/grafana 是要轉去哪裡,svc 就是 Service 的縮寫,後面接名字。這裡也可以寫成 pod/某個pod 或 deploy/某個deployment。3000:80 是兩個埠,左邊是我們自己電腦的埠,右邊是叢集裡面那個 Service 的埠,順序很容易寫反。所以整行的意思就是:瀏覽器打開 localhost:3000,實際上連到的是叢集裡 grafana 這個 Service 的 80 埠。有兩件事需要特別注意。第一,這個指令要一直開著,按了 Ctrl+C 連線就斷了,所以通常會另外開一個終端機視窗放它。第二,它預設只綁在 127.0.0.1,也就是只有自己這台電腦連得到,同一個區網的其他人是連不進來的,在這裡只是用於練習與示範所以沒有關係。
因為我的主軸是可觀測性而不是流量入口,所以這裡不裝 ingress、也不裝 MetalLB。
kind delete cluster --name obs
這行指令這 30 天會用非常多次。遇到無法排除的錯誤,可以直接砍掉叢集從頭開始,這是本機叢集最方便的地方,也是最後幾天要講的 IaC 的好處:如果重建只要幾十秒,我們就不會捨不得砍。
今天做完的事情:
port-forward 開畫面明天 Day 6 要部署示範服務,並且故意在裡面埋三個故障。這三個故障是整個系列的主線,後面會分別用 logs、traces 和告警把它們挖出來。