二进制部署Kubernetes高可用:从单master到双master+双LB实践
做二进制方式部署 Kubernetes 的人多少带点“倔”——不想被 kubeadm 那堆自动生成的证书和静态 Pod 挡住视线就想亲手把 etcd、apiserver、controller-manager、scheduler 一个个进程拉起来看它们到底是怎么协作的。我前阵子刚把一套“单 master 单 worker”的二进制集群扩张成“双 master 双 LB”的高可用形态中间踩了不少坑也把几个关键原理彻底捋了一遍。这篇就把从单节点演进到多节点高可用的完整过程讲清楚重点是两块一是在已有 master1 的基础上新增 master2二是用两台 nginx keepalived 做 API Server 的负载均衡和高可用。整个过程适合已经有一台二进制部署 master、想升级成控制面高可用结构的同学参考。你不需要从零开始只要照着已有单节点补组件就行。1. 扩容前先盘家底单节点集群到底缺了哪几块高可用拼图1.1 现有集群的组件清单与角色分布动手之前我习惯先列一份清单把现有集群里每个节点跑什么组件写清楚。我当时的环境是节点名IP运行组件状态k8s-master1192.168.31.10etcd1、kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、kube-proxy已有k8s-worker1192.168.31.12kubelet、kube-proxy已有k8s-master2192.168.31.11待部署新增k8s-lb1192.168.31.20nginx、keepalived新增k8s-lb2192.168.31.21nginx、keepalived新增VIP192.168.31.30统一 API Server 入口新增这里面有几个角色划分要提前想清楚master1 上的 etcd 目前是单实例里面存着整个集群的元数据包括 Node、Pod、ConfigMap、RBAC 这些核心数据。kube-apiserver 目前只有一台所有 kubectl、kubelet、controller-manager、scheduler 都连它。kube-controller-manager 和 kube-scheduler 也是单副本在跑没有选主机制。worker1 上的 kubelet 和 kube-proxy 的 kubeconfig 直接写死了 master1 的 IP 和 6443 端口。单点集群最大的问题不是某台机器容易坏而是控制面全部绑死在一台机器上。master1 一旦关机别说调度 Pod连 kubectl get nodes 都连不上去。1.2 高可用不是“把组件复制一遍”那么简单很多人以为新加 master2 就是把 master1 的文件复制过去再跑一遍 systemd 服务就行。真这么干会出现两个 apiserver 互相看不到对方、两个 controller-manager 同时抢同一个资源之类的怪问题。高可用改造的核心逻辑是这样的组件单节点状态多节点高可用方案kube-apiserver单进程master1、master2 各起一个前面挂 LB 统一入口kube-controller-manager单进程两个节点都起开启--leader-electtrue选主kube-scheduler单进程两个节点都起开启--leader-electtrue选主etcd单实例master1、master2 组成 etcd 集群API Server 入口master1 IPVIP 192.168.31.30 nginx keepalived这里有个容易混淆的概念keepalived 并不是负载均衡器它只负责“VIP 漂移”。真正把流量分发给两台 master 的是 nginx 的四层转发。keepalived 做的事情是保证 VIP 这个入口永远存在于 LB1 或 LB2 上谁优先级高、谁活着VIP 就在谁身上。nginx 负责把打向 VIP:16443 的流量轮询转发到 master1:6443 和 master2:6443。为什么用 16443 而不是 6443因为 master 节点本身已经监听 6443LB 再用 6443 会和后端端口混在一起看的时候容易乱。VIP 上开 16443看一眼端口就能知道流量经过了 LB。这只是约定不是强制你完全可以用 8443。还有一个事必须提前接受如果把 etcd 只放在 master1 和 master2 上那就是两节点的 etcd 集群。两节点的 quorum 是 2意味着必须两个 etcd 同时在线才能写。所以后面我实测时会刻意区分“master1 只挂 apiserver”和“master1 整机断电”两种情况前者能自动恢复后者会卡在 etcd 仲裁上。生产环境我建议至少三控制面节点别拿两节点 etcd 扛生产。2. 证书与配置文件准备给 master2 发“身份证”把 VIP 写进 SAN2.1 这套集群的证书体系要分清二进制部署里证书体系是最容易翻车的地方。很多人一上来就复制整个 pki 目录结果 master2 上证书里的 IP 还是 master1 的apiserver 一启动就报证书校验失败。我先按角色把证书分成三组Kubernetes 主 CA 签的证书apiserver 证书、admin 客户端证书、kubelet 客户端证书、controller-manager 和 scheduler 的客户端证书。etcd 独立 CA 签的证书etcd 节点间的 peer 证书、server 证书、client 证书。service account 相关的 sa.key 和 sa.pub这个不涉及 CA但要保证所有控制面节点一致。新增 master2 时真正要“重新签”的只有两类apiserver 的服务端证书以及 etcd2 的 peer/server 证书。其余大部分是可以直接复制或只改 server 地址就能用的。2.2 重新签发 apiserver 证书SAN 里必须包含新节点和 VIPapiserver 证书是集群里最特殊的服务端证书因为客户端可能通过多种方式访问它直接访问 master1 IP、直接访问 master2 IP、访问 VIP、Pod 内部访问 service 的 ClusterIP、访问 kubernetes.default.svc 这个 DNS 名。如果证书 SAN 缺了任何一项对应路径就会报 x509 certificate is valid for ... not for ... 错误。我习惯在 master1 上准备一个 SAN 配置文件目录固定放在 /opt/kubernetes/pki[req] distinguished_name dn req_extensions ext [dn] [ext] subjectAltName alt_names [alt_names] IP.1 192.168.31.10 IP.2 192.168.31.11 IP.3 192.168.31.20 IP.4 192.168.31.21 IP.5 192.168.31.30 IP.6 10.96.0.1 DNS.1 kubernetes DNS.2 kubernetes.default DNS.3 kubernetes.default.svc DNS.4 localhostIP.6 是 service 网段的第一个地址。我这边 service 网段是 10.96.0.0/12所以第一个可用 IP 是 10.96.0.1。这个地址很容易被忘掉忘了之后 Pod 里访问 Kubernetes service 会直接证书校验失败。然后重新签发cd /opt/kubernetes/pki openssl genrsa -out apiserver-new.key 2048 openssl req -new -key apiserver-new.key -subj /CNkube-apiserver -config openssl-apiserver.cnf -out apiserver-new.csr openssl x509 -req -in apiserver-new.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out apiserver-new.pem -days 3650 -extensions ext -extfile openssl-apiserver.cnf生成以后把 apiserver-new.pem 和 apiserver-new.key 分别替换到 master1 和 master2 的 /opt/kubernetes/pki 下。注意ca.key 只在签发的这台机器上留一份不要往 master2 传。master2 上只需要 ca.pem不需要 ca.key。2.3 controller-manager 和 scheduler 的 kubeconfig 怎么处理这两个组件的客户端证书是集群级别的CN 固定是 system:kube-controller-manager 和 system:kube-scheduler并不绑定某台具体节点。所以最省事的做法是直接从 master1 复制 kubeconfig 到 master2然后只改里面的 server 地址。我这边把所有控制面组件统一指到了 VIP 上sed -i s#https://192.168.31.10:6443#https://192.168.31.30:16443# /opt/kubernetes/kubeconfig/kube-controller-manager.kubeconfig sed -i s#https://192.168.31.10:6443#https://192.168.31.30:16443# /opt/kubernetes/kubeconfig/kube-scheduler.kubeconfig这样 controller-manager 和 scheduler 无论跑在哪个 master 上都是通过同一个入口访问 apiserver语义上比较干净。如果你的单节点集群里这两个组件当初没有开--leader-electtrue那现在必须改掉。两个 master 都启动 controller-manager如果不开 leader election两个进程会同时去改 Deployment、同步副本数直接造成资源竞争和状态混乱。2.4 别忘了 sa.key 和 sa.pubservice account 的密钥对也是控制面组件之间协作的关键。kube-apiserver 用 sa.pub 校验 ServiceAccount Tokenkube-controller-manager 用 sa.key 签发 Token。如果 master2 上只有 sa.pub 没有 sa.keycontroller-manager 启动时 --service-account-private-key-file 会直接报找不到文件。所以这一步的完整复制清单是scp /opt/kubernetes/pki/ca.pem 192.168.31.11:/opt/kubernetes/pki/ scp /opt/kubernetes/pki/sa.pub 192.168.31.11:/opt/kubernetes/pki/ scp /opt/kubernetes/pki/sa.key 192.168.31.11:/opt/kubernetes/pki/ scp /opt/kubernetes/pki/apiserver-new.pem 192.168.31.11:/opt/kubernetes/pki/apiserver.pem scp /opt/kubernetes/pki/apiserver-new.key 192.168.31.11:/opt/kubernetes/pki/apiserver.keyetcd 的证书我建议单独放在 /opt/kubernetes/pki/etcd 目录下和 Kubernetes 主 CA 分开管理。万一哪天要吊销某个节点的证书独立的 etcd CA 至少不会让整条信任链一起被动。3. master2 环境和组件安装etcd 集群化与控制面三件套就位3.1 二进制文件和目录同步master2 上先把基础目录建好然后从 master1 同步二进制mkdir -p /opt/kubernetes/{bin,pki,pki/etcd,kubeconfig,log} rsync -avz 192.168.31.10:/opt/kubernetes/bin/ /opt/kubernetes/bin/同步完检查一下版本二进制部署最怕 master1 和 master2 之间版本不一致。比如 master1 是 v1.26.5master2 却下成了 v1.27.1两个 apiserver 都能起来但 apiserver 和 etcd 之间的兼容性、kubelet 和 apiserver 的版本差都会埋雷。我的习惯是全部先看一遍--version完全一致再往下走。3.2 把 etcd2 作为新成员加入现有集群etcd 不是重新起一个就行必须作为新成员加入 master1 那个已有 etcd否则两个 etcd 各存各的数据apiserver 连哪个都不对。这一步先到 master1 上执行 member addexport ETCDCTL_API3 etcdctl --endpointshttps://192.168.31.10:2379 \ --cacert/opt/kubernetes/pki/etcd/ca.pem \ --cert/opt/kubernetes/pki/etcd/client.pem \ --key/opt/kubernetes/pki/etcd/client-key.pem \ member add etcd2 --peer-urlshttps://192.168.31.11:2380命令执行后会返回一段提示信息里面会明确告诉你启动 etcd2 时要用什么 initial-cluster。很多人在这一步把返回的ETCD_INITIAL_CLUSTER抄错少写一个成员或者写错 IPetcd2 起来后反复报 “not found in member list”。返回的 initial-cluster 大概是这种格式etcd1https://192.168.31.10:2380,etcd2https://192.168.31.11:2380把这个值原样写进 master2 的 etcd systemd 单元里。3.3 master2 的 etcd systemd 配置master2 上 etcd 的启动参数里--name etcd2、--initial-cluster-stateexisting是必须的。前者标识成员身份后者告诉 etcd 我已经是一个集群的既有成员不是要从头初始化。[Unit] DescriptionEtcd Server Afternetwork.target [Service] ExecStart/opt/kubernetes/bin/etcd \ --name etcd2 \ --data-dir/var/lib/etcd \ --initial-advertise-peer-urls https://192.168.31.11:2380 \ --listen-peer-urls https://192.168.31.11:2380 \ --advertise-client-urls https://192.168.31.11:2379 \ --listen-client-urls https://192.168.31.11:2379 \ --cert-file/opt/kubernetes/pki/etcd/etcd2-server.pem \ --key-file/opt/kubernetes/pki/etcd/etcd2-server-key.pem \ --peer-cert-file/opt/kubernetes/pki/etcd/etcd2-peer.pem \ --peer-key-file/opt/kubernetes/pki/etcd/etcd2-peer-key.pem \ --peer-client-cert-authtrue \ --peer-trusted-ca-file/opt/kubernetes/pki/etcd/ca.pem \ --client-cert-authtrue \ --trusted-ca-file/opt/kubernetes/pki/etcd/ca.pem \ --initial-cluster-stateexisting \ --initial-clusteretcd1https://192.168.31.10:2380,etcd2https://192.168.31.11:2380 Restarton-failure LimitNOFILE65536 [Install] WantedBymulti-user.target启动后回到 master1 验证集群状态etcdctl --endpointshttps://192.168.31.10:2379,https://192.168.31.11:2379 \ --cacert/opt/kubernetes/pki/etcd/ca.pem \ --cert/opt/kubernetes/pki/etcd/client.pem \ --key/opt/kubernetes/pki/etcd/client-key.pem \ member list etcdctl ... endpoint healthmember list 里能看到两个成员endpoint health 显示两个 endpoint 都是 healthy这一步才算过。3.4 master2 上拉起 apiserver、controller-manager、schedulerapiserver 的参数里--advertise-address192.168.31.11和--etcd-servers要写全。etcd-servers 不是只写本机的 2379而是把两个 etcd 都写上这样任意一个 etcd 挂了apiserver 还能连另一个。[Unit] DescriptionKubernetes API Server Afternetwork.target [Service] ExecStart/opt/kubernetes/bin/kube-apiserver \ --bind-address0.0.0.0 \ --secure-port6443 \ --advertise-address192.168.31.11 \ --etcd-servershttps://192.168.31.10:2379,https://192.168.31.11:2379 \ --etcd-cafile/opt/kubernetes/pki/etcd/ca.pem \ --etcd-certfile/opt/kubernetes/pki/etcd/client.pem \ --etcd-keyfile/opt/kubernetes/pki/etcd/client-key.pem \ --client-ca-file/opt/kubernetes/pki/ca.pem \ --tls-cert-file/opt/kubernetes/pki/apiserver.pem \ --tls-private-key-file/opt/kubernetes/pki/apiserver.key \ --service-cluster-ip-range10.96.0.0/12 \ --service-node-port-range30000-32767 \ --authorization-modeNode,RBAC \ --allow-privilegedtrue \ --service-account-key-file/opt/kubernetes/pki/sa.pub Restarton-failure [Install] WantedBymulti-user.targetcontroller-manager 和 scheduler 两个服务比较简单核心就是 kubeconfig 路径和 leader-elect[Service] ExecStart/opt/kubernetes/bin/kube-controller-manager \ --kubeconfig/opt/kubernetes/kubeconfig/kube-controller-manager.kubeconfig \ --leader-electtrue \ --service-account-private-key-file/opt/kubernetes/pki/sa.key [Service] ExecStart/opt/kubernetes/bin/kube-scheduler \ --kubeconfig/opt/kubernetes/kubeconfig/kube-scheduler.kubeconfig \ --leader-electtrue全部起来后master2 的 apiserver 直接 curl 自检curl -k --cacert /opt/kubernetes/pki/ca.pem https://192.168.31.11:6443/healthz返回 ok 就说明这一节点控制面基本通了。3.5 回 master1 改配置并重启master1 不能一直用旧的 apiserver 证书和单 etcd 地址。把新签好的 apiserver.pem、apiserver.key 替换进去同时把 systemd 单元里的 --etcd-servers 改成两个地址然后重启 kube-apiserver。我建议先在 master2 全部起来之后再动 master1这样重启 master1 的 apiserver 期间至少还有一个 apiserver 撑着不会出现整段窗口没人响应。如果这时候 LB 还没配好kubectl 暂时还是走 master1 的直连地址会有一两秒钟的连接闪断等 LB 配好后就彻底没有这个窗口了。4. 两台 LB 上 nginx 和 keepalived 的完整配置VIP 是灵魂健康检查是保命符4.1 先分清楚 nginx 和 keepalived 各自的职责这个架构里LB 节点上跑两个软件很多人搞混它们的边界nginx 只做数据面的四层转发监听 VIP:16443把请求轮询转发给 master1:6443 和 master2:6443。keepalived 只做控制面的 VIP 管理两台 LB 上分别配置 VRRP谁优先级高谁持有 VIP 192.168.31.30。keepalived 检测到 nginx 进程挂了就降自己的优先级让另一台把 VIP 抢过去。nginx 挂了VIP 漂移后端 apiserver 挂了nginx 会通过 fail_timeout 把它临时摘除。这两层健康检查的目标不同一个是保入口一个是保后端缺一不可。4.2 nginx 四层转发配置nginx 的 stream 模块需要确认编译进去了nginx -V 21 | grep stream如果没有就从官方仓库装一个带 --with-stream 的版本或者用 openresty 这类自带 stream 的分发包。配置文件放在 /etc/nginx/nginx.conf 的顶层注意 stream 块不能写在 http 块里面这是新手最常见的 nginx -t 报错原因stream { upstream kube_apiserver { server 192.168.31.10:6443 max_fails3 fail_timeout10s; server 192.168.31.11:6443 max_fails3 fail_timeout10s; } server { listen 16443; proxy_pass kube_apiserver; proxy_connect_timeout 5s; proxy_timeout 30s; } }max_fails3 和 fail_timeout10s 是给后端 apiserver 做被动健康检查的10 秒内失败 3 次nginx 就把这个后端临时摘掉10 秒后再重新探测。开源版 nginx 的 stream 模块没有主动健康检查这个配置在 apiserver 拒绝连接或进程崩溃的场景下够用。如果 apiserver 进程假死但端口还在这层就测不出来只能靠系统层重启或者监控及时发现。4.3 keepalived 配置两台 LB 上只差几个字段LB1 是 MASTER优先级 100LB2 是 BACKUP优先级 90。除了 router_id、priority、unicast_src_ip 不一样其余配置要保持一致尤其是 virtual_router_id 和 auth_pass两台不一样会导致 VRRP 各自为政出现两个 VIP 同时存在的脑裂。LB1 上的配置global_defs { router_id LB_1 } vrrp_script check_nginx { script /etc/keepalived/check_nginx.sh interval 2 weight -20 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } unicast_src_ip 192.168.31.20 unicast_peer { 192.168.31.21 } virtual_ipaddress { 192.168.31.30/24 dev eth0 } track_script { check_nginx } }LB2 上把 router_id 改成 LB_2state 改成 BACKUPpriority 改成 90unicast_src_ip 改成 192.168.31.21unicast_peer 改成 192.168.31.20其他不动。我特意用了 unicast 而不是默认的 multicast。很多云环境和办公网会屏蔽 VRRP 组播包两边的 keepalived 互相收不到通告结果两台同时认为自己是 MASTERVIP 就飘成了两个。用 unicast 后VRRP 报文走单播依赖的只是两台 LB 之间的 TCP/IP 连通性兼容性好很多。健康检查脚本 /etc/keepalived/check_nginx.sh 内容很简单#!/bin/bash if pgrep -x nginx /dev/null 21; then exit 0 else exit 1 fi加上可执行权限chmod x /etc/keepalived/check_nginx.sh脚本失败时 keepalived 会把自己这台节点的优先级降 20。MASTER 的 100 降到 80低于 BACKUP 的 90VIP 自然就被 BACKUP 抢走。这种方式比脚本里直接systemctl stop keepalived温和nginx 恢复后优先级还能自动回来VIP 不需要人为干预就能回切。4.4 启动顺序和防火墙注意事项我习惯先启动 nginx再启动 keepalivedsystemctl start nginx systemctl start keepalived其实 keepalived 先启动也不会有大问题它抢到 VIP 后如果 nginx 还没起来流量就会断所以干脆顺序固定。注意两台 LB 都要放行 16443 端口VRRP 如果走 unicast 则不需要额外放行组播协议。启动后在 LB1 上确认 VIP 是否掌握ip addr show | grep 192.168.31.30在任意一台机器上测试curl -k https://192.168.31.30:16443/healthz返回 ok 就说明 nginx 转发到 apiserver 这条链路是通的。5. 全链路验证与故障切换实测把 master1 拔网线之后才知道的事5.1 先统一入口再开始验证LB 配好后把 admin kubeconfig 也切到 VIPworker 节点上的 kubelet、kube-proxy 同样要切。kubelet 的 kubeconfig 通常写在 /etc/kubernetes/kubelet.kubeconfig 或 systemd 单元的 --kubeconfig 参数里把 server 从 https://192.168.31.10:6443 改成 https://192.168.31.30:16443然后重启 kubelet 和 kube-proxy。使用 kubectl 的机器把 ~/.kube/config 也改成 VIPsed -i s#https://192.168.31.10:6443#https://192.168.31.30:16443# ~/.kube/config全部切完之后执行一轮基础验证我按下面这张表挨个过验证项命令预期结果节点状态kubectl get nodesmaster1、master2、worker1 都是 Readyetcd 集群etcdctl member list两个成员healthyLB 入口curl -k https://192.168.31.30:16443/healthzokpod 运行kubectl get pods -A所有组件 Running工作负载调度kubectl run demo --imagenginx:alpine能创建成功5.2 场景一master1 只挂 apiserver集群不感知我在 master1 上执行systemctl stop kube-apiserver模拟控制面单点故障。这个时候大量请求会走向 VIPnginx 会在连接失败后把请求转发到 master2 的 apiserver。实测下来kubectl get nodes能正常返回创建 Pod 也正常。原因有两层nginx 摘除了 master1 这个后端etcd1 和 etcd2 都活着etcd 依然满足两节点的 quorum。只要 etcd 有仲裁任何一台 apiserver 都能提供完整的读写服务。这个场景是这台二 master 集群能兜住的主要故障类型。5.3 场景二LB1 挂掉VIP 自动漂移到 LB2我在 LB1 上执行systemctl stop keepalived观察 VIP 变化。2 到 3 秒后192.168.31.30 从 LB1 消失出现在 LB2 上。这个动作里 nginx 并没有重启keepalived 只是把入口换了一个宿主流量的数据面依然是 nginx 转发到后端两台 apiserver。反过来测试更值得做在 LB2 持有 VIP 的情况下到 LB1 启动 nginx 和 keepalived观察 VIP 是否自动回切。默认配置下 MASTER 优先级恢复后会自动抢回 VIP这个回切过程对客户端来说是无感的TCP 连接建立走新入口后开始正常转发。有一类坑值得提如果两台 LB 上的 virtual_router_id 不一致VIP 会同时出现在两台机器上客户端请求会被随机分流表现就是时通时不通。遇到这种问题先别急着看 nginx 日志先确认两台 keepalived 的 virtual_router_id 和 auth_pass 完全一致。5.4 场景三master1 整机断电二 master 结构的边界在哪这是我最想强调的部分。如果 master1 整机关机而不是只停 apiserver那么 etcd1 也没了。当前 etcd 集群只有两个成员quorum 需要两个成员都在只剩 etcd2 时无法达成仲裁etcd 会拒绝写请求。此时从 LB 访问 apiserver 仍然能建立 TLS 连接/healthz也可能返回 ok因为 apiserver 进程还活着但它内部访问 etcd 时写操作会超时。表现就是 kubectl get nodes 偶尔能出来一执行 kubectl apply 或者删除 Pod 就卡住最后报 etcd server: request timed out。这不是部署失败而是二控制面加二 etcd 的结构性约束。如果你只有两台机器还有个妥协办法是把 etcd 也放到两台 LB 上凑成四节点 etcd 集群仲裁需要三台这样任意一台控制面或 LB 单点故障都不影响 etcd。代价是 LB 节点上既要跑 nginx/keepalived又要跑 etcd角色混部出问题时排查面会变大。生产环境更稳妥的选择还是三控制面节点etcd 在三台或五台上分布再做前置 LB。5.5 验证后别忘了回切测试故障演练不是演练完就算完还要把服务恢复到正常状态。master1 恢复后etcd1 会自动重新加入集群kube-apiserver 重新注册到 nginx 后端kubectl get nodes 会看到 master1 重新 Ready。我的习惯是恢复后再做一轮同样的基础验证确认没有遗留的异常。尤其是检查 etcd member list防止 etcd2 起来时因为网络抖动被误判为异常节点。6. 最后分享几个我踩过之后才记住的细节第一个是版本一致性。两个 master、两个 LB 上的 kubectl、etcd、kubelet 版本尽量保持完全一致。别小看这个etcd 版本不一致会导致 member 之间协议行为不一致kubelet 版本过旧可能无法和新的 apiserver 正常协商。第二个是 master 上的 kubelet 也要改配置指向 VIP。很多教程只改 worker 的 kubelet忘了 master 本身也跑了 kubelet结果 master 节点状态不稳定Pod 调度过去之后 kubelet 连不上 apiserver节点反复 NotReady。第三个是 keepalived 的日志。如果 VIP 没有按预期漂移先看 /var/log/messages 或者 journalctl -u keepalived。VRRP 状态切换、脚本检测失败、auth 错误都会在这里留下明确记录比瞎猜配置快得多。第四个是证书更新的连锁影响。替换 apiserver 证书后所有和它通信的组件要重新加载信任关系。controller-manager、scheduler 这些进程通常要重启一次才能读新的证书文件。kubelet 也可能因为 apiserver 证书变化导致 TLS 校验报错重启 kubelet 一般能解决。这套架构从单 master 演进到双 master 加双 LB 之后日常运维里最明显的感受是重启一台 master 不再需要挑凌晨窗口了。LB 会自动把流量分走Keepalived 会把入口保住剩下的就是观察 etcd 是否还有 quorum。但我也得再强调一次两节点 etcd 只能容忍一台 apiserver 挂掉不能容忍一台机器整体断电。真正要免维护窗口还是尽早往三控制面节点走。

