集群升级与维护
概念引入
你还记得第一次用 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
自检问题
-
基础
kubectl cordon和kubectl drain有什么区别?什么时候用哪个? -
理解 K8s 为什么不允许跳版本升级(如 1.28 → 1.30)?etcd 备份为什么必须异地存储?
-
应用 你管理一个 3 节点生产集群(K8s 1.28),需要升级到 1.29。写出完整的升级步骤和执行顺序。
查看答案
-
Cordon 只是标记节点不可调度,不影响已有 Pod 。Drain 会驱逐所有 Pod 然后标记不可调度。Cordon 用在"我想让这个节点慢慢闲置"(比如怀疑硬件有问题但不确定),Drain 用在"我现在就要把节点拿去做维护"(比如升级内核、换硬盘)。
-
不允许跳版本 是因为 API 废弃(deprecation)策略------K8s API 的 Beta 版本在 3 个版本后移除。如果你从 1.25 跳到 1.29,中间可能有些 API 版本已被移除,你的老 YAML 直接报错找不到。逐版本升级可以让你在每个版本检测 API 废弃警告并修复。etcd 备份必须异地因为如果备份和集群在同一台机器/磁盘上,硬件故障(磁盘坏、机房断电)会同时毁掉集群和备份。
-
完整步骤:
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 篇的知识全部串起来,部署一个完整的微服务应用:
📚 本文来自 K8s Guide ------ 开源免费的 Kubernetes 中文学习指南
- 🗺️ 初学者轨道 + 面试轨道,从零基础到拿 Offer 一站式覆盖
- 🧪 每篇文章配套 Kind 实验脚本,本地一键运行
- 🔗 本文源码:docs/beginner/20-gateway-api.md
- ⭐ 如果对你有帮助,欢迎 Star! github.com/callmebg/k8s-guide