k8s集群切换master
比起君子讷于言而敏于行,我更喜欢君子善于言且敏于行。
目录
4. kubelet 配置确认(所有 master 和 node 都要执行)
4. 只清理kubeadm和kubelet的状态文件(关键!不碰CNI和iptables)
前言
原来k8s是单master,设备逐渐多起来,开始考虑拿几台node出来,做多master的形式,是一个比较繁琐的活儿,记录一下。本质上是在做“etcd 集群扩容 + control-plane 转移”。原来只有北京的一台master,现在要把上海node节点加入成master,丝滑剔除北京master。最终还要做高可用,当然了高可用的内容可能得很久以后再补充了,涉及到证书的问题,目前没有这个时间和精力去做这个大动作。更多的是希望丝滑的切换master。
一、集群现状摸底
1. 基础信息确认(master 执行)
# 集群基本信息
kubectl cluster-info
# 所有节点详细信息
kubectl get nodes -o wide
# 集群版本
kubectl version --short
2. 安装方式确认(master 执行)
# 检查是否为kubeadm安装(核心验证)
ls -la /etc/kubernetes/manifests/
# 预期输出:包含kube-apiserver.yaml、etcd.yaml等静态Pod文件
# 检查kubeadm版本
kubeadm version
# 检查静态Pod路径配置
ps aux | grep kubelet | grep pod-manifest-path
# 预期输出:--pod-manifest-path=/etc/kubernetes/manifests
ubuntu@ubuntu-R730xd-01:~$ ps aux | grep kubelet | grep pod-manifest-path
3. etcd 类型与状态确认(master 执行)
# 检查是否为stacked etcd(与master同节点)
kubectl get pods -n kube-system | grep etcd
# 预期输出:etcd-<master-hostname> 1/1 Running
1/1 Running 30 200d
# 检查etcd集群成员
ETCDCTL_API=3 etcdctl member list \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 检查etcd健康状态
ETCDCTL_API=3 etcdctl endpoint health \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
4. kubelet 配置确认(所有 master 和 node 都要执行)
# 4.1 查看kubelet当前连接的apiserver地址
grep server /etc/kubernetes/kubelet.conf
# 4.2 查看kubelet配置
cat /var/lib/kubelet/config.yaml
# 4.3 查看kubelet服务状态
systemctl status kubelet
# 4.4 查看kubelet节点租约时间
ps aux | grep kubelet | grep node-status-update-frequency
# 默认值:10秒
5. 证书信息确认(master 执行)
# 列出所有证书文件
ls -la /etc/kubernetes/pki/
ls -la /etc/kubernetes/pki/etcd/
# 检查apiserver证书SAN扩展(非常重要)
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout | grep -A 10 "X509v3 Subject Alternative Name"
# 检查所有证书有效期
for cert in /etc/kubernetes/pki/*.crt /etc/kubernetes/pki/etcd/*.crt; do
echo "=== $cert ==="
openssl x509 -in $cert -noout -dates
done
6. 负载均衡与网络确认
# 检查是否有本地负载均衡(master执行)
systemctl status haproxy nginx keepalived
# 检查kube-proxy模式(**所有node执行**)
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
# 预期输出:mode: "iptables" 或 mode: "ipvs"
# 检查CNI插件(master执行)
kubectl get pods -n kube-system | grep -E "calico|flannel|cilium|weave"
# 检查CoreDNS状态
kubectl get pods -n kube-system | grep coredns
1/1 Running 3 171d
7. 业务风险确认(master 执行)
# 查找所有单副本Deployment
kubectl get deployment --all-namespaces -o json | jq '.items[] | select(.spec.replicas == 1) | .metadata.namespace + "/" + .metadata.name'
# 查找所有单副本StatefulSet
kubectl get statefulset --all-namespaces -o json | jq '.items[] | select(.spec.replicas == 1) | .metadata.namespace + "/" + .metadata.name'
# 查找所有没有副本控制器的独立Pod
kubectl get pods --all-namespaces -o json | jq '.items[] | select(.metadata.ownerReferences == null) | .metadata.namespace + "/" + .metadata.name'
# 检查所有Service和Ingress
kubectl get svc --all-namespaces
kubectl get ingress --all-namespaces
8. 控制平面组件状态确认(master 执行)
# 所有控制平面Pod状态
kubectl get pods -n kube-system
# cheduler和controller-manager leader状态
kubectl get leases -n kube-system
# 检查所有事件
kubectl get events --all-namespaces --sort-by='.lastTimestamp' | tail -50
二、etcd / 集群备份
1. 确认etcd的版本
ETCDCTL_API=3 etcdctl version
2. 备份etcd全量快照(最关键)
ETCDCTL_API=3 etcdctl snapshot save /root/etcd-snapshot-$(date +%Y%m%d-%H%M).db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 验证:输出 "Snapshot saved at /root/etcd-snapshot-xxx.db"
3. 备份kubeadm配置
kubectl get cm kubeadm-config -n kube-system -o yaml > /root/kubeadm-config-cm-$(date +%Y%m%d-%H%M).yaml
4. 备份完整PKI证书目录
cp -r /etc/kubernetes/pki /root/pki-backup-$(date +%Y%m%d-%H%M)
三、环境校准
执行节点:上海master
1. 验证版本一致性(必须与北京master完全一致)
kubeadm version
kubelet --version
docker --version
# 验证:输出均为v1.21.10,docker版本为19.3.15或20.10.21
2. 关闭swap(必须)
free -h
# 如果swap不为0,执行:
swapoff -a
sed -ri '/swap/s/^/#/' /etc/fstab
3. 验证kubelet服务正常
systemctl status kubelet
# 验证:active (running)
4. 验证与北京master网络互通
telnet <北京master-ip> 6443
telnet <北京master-ip> 2379
telnet <北京master-ip> 2380
# 验证:全部显示Connected
四、新增 control-plane
执行节点:北京 master
1. 生成有效期24小时的加入token
kubeadm token create
# 输出示例:abcdef.012389absvef,复制保存这个token
2. 获取CA证书哈希。
命令是在计算 Kubernetes 集群根 CA(ca.crt)的 SHA256 指纹(fingerprint/hash)。它的作用是:让新节点在 kubeadm join 时,确认自己连接的 master 是“真正的你的集群”,而不是假的 APIServer。“防中间人攻击(MITM)验证”。
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'
# 输出示例:a1b2c3d4e5f6a7b8c9d0e1f2a3b7d8e9f0a1b2,复制保存这个哈希值
3. 上传控制平面证书并获取certificate-key
作用是:把当前 master 的 control-plane 证书,加密上传到集群,方便新的master自动下载和恢复。
kubeadm init phase upload-certs --upload-certs
# 输出最后一行示例:Using certificate key: 1234567890abcdef12345ef1234567890abcdef,复制保存这个certificate-key
五、驱逐上海node节点
# 北京master执行
1. 驱逐上海124上的业务Pod
注意:一定一定一定要确认好自己的pod驱逐之后依旧是有符合规则的其他机器能run的
kubectl drain <sh-ip> \
--ignore-daemonsets \
--delete-emptydir-data \
--force
# 验证:除了DaemonSet外,所有普通Pod都已被驱逐
2. 从集群中删除上海的worker节点对象
把它从之前的node节点踢出来
kubectl delete node <sh-ip>
3. 停止kubelet服务
【上海执行(root 用户)】
systemctl stop kubelet
4. 只清理kubeadm和kubelet的状态文件(关键!不碰CNI和iptables)
mv /etc/kubernetes /etc/kubernetes-node.$(date +%Y.%m.%d_%H:%M).bak
mv /var/lib/kubelet/pki /var/lib/kubelet/pki-node.$(date +%Y.%m.%d_%H:%M).bak
mv /var/lib/kubelet/config.yaml /var/lib/kubelet/config.yaml-node.$(date +%Y.%m.%d_%H:%M).bak
5. 重新加载systemd配置
systemctl daemon-reload
6. 确认10250端口已释放
ss -lntp | grep 10250
# 验证:无任何输出
六、上海 control-plane 接入
1. 替换下面三个值为上面四复制的内容,执行加入命令
kubeadm join 10.155.172.238:6443 \
--token 你的token \
--discovery-token-ca-cert-hash sha256:你的哈希值 \
--control-plane \
--certificate-key 你的certificate-key
# 过程约2-5分钟
# 验证:输出 "This node has joined the cluster and a new control plane instance was created"
2. 遇到了报错:
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
error execution phase preflight:
One or more conditions for hosting a new control plane instance is not satisfied.
unable to add a new control plane instance a cluster that doesn't have a stable controlPlaneEndpoint address
Please ensure that:
* The cluster has a stable controlPlaneEndpoint address.
* The certificates that must be shared among control plane instances are provided.
To see the stack trace of this error execute with --v=5 or higher
这个报错是说“你这个集群当年初始化的时候,是单 master 集群,没有定义一个统一的 control-plane 入口,所以 kubeadm 不允许你再加第二个 master。”简单版,以前的node只找北京master,现在要加入一个新master,node不知道到底该找哪一个。现在先配置一下,告诉node们,哪怕有两个甚至多个master依旧还是找北京的master。消除这个报错,让上海master成功先加入进来。注意:这儿只是“告诉 kubeadm:集群有统一入口了” 。这个主要是给:kubeadm join --control-plane看的。真正决定node连谁的,是:每个 node 上的:/etc/kubernetes/kubelet.conf里面这一行:server: https://<北京-master ip>:6443
controlPlaneEndpoint 主要是kubeadm 自己用的。它主要影响:新 master 加入、kubeadm upgrade、kubeadm cert renew、kubeconfig 自动生成
等集群稳定后长期使用中,这两处的ip要保持一致
# 1. 编辑kubeadm-config ConfigMap
kubectl edit cm kubeadm-config -n kube-system
把controlPlaneEndpoint: "<北京-master ip>:6443"加在
kubernetesVersion: v1.21.10 的下一行,和这一行对齐即可
kubernetesVersion: v1.21.10
controlPlaneEndpoint: "<北京-master ip>:6443"
如果折腾的时间很长,超过2小时或者安全起见那就
# 重新上传控制平面证书,获取新的certificate-key
kubeadm init phase upload-certs --upload-certs
再次执行
kubeadm join 10.155.172.238:6443 \
--token 你的token \
--discovery-token-ca-cert-hash sha256:你的哈希值 \
--control-plane \
--certificate-key 你的certificate-key
我没有重新上传,短时间内修改完,并重新join也是可以成功的
再次遇到报错:拉镜像失败
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
error execution phase preflight: [preflight] Some fatal errors occurred:
[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-apiserver:v1.21.10: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
, error: exit status 1
[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-controller-manager:v1.21.10: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
, error: exit status 1
[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-scheduler:v1.21.10: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
, error: exit status 1
[ERROR ImagePull]: failed to pull image k8s.gcr.io/etcd:3.4.13-0: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
, error: exit status 1
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
为了方便操作,我们直接从北京master打包所有需要的镜像,scp过来,后续再加master做HA的时候,可以把这一步放在前面去做
# 北京master 查看所有涉及到的镜像
~ # docker images | egrep 'kube|etcd|pause|coredns'
k8s.gcr.io/kube-apiserver v1.21.10 704b64a9bcd2 4 years ago 126MB
k8s.gcr.io/kube-controller-manager v1.21.10 eeb3ff937407 4 years ago 120MB
k8s.gcr.io/kube-scheduler v1.21.10 2f776f473131 4 years ago 50.9MB
k8s.gcr.io/kube-proxy v1.21.10 ab8993ba3211 4 years ago 104MB
k8s.gcr.io/pause 3.4.1 0f8457a4c2ec 5 years ago 683kB
k8s.gcr.io/coredns/coredns v1.8.0 296a6d5035e2 5 years ago 42.5MB
k8s.gcr.io/etcd 3.4.13-0 0369cf4303ff 5 years ago 253MB
# 北京master打包
~# docker save -o /root/k8s-v1.21.10-all.tar \
k8s.gcr.io/kube-apiserver:v1.21.10 \
k8s.gcr.io/kube-controller-manager:v1.21.10 \
k8s.gcr.io/kube-scheduler:v1.21.10 \
k8s.gcr.io/kube-proxy:v1.21.10 \
k8s.gcr.io/etcd:3.4.13-0 \
k8s.gcr.io/pause:3.4.1 \
k8s.gcr.io/coredns/coredns:v1.8.0
# 上海scp拉取一下包
cd ~ && scp -r <北京-master ip>:/root/k8s-v1.21.10-all.tar .
# 解压导入
docker load -i /root/k8s-v1.21.10-all.tar
再次执行kubeadm join,注意折腾时间过长最好是,重新上传控制平面证书,获取新的certificate-key
kubeadm init phase upload-certs --upload-certs
七、验证
北京master执行
1. 验证节点角色
kubectl get nodes
# 验证:上海新增的master应该显示为 Ready,control-plane,master
2. 验证控制平面组件
kubectl get pods -n kube-system -o wide | grep 上海master-ip
3. 验证etcd双节点集群
ETCDCTL_API=3 etcdctl member list \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 验证:输出包含北京和上海两个etcd成员
4. 观察 10~20 分钟
# 重点观察:
kubectl get nodes
kubectl get pods -A
# 确认:没有大规模重启、没有 NotReady、Ceph 正常、etcd 正常、apiserver 正常
八、切换所有节点到上海,一台一台的去切,不要一口气全部搞
修改admin.conf、kubelet.conf——重启kubelet——重启kube-proxy
1. 修改kubelet连接地址
(最重要的就是修改这个。它负责:节点注册、Pod状态上报、拉镜像、启动容器,这个配置文件有问题的话,node节点直接 NotReady)
sudo cp /etc/kubernetes/kubelet.conf /etc/kubernetes/kubelet.conf.$(date +%Y%m%d_%H%M).bak
sed -i 's#北京masterip#上海masterip#g' /etc/kubernetes/kubelet.conf
#bootstrap-kubelet.conf类似于新生儿临时身份证,只有join 集群时才使用。join 成功后会换成kubelet.conf。所以机器后来没有这个配置文件也完全正常。
2. 修改本地kubectl配置
(自己的用户配置,如果node节点不需要执行kubectl的命令,通常也就不会有这个配置文件。个人习惯,我每个node都放,恨不得每个node每个用户都放,别问,问就是希望遍地开花,哪儿哪儿都用的方便)
sed -i 's#北京masterip#上海masterip#g' ~/.kube/config
3. 重启服务生效
sudo systemctl restart kubelet
systemctl restart kube-proxy #二进制部署执行这个,kubedam要手动去删pod。
4. 修改kube-proxy连接地址
(不一定有这个配置文件,kubeadm 集群的话kube-proxy会是pod的形式,所以不会有这个配置文件)踩坑:这里一定一定一定要修改,没有配置文件就修改yaml。我做的时候没有想到修改yaml,导致切换完master之后pod网络出问题,又排错,手动在rancher的图形化页面上修改了。
最好是这儿就给它改好,我记录一下修改yaml的方式。
sed -i 's#北京masterip#上海masterip#g' /var/lib/kube-proxy/kubeconfig
#如果没有那个文件的话
#先查看yaml,会发现这里写的是之前的ip
kubectl -n kube-system get configmap kube-proxy -o yaml
#导出yaml
kubectl -n kube-system get configmap kube-proxy -o yaml > kube-proxy.yaml
#备份
cp kube-proxy.yaml kube-proxy.yaml.bak
#编辑
vim kube-proxy.yaml
修改以下字段
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: https://改这儿的ip为新的master ip:6443
#导入yaml
kubectl -n kube-system apply -f kube-proxy.yaml
#重启 kube-proxy DaemonSet
kubectl -n kube-system rollout restart ds kube-proxy
#验证
kubectl -n kube-system get pods -l k8s-app=kube-proxy -o wide
#推荐重启coredns和ingress让它们重新建立和 apiserver 的连接即可。重启是为了确保它们 watch 的 endpoints/服务状态是最新的。
kubectl -n kube-system rollout restart deployment coredns
kubectl -n ingress-nginx rollout restart deployment ingress-nginx-controller
#最好也重启一下flannel或者calico的pod。我这个集群比较老,所以flannel是部署在node上的,并不是使用pod的形式部署的,所以并没有重启pod。后续如果做新集群的话,准备也做成pod的形式,统一管理
5. 验证服务
kubectl get nodes
# 验证:所有节点状态为Ready,没有NotReady
sudo grep server: /etc/kubernetes/kubelet.conf
#应该看到的是修改后的ip
6. 业务稳定观察
# 持续观察30-60分钟
watch -n 10 kubectl get pods -A
# 验证:
# 1. 没有大规模Pod重启
# 2. 没有新的Pending/Error状态Pod
# 3. Ceph相关Pod状态正常
九、rancher镜像获取
北京打包
cd /tmp
sudo docker save -o rancher-images.tar \
> rancher/rancher:v2.5.16 \
> rancher/rancher-webhook:v0.1.6 \
> rancher/fleet:v0.3.9 \
> rancher/fleet-agent:v0.3.9 \
> rancher/gitjob:v0.1.26
sudo chmod 644 rancher-images.tar
上海拉取
~$ sudo scp -r ubuntu@<北京masterip>:/tmp/rancher-images.tar .
rancher-images.tar 100% 1409MB 22.4MB/s 01:03
导入镜像
sudo docker load -i ./rancher-images.tar
十、安全下线北京master
令令令申申申申申:
1. 一定一定一定要确保master上所有的pod(尤其是网络相关的包括但不限于:dns、ingress、cattle-systemfleet-system)都可以在其他节点上成功拉起来之后再下线。否则,排错真的很苦恼!!!!!!
2. /etc/kubernetes/admin.conf——admin.conf = kubectl 的“钥匙”,告诉你在哪里操作集群、用什么身份、走哪条路。
/etc/kubernetes/kubelet.conf——kubelet.conf = kubelet 的“身份证”,告诉 kubelet 它是谁、去哪里登记、怎么和 API Server 认证。
配置文件内一定要切换成现在的master ip。
#查看北京的msater上都有哪些pod
kubectl get pods -A -o wide | grep 北京master
#禁止新的pod调度到北京master节点上但节点上正在运行的现有 Pod 不会被驱逐或停止,它们会继续工作。
kubectl cordon ubuntu-r730xd-01
一个一个的去看pod,一个一个去迁移。全部迁移完毕之后且集群稳定运行一阵子,再进行驱逐北京master。
以下是不需要迁的
第一类:必须保留在旧 master 的
这些是:Kubernetes 控制面组件,不能随便迁。
1. etcd K8s 数据库 必须在 control-plane 节点。不要迁。
2. kube-apiserver K8s API 核心。必须保留。
3. kube-controller-manager K8s 控制器。必须保留。
4. kube-scheduler 调度器。必须保留。
第二类:不需要迁移的(DaemonSet)
这些属于:每个节点都会有一个,所以不用管。
1. kube-flannel 这是 CNI 网络。每个节点都有。不用迁。
2. kube-proxy 每节点一个。不用迁。
3. nvidia-device-plugin GPU 插件。只要节点有 GPU 就会部署。不用迁。
4. rook-ceph CSI
csi-cephfsplugin
csi-rbdplugin
这是:Ceph CSI 节点插件,也是 DaemonSet。每节点都有。不用迁。
再次确认所有的配置文件都已经修改为上海的ip、所有pod都可以在其他node节点上拉起,才可以驱逐
执行节点:上海 124
# 1 驱逐北京master上的业务Pod
kubectl drain 北京master \
--ignore-daemonsets \
--delete-emptydir-data \
--force
# 2 从集群中删除北京master节点
kubectl delete node 北京master
# 3 从etcd集群中移除北京成员(极其关键)
# 先查看etcd成员ID,etcdctl需要自行安装,如果本地没有的话,直接使用集群内 etcd Pod 操作里面执行这个命令就能查看了。
(1.查看etcd的pod信息 kubectl -n kube-system get pods -l component=etcd)
(2.进到pod里面去 kubectl -n kube-system exec -it etcd-xxxxxxxx#这是上条命令查到的其中一个podname -- sh)
ETCDCTL_API=3 etcdctl member list \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 找到北京节点对应的ID(示例:d10c9b7ccce54813)
# 4 删除北京etcd成员
ETCDCTL_API=3 etcdctl member remove 北京etcd成员ID \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
十一、最终验证
执行节点:上海 124
# 1 验证节点状态
kubectl get nodes
# 验证:北京master已不在列表中,其余节点全部Ready
# 2 验证etcd单节点健康
ETCDCTL_API=3 etcdctl endpoint health \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# 验证:输出 "https://127.0.0.1:2379 is healthy"
# 3 验证控制平面leader已切换
kubectl get leases -n kube-system
# 验证:holderIdentity显示为上海开头的字符串
# 4 验证Rancher可访问
# 浏览器访问:https://上海master/login
# 验证:可以正常登录并管理集群
总结
彻底安全下线北京master节点,排错的过程中还恶补了k8s集群的所有网络知识。感兴趣的可以主要看一下。
更多推荐




所有评论(0)