相关新闻

Metadef实践指南:CANN自定义算子开发的基石

Metadef实践指南:CANN自定义算子开发的基石

Metadef 这个名字在自定义算子开发圈子里,说实话一开始很容易被忽略。很多人刚接触 CANN 算子开发时,目光全放在 kernel 怎么实现、tiling 怎么切分上,等真正动手写第一个算子才发现,最卡脖子的不是计算逻辑,而是“这个…

2026/10/19 3:16:05 阅读更多 →
多平台OLED12864驱动例程详解:基于SSD1306的I2C/SPI显示方案

多平台OLED12864驱动例程详解:基于SSD1306的I2C/SPI显示方案

简介:OLED12864在多种单片机上的驱动例程包,面向嵌入式初学者与物联网项目开发者,旨在解决不同平台间屏幕驱动与引脚配置问题。例程全部使用软件模拟SPI,通过普通GPIO即可完成通信,适合无硬件SPI外设或希望灵活连线的场…

2026/10/19 3:16:05 阅读更多 →
双指针算法详解:从对撞、快慢到滑动窗口的实战应用

双指针算法详解:从对撞、快慢到滑动窗口的实战应用

双指针这个算法,我在面试和实际项目里都吃过不少亏,也尝过甜头。很多人一听到“双指针”觉得简单,无非就是两个下标来回动,可真到做题、写业务代码的时候,要么想不到用它,要么写出了死循环,要么…

