chundev
日期:2026-03-20 環境:K3s(單節點 & 多節點)
K3s 雖然輕量但仍需要系統性維運。本文整理 13 個維運面向:憑證管理、etcd 備份與碎片整理、節點健康監控、版本升級、網路與 DNS、儲存、安全強化、監控告警、災難恢復、故障排除、維護程序、自動化工具、實戰模式。最後附上每日/每週/每月/每季的維護排程表,可直接作為 runbook 使用。
| 類型 | 有效期 | 自動更新 |
|---|---|---|
| Client/Server Certificate(Leaf Certificate) | 365 天 | 到期前 120 天,K3s 啟動時自動更新 |
| CA Certificate | 10 年 | ❌ 不自動更新,需手動輪替 |
⚠️ 2025 年 5 月前的版本觸發門檻是 90 天而非 120 天。
長期不重啟的節點可能讓憑證過期,導致整個叢集無法操作(x509: certificate has expired)。
# 表格形式檢查所有憑證
k3s certificate check --output table
# 手動輪替(每個節點都要做,先 server 再 agent)
systemctl stop k3s
k3s certificate rotate
systemctl start k3s
# CA 輪替(慎用)
k3s certificate rotate-ca --path=/path/to/new/certs/
CertificateExpirationWarning 事件設定告警k3s certificate check| 選項 | 適用場景 |
|---|---|
| SQLite(預設) | 單 server 節點 |
| Embedded etcd | HA 叢集(3+ server) |
| External DB(PostgreSQL/MySQL) | 企業級部署 |
K3s 預設每 12 小時自動建立 etcd snapshot,保留 5 份。
Production 建議設定(/etc/rancher/k3s/config.yaml):
etcd-snapshot-schedule-cron: "0 */4 * * *" # 每 4 小時
etcd-snapshot-retention: 24 # 保留 24 份(4 天)
etcd-snapshot-compress: true
手動操作:
k3s etcd-snapshot save --name manual-$(date +%Y%m%d)
k3s etcd-snapshot list
k3s etcd-snapshot prune
S3 遠端備份:
etcd-s3: true
etcd-s3-endpoint: s3.amazonaws.com
etcd-s3-bucket: k3s-backups
etcd-s3-region: ap-northeast-1
etcd-s3-folder: cluster-01
⚠️ Snapshot 包含完整 etcd 資料 + CA 憑證和私鑰,必須視為機密資料保管。
etcd 保留完整的 key-value 修改歷史,需定期壓縮防止空間耗盡:
# 取得目前 revision
rev=$(etcdctl endpoint status --write-out="json" | jq '.[0].Status.header.revision')
# 壓縮
etcdctl compact $rev
壓縮後的空間不會立即釋放回檔案系統:
# 單一成員(會阻塞讀寫,每 GB 約 10 秒)
etcdctl defrag
# 整個叢集(逐一執行!)
etcdctl defrag --cluster
⚠️ 碎片整理期間會阻塞讀寫,多節點叢集必須逐一執行。
etcd 超過空間配額會觸發 NOSPACE alarm,進入唯讀模式:
rev=$(etcdctl endpoint status --write-out="json" | jq '.[0].Status.header.revision')
etcdctl compact $rev
etcdctl defrag
etcdctl alarm disarm
| Metric | 建議閾值 |
|---|---|
etcd_mvcc_db_total_size_in_use_in_bytes |
> 2 GB 應碎片整理 |
etcd_disk_wal_fsync_duration_seconds (p99) |
> 10ms 需查磁碟效能 |
etcd_disk_backend_commit_duration_seconds (p99) |
> 25ms 需查 |
| Condition | 觸發條件 | 影響 |
|---|---|---|
MemoryPressure |
memory.available < 100Mi |
阻止 BestEffort Pod 排程 |
DiskPressure |
nodefs.available < 10% |
阻止新 Pod 排程 |
PIDPressure |
pid.available < 4% |
阻止新 Pod 排程 |
| 信號 | Soft(有寬限期) | Hard(立即驅逐) |
|---|---|---|
memory.available |
< 100Mi | 預設停用 |
nodefs.available |
< 10% | < 5% |
nodefs.inodesFree |
< 5% | < 4% |
imagefs.available |
< 15% | < 5% |
Kubelet 每 10 秒檢查一次驅逐信號。
| 參數 | 預設值 |
|---|---|
imageGCHighThresholdPercent |
85%(開始清理) |
imageGCLowThresholdPercent |
80%(停止清理) |
| 檢查頻率 | 每 5 分鐘 |
# 調整 kubelet GC 參數
k3s server \
--kubelet-arg="image-gc-high-threshold=70" \
--kubelet-arg="image-gc-low-threshold=60"
Container 日誌(kubelet 管理):
| 參數 | 預設值 |
|---|---|
containerLogMaxSize |
10Mi |
containerLogMaxFiles |
5 |
⚠️ 不要用 logrotate 管理 container 日誌,rename/truncate 操作可能破壞 Kubernetes 的日誌管理。
K3s 系統日誌(systemd journal):
# /etc/systemd/journald.conf
[Journal]
SystemMaxUse=1G
MaxRetentionSec=7day
kubectl get nodes -o wide
kubectl describe node <NODE> | grep -A 5 Conditions
kubectl top nodes
kubectl top pods --all-namespaces --sort-by=memory
df -h /var/lib/rancher/k3s/
手動升級:
# 先 server,再 agent,逐一執行
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=vX.Y.Z+k3s1 sh -
System Upgrade Controller(自動化):
kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/crd.yaml
kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/system-upgrade-controller.yaml
建立 Plan 資源指定升級 channel 和排程,controller 會自動 cordon → drain → 升級 → uncordon。
k3s etcd-snapshot save --name pre-upgrade 備份cp /var/lib/rancher/k3s/server/token /safe/location/ 備份 tokenkubectl get nodes 確認所有節點健康kubectl get pods -A | grep -v Running 確認無異常 PodK3s 不支援直接降級,必須透過 snapshot 還原:
systemctl stop k3s
# 在主 server 還原
k3s server --cluster-reset --cluster-reset-restore-path=<SNAPSHOT-PATH>
# 其他 server 刪除 DB 再啟動
rm -rf /var/lib/rancher/k3s/server/db/
systemctl start k3s
| 資源 | 說明 |
|---|---|
| K3s CVE Scans | 官方 CVE 掃描結果 |
| K3s GitHub Scans | 掃描原始碼與報告 |
| K3s Release Notes | 每版安全修復說明 |
⚠️ CVE-2025-62878(CVSS 10.0):Rancher Local Path Provisioner < v0.0.34,允許讀寫刪除主機任意目錄。這是 K3s 預設 storage backend,務必升級。
# Pod 狀態
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 從 Pod 內測試解析
kubectl run dnsutils --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default
# 日誌
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
Prometheus 指標(port 9153):
coredns_dns_requests_total — 請求總數coredns_dns_request_duration_seconds — 延遲(> 500ms 應告警)coredns_cache_hits_total / coredns_cache_misses_total — 快取命中率⚠️ 不要直接編輯 /var/lib/rancher/k3s/server/manifests/traefik.yaml(K3s 啟動時會覆寫)。
正確方式 — 建立 HelmChartConfig:
# /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
logs:
access:
enabled: true
K3s 內建 kube-router 提供 NetworkPolicy 支援:
# 基本 namespace 隔離
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
需要更進階的 policy(如 egress 控制)可替換為 Calico。
預設路徑: /opt/local-path-provisioner
關鍵限制:
ReadWriteOnce⚠️ CVE-2025-62878(CVSS 10.0):< v0.0.34 允許任意目錄存取,務必升級。
kubectl get pv
kubectl get pvc --all-namespaces
df -h /opt/local-path-provisioner
Prometheus 指標:
kubelet_volume_stats_available_bytes — PV 可用空間kubelet_volume_stats_capacity_bytes — PV 總容量告警建議: > 80% warning,> 90% critical
K3s 提供 CIS Benchmark hardening guide,Server 強化設定:
# /etc/rancher/k3s/config.yaml
protect-kernel-defaults: true
secrets-encryption: true
kube-apiserver-arg:
- "enable-admission-plugins=NodeRestriction,EventRateLimit"
- "audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log"
- "audit-log-maxage=30"
- "audit-log-maxbackup=10"
- "audit-log-maxsize=100"
kubelet-arg:
- "streaming-connection-idle-timeout=5m"
- "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
Host 層級:
# /etc/sysctl.d/90-kubelet.conf
vm.panic_on_oom=0
vm.overcommit_memory=1
kernel.panic=10
kernel.panic_on_oops=1
K3s v1.25+ 支援(取代已棄用的 PodSecurityPolicy):
privileged — 無限制baseline — 阻擋已知特權升級restricted — 最嚴格,遵循 Pod hardening best practices# 檢查 ClusterRoleBinding
kubectl get clusterrolebindings -o wide
# 停用預設 ServiceAccount token 自動掛載
kubectl patch serviceaccount default -n default \
-p '{"automountServiceAccountToken": false}'
# Trivy CLI 掃描
trivy image <IMAGE>
# Trivy Operator(叢集內持續掃描)
helm install trivy-operator aqua/trivy-operator -n trivy-system --create-namespace
K3s 作為單一 binary 運行,部分控制平面指標預設不曝露:
| 元件 | Port | 需額外設定 |
|---|---|---|
| API Server | 6443 | 否 |
| Kubelet | 10250 | 否 |
| Controller Manager | 10257 | 需加 bind-address=0.0.0.0 |
| Scheduler | 10259 | 需加 bind-address=0.0.0.0 |
| etcd | 2381 | 需加 etcd-expose-metrics: true |
| 告警 | 條件 | 嚴重度 |
|---|---|---|
| Node Down | 不可用 > 5 min | 🔴 Critical |
| API Server Latency | p99 > 1s | 🔴 Critical |
| CPU 過高 | > 85% 持續 10 min | 🟡 Warning |
| Memory 不足 | 可用 < 15% | 🟡 Warning |
| Disk 空間不足 | 可用 < 20% | 🟡 Warning |
| Pod 頻繁重啟 | > 5 次/小時 | 🟡 Warning |
| Deployment Unavailable | unavailable > 0 持續 15 min | 🟡 Warning |
| Pod Pending | > 15 min | 🟡 Warning |
| etcd DB 過大 | 超過 2 GB | 🟡 Warning |
| 工具 | 記憶體需求 | 適合場景 |
|---|---|---|
| metrics-server | ~30MB | 必裝基礎(kubectl top 依賴) |
| Prometheus + Grafana | ~500MB-1GB | 完整監控 |
| VictoriaMetrics | ~100-200MB | 資源敏感場景 |
| Uptime Kuma | ~50MB | 簡單可用性監控 |
K3s Grafana Dashboard: ID 15282(RKE Cluster)、19972(K3S Monitoring)
kubectl get --raw='/livez' # API server 是否存活
kubectl get --raw='/readyz' # API server 是否就緒
kubectl get --raw='/readyz?verbose' # 各子系統詳細狀態
| 項目 | 路徑 | 重要性 |
|---|---|---|
| etcd snapshot | ${data-dir}/db/snapshots |
🔴 叢集狀態 |
| Server token | /var/lib/rancher/k3s/server/token |
🔴 加密金鑰(遺失則 snapshot 無法還原) |
| TLS 憑證 | /var/lib/rancher/k3s/server/tls/ |
🟡 可重新生成 |
| PV 資料 | 依 StorageClass | 🔴 應用資料 |
| 資料庫 | pg_dump / mysqldump | 🔴 商業資料 |
⚠️ etcd snapshot 不包含 PV 資料、container images、application logs。
單節點:
systemctl stop k3s
k3s server --cluster-reset --cluster-reset-restore-path=<SNAPSHOT-PATH>
systemctl start k3s
多節點:
# 1. 停止所有節點
# 2. 主 server 執行 cluster-reset
k3s server --cluster-reset --cluster-reset-restore-path=<SNAPSHOT>
# 3. 其他 server 刪除 DB 再啟動
rm -rf /var/lib/rancher/k3s/server/db/
systemctl start k3s
# 4. 啟動 agent
systemctl start k3s-agent
跨主機還原(災難恢復):
k3s server \
--cluster-init \
--cluster-reset \
--cluster-reset-restore-path=<SNAPSHOT> \
--token=<BACKED-UP-TOKEN>
| 指標 | 建議值 | 實現方式 |
|---|---|---|
| RPO(最大資料遺失) | 4-6 小時 | etcd snapshot 每 4 小時 + DB backup 每日 |
| RTO(恢復時間) | < 30 分鐘(本地)/ < 2 小時(S3) | 本地 snapshot 快速還原 |
| 備份測試 | 每月一次 | 在隔離環境還原測試 |
| 備份規則 | 3-2-1 | 3 份副本、2 種媒體、1 份異地 |
sudo systemctl status k3s
sudo journalctl -u k3s -n 100 --no-pager
# 常見原因
sudo ss -tlnp | grep -E '6443|10250' # 端口被占用
sudo openssl x509 -in /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt -noout -dates # 憑證過期
df -h /var/lib/rancher/k3s/ # 磁碟空間不足
dmesg | grep -i "oom\|killed" # OOM
kubectl describe node <NODE> | grep -A 20 "Conditions:"
sudo systemctl status k3s-agent
ls /var/lib/rancher/k3s/agent/etc/cni/net.d/ # CNI 問題
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default
kubectl rollout restart deployment coredns -n kube-system # 快速恢復
sudo k3s crictl rmi --prune # 清理未使用映像
kubectl delete pods --field-selector=status.phase==Succeeded -A # 清理已完成 Pod
kubectl delete pods --field-selector=status.phase==Failed -A # 清理失敗 Pod
dmesg | grep -i "oom\|killed" | tail -20
kubectl top pods --all-namespaces --sort-by=memory | head -20
# 維護前
kubectl cordon <NODE>
kubectl drain <NODE> --ignore-daemonsets --delete-emptydir-data --timeout=120s
# 執行維護
sudo systemctl restart k3s
# 維護後
kubectl uncordon <NODE>
kubectl get nodes
kubectl get pods -A | grep -v Running | grep -v Completed
#!/bin/bash
NODES=$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}')
for NODE in $NODES; do
echo "=== 處理節點: $NODE ==="
kubectl cordon "$NODE"
kubectl drain "$NODE" --ignore-daemonsets --delete-emptydir-data --timeout=300s
sleep 30
ssh "$NODE" "sudo systemctl restart k3s-agent || sudo systemctl restart k3s"
kubectl wait --for=condition=Ready "node/$NODE" --timeout=300s
kubectl uncordon "$NODE"
sleep 60
done
#!/bin/bash
echo "=== K3s 維護前檢查 ==="
kubectl get nodes -o wide
kubectl get pods -A | grep -v Running | grep -v Completed
kubectl get pvc -A | grep -v Bound
ls -la /var/lib/rancher/k3s/server/db/snapshots/ 2>/dev/null
df -h / /var/lib/rancher/k3s
free -h
openssl x509 -in /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt -noout -enddate 2>/dev/null
#!/bin/bash
echo "=== 維護後驗證 ==="
kubectl get nodes
kubectl get pods -n kube-system
kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded
kubectl run dns-verify --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default 2>/dev/null
kubectl cluster-info
kubectl get endpoints -A | grep -E "kubernetes|coredns"
| 工具 | 用途 | 適合 K3s |
|---|---|---|
| Popeye | 叢集資源掃描、最佳實踐 | ✅ 極佳(輕量) |
| kubescape | 安全合規掃描(NSA/MITRE) | ✅ 佳 |
| kube-bench | CIS Benchmark 檢查 | ✅ 佳 |
| k9s | 互動式叢集管理終端 | ✅ 必裝 |
| kubent | 檢查廢棄 API 版本 | ✅ 升級前必用 |
popeye # 快速掃描叢集健康
kubescape scan # 安全掃描
kubent # 升級前 API 相容性檢查
#!/bin/bash
# k3s-daily-health.sh — 放入 crontab 每日執行
echo "=== K3s Daily Report $(date) ==="
echo "## Nodes"
kubectl get nodes -o wide
echo "## Abnormal Pods"
ABNORMAL=$(kubectl get pods -A --no-headers | grep -v Running | grep -v Completed)
[ -n "$ABNORMAL" ] && echo "$ABNORMAL" || echo "All pods healthy"
echo "## Resource Usage"
kubectl top nodes 2>/dev/null || echo "metrics-server not installed"
echo "## Disk Usage"
df -h / /var/lib/rancher/k3s | tail -2
echo "## Certificate Expiry"
CERT_END=$(openssl x509 -in /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt -noout -enddate 2>/dev/null | cut -d= -f2)
echo "API Server cert expires: $CERT_END"
echo "## Latest Snapshots"
ls -lt /var/lib/rancher/k3s/server/db/snapshots/ 2>/dev/null | head -5
| 工具 | 記憶體需求 | 適合單節點 |
|---|---|---|
| Flux | ~128MB | ✅ 推薦 |
| ArgoCD | ~500MB+ | ⚠️ 吃資源 |
| 手動腳本 | 0 | ✅ 簡單場景最實用 |
| 面向 | 單節點 | 多節點(3+) |
|---|---|---|
| 維護方式 | 停機(需排維護窗口) | Rolling update(零停機) |
| 備份重要性 | 🔴 極高(唯一資料來源) | 🟡 高(有冗餘) |
| drain 行為 | Pod 變 Pending(無處遷移) | Pod 遷移到其他節點 |
| etcd | SQLite(預設) | 建議 embedded etcd |
| 升級風險 | 較高(失敗=全掛) | 較低(可逐步回滾) |
單節點(< 20 Pod): 2 CPU / 4GB RAM / 20GB SSD
單節點(20-50 Pod): 4 CPU / 8GB RAM / 50GB SSD
3 節點 HA: 每節點 2 CPU / 4GB RAM / 30GB SSD
K3s 自身開銷(不含 workload):
Server: ~500MB RAM, ~10% CPU (idle)
Agent: ~300MB RAM, ~5% CPU (idle)
| 狀況 | 行動 |
|---|---|
| 憑證輪換後 | 重啟 |
| 節點 memory leak | 重啟 |
| 安全漏洞修補 | 升級 |
| 需要新 K8s API 功能 | 升級 |
| 效能下降原因不明 | 先重啟,無效再升級 |
| 跨大版本(1.28→1.30) | 逐版升級 |
kubectl top nodes/pods 檢視資源趨勢popeye 掃描叢集最佳實踐k3s crictl rmi --prunek3s certificate check 檢查憑證到期kubescape scan 安全合規掃描kube-bench CIS 安全基準掃描