19-集群升级与维护

集群升级与维护

概念引入

你还记得第一次用 Kind 创建集群的场景吗(#02)?创建集群只是开始------生产环境的集群需要定期升级来修漏洞、加功能。

集群不是装完就一劳永逸的。K8s 每 4 个月发一个新版本,每个版本维护约 14 个月。不升级意味着:

  • 🔒 安全漏洞没人修
  • 🐛 Bug 永远存在
  • 🚫 新特性用不上

集群运维的三大核心任务:
#mermaid-svg-TxHOC2rbLnViAzWa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TxHOC2rbLnViAzWa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TxHOC2rbLnViAzWa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TxHOC2rbLnViAzWa .error-icon{fill:#552222;}#mermaid-svg-TxHOC2rbLnViAzWa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TxHOC2rbLnViAzWa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TxHOC2rbLnViAzWa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TxHOC2rbLnViAzWa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TxHOC2rbLnViAzWa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TxHOC2rbLnViAzWa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TxHOC2rbLnViAzWa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TxHOC2rbLnViAzWa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TxHOC2rbLnViAzWa .marker.cross{stroke:#333333;}#mermaid-svg-TxHOC2rbLnViAzWa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TxHOC2rbLnViAzWa p{margin:0;}#mermaid-svg-TxHOC2rbLnViAzWa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TxHOC2rbLnViAzWa .cluster-label text{fill:#333;}#mermaid-svg-TxHOC2rbLnViAzWa .cluster-label span{color:#333;}#mermaid-svg-TxHOC2rbLnViAzWa .cluster-label span p{background-color:transparent;}#mermaid-svg-TxHOC2rbLnViAzWa .label text,#mermaid-svg-TxHOC2rbLnViAzWa span{fill:#333;color:#333;}#mermaid-svg-TxHOC2rbLnViAzWa .node rect,#mermaid-svg-TxHOC2rbLnViAzWa .node circle,#mermaid-svg-TxHOC2rbLnViAzWa .node ellipse,#mermaid-svg-TxHOC2rbLnViAzWa .node polygon,#mermaid-svg-TxHOC2rbLnViAzWa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TxHOC2rbLnViAzWa .rough-node .label text,#mermaid-svg-TxHOC2rbLnViAzWa .node .label text,#mermaid-svg-TxHOC2rbLnViAzWa .image-shape .label,#mermaid-svg-TxHOC2rbLnViAzWa .icon-shape .label{text-anchor:middle;}#mermaid-svg-TxHOC2rbLnViAzWa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TxHOC2rbLnViAzWa .rough-node .label,#mermaid-svg-TxHOC2rbLnViAzWa .node .label,#mermaid-svg-TxHOC2rbLnViAzWa .image-shape .label,#mermaid-svg-TxHOC2rbLnViAzWa .icon-shape .label{text-align:center;}#mermaid-svg-TxHOC2rbLnViAzWa .node.clickable{cursor:pointer;}#mermaid-svg-TxHOC2rbLnViAzWa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TxHOC2rbLnViAzWa .arrowheadPath{fill:#333333;}#mermaid-svg-TxHOC2rbLnViAzWa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TxHOC2rbLnViAzWa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TxHOC2rbLnViAzWa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TxHOC2rbLnViAzWa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TxHOC2rbLnViAzWa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TxHOC2rbLnViAzWa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TxHOC2rbLnViAzWa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TxHOC2rbLnViAzWa .cluster text{fill:#333;}#mermaid-svg-TxHOC2rbLnViAzWa .cluster span{color:#333;}#mermaid-svg-TxHOC2rbLnViAzWa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TxHOC2rbLnViAzWa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TxHOC2rbLnViAzWa rect.text{fill:none;stroke-width:0;}#mermaid-svg-TxHOC2rbLnViAzWa .icon-shape,#mermaid-svg-TxHOC2rbLnViAzWa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TxHOC2rbLnViAzWa .icon-shape p,#mermaid-svg-TxHOC2rbLnViAzWa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TxHOC2rbLnViAzWa .icon-shape .label rect,#mermaid-svg-TxHOC2rbLnViAzWa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TxHOC2rbLnViAzWa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TxHOC2rbLnViAzWa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TxHOC2rbLnViAzWa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 集群运维三大任务
滚动升级控制面
定期备份
过期前续期
🔧 升级

kubeadm upgrade
💾 备份

etcd snapshot
📜 证书

certs check/renew
Control Plane
S3/远程存储
证书文件

学完本篇你将能够:

  • 用 kubeadm 执行集群控制面的滚动升级,最小化停机时间
  • 用 etcdctl 创建和恢复 etcd 快照,应对集群灾难场景
  • 检查和续期集群证书,防止证书过期导致集群不可用

原理讲解

etcd 备份与恢复

etcd 是 K8s 的"大脑"------所有集群状态都存在里面。etcd 坏了 = 集群失忆 = 灾难。

bash 复制代码
# 安装 etcdctl
ETCD_VER=v3.5.17
curl -L https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz | tar xz
sudo mv etcd-${ETCD_VER}-linux-amd64/etcdctl /usr/local/bin/
bash 复制代码
# 创建快照(备份)
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 验证快照
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-20260722.db --write-out=table

# 恢复快照
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-20260722.db \
  --data-dir=/var/lib/etcd-restore
# 然后修改 etcd manifest 的 data-dir 指向恢复的目录

#mermaid-svg-g7WcgQDN6vhQgVXS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-g7WcgQDN6vhQgVXS .error-icon{fill:#552222;}#mermaid-svg-g7WcgQDN6vhQgVXS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-g7WcgQDN6vhQgVXS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-g7WcgQDN6vhQgVXS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-g7WcgQDN6vhQgVXS .marker.cross{stroke:#333333;}#mermaid-svg-g7WcgQDN6vhQgVXS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-g7WcgQDN6vhQgVXS p{margin:0;}#mermaid-svg-g7WcgQDN6vhQgVXS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-g7WcgQDN6vhQgVXS .cluster-label text{fill:#333;}#mermaid-svg-g7WcgQDN6vhQgVXS .cluster-label span{color:#333;}#mermaid-svg-g7WcgQDN6vhQgVXS .cluster-label span p{background-color:transparent;}#mermaid-svg-g7WcgQDN6vhQgVXS .label text,#mermaid-svg-g7WcgQDN6vhQgVXS span{fill:#333;color:#333;}#mermaid-svg-g7WcgQDN6vhQgVXS .node rect,#mermaid-svg-g7WcgQDN6vhQgVXS .node circle,#mermaid-svg-g7WcgQDN6vhQgVXS .node ellipse,#mermaid-svg-g7WcgQDN6vhQgVXS .node polygon,#mermaid-svg-g7WcgQDN6vhQgVXS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-g7WcgQDN6vhQgVXS .rough-node .label text,#mermaid-svg-g7WcgQDN6vhQgVXS .node .label text,#mermaid-svg-g7WcgQDN6vhQgVXS .image-shape .label,#mermaid-svg-g7WcgQDN6vhQgVXS .icon-shape .label{text-anchor:middle;}#mermaid-svg-g7WcgQDN6vhQgVXS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-g7WcgQDN6vhQgVXS .rough-node .label,#mermaid-svg-g7WcgQDN6vhQgVXS .node .label,#mermaid-svg-g7WcgQDN6vhQgVXS .image-shape .label,#mermaid-svg-g7WcgQDN6vhQgVXS .icon-shape .label{text-align:center;}#mermaid-svg-g7WcgQDN6vhQgVXS .node.clickable{cursor:pointer;}#mermaid-svg-g7WcgQDN6vhQgVXS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-g7WcgQDN6vhQgVXS .arrowheadPath{fill:#333333;}#mermaid-svg-g7WcgQDN6vhQgVXS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-g7WcgQDN6vhQgVXS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-g7WcgQDN6vhQgVXS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g7WcgQDN6vhQgVXS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-g7WcgQDN6vhQgVXS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g7WcgQDN6vhQgVXS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-g7WcgQDN6vhQgVXS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-g7WcgQDN6vhQgVXS .cluster text{fill:#333;}#mermaid-svg-g7WcgQDN6vhQgVXS .cluster span{color:#333;}#mermaid-svg-g7WcgQDN6vhQgVXS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-g7WcgQDN6vhQgVXS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-g7WcgQDN6vhQgVXS rect.text{fill:none;stroke-width:0;}#mermaid-svg-g7WcgQDN6vhQgVXS .icon-shape,#mermaid-svg-g7WcgQDN6vhQgVXS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g7WcgQDN6vhQgVXS .icon-shape p,#mermaid-svg-g7WcgQDN6vhQgVXS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-g7WcgQDN6vhQgVXS .icon-shape .label rect,#mermaid-svg-g7WcgQDN6vhQgVXS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g7WcgQDN6vhQgVXS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-g7WcgQDN6vhQgVXS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-g7WcgQDN6vhQgVXS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 恢复流程
备份流程
正常运行
etcd
API Server
etcdctl snapshot save

→ etcd-20260722.db
etcdctl snapshot restore

→ 恢复数据目录

建议 说明
备份频率 每日 自动备份(CronJob 或 systemd timer)
备份位置 异地存储(S3/GCS/NFS),别和集群放在同一台机器
验证备份 每月 做一次恢复演练
保留策略 保留最近 30 天 的备份

kubeadm 集群升级

升级有严格的顺序和版本限制:

复制代码
版本升级规则:
  - 不能跳版本!1.28 → 1.29 ✅   1.28 → 1.30 ❌
  - 控制面先升级,再升级节点
  - kubectl / kubelet 版本 ≤ kube-apiserver 版本

#mermaid-svg-r0IfWbYt0KyH6XzT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r0IfWbYt0KyH6XzT .error-icon{fill:#552222;}#mermaid-svg-r0IfWbYt0KyH6XzT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r0IfWbYt0KyH6XzT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r0IfWbYt0KyH6XzT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r0IfWbYt0KyH6XzT .marker.cross{stroke:#333333;}#mermaid-svg-r0IfWbYt0KyH6XzT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r0IfWbYt0KyH6XzT p{margin:0;}#mermaid-svg-r0IfWbYt0KyH6XzT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-r0IfWbYt0KyH6XzT .cluster-label text{fill:#333;}#mermaid-svg-r0IfWbYt0KyH6XzT .cluster-label span{color:#333;}#mermaid-svg-r0IfWbYt0KyH6XzT .cluster-label span p{background-color:transparent;}#mermaid-svg-r0IfWbYt0KyH6XzT .label text,#mermaid-svg-r0IfWbYt0KyH6XzT span{fill:#333;color:#333;}#mermaid-svg-r0IfWbYt0KyH6XzT .node rect,#mermaid-svg-r0IfWbYt0KyH6XzT .node circle,#mermaid-svg-r0IfWbYt0KyH6XzT .node ellipse,#mermaid-svg-r0IfWbYt0KyH6XzT .node polygon,#mermaid-svg-r0IfWbYt0KyH6XzT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r0IfWbYt0KyH6XzT .rough-node .label text,#mermaid-svg-r0IfWbYt0KyH6XzT .node .label text,#mermaid-svg-r0IfWbYt0KyH6XzT .image-shape .label,#mermaid-svg-r0IfWbYt0KyH6XzT .icon-shape .label{text-anchor:middle;}#mermaid-svg-r0IfWbYt0KyH6XzT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r0IfWbYt0KyH6XzT .rough-node .label,#mermaid-svg-r0IfWbYt0KyH6XzT .node .label,#mermaid-svg-r0IfWbYt0KyH6XzT .image-shape .label,#mermaid-svg-r0IfWbYt0KyH6XzT .icon-shape .label{text-align:center;}#mermaid-svg-r0IfWbYt0KyH6XzT .node.clickable{cursor:pointer;}#mermaid-svg-r0IfWbYt0KyH6XzT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r0IfWbYt0KyH6XzT .arrowheadPath{fill:#333333;}#mermaid-svg-r0IfWbYt0KyH6XzT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r0IfWbYt0KyH6XzT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r0IfWbYt0KyH6XzT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r0IfWbYt0KyH6XzT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r0IfWbYt0KyH6XzT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r0IfWbYt0KyH6XzT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r0IfWbYt0KyH6XzT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r0IfWbYt0KyH6XzT .cluster text{fill:#333;}#mermaid-svg-r0IfWbYt0KyH6XzT .cluster span{color:#333;}#mermaid-svg-r0IfWbYt0KyH6XzT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-r0IfWbYt0KyH6XzT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r0IfWbYt0KyH6XzT rect.text{fill:none;stroke-width:0;}#mermaid-svg-r0IfWbYt0KyH6XzT .icon-shape,#mermaid-svg-r0IfWbYt0KyH6XzT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r0IfWbYt0KyH6XzT .icon-shape p,#mermaid-svg-r0IfWbYt0KyH6XzT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r0IfWbYt0KyH6XzT .icon-shape .label rect,#mermaid-svg-r0IfWbYt0KyH6XzT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r0IfWbYt0KyH6XzT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r0IfWbYt0KyH6XzT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r0IfWbYt0KyH6XzT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Step 3: 升级工作节点
kubectl drain ← 先排空
kubeadm upgrade node
升级 kubelet + 重启
kubectl uncordon ← 恢复调度
Step 2: 升级控制面
kubeadm upgrade apply v1.29.x
升级 kubelet + kubectl
重启 kubelet
Step 1: 升级 kubeadm
apt upgrade kubeadm
kubeadm upgrade plan ← 检查升级路径

bash 复制代码
# 1. 查看当前版本
kubectl version --short

# 2. 查看升级计划
kubeadm upgrade plan

# 3. 执行控制面升级
kubeadm upgrade apply v1.29.6

# 4. 升级 kubelet 和 kubectl
apt install -y kubelet=1.29.6-* kubectl=1.29.6-*
systemctl daemon-reload && systemctl restart kubelet

# 5. 升级工作节点(在每个节点上)
kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data
# SSH 到 worker-1
kubeadm upgrade node
apt install -y kubelet=1.29.6-*
systemctl restart kubelet
kubectl uncordon worker-1

证书管理

K8s 控制面的所有组件都用证书做 TLS 通信。证书有有效期------过期了控制面就挂了。

bash 复制代码
# 检查所有证书的过期时间
kubeadm certs check-expiration
# 输出:
# [check-expiration]
# admin.conf                 Jul 22, 2027 15:00 UTC   364d
# apiserver                  Jul 22, 2027 15:00 UTC   364d
# apiserver-kubelet-client   Jul 22, 2027 15:00 UTC   364d
# ...

# 续期所有证书
kubeadm certs renew all

# 续期后重启控制面组件才能生效
#(kubeadm 自动处理 manifest 目录变化,kubelet 会重启静态 Pod)

#mermaid-svg-7EvWgHrU8c9Sf0WY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7EvWgHrU8c9Sf0WY .error-icon{fill:#552222;}#mermaid-svg-7EvWgHrU8c9Sf0WY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7EvWgHrU8c9Sf0WY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .marker.cross{stroke:#333333;}#mermaid-svg-7EvWgHrU8c9Sf0WY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7EvWgHrU8c9Sf0WY p{margin:0;}#mermaid-svg-7EvWgHrU8c9Sf0WY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .cluster-label text{fill:#333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .cluster-label span{color:#333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .cluster-label span p{background-color:transparent;}#mermaid-svg-7EvWgHrU8c9Sf0WY .label text,#mermaid-svg-7EvWgHrU8c9Sf0WY span{fill:#333;color:#333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .node rect,#mermaid-svg-7EvWgHrU8c9Sf0WY .node circle,#mermaid-svg-7EvWgHrU8c9Sf0WY .node ellipse,#mermaid-svg-7EvWgHrU8c9Sf0WY .node polygon,#mermaid-svg-7EvWgHrU8c9Sf0WY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7EvWgHrU8c9Sf0WY .rough-node .label text,#mermaid-svg-7EvWgHrU8c9Sf0WY .node .label text,#mermaid-svg-7EvWgHrU8c9Sf0WY .image-shape .label,#mermaid-svg-7EvWgHrU8c9Sf0WY .icon-shape .label{text-anchor:middle;}#mermaid-svg-7EvWgHrU8c9Sf0WY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7EvWgHrU8c9Sf0WY .rough-node .label,#mermaid-svg-7EvWgHrU8c9Sf0WY .node .label,#mermaid-svg-7EvWgHrU8c9Sf0WY .image-shape .label,#mermaid-svg-7EvWgHrU8c9Sf0WY .icon-shape .label{text-align:center;}#mermaid-svg-7EvWgHrU8c9Sf0WY .node.clickable{cursor:pointer;}#mermaid-svg-7EvWgHrU8c9Sf0WY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .arrowheadPath{fill:#333333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7EvWgHrU8c9Sf0WY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7EvWgHrU8c9Sf0WY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7EvWgHrU8c9Sf0WY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7EvWgHrU8c9Sf0WY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7EvWgHrU8c9Sf0WY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7EvWgHrU8c9Sf0WY .cluster text{fill:#333;}#mermaid-svg-7EvWgHrU8c9Sf0WY .cluster span{color:#333;}#mermaid-svg-7EvWgHrU8c9Sf0WY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7EvWgHrU8c9Sf0WY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7EvWgHrU8c9Sf0WY rect.text{fill:none;stroke-width:0;}#mermaid-svg-7EvWgHrU8c9Sf0WY .icon-shape,#mermaid-svg-7EvWgHrU8c9Sf0WY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7EvWgHrU8c9Sf0WY .icon-shape p,#mermaid-svg-7EvWgHrU8c9Sf0WY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7EvWgHrU8c9Sf0WY .icon-shape .label rect,#mermaid-svg-7EvWgHrU8c9Sf0WY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7EvWgHrU8c9Sf0WY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7EvWgHrU8c9Sf0WY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7EvWgHrU8c9Sf0WY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 关键证书
ca.crt

K8s CA 根证书

(10 年有效)
apiserver.crt

API Server 服务端证书
etcd/server.crt

etcd 服务端证书
front-proxy-client.crt

聚合 API 客户端证书
⚠️ CA 证书过期 = 整个集群需要重建

定期用 kubeadm certs check-expiration 检查!

节点维护:Cordon 与 Drain

命令 作用 Pod 行为
kubectl cordon 标记节点为不可调度 已有 Pod 继续运行,新 Pod 不会调度过来
kubectl drain 排空节点 驱逐所有 Pod + 标记不可调度
kubectl uncordon 恢复调度 允许新 Pod 调度过来
bash 复制代码
# 维护前
kubectl cordon worker-1          # 1. 先阻止新 Pod
kubectl drain worker-1           # 2. 再排空已有 Pod
        --ignore-daemonsets       #    DaemonSet 不管
        --delete-emptydir-data    #    允许删除 emptydir 数据
        --timeout=300s            #    最多等 5 分钟

# 维护完成后
kubectl uncordon worker-1         # 3. 恢复调度

动手实验

配套实验位于 docs/labs/beginner/cluster-upgrade/

Kind 用 Docker 容器模拟节点,升级流程有所不同。本实验模拟核心操作。

步骤 1:部署实验环境

bash 复制代码
cd docs/labs/beginner/cluster-upgrade
bash setup.sh

步骤 2:检查当前版本

bash 复制代码
kubectl version --short 2>/dev/null || kubectl version
kubectl get nodes -o wide

步骤 3:模拟 etcd 备份

bash 复制代码
# etcdctl 在 etcd 静态 Pod 容器内,不在 Kind 外层容器中
# 通过 kubectl exec 进入 etcd Pod 执行备份
ETCD_POD=$(kubectl get pods -n kube-system -l component=etcd -o jsonpath='{.items[0].metadata.name}')

kubectl exec -n kube-system $ETCD_POD -- \
  etcdctl snapshot save /tmp/etcd-backup.db \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/etcd/server.crt \
    --key=/etc/kubernetes/pki/etcd/server.key

# 验证快照
kubectl exec -n kube-system $ETCD_POD -- \
  etcdctl snapshot status /tmp/etcd-backup.db --write-out=table

步骤 4:检查证书过期时间

bash 复制代码
docker exec k8s-guide-control-plane kubeadm certs check-expiration

步骤 5:模拟节点 Cordon + Uncordon

bash 复制代码
# 获取节点名
NODE=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')

# Cordon(不可调度)
kubectl cordon $NODE
kubectl get nodes  # 观察 STATUS 列出现 SchedulingDisabled

# 尝试创建新 Pod(会被挂起)
kubectl run test-schedule --image=busybox:1.36 --restart=Never -- sleep 10
kubectl get pods -w  # 观察 test-schedule 处于 Pending

# Uncordon(恢复调度)
kubectl uncordon $NODE
kubectl get pods  # test-schedule 应该被调度并完成

步骤 6:清理

bash 复制代码
bash teardown.sh

自检问题

  1. 基础 kubectl cordonkubectl drain 有什么区别?什么时候用哪个?

  2. 理解 K8s 为什么不允许跳版本升级(如 1.28 → 1.30)?etcd 备份为什么必须异地存储?

  3. 应用 你管理一个 3 节点生产集群(K8s 1.28),需要升级到 1.29。写出完整的升级步骤和执行顺序。

查看答案

  1. Cordon 只是标记节点不可调度,不影响已有 PodDrain 会驱逐所有 Pod 然后标记不可调度。Cordon 用在"我想让这个节点慢慢闲置"(比如怀疑硬件有问题但不确定),Drain 用在"我现在就要把节点拿去做维护"(比如升级内核、换硬盘)。

  2. 不允许跳版本 是因为 API 废弃(deprecation)策略------K8s API 的 Beta 版本在 3 个版本后移除。如果你从 1.25 跳到 1.29,中间可能有些 API 版本已被移除,你的老 YAML 直接报错找不到。逐版本升级可以让你在每个版本检测 API 废弃警告并修复。etcd 备份必须异地因为如果备份和集群在同一台机器/磁盘上,硬件故障(磁盘坏、机房断电)会同时毁掉集群和备份。

  3. 完整步骤:

bash 复制代码
# 准备工作
# 1. etcd 备份
etcdctl snapshot save /backup/pre-upgrade-1.29.db
# 2. 确认所有节点状态正常
kubectl get nodes && kubectl get pods -A | grep -v Running

# 控制面节点
# 3. 升级 kubeadm
apt install -y kubeadm=1.29.6-*
# 4. 查看升级计划
kubeadm upgrade plan
# 5. 执行升级
kubeadm upgrade apply v1.29.6
# 6. 升级 kubelet
apt install -y kubelet=1.29.6-* kubectl=1.29.6-*
systemctl restart kubelet

# 工作节点(逐个操作)
# 7. drain
kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data
# 8. 在 worker-1 上升级 kubeadm + kubelet
# 9. uncordon
kubectl uncordon worker-1
# 10. 重复 7-9 直到所有工作节点升级完毕

# 验证
kubectl version && kubectl get nodes

下一步

运维技能齐了。最后一篇------把 29 篇的知识全部串起来,部署一个完整的微服务应用:

30. 综合实战:微服务部署

📚 本文来自 K8s Guide ------ 开源免费的 Kubernetes 中文学习指南

  • 🗺️ 初学者轨道 + 面试轨道,从零基础到拿 Offer 一站式覆盖
  • 🧪 每篇文章配套 Kind 实验脚本,本地一键运行
  • 🔗 本文源码:docs/beginner/20-gateway-api.md
  • ⭐ 如果对你有帮助,欢迎 Star! github.com/callmebg/k8s-guide
相关推荐
凌虚1 小时前
Kubernetes 编年史:从 Borg 到云原生操作系统
后端·程序员·kubernetes
风曦Kisaki4 小时前
Kubernetes(K8s)笔记Day07: StatefulSet 有状态控制器详解与使用案例
linux·docker·云原生·容器·kubernetes
2301_780789661 天前
零信任时代:WAF 从边界防护到微隔离的架构跃迁
网络·安全·web安全·kubernetes·ddos
隔窗听雨眠1 天前
生产级Kubernetes集群部署完全指南:从kubeadm到高可用架构落地
容器·架构·kubernetes
运维大师1 天前
【K8S 运维实战】34-多集群管理Karmada
java·运维·kubernetes
heimeiyingwang2 天前
【架构实战】Kubernetes调度器深度剖析:从Pod调度到自定义调度器
java·架构·kubernetes
上海安当技术2 天前
统一身份认证平台怎么落地?11 个异构业务系统接入 ASP 的完整实施路径
数据库·servlet·架构·kubernetes·jenkins
运维大师2 天前
【K8S 运维实战】32-GitOps实践ArgoCD
运维·kubernetes·argocd