2026/10/19 3:16:05 阅读更多 →

最新新闻

LSTM原理详解与工程加速:从梯度消失到推理优化

LSTM原理详解与工程加速:从梯度消失到推理优化

最近在某个工业设备的时序预测项目里,我把LSTM重新捡起来用了一遍。说实话,这两年Transformer在序列任务上的声量确实大,很多新入行的同学一上来就研究注意力机制,反而把LSTM这个经典的循环结构给忽略了。但手头这个项目跑下来&am…

2026/10/19 4:08:35 阅读更多 →
Linux权限管理实战:chmod、chown与chgrp用法详解

Linux权限管理实战:chmod、chown与chgrp用法详解

在Linux下折腾权限,估计是每个接触它的人逃不掉的一课。我见过不少同事,代码在本地跑得好好的,一部署到服务器就报Permission denied,第一反应就是随便敲个sudo chmod -R 777图省事,结果要么服务能跑了但文件权限全乱&…

2026/10/19 4:08:35 阅读更多 →
Qt虚拟键盘实现:基于QWidget的嵌入式触摸屏输入方案与避坑指南

Qt虚拟键盘实现:基于QWidget的嵌入式触摸屏输入方案与避坑指南

简介:一款基于Qt开发的虚拟小键盘实现,面向需要无物理键盘输入或希望为界面内嵌软键盘的Qt开发者。它采用无边框嵌入方式,不创建独立窗口,而是接收自定义myspinbox(继承自QSpinBox)指针,将键盘的…

