Share Notes

chundev

View the Project on GitHub latteouka/share-notes

K3s 生產環境維運完整指南:從日常巡檢到災難恢復

日期:2026-03-20 環境:K3s(單節點 & 多節點)


TL;DR

K3s 雖然輕量但仍需要系統性維運。本文整理 13 個維運面向:憑證管理、etcd 備份與碎片整理、節點健康監控、版本升級、網路與 DNS、儲存、安全強化、監控告警、災難恢復、故障排除、維護程序、自動化工具、實戰模式。最後附上每日/每週/每月/每季的維護排程表,可直接作為 runbook 使用。


一、憑證管理

K3s 內部憑證生命週期

類型 有效期 自動更新
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/

監控建議


二、Etcd / 資料庫維護

儲存後端選項

選項 適用場景
SQLite(預設) 單 server 節點
Embedded etcd HA 叢集(3+ server)
External DB(PostgreSQL/MySQL) 企業級部署

Snapshot 備份

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 憑證和私鑰,必須視為機密資料保管。

Compaction(壓縮歷史紀錄)

etcd 保留完整的 key-value 修改歷史,需定期壓縮防止空間耗盡:

# 取得目前 revision
rev=$(etcdctl endpoint status --write-out="json" | jq '.[0].Status.header.revision')

# 壓縮
etcdctl compact $rev

Defragmentation(碎片整理)

壓縮後的空間不會立即釋放回檔案系統:

# 單一成員(會阻塞讀寫,每 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 需查

三、節點健康與資源管理

Node Conditions 監控

Condition 觸發條件 影響
MemoryPressure memory.available < 100Mi 阻止 BestEffort Pod 排程
DiskPressure nodefs.available < 10% 阻止新 Pod 排程
PIDPressure pid.available < 4% 阻止新 Pod 排程

Eviction Thresholds(驅逐閾值)

信號 Soft(有寬限期) Hard(立即驅逐)
memory.available < 100Mi 預設停用
nodefs.available < 10% < 5%
nodefs.inodesFree < 5% < 4%
imagefs.available < 15% < 5%

Kubelet 每 10 秒檢查一次驅逐信號。

Image Garbage Collection

參數 預設值
imageGCHighThresholdPercent 85%(開始清理)
imageGCLowThresholdPercent 80%(停止清理)
檢查頻率 每 5 分鐘
# 調整 kubelet GC 參數
k3s server \
  --kubelet-arg="image-gc-high-threshold=70" \
  --kubelet-arg="image-gc-low-threshold=60"

Log Rotation

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/

四、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。

Version Skew Policy

升級前檢查清單

  1. k3s etcd-snapshot save --name pre-upgrade 備份
  2. cp /var/lib/rancher/k3s/server/token /safe/location/ 備份 token
  3. ✅ 確認目標版本 release notes,注意 breaking changes
  4. ✅ 確認 version skew(不跳 minor)
  5. kubectl get nodes 確認所有節點健康
  6. kubectl get pods -A | grep -v Running 確認無異常 Pod
  7. ✅ 注意:K3s 1.32+ 從 Traefik v2 升級為 v3

Rollback

K3s 不支援直接降級,必須透過 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

CVE 監控

資源 說明
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,務必升級。


五、Networking & DNS

CoreDNS 健康檢查

# 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):

Traefik Ingress 自訂

⚠️ 不要直接編輯 /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

Network Policy

K3s 內建 kube-router 提供 NetworkPolicy 支援:

# 基本 namespace 隔離
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

需要更進階的 policy(如 egress 控制)可替換為 Calico。


六、Storage

Local Path Provisioner(K3s 預設)

預設路徑: /opt/local-path-provisioner

關鍵限制:

⚠️ CVE-2025-62878(CVSS 10.0):< v0.0.34 允許任意目錄存取,務必升級。

PV 監控

kubectl get pv
kubectl get pvc --all-namespaces
df -h /opt/local-path-provisioner

Prometheus 指標:

告警建議: > 80% warning,> 90% critical

Longhorn(Production 替代方案)


七、Security Hardening

CIS Benchmark 合規

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

Pod Security Admission(PSA)

K3s v1.25+ 支援(取代已棄用的 PodSecurityPolicy):

RBAC 稽核

# 檢查 ClusterRoleBinding
kubectl get clusterrolebindings -o wide

# 停用預設 ServiceAccount token 自動掛載
kubectl patch serviceaccount default -n default \
  -p '{"automountServiceAccountToken": false}'

Container Image Scanning

# Trivy CLI 掃描
trivy image <IMAGE>

# Trivy Operator(叢集內持續掃描)
helm install trivy-operator aqua/trivy-operator -n trivy-system --create-namespace

八、Monitoring & Alerting

控制平面指標曝露

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)

Health Check Endpoints

kubectl get --raw='/livez'     # API server 是否存活
kubectl get --raw='/readyz'    # API server 是否就緒
kubectl get --raw='/readyz?verbose'  # 各子系統詳細狀態

九、Backup & Disaster Recovery

必須備份的項目

項目 路徑 重要性
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>

RTO / RPO 規劃

指標 建議值 實現方式
RPO(最大資料遺失) 4-6 小時 etcd snapshot 每 4 小時 + DB backup 每日
RTO(恢復時間) < 30 分鐘(本地)/ < 2 小時(S3) 本地 snapshot 快速還原
備份測試 每月一次 在隔離環境還原測試
備份規則 3-2-1 3 份副本、2 種媒體、1 份異地

十、常見故障排除

K3s 服務無法啟動

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

Node NotReady

kubectl describe node <NODE> | grep -A 20 "Conditions:"
sudo systemctl status k3s-agent
ls /var/lib/rancher/k3s/agent/etc/cni/net.d/           # CNI 問題

DNS 解析失敗

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  # 快速恢復

Disk Pressure Eviction

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

OOM Kill

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

多節點 Rolling Restart

#!/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

Pre-maintenance Checklist

#!/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

Post-maintenance Verification

#!/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

GitOps 選擇

工具 記憶體需求 適合單節點
Flux ~128MB ✅ 推薦
ArgoCD ~500MB+ ⚠️ 吃資源
手動腳本 0 ✅ 簡單場景最實用

十三、實戰模式

單節點 vs 多節點維運差異

面向 單節點 多節點(3+)
維護方式 停機(需排維護窗口) Rolling update(零停機)
備份重要性 🔴 極高(唯一資料來源) 🟡 高(有冗餘)
drain 行為 Pod 變 Pending(無處遷移) Pod 遷移到其他節點
etcd SQLite(預設) 建議 embedded etcd
升級風險 較高(失敗=全掛) 較低(可逐步回滾)

K3s 自身資源規劃

單節點(< 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)

何時重啟 vs 何時升級

狀況 行動
憑證輪換後 重啟
節點 memory leak 重啟
安全漏洞修補 升級
需要新 K8s API 功能 升級
效能下降原因不明 先重啟,無效再升級
跨大版本(1.28→1.30) 逐版升級

定期維護排程總表

每日(自動化)

每週(半自動)

每月

每季


學到的事


參考資料