單一節點 k3s VPS 值得使用嗎?
在單一 VPS 執行 k3s 可取得真正的 Kubernetes API,但會消耗額外記憶體,安裝時還可能因 Traefik 佔用 port 80 而衝突。
k3s 是什麼,以及單一節點能提供什麼
k3s 是以單一執行檔封裝的完整 Kubernetes 發行版。在單一 VPS 上執行 k3s,即可取得真正的 Kubernetes API,不需要三台機器組成的 control plane。k3s 是通過認證的 Kubernetes 發行版,因此稍後套用到受管理叢集的 manifest,也能套用在這裡。安裝只需執行一個命令,約需一分鐘。代價是可供應用程式使用的記憶體會減少,並且會引入 Docker Compose 不會遇到的一組失效情況。
SUSE 為 edge site 和小型安裝環境打造 k3s,與上游 Kubernetes 的每項差異,目的都是讓 k3s 更精簡。預設資料儲存區是由名為 kine 的 shim 提供的 sqlite,而不是 etcd,因此不需要維護 etcd quorum。containerd 會內嵌在執行檔中,不必另外安裝。同一個執行檔也會提供叢集 DNS 使用的 CoreDNS、作為 ingress controller 的 Traefik、ServiceLB(也稱為 klipper-lb),讓 LoadBalancer service 在沒有 cloud provider 的情況下運作、用於 persistent volume 的 local-path provisioner、metrics-server,以及 pod 網路使用的 flannel。這些元件預設都會啟動。因此,對原本已在執行其他服務的 VPS 而言,下文所述的連接埠衝突是最常見的第一個問題。
單一 k3s 節點何時值得使用
請依照以下規則決定。如果您需要的是 Kubernetes API,就執行 k3s:例如在自己管理的機器上學習 Kubernetes,或所需軟體只提供 Helm chart。Manifest 的可攜性也是考量因素,因為您在此撰寫的 Deployment 可原樣移至受管控的叢集。如果您需要的是應用程式本身,就使用 Docker Compose。Compose 能以少得多的元件啟動相同的容器,而且一年後回頭查看時,VPS 上的 Compose 檔案比一整個 manifest 目錄更容易閱讀。
請清楚了解單一節點無法提供以下功能。
- 不具備高可用性。VPS 重新啟動時,所有工作負載都會停止。Kubernetes 會將 pod 重新排程到其他節點,但目前沒有其他節點可用。
- 無法在不中斷服務的情況下執行 rolling update,除非應用程式允許在同一台機器上執行 2 個 replica,並共用同一個 volume。
- 儲存空間會固定在該機器上,原因如下方 local-path 區段所述。
- 無論是否部署任何工作負載,control plane 都會占用約 1 GB RAM。
這些限制不代表 k3s 是錯誤的選擇。但如果選擇 k3s 的理由是可靠性,就不適合。人們通常會以可靠性作為理由。如果您實際需要的是多台機器,以建立真正的多節點叢集,應先做出這項決定:自行管理硬體上的 Proxmox,還是租用的 VPS會先決定節點的來源,再由 k3s 決定節點上要執行的內容。
部署任何工作負載前,k3s 的 RAM 與 CPU 用量
k3s 專案公布的是實測數據,不是估算值。請仔細閱讀,因為大家引用的數字不是閒置狀態下的 k3s 用量。
The data behind this chart
[
{
"label": "Server, sqlite datastore",
"ram_mb": "1,596",
"cpu_percent_of_one_core": 6
},
{
"label": "Server, embedded etcd",
"ram_mb": "1,606",
"cpu_percent_of_one_core": 6
},
{
"label": "Agent node only",
"ram_mb": "275",
"cpu_percent_of_one_core": 3
}
]該測試中的 server node 在第 95 百分位使用 1,596 MB 的 RAM,約使用單一核心的 6%。這些是已公布的數據,不是本指南的測量結果。測試執行的是 k3s v1.26.5,啟用所有隨附元件,並搭配 Prometheus 與 Grafana 監控堆疊,因此數字包含實際工作負載,而不是空的叢集。將 sqlite 替換為 embedded etcd 後,RAM 用量為 1,606 MB。agent node 只執行 kubelet 與 containerd,不包含 control plane,使用了 275 MB。文件記載的 server 最低需求為 2 個核心與 2 GB RAM;這項最低需求涵蓋 k3s 及其隨附元件,但不包含你的工作負載。
實際解讀如下:在 2 GB VPS 上,control plane 與隨附的附加元件會幾乎耗盡可用資源,資源不足時首先發生的情況是 kubelet 驅逐 pod。對於執行幾個小型服務的單一節點,4 GB 是較寬裕的最低配置。請測量你自己的主機,不要直接相信任何已公布的數據,包括本節提供的數據。
free -h
sudo k3s kubectl top node
sudo k3s kubectl get pods -A在安裝前執行 free -h,並在 kube-system 中的每個 pod 都顯示 Running 後再執行一次。兩次結果的差異,就是 control plane 在你的硬體上的成本。安裝後的前一兩分鐘,k3s kubectl top node 會回傳 error: Metrics API not available,因為 metrics-server 尚未收集任何資料。這不是錯誤。如果你要在同一台主機上同時配置這項服務與其他工作,為 VPS 配置 RAM 與 CPU 的計算方式可以直接套用。
將 k3s 鎖定至特定版本,而不是 latest
大家都會複製的快速入門指令,會採用你執行指令當天 stable channel 所指向的版本。對於打算長期維護的機器,請將版本鎖定。k3s 會針對每個 Kubernetes minor 版本發布一個 channel,因此 INSTALL_K3S_CHANNEL=v1.36 會在 v1.36 內跟隨 patch 版本更新,不會自行跳到其他 minor 版本。截至 2026 年 8 月,stable channel 指向 v1.36.3+k3s1。
先寫入設定檔,再進行安裝。k3s 啟動時會讀取 /etc/rancher/k3s/config.yaml,因此其中的設定會套用至第一次啟動,以及之後每次啟動。
sudo mkdir -p /etc/rancher/k3s
sudo tee /etc/rancher/k3s/config.yaml >/dev/null <<'EOF'
tls-san:
- k3s.example.com
EOF
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=v1.36 sh -若要鎖定單一確切版本,而不是使用 channel,請使用 INSTALL_K3S_VERSION=v1.36.3+k3s1。加號是 tag 的一部分。接著確認服務已啟動。
k3s --version
sudo systemctl status k3s
sudo k3s kubectl get node
sudo k3s kubectl get pods -Akubectl get node 應在約三十秒內列出一個 STATUS 為 Ready 的節點,而 kube-system 中的每個 pod 都應進入 Running 或 Completed。節點若停留在 NotReady,通常表示 container runtime 未啟動,因此請查看 sudo journalctl -u k3s -n 100 --no-pager。如果使用的是不常見的 VPS 映像檔,請先執行 sudo k3s check-config,再進行其他除錯:它會回報缺少的核心功能,比閱讀日誌更快找到答案。
為什麼埠 80 已在使用中,以及修正時必須放棄什麼
這是 VPS 已經提供其他服務時最常見的問題。安裝會成功,但 Traefik 始終無法取得位址。原本執行中的網站也會繼續運作,因此在嘗試連線到 ingress 之前,看不出任何異常。
運作機制如下:內建的 Traefik chart 會在埠 80 和 443 上建立類型為 LoadBalancer 的 Service。ServiceLB 會建立一組小型 Pod 的 DaemonSet,名稱以 svclb- 為前綴,並在每個節點上以 hostPort 佔用這些埠號。hostPort 會將容器埠直接發布到節點自身的 network namespace,運作方式與 docker run -p 80:80 完全相同。如果 nginx、Caddy、Apache 或其他容器已經佔用埠 80,kernel 不會將同一個埠重複分配,因此 scheduler 沒有可放置該 Pod 的位置。
sudo k3s kubectl -n kube-system get pods
sudo k3s kubectl -n kube-system get svc traefik
sudo ss -lntp '( sport = :80 or sport = :443 )'您會看到 svclb Pod 處於 Pending 狀態,而 Service 沒有外部位址:
svclb-traefik-8f2c1a-r6k9x 0/2 Pending 0 3m
traefik LoadBalancer 10.43.62.11 <pending> 80:31480/TCP,443:30219/TCP執行 kubectl -n kube-system describe pod svclb-traefik-... 可直接找出原因:
0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.ss -lntp 會告訴您哪個程序佔用該埠。有多種處理方式,而每種方式都必須放棄一些項目。
將埠交給 k3s。 停止並停用現有的 web server,讓 Traefik 管理 80 和 443。當 VPS 將專門作為 k3s 主機使用,且原本提供的所有服務都會移到 Ingress 後方時,這是正確的做法。
停用 ServiceLB,保留現有的 proxy。 使用 --disable=servicelb 安裝。類型為 LoadBalancer 的 Service 仍會配置 NodePort,因此 Traefik 會繼續透過 31480 等高位埠連線,而您的 nginx 或 Caddy 則將請求 proxy 到 127.0.0.1:31480。您放棄的是外部位址:Service 會永遠回報 <pending>。這看似故障,但實際上是您選擇的結果。
停用 Traefik,使用自有 proxy 進行路由。 使用 --disable=traefik 安裝。此時沒有 ingress controller,因此 Ingress 物件完全不會生效:它們會留在 API 中,沒有 controller 監看。若您是從主機 proxy 路由到 NodePort,這樣做沒有問題。如果您已經確定要如何處理 HTTP,這也是直接且明確的選擇。若您尚未決定應放置哪個元件在前方,請先確認 nginx、Caddy 與 Traefik 作為 reverse proxy 的選擇,再停用任何元件。
這兩個旗標都應加在 installer 上,或寫入設定檔:
tls-san:
- k3s.example.com
disable:
- traefik
- servicelb安裝後編輯該檔案,再執行 sudo systemctl restart k3s 也可以生效,因為 --disable 不只是安裝時略過元件。它也會刪除已經部署的元件,因此變更會套用到執行中的 cluster。
若要保留 Traefik,但變更 chart 的設定方式,請勿編輯 /var/lib/rancher/k3s/server/manifests/traefik.yaml。k3s 每次啟動時都會以預設值重寫該檔案。請改在相同目錄中加入個別檔案,因為 /var/lib/rancher/k3s/server/manifests 中的內容會在啟動時,以及檔案在磁碟上變更時,自動套用。
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
ports:
web:
forwardedHeaders:
trustedIPs:
- 10.0.0.0/8此範例設定 Traefik chart 的一個值,也就是受信任的 proxy 位址。同樣的機制可設定 chart 公開的其他值,包括其埠號。
單一節點上的持久化儲存
k3s 會提供名為 local-path 的預設 StorageClass,其後端由 Rancher 的 local-path provisioner 提供。未指定 storageClassName 的 PersistentVolumeClaim 會使用此 StorageClass。Volume 位於 /var/lib/rancher/k3s/storage,每個 Volume 在節點自己的磁碟上各有一個子目錄。
sudo k3s kubectl get storageclass
sudo k3s kubectl get pvc -A
sudo ls -l /var/lib/rancher/k3s/storage「位於節點自己的磁碟上」會造成兩項影響,而且通常要到日後才會發生問題。
此 StorageClass 使用 volumeBindingMode: WaitForFirstConsumer,因此新的 PVC 會持續處於 Pending 狀態,直到 Pod 實際掛載它為止。kubectl describe pvc 的輸出如下:
waiting for first consumer to be created before binding這是正常行為。因此,單獨建立 PVC 後等待,不會讓它脫離 Pending 狀態。
PVC 綁定後,Volume 會帶有建立它的節點之節點親和性。使用該 Claim 的所有 Pod,在此 Volume 的生命週期內都會被固定到該節點。在單一節點上不會察覺此問題。日後加入第二個節點後,如果某個 Pod 無法移動,在執行 kubectl get pv -o yaml 並發現 nodeAffinity 中記錄了該主機名稱之前,看起來就像是排程器發生錯誤。
備份由你負責。重建 VPS 會刪除該目錄,下方的解除安裝指令碼也會刪除該目錄。請備份 /var/lib/rancher/k3s/storage,以及位於 /var/lib/rancher/k3s/server/db/state.db 的 sqlite datastore。複製 sqlite datastore 時必須先停止服務,因為它是使用中的資料庫。另一種做法是將整個叢集視為可拋棄資源,並將所有 manifest 保存在 git 中。
Ingress 與 TLS
啟用 Traefik 後,標準的 Ingress 物件即可滿足需求。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
ingressClassName: traefik
tls:
- hosts:
- hello.example.com
secretName: hello-tls
rules:
- host: hello.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello
port:
number: 80憑證不會自行出現。通常的作法是使用 cert-manager,從其發布的 manifest 安裝,再建立一個 ClusterIssuer。截至 2026 年 8 月,最新版本為 v1.21.1。
sudo k3s kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.1/cert-manager.yamlapiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
email: you@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-account-key
solvers:
- http01:
ingress:
ingressClassName: traefikHTTP-01 challenge 表示 ACME(automatic certificate management environment)伺服器會從公開網際網路連線至 http://hello.example.com/.well-known/acme-challenge/...。因此,DNS A 記錄必須已指向 VPS,且連接埠 80 必須能連到 Traefik。若已停用 ServiceLB,並在前方放置自有 proxy,該 proxy 也必須轉送 challenge 路徑,否則 cert-manager 會停滯,且在 Challenge 物件中顯示以下內容:
Waiting for HTTP-01 challenge propagation: wrong status code '404', expected '200'使用 sudo k3s kubectl describe certificate hello-tls 和 sudo k3s kubectl get order,challenge -A 監控憑證簽發狀態。
kubeconfig,以及 API server 為何維持私有
k3s 會將管理員憑證寫入 /etc/rancher/k3s/k3s.yaml。此檔案的擁有者是 root,預設權限為 600。檔案內含具備 cluster-admin 權限的用戶端憑證,因此任何能讀取此檔案的人,都能控制整個叢集。
sudo k3s kubectl get node
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get node未設定 KUBECONFIG 的獨立安裝 kubectl 會因 The connection to the server localhost:8080 was refused - did you specify the right host or port? 而失敗,因為它會回退到與 k3s 無關的預設值。請設定 KUBECONFIG,或使用 sudo k3s kubectl,由它自行讀取正確的檔案。
你會看到建議設定 --write-kubeconfig-mode 644,讓一般使用者可以執行 kubectl。請了解這項設定的影響:它會讓機器上的每個本機帳號都能讀取具備 cluster-admin 權限的憑證。在單一管理員使用的主機上,這可能是可接受的取捨。在共用主機上則不然。複製檔案可以只授予單一使用者存取權,而不公開該檔案:
mkdir -p ~/.kube
sudo install -o $USER -g $USER -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config該檔案中的 server: 行會讀取 https://127.0.0.1:6443。若要從筆記型電腦使用 kubectl,請勿將 6443 開放至網際網路。公開的 Kubernetes API 會持續遭受掃描與攻擊;暴露 API 可能讓小型叢集被他人拿來挖掘加密貨幣。請透過 SSH 建立通道,並保留檔案中的位址不變:
ssh -N -L 6443:127.0.0.1:6443 user@your-vps
KUBECONFIG=./k3s.yaml kubectl get node如果必須透過私有網路位址連線至 API,請在安裝時使用 tls-san 列出該名稱或位址,然後編輯複製檔案中的 server: 行,使其相符。若沒有 SAN(subject alternative name)項目,kubectl 會拒絕連線:
x509: certificate is valid for 127.0.0.1, 10.43.0.1, not 203.0.113.10叢集文件列出的入站連接埠包括:API 使用 TCP 6443、節點之間的 flannel VXLAN 使用 UDP 8472,以及 kubelet metrics 使用 TCP 10250。在單一節點上,這些連接埠都不需要對網際網路開放。
containerd 並不是 Docker
k3s 會使用內嵌的 containerd,且不會與 Docker 共用映像檔存放區。使用 docker build 剛建立的映像檔對 k3s 不可見,因此 pod 會因 ErrImagePull 而失敗,即使 docker images 列出了該映像檔。請明確匯入:
docker save myapp:0.1 | sudo k3s ctr images import -
sudo k3s crictl images接著,請避免在該容器上使用 :latest 標籤,因為 :latest 預設為 Always 的 imagePullPolicy,kubelet 因此仍會連線到 registry。其他任何標籤的預設值都是 IfNotPresent,會使用剛匯入的映像檔。在同一台 VPS 上同時執行兩者沒有問題;另外請注意,VPS 上的一般 Docker 安裝與 k3s 在同一台機器上各自維護獨立的映像檔存放區與 iptables 規則。
如何移除 k3s
安裝程式會寫入解除安裝指令碼。此程序不支援部分移除,也無法復原。
sudo /usr/local/bin/k3s-uninstall.sh此指令碼會停止並移除服務、刪除資料儲存區、刪除持久性磁碟區資料、移除節點設定,以及移除安裝程式新增的工具。在 agent node 上,指令碼則是 k3s-agent-uninstall.sh。請先將 /var/lib/rancher/k3s/storage 下的所有內容複製到其他位置,因為該目錄也會一併刪除。完成後,使用 ip link show 和 sudo ss -lntp 檢查是否仍有程序占用連接埠或網路介面。遺留的 cni0 或 flannel.1 介面會在下次重新開機時清除。
發現 Kubernetes 的單一節點比工作需求複雜,是正常結果,不代表失敗。通常只需一個下午,就能將這些工作負載移回 Compose。
故障模式與你會看到的訊息
Node NotReady,或 k3s 不斷重新啟動。 先閱讀 sudo journalctl -u k3s -n 200 --no-pager。在小型 VPS 上,常見原因是 kernel out-of-memory killer 終止該程序;dmesg 會出現一行列出 k3s-server 的訊息。文件所述的 2 GB 最低需求是實際的最低門檻。
Pod 卡在 Pending。 kubectl describe pod 每次都會指出原因。Insufficient memory 或 Insufficient cpu 表示節點已沒有可用資源。didn't have free ports 是上述的 hostPort 衝突。PVC 上出現 waiting for first consumer,表示 WaitForFirstConsumer 正常運作。
ImagePullBackOff。 可能是該 tag 不存在於節點可連線的任何 registry,或是你使用 Docker 建立 image,卻未將它匯入 containerd。
Traefik 有回應,但應用程式沒有。 回應內容為 404 page not found 時,表示這是 Traefik 本身產生的回應;請求已抵達,但沒有任何 router 符合。確認 Ingress 的 host 是否符合你輸入的名稱,並確認 ingressClassName 為 traefik。
Cluster DNS 失效,但 host 可正常解析。 使用 sudo k3s kubectl -n kube-system logs -l k8s-app=kube-dns 檢查 CoreDNS。若出現類似 plugin/loop: Loop ... detected for zone "." 的訊息,CoreDNS 便無法啟動。原因是 CoreDNS 轉送到另一個會再轉送回 CoreDNS 的 resolver;/etc/resolv.conf 中的 loopback address 就會造成這種情況。使用 --resolv-conf /run/systemd/resolve/resolv.conf,讓 k3s 指向實際的 upstream 檔案。
FAQ
在單一 VPS 上執行 k3s 值得嗎?
如果你需要 Kubernetes API,這麼做就值得。例如,在自己管理的機器上學習 Kubernetes、以 manifest 保持部署可移植、執行只提供 Helm chart 的軟體,或先建置日後會搬遷至代管叢集的環境。如果你只想執行容器,這麼做就不值得,因為 Docker Compose 能以少得多的維護成本完成相同工作,並且通常可多保留約 1 GB 的可用 RAM。單一節點不具備高可用性,因此可靠性不應成為選擇它的理由。
為什麼我的 k3s LoadBalancer 服務一直停留在 Pending?
ServiceLB 會建立 svclb- 個 pod,並在節點上以 hostPort 佔用服務的連接埠,因此只有在這些連接埠可用的節點上才能排程。如果 nginx 或其他代理程式已經佔用 port 80,pod 就會停留在 Pending,服務也不會取得外部位址。kubectl -n kube-system describe pod svclb-... 會回報 0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports。釋放該連接埠,或使用 --disable=servicelb 重新安裝,並將代理請求轉送至服務仍會配置的 NodePort。
k3s 在 VPS 上需要多少 RAM?
文件記載的 server node 最低需求是 2 cores 和 2 GB,這只涵蓋 k3s 及其隨附元件,尚未包含你的工作負載。該專案自行進行的效能分析顯示,在執行 monitoring stack 時,server node 使用 1,596 MB,因此應將 2 GB 視為最低配置,4 GB 才是單一節點開始能舒適運作的配置。安裝前先使用 free -h 測量自己的主機,並在 kube-system 中每個 pod 都顯示 Running 後再次測量。
可以在同一台 VPS 上執行 Docker 和 k3s 嗎?
可以,兩者會彼此分開。k3s 使用自己的內嵌 containerd,因此使用 docker build 建立的映像檔,必須執行 docker save myapp:0.1 | sudo k3s ctr images import - 後 k3s 才能看見。兩者也各自寫入 iptables 規則,並建立自己的 bridge network。請注意 RAM 總量,因為 Docker、k3s 與你的容器加總後,無法在 2 GB 的主機上正常容納。
如何完整移除 k3s?
在 server node 上執行 sudo /usr/local/bin/k3s-uninstall.sh,或在 agent 上執行 sudo /usr/local/bin/k3s-agent-uninstall.sh。這會停止服務、刪除 datastore、刪除 /var/lib/rancher/k3s/storage 下的 persistent volume 資料,並移除隨附工具。請先將需要保留的資料複製到主機外部,因為此操作無法復原。遺留的 cni0 或 flannel.1 network interface 會在下次重新開機時消失。