2026/10/19 4:08:35 阅读更多 →
AI编程最佳实践:用提示词与代码审查打造高质量代码

AI编程最佳实践:用提示词与代码审查打造高质量代码

最近这段时间,AI 编程大概是开发者圈子里最绕不开的话题。打开技术社区,到处能看到“用 AI 半天写完一个功能”或者“AI 帮我重构了祖传代码”的分享。不过我也发现一个很有意思的现象:同样是开着 AI 编程助手写代码,有人拿到的是…

2026/10/19 4:08:35 阅读更多 →
JavaScript数组按属性分组:从reduce手写到Map安全实现与性能避坑

JavaScript数组按属性分组:从reduce手写到Map安全实现与性能避坑

做开发这几年,“JSON 数组按某个属性分组”绝对是我见过最常被问到的需求之一。后端丢过来一百条订单,前端要按日期分成几块展示;导出报表的时候,要把同部门的数据合并到一起;甚至只是写个数据清洗脚本,也绕…

2026/10/19 4:08:35 阅读更多 →
CefSharp Cookie持久化实战:存储、读取与多账号隔离

CefSharp Cookie持久化实战:存储、读取与多账号隔离

接手过几个需要内嵌浏览器的桌面项目之后,我对“CefSharp存储Cookie和读Cookie”这件事算是从踩坑踩到了门清。很多刚接触CefSharp的朋友会以为,Cookie存取无非就是调一个API的事,但实际上这套基于Chromium内核的封装,在会话持久化…

2026/10/19 4:07:34 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →