home-lab 2.0 part 4: 使用 Talos Linux 打造純淨的 K8s 叢集
TL;DR
大家好,接續之前翻新家庭實驗室的文章,這次想說把舊的叢集打掉重練,決定全面改用 Talos Linux 來搭建 Kubernetes 叢集。
這篇文章就來記錄一下 Talos Linux 的安裝過程,以及我是如何利用 talosctl 一步步把叢集建置起來的,萬一之後搞壞了可以不用從頭查文件。
現況
之前我可能會使用 kubeadm 來搭建叢集,或是考慮資源開銷更小的 k3s。
但在家裡弄 home-lab 其實最怕的就是維護作業系統所產生的各種瑣碎問題。
以下稍微比較一下:
kubeadm
這大概是大家最熟悉的標準作法,網路插件或周邊設備的相容性最好。
但痛點在於,需要自行打理底層 OS(像是安裝 Ubuntu、關閉 Swap、設定網路跟防火牆、更新系統套件等等)。常常會因為 OS 更新或是某個套件打架,一不小心就把 Kubernetes 給搞壞了。
你說更新前怎麼不看文件,那只有上班才會看,home-lab 哪有人在看的,都直接衝了。k3s
對於像家裡舊機器或樹莓派這種邊緣裝置,確實是一大福音。它把許多獨立的元件打包成了單一的二進位檔案,不僅安裝快,佔用資源也少。但它依然依賴底層 OS,你終究還是得花時間去管理系統本身。
Talos Linux
那為何這次翻新要選 Talos 呢?Talos 是一個專為 Kubernetes 打造的 Linux 發行版。它的核心理念是 Immutable (不可變)、Ephemeral (短暫),整個 OS 透過 SquashFS 掛載,且在記憶體中執行。
最特別的是:沒有 SSH、沒有 Console! 所有的操作完全透過 API talosctl 來進行。由於它捨棄了所有與執行容器無關的套件,攻擊面極小,且升級也只需替換 image,大幅降低了維護底層 OS 的心智負擔。
簡單來說,Talos 就是「只為了跑 Kubernetes 而生的作業系統」。
以下簡單整理這三種常見方案的差異,方便各位讀者(還有未來的我)快速回顧:
| 比較項目 | kubeadm | k3s | Talos Linux |
|---|---|---|---|
| 設計理念 | 最純正的 K8s 標準安裝工具 | 專為邊緣運算設計的輕量級 K8s | 完全為 K8s 打造的不可變 OS |
| 底層 OS 依賴 | ⭐⭐⭐ 高 (需自行安裝、維護 OS) | ⭐⭐ 中 (依賴底層 OS,但安裝腳本包辦很多事) | ⭐ 極低 (OS 與 K8s 綁定,無需維護) |
| 資源消耗 | ⭐⭐⭐ 高 | ⭐ 極低 | ⭐⭐ 中低 (OS 極度精簡,主要看 K8s 元件) |
| OS 攻擊面與安全性 | 較寬廣 (取決於 OS 有哪些套件與服務) | 較寬廣 (取決於 OS 有哪些套件與服務) | 極小 (無 SSH、無 Shell、唯讀根目錄) |
| 操作與維護方式 | SSH 登入節點 + kubeadm 指令 |
SSH 登入節點 + k3s 腳本 |
全 API 驅動 (talosctl) |
| 升級體驗 | 繁瑣 (需分別升級 OS 套件與 K8s 元件) | 容易 (重新執行腳本覆蓋 binary) | 優雅 (單一 API 抽換整個 OS Image) |
| 學習曲線 | 中等 (需具備基礎 Linux 與 K8s 知識) | 低 (一鍵安裝,對新手最友善) | 陡峭 (需適應無 Console 與全 API 操作) |
| 除錯難度 (網路故障時) | 容易 (可 SSH 進去打指令看 Log) | 容易 (可 SSH 進去打指令看 Log) | 困難 (網路一旦炸了,就連不進去拔 Log) |
以下這篇文章很好的介紹了 Talos Linux 相關的特色。


架構設計與選型原則:榨乾效能與穩定性的平衡
在正式動手之前,我想先聊聊這次 home-lab 2.0 的整體架構設計想法。在家裡蓋機房,最大痛點往往不是效能,而是「電費」跟「維護心力」。因此我給自己的架構定了幾個核心原則,我們回顧一下:
避免單點故障產生雪崩效應
我將基礎設施拆分得很細。需要 24 小時長期運行的核心網路與儲存服務
,例如:Nginx Proxy Manager、Tailscale Router、AdGuard DNS、TrueNAS
全部實體搬遷到低功耗的單板電腦(Raspberry Pi 4、ZimaBlade)上。
這樣就算我的 Proxmox 虛擬化叢集掛了,家裡的網路跟基礎儲存也依然存活。
- 電源最佳化與考慮降級運行
這套 Kubernetes 叢集是跑在 Proxmox VM 裡面,主要負責乘載高效能的應用(例如: Metrics 監控、Kafka、Object Storage 等)。
我特別設計成即使出遠門,多數服務以及實體伺服器都能直接從遠端關機。
不用手動艱辛地drainKubernetes node,透過底層 Proxmox 與 Talos 的韌性,讓叢集能夠安全地進入「降級運行」狀態,把負載遷移到還活著的幾台省電實體機上。
基於這些原則,我在 Kubernetes 內部的組件選型上也以「穩定、輕量、高效」為主:
- 為什麼選 Flannel ?
在同一個 L2 網段中,我們可以捨棄預設的 VXLan,改用 host-gw 模式。
這種模式利用主機的路由表直接轉發封包,完全沒有封裝與解封裝 (encapsulation) 的負擔,能帶來極高的網路傳輸效能,完美契合我們榨乾硬體的初衷。 - 為什麼選 TopoLVM ?
因為我的 Worker 節點本身有掛載實體硬碟(Storage Worker VM 底層有 NVMe SSD 硬碟)。
在單節點儲存的選擇上,雖然 k3s 內建的 local-path-provisioner 非常簡單好用,但它底層只是單純的hostPath目錄分配,缺乏容量隔離與進階的管理功能。
相比之下,TopoLVM 直接使用 Linux LVM (Logical Volume Manager) 進行本機磁碟的動態調度。它支援容量感知調度 (Capacity-aware scheduling) 與線上擴容 (Volume Expansion)。
而且設定檔非常單純,完美契合我們想穩定榨乾磁碟效能的初衷。
Step by Step:叢集安裝紀錄
這一次我使用的節點主要分為 ZimaBoard (Bare Metal) 還有在 Proxmox 上的虛擬機。透過 Talos,我們只要準備好映像檔、寫好參數、發個 API,叢集就起來了。
1. 準備系統映像檔 (System Image)
Talos 提供了一個非常實用的線上工具 Image Factory,可以讓你針對不同的硬體需求客製化含有指定擴充套件 (Extensions) 的 Image。

在我這次的環境中,大致上分為兩種:
- ZimaBoard
包含了一些 Intel 的驅動與實用工具,對應 Bare-metal Machine。siderolabs/i915,siderolabs/intel-ice-firmware,siderolabs/intel-ucode,siderolabs/iscsi-tools,siderolabs/util-linux-tools,主要當作 Control Plane 以及跑一些不太吃資源的 Controller。 - Proxmox
針對虛擬化環境,Talos 官方強烈建議加入特定的系統擴充套件以確保效能與網路功能正常。對應 Cloud Server。
因此我加入了siderolabs/qemu-guest-agent來讓 PVE 可以抓到虛機的 IP 與優雅關機,並加上siderolabs/iscsi-tools與siderolabs/util-linux-tools來完善儲存與效能相關的支援。

💡 補充:Proxmox 虛擬機最佳建議設定
要在 Proxmox 上獲得穩定且高效的 Talos Linux 體驗,根據官方的最新安裝指南,強烈建議在建立虛擬機時遵照以下硬體與系統選項設定:
- 系統 (System):
- BIOS: 務必選擇
OVMF (UEFI)。現代的 Talos 映象檔多採 UEFI 啟動,能提供更好的硬體支援與 Secure Boot,這邊也記得幫 EFI Disk 分配一下儲存空間。 - Machine: 建議選擇現代化的 PCIe 架構
q35。 - SCSI Controller: 選擇
VirtIO SCSI(⚠️ 官方特別標註:不要選到 "VirtIO SCSI Single")。 - QEMU Guest Agent: 必須打勾。開啟後 Proxmox 才能正確獲取虛機的 IP 位址,並能從外部對 Talos 進行優雅關機 (Graceful Shutdown)。
- BIOS: 務必選擇

- 處理器 (CPU): 類別 (Type) 建議選擇
host,讓虛擬機直接使用實體 CPU 的指令集,對加密運算及容器效能有顯著提升。

- 記憶體 (Memory): 建議關閉
Ballooning Device(或將 Minimum memory 設為與總記憶體相等),防止 Talos 在記憶體回收時產生不必要的延遲。

- 磁碟 (Hard Disk):
- 如果像我一樣有打算給 Worker 節點跑 TopoLVM,可以掛載兩顆硬碟(例如:
scsi0 32G當系統碟、scsi1 128G當專屬資料碟)。 - 關鍵細節:務必勾選
Discard(有利於 SSD 空間回收) 以及SSD emulation(讓 OS 知道這是一顆固態硬碟)。
- 如果像我一樣有打算給 Worker 節點跑 TopoLVM,可以掛載兩顆硬碟(例如:

- 網路 (Network Device): 選擇
VirtIO (paravirtualized)以獲得最高頻寬與最低 CPU 負載。

2. 定義環境變數
正式開始前,我們先把會用到的 IP 與名稱寫成變數,後續直接複製貼上會比較好管理:
export CLUSTER_NAME="home-infra"
export CONTROL_PLANE_VIP="192.168.0.10"
export CONTROL_PLANE_IP=("192.168.0.20" "192.168.0.21" "192.168.0.22")
export WORKER_IP=("192.168.0.30" "192.168.0.31" "192.168.0.32")
3. 加入 Talos Secrets
這步用來生成你的 Cluster 的加密金鑰,執行後就會在目錄下產生一個 secrets.yaml。
注意:這個檔案請務必保管好,千萬不要丟到公開的 repository!
talosctl gen secrets -o secrets.yaml
如果有需要確認機器的網卡跟硬碟位置,可以用talosctl get links --insecure --nodes <IP>與talosctl get disks來查詢。
4. 準備自定義的 Patch 設定檔
在產生設定檔之前,我們通常會需要對 Talos 的預設配置做一些微調。
這就是 patch-c.yaml (Control Plane) 與 patch-w-s.yaml (Worker) 發揮作用的地方。
以下是我在這次環境中特別設定的幾個重點(以 patch-c.yaml 為例):
machine:
install:
disk: /dev/mmcblk0 # 透過 talosctl get disks --insecure --nodes <IP> 查詢
wipe: true
image: factory.talos.dev/metal-installer/xxxxxxx:v1.12.4 # 透過 Image Factory 生成
kubelet:
clusterDNS:
- 169.254.20.10
extraArgs:
rotate-server-certificates: true
cluster:
controllerManager:
extraArgs:
bind-address: 0.0.0.0
scheduler:
extraArgs:
bind-address: 0.0.0.0
etcd:
extraArgs:
listen-metrics-urls: http://0.0.0.0:2381
network:
cni:
name: none # 因為我們後面會自己用 Helm/Kustomize 裝 Flannel
proxy:
mode: ipvs # 捨棄 iptables,改用效能更好的 ipvs 模式
extraArgs:
ipvs-strict-arp: true # MetalLB 在 L2 模式下需要這個設定
---
apiVersion: v1alpha1
kind: Layer2VIPConfig
name: 192.168.0.10
link: enp3s0 # talosctl get links --insecure --nodes <IP> 查詢
---
apiVersion: v1alpha1
kind: HostnameConfig
auto: off
hostname: HOSTNAME
💡有幾個必備的踩坑點一定要注意
cni: name: none: 這是因為 Talos 內建會想幫你裝 Flannel 或是 Cilium,但為了統一用 GitOps 或 Kustomize 管理,我選擇把它關掉,晚點再自己裝。proxy.mode: ipvs&ipvs-strict-arp: 為了追求更好的網路轉發效能,我將 kube-proxy 預設的 iptables 替換成了 IPVS 模式。如果你跟我一樣打算在叢集裡面裝 MetalLB 來透過 L2 模式發放 LoadBalancer IP,根據 MetalLB 官方安裝指南,在 Kubernetes v1.14.2 之後的版本中,只要 kube-proxy 啟用了 IPVS 模式,就必須強制開啟 Strict ARP (ipvs-strict-arp: true),否則你的 L2 路由會因為 ARP 回應錯亂而無法正常運作。Layer2VIPConfig: 這是 Talos 提供的一個超讚功能!利用這段設定,Talos 自己就會在你的 Control Plane 節點間幫你用 VRRP 扛出一個 VIP (192.168.0.100)。這意味著你不需要再另外架設 HAProxy 或是 Keepalived 就能擁有一個高可用性的 Kubernetes API Server 節點!HostnameConfig: 預設情況下,Talos 會用節點的 UUID 自動幫你隨機產生一個落落長的主機名稱(例如talos-2ld-dvw)。為了讓後續用指令替換HOSTNAME(如talos-c-0) 順利生效,我們必須將auto設為off,把主機名稱的控制權拿回來。參考文件:Machine Configuration - Hostname - Sidero Documentation
- 效能與監控配置:
listen-metrics-urls: http://0.0.0.0:2381則是為了把 etcd 的監控數據暴露出來。參考文件:Expose the Etcd Metrics Endpoint - Sidero Documentation
controllerManager&scheduler的bind-address: 0.0.0.0: 由於 Kubernetes 這些核心組件預設只綁定在127.0.0.1,這會導致我們安裝在叢集內的 Prometheus 抓不到它們的監控數據。因此,我們透過這個設定把它們的 Metrics 端點向內網公開,這樣之後打造監控面板時才不會少數據!
💡 Worker 節點的 patch-w-s.yaml 設定也大同小異,主要是把 Control Plane 特有的 API 和 VIP 拔掉,並一樣保留了 CNI none 與 IPVS 的設定。
machine:
install:
disk: /dev/sda
wipe: true
image: factory.talos.dev/nocloud-installer/xxxxxxx:v1.12.4
kubelet:
clusterDNS:
- 169.254.20.10
extraArgs:
rotate-server-certificates: true
cluster:
network:
cni:
name: none
proxy:
mode: ipvs
extraArgs:
ipvs-strict-arp: true
---
apiVersion: v1alpha1
kind: HostnameConfig
auto: off
hostname: HOSTNAME
5. 產生 Control Plane 設定檔
有了 secrets 和剛剛解說過的 patch 設定檔(patch-c.yaml)後,我們用指令產生控制平面使用的參數與 talosconfig(Talos 的憑證登入檔)。
talosctl gen config --with-secrets secrets.yaml \
--config-patch @patch-c.yaml \
--output-types controlplane,talosconfig \
--output _out \
--force $CLUSTER_NAME "https://$CONTROL_PLANE_VIP:6443"
6. 建立 Control Plane
設定檔出來後,就可以往機器灌了。這裡以我的第一台主控節點 (192.168.0.20) 為例:
# 修改 hostname 後應用到節點
cp ./_out/controlplane.yaml ./_out/c-0.yaml
sed -i 's/HOSTNAME/talos-c-0/g' ./_out/c-0.yaml
talosctl apply-config --insecure --nodes 192.168.0.20 --file ./_out/c-0.yaml
接著,我們要告訴 Talos 工具我們的 endpoint:
export TALOSCONFIG="_out/talosconfig"
talosctl config endpoint $CONTROL_PLANE_IP
talosctl config node $CONTROL_PLANE_IP
叢集設定好了,我們對它進行啟動 (bootstrap):
talosctl bootstrap --nodes 192.168.0.20
完成後,將設定檔搬到家目錄,順便把 Kubernetes 的 kubeconfig 也吐出來保存:
cp ./_out/talosconfig ~/.talos/config
talosctl kubeconfig _out --nodes 192.168.0.20
cp ./_out/kubeconfig ~/.kube/config
7. 建立 Worker 節點 (Storage Worker)
有 Worker 才能跑我們自己寫的服務。這裡的方法跟 Control Plane 一樣,只是改產出 worker 類型的設定檔。
# 產出 Worker 系列的設定檔
talosctl gen config --with-secrets secrets.yaml \
--config-patch @patch-w-s.yaml \
--output-types worker \
--output _out/worker-s.yaml \
--force $CLUSTER_NAME "https://$CONTROL_PLANE_VIP:6443"
# 應用到 Worker 節點 (以第一台 192.168.0.30 作為示範)
cp ./_out/worker-s.yaml ./_out/w-s-0.yaml
sed -i 's/HOSTNAME/talos-w-s-0/g' ./_out/w-s-0.yaml
talosctl apply-config --insecure --nodes 192.168.0.30 --file ./_out/w-s-0.yaml
# 給上對應的 Label
kubectl label node talos-w-s-0 node-role.kubernetes.io/worker-storage=""
最後再把所有 Node 加進 Talos 的 endpoint list 裡,大功告成!
talosctl config node $CONTROL_PLANE_IP $WORKER_IP
cp ./_out/talosconfig ~/.talos/config

8. 安裝 CNI 網路插件 (Flannel) 與確保叢集安全性 (Pod Security)
叢集架好後如果沒有 CNI,節點會一直卡在 `NotReady` 的狀態。這邊我是選擇最簡單耐用的 Flannel 作為家裡的網路插件。透過 Kustomize 的 Helm 支援特性(關於 Kustomize 的基礎與進階用法,可以參考我之前寫文章,可以直接拉取 Flannel 官方的 Chart 進行部署:

在我的專案中,kustomization.yaml 會用 helmCharts 的方式去定義版本:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kube-flannel
helmCharts:
- repo: https://flannel-io.github.io/flannel
name: flannel
version: v0.28.0
namespace: kube-flannel
kubectl create ns kube-flannel
# 透過 kustomize 直接渲染並套用 flannel 設定
kustomize build --enable-helm | kubectl apply -n kube-flannel -f -
另外特別提一下,雖然我們在這個叢集中使用了 Talos 的預設設定(官方會實施嚴謹的 Pod Security Admission 來控管這座叢集),但當我們遇到像 Flannel 這類需要存取實體主機資源的高權限應用時,不用擔心會被擋下來。我們依然可以透過設定 Kubernetes Namespace 的 Label 來獨立放寬該空間的安全性限制。
例如,在部署 Flannel 前,可以像我一樣準備一個 namespace.yaml,幫 kube-flannel 這個 namespace 打上 pod-security.kubernetes.io/enforce: privileged 的標籤,讓它擁有足夠的權限去配置節點網路:
apiVersion: v1
kind: Namespace
metadata:
name: kube-flannel
labels:
pod-security.kubernetes.io/enforce: privileged
9. 基礎叢集組件 (kube-system)
網路通了之後,接著就是要補齊 Kubernetes 的基礎服務。以下是一些可以讓叢集跑得更順暢的核心組件:
- metrics-server: 這個太重要啦,裝完之後我們才能下
kubectl top node看資源使用量,HPA (Horizontal Pod Autoscaler) 也需要靠它。為了高可用性 (HA) 以及安全性,我有加裝kubelet-serving-cert-approver來自動批准 Kubelet 的憑證,並且設定nodeSelector與tolerations,讓這兩個服務專心跑在 Control Plane 節點上就好。
另外,還記得我們在前面的patch-c.yaml裡加了一段rotate-server-certificates: true嗎?那就是為了讓 kubelet 自動輪替伺服器憑證,這可是 Metrics Server 能夠正常抓取節點數據的必要條件!
- vertical-pod-autoscaler (VPA): 既然有了監控數據,當然不能少了 VPA!它可以幫我們自動化評估和調整 Pod 的資源請求 (CPU/Memory)。
- node-local-dns: 這是一個可以大幅減輕 CoreDNS 負載的服務。它會在每個節點上跑一個 Local DNS caching agent (DaemonSet),幫助解析內部請求。雖然一般家裡不會有那麼大的流量,但本著榨乾效能的原則,裝上去就對了!安裝上只要參考官方準備好的 YAML 清單,把裡面的
__PILLAR__LOCAL__DNS__等變數替換成你叢集的設定即可。


10. 儲存解決方案 (TopoLVM)
最後是儲存系統!因為我們的 Worker 節點有實體的硬碟 (ZimaBoard 與 Storage Worker),我選擇了 TopoLVM 作為地端的儲存解決方案。比起以前的 NFS 或 Longhorn,TopoLVM 使用 LVM (Logical Volume Manager) 的機制,I/O 效能更好!
要安裝 TopoLVM,你的 Worker 節點必須事先把要提供給 Kubernetes 的硬碟切成 LVM 的 Volume Group (VG)。不過,在不提供預設 SSH 終端機介面的 Talos 叢集中,我們要怎麼直接操作硬碟呢?這時候就可以善用社群強大的 kubectl-node-shell 外掛,它能在目標節點上啟動一個高特權 (Privileged) 的容器,讓我們以 root 身分管理節點資源。
進入 Node Shell 之後,我們只需要安裝好 lvm2 套件,就能開始分配實體硬碟了:
# 進入 node-shell 後,安裝並更新 LVM 工具包
apt update && apt install -y lvm2
# 查看叢集上的磁碟列表,確認你要拿來當儲存空間的空硬碟代號 (例如 /dev/sdb)
lsblk
# 將該磁碟建立為 Physical Volume (PV)
pvcreate /dev/sdb
pvdisplay
# 接著將它建立為 Volume Group (VG),這個 VG 名稱請記好,稍後會用到
vgcreate virtio-scsi-nvme-ssd /dev/sdb
vgdisplay
完成節點這端的硬碟切割作業後,回到 Kubernetes 這端,一樣是可以透過 Kustomize 結合 Helm Chart 來無腦安裝 TopoLVM:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: topolvm-system
helmCharts:
- repo: https://topolvm.github.io/topolvm
name: topolvm
version: 15.9.0
releaseName: topolvm
valuesFile: ./values.yaml
我們只要準備一份 values.yaml,告訴 TopoLVM 我們在哪個 node 準備了什麼樣的 VG 給它(例如下方的 virtio-scsi-nvme-ssd):
# values.yaml 節錄
lvmd:
managed: true
deviceClasses:
- name: virtio-scsi-nvme-ssd
volume-group: virtio-scsi-nvme-ssd
spare-gb: 8
default: true
部署下去之後:
kustomize build --enable-helm | kubectl apply -n topolvm-system -f -
透過這些簡單的設定檔,就能讓每台 Worker 的磁碟資源被 Kubernetes 原生 CSI 完美調度,未來如果需要擴充硬碟也非常方便!完整細節與進階設定,強烈建議去翻閱官方文件。
結語

至此,一個嶄新的、不用操心底層 OS 版本依賴的 Talos + Kubernetes 叢集就架設完成了。從安裝流程來看,它的思維非常 API-First。相較於之前用 ansible 去敲指令更新 ubuntu,現在只需要 talosctl 就能掌控一切。
如果你對升級流程或是遇到奇怪的問題有興趣,推薦去看看官方提供的疑難雜症文件,這陣子在翻新實驗室時也幫了我很多忙。
之後有時間再來分享我怎麼配置叢集內的 GitOps 與其他服務。





