ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

二进制部署Kubernetes集群证书过期更新完整指南

二进制部署Kubernetes集群证书过期更新完整指南 1. 二进制没有“自动续期”先认清证书分布和有效期1.1 为什么kubeadm能一键续期二进制部署不行很多用kubeadm装集群的朋友对证书的认知就是一句kubeadm certs renew all的事甚至不少人从来没手动看过证书长什么样。但切换到二进制部署的K8s集群后画风完全不同apiserver、etcd、kubelet这些组件的证书全是当初部署时用cfssl或openssl手工签发的集群里没有任何一个组件会自动去续签证书。到期就是到期apiserver的HTTPS端口会直接拒绝客户端握手kubelet心跳中断后节点变成NotReady之后你看到的就是一堆莫名其妙的报错。这也是二进制部署和kubeadm最大的区别之一。kubeadm把证书生命周期纳入了自身的管理体系每年自动续签一轮而二进制部署更像你自己维护一台没有任何定时任务的“裸服务器”所有证书文件都在磁盘上躺着过了notAfter时间就失效。所以如果你维护的是二进制部署的集群提前把证书更新这件事纳入日常运维远比临时救火要舒服得多。本文就是围绕“证书到期后怎么更新”这件事讲清楚原理、步骤和坑主攻手写部署方案的运维同学可以直接照着操作。1.2 盘点二进制集群里到底有哪些证书二进制部署的集群证书数量不少而且分散在master和worker节点上。我以最常见的目录结构为例先把家底盘清楚证书/配置文件典型路径作用使用方K8s CA/etc/kubernetes/ssl/ca.pem集群信任根签发各组件证书所有组件kube-apiserver 服务端证书/etc/kubernetes/ssl/apiserver.pem对外提供HTTPS接口apiserveradmin 客户端证书/etc/kubernetes/ssl/admin.pem管理员kubectl访问kubectlcontroller-manager 客户端证书/etc/kubernetes/ssl/controller-manager.pem连接apiserver的认证凭据kube-controller-managerscheduler 客户端证书/etc/kubernetes/ssl/scheduler.pem连接apiserver的认证凭据kube-schedulerkubelet 证书/etc/kubernetes/ssl/kubelet.pem既是10250端口服务端证书也是连接apiserver的客户端证书kubeletkube-proxy 客户端证书/etc/kubernetes/ssl/kube-proxy.pem连接apiserver的认证凭据kube-proxyETCD CA/etc/etcd/ssl/ca.pemetcd信任根etcd集群ETCD Server证书/etc/etcd/ssl/etcd-server.pemetcd 2379端口服务端证书etcdETCD Peer证书/etc/etcd/ssl/etcd-peer.pemetcd节点间集群通信认证etcd除了这些pem文件更需要注意的是kubeconfig。controller-manager、scheduler、kubelet、kube-proxy、admin实际使用的时候并不直接指定pem文件而是通过/etc/kubernetes/controller-manager.kubeconfig、scheduler.kubeconfig、kubelet.kubeconfig、kube-proxy.kubeconfig和~/.kube/config这类文件连接apiserver。这些kubeconfig里嵌入了对应的客户端证书和私钥更新时要么重新生成整个kubeconfig要么把新证书base64编码后替换掉文件里的client-certificate-data和client-key-data字段。这里有个很容易被忽略的点etcd集群的证书路径和K8s侧的证书路径通常不在同一个目录。很多二进制部署方案会把etcd单独放在/etc/etcd/ssl下更新时如果只盯着/etc/kubernetes目录操作容易漏掉etcd这一层的证书。1.3 有效期是怎么定的CFSSL配置里的参数当初部署时如果用cfssl签发证书那有效期是在ca-config.json里定义的。最常见的配置是这样{ signing: { default: { expiry: 8760h }, profiles: { kubernetes: { expiry: 8760h, usages: [signing, key encipherment, server auth, client auth] } } } }8760h就是365天这也是为什么很多二进制集群运行一年后集中爆发证书过期问题的原因。如果当初为CA单独签发了更长的有效期比如10年甚至20年那CA一般还能用很久但如果CA也按默认一年签的那就麻烦了后面我会专门说CA过期的情况。另外有些部署教程直接用openssl命令签指定-days 365效果一样都是到期后组件间握手失败。建议读者现在就去做一件事登录master节点看看/etc/kubernetes/ssl/ca.pem和几份组件证书的notAfter时间做到心里有数。2. 更新前先做“体检”5分钟摸清全部证书到期时间2.1 查看pem证书文件的统一姿势检查证书到期时间最常用的是openssl命令。在任意一个持有证书的节点上执行openssl x509 -in /etc/kubernetes/ssl/apiserver.pem -noout -dates -subject输出会包含两行关键信息notBeforeApr 10 08:00:00 2024 GMT notAfterApr 10 08:00:00 2025 GMTnotAfter就是证书到期时间。如果想看证书签发给谁、包含哪些域名和IP还可以追加参数openssl x509 -in /etc/kubernetes/ssl/apiserver.pem -noout -text | grep -A2 Subject Alternative Name这个命令在排查SAN问题时特别有用后面第五部分会提到。2.2 kubeconfig里嵌入的证书怎么看pem文件可以直接用-in指定但kubeconfig里的证书是base64编码的字符串没法直接openssl。需要先提取再解码。以~/.kube/config为例kubectl config view --raw -o jsonpath{.users[0].user.client-certificate-data} | base64 -d | openssl x509 -noout -dates如果是直接查看某个kubeconfig文件可以用yq或grep提取字段。比如grep client-certificate-data /etc/kubernetes/kubelet.kubeconfig | awk {print $2} | base64 -d | openssl x509 -noout -dates这一步非常管用因为controller-manager、scheduler这类组件本身不对外提供HTTPS服务你没法通过端口探测去看它们的证书只能从kubeconfig里取出来检查。2.3 线上端口证书的实际过期时间还有一种“体检”方式更贴近真实运行状态直接探测端口。比如apiserver监听在6443可以这样看客户端实际收到的证书echo | openssl s_client -connect 127.0.0.1:6443 -servername kubernetes 2/dev/null | openssl x509 -noout -datesetcd的2379端口同理echo | openssl s_client -connect 127.0.0.1:2379 2/dev/null | openssl x509 -noout -dates这种方式能验证“组件实际加载的证书”和“磁盘上存的证书”是否一致。如果systemd配置指向的证书路径出错或者改了配置文件没重启可能出现磁盘上新证书、端口上还是旧证书的情况。2.4 体检结果怎么判断只续组件证书还是CA也保不住了把集群内所有证书的到期时间列成一张表后接下来要做一个关键判断如果只有组件证书到期CA还没到期比如CA要到2034年才过期而apiserver、kubelet的证书下个月到期这种情况最简单继续用现有CA重新签发组件证书就行。如果CA也快到期了那就要慎重。CA是整个集群的信任根CA过期意味着所有由它签发的证书在验证链路层面都会出问题这时候只替换个别组件证书是治标不治本。比较稳妥的做法是重新规划信任体系生成新CA给所有组件重新签发证书然后按序替换全部kubeconfig和pem。工作量约等于重建集群的证书层需要安排专门的维护窗口。另外要特别注意kubelet启动时配置的--certificate-authority和etcd的--trusted-ca-file指向的是哪一份CA。如果当初部署时混用了多套CA体检时要把每套CA的都查一遍。3. 证书轮换实操从重新签发到逐步替换3.1 工具准备cfssl没装的话先补齐多数二进制部署教程用cfssl签发证书所以更新时最好也用同一把工具避免openssl重新签出来的证书格式、扩展属性不一致。如果你的机器上已经删掉了cfssl可以从官方GitHub发布页下载对应版本放到/usr/local/bin下然后赋予执行权限chmod x /usr/local/bin/cfssl /usr/local/bin/cfssljson下载cfssl时最好核对一下sha256校验值别从来路不明的镜像站拿文件。此外确认一下当初签发证书时用的ca-config.json和CSR模板还在不在如果找不到了也没有关系可以参考现有证书的subject和SAN信息手工重建一个。3.2 用现有CA重新签发kube-apiserver示例下面以kube-apiserver为例演示用现有CA重新签发一份新证书。首先创建CSR配置文件文件名随意比如apiserver-csr.json{ CN: kubernetes, hosts: [ 10.0.0.1, 192.168.1.10, 192.168.1.11, 192.168.1.12, 10.96.0.1, kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster.local ], key: { algo: rsa, size: 2048 }, names: [ { C: CN, ST: Beijing, L: Beijing, O: kubernetes, OU: kubernetes } ] }hosts数组里的每一项都会被写进证书的SANSubject Alternative Name。这里的IP和域名必须包含所有master节点的IP、负载均衡的VIP以及Service网段里的首个ClusterIP通常就是kubernetes.default.svc对应的10.96.0.1。漏掉任何一个都可能导致kubelet或其他客户端通过该地址访问apiserver时出现证书校验失败。然后执行签发命令cfssl gencert -caca.pem -ca-keyca-key.pem -configca-config.json -profilekubernetes apiserver-csr.json | cfssljson -bare apiserver执行后会生成apiserver.pem和apiserver-key.pem两个文件。签发完先别急着覆盖线上文件先检查一下SAN和有效期openssl x509 -in apiserver.pem -noout -text | grep -A2 Subject Alternative Name openssl x509 -in apiserver.pem -noout -dates确认无误后备份旧文件再拷贝新文件到指定目录并重启服务cp apiserver.pem apiserver-key.pem /etc/kubernetes/ssl/ systemctl restart kube-apiserver其他组件的证书签发逻辑完全一样只是CSR里的CN、O字段和hosts列表不同。比如apiserver需要很多hosts而controller-manager、scheduler、kubelet这些客户端证书通常不需要hosts只要CN匹配就好。3.3 替换etcd证书集群健康优先etcd证书更新有它自己的特殊性。etcd集群是多节点组成的如果同时把所有节点停了再换证书很可能在重启过程中出现选举异常甚至脑裂风险。稳妥的做法是逐节点滚动更新。以单节点为例先把新签发的etcd-server、etcd-peer证书放到/etc/etcd/ssl/然后重启该节点的etcd服务systemctl restart etcd重启后立刻检查集群健康状态。注意要使用新版etcdctl并指定证书相关参数etcdctl --endpointshttps://192.168.1.10:2379 \ --cacert/etc/etcd/ssl/ca.pem \ --cert/etc/etcd/ssl/etcd-server.pem \ --key/etc/etcd/ssl/etcd-server-key.pem \ endpoint health --cluster看到所有成员都返回healthy再继续操作下一个节点。如果某个节点返回报错先排查是不是证书不匹配、是不是拷贝遗漏确认没问题再继续不要抱有“先全换完再统一排错”的想法那样会把排查范围瞬间扩大好几倍。3.4 替换kube-apiserver证书SAN不能错前文已经提过apiserver证书的核心是SAN。这里再展开说说为什么它这么重要kubelet连接apiserver时是通过--server参数里指定的地址访问的如果这个地址是https://192.168.1.10:6443那kubelet会用192.168.1.10这个IP去校验apiserver证书的SAN如果新证书里没有这个IPkubelet会直接拒绝连接节点状态马上变成NotReady。实际操作时需要把以下几类地址全部检查一遍所有master节点的内网IP、主机名负载均衡VIP如果用keepalivedhaproxyService网段里的ClusterIP尤其是kubernetes这个Service对应的IP如果有域名解析也要加入域名签发完新证书并替换文件后重启kube-apiserver前可以先在本地用一个简单请求验证curl --cacert /etc/kubernetes/ssl/ca.pem https://127.0.0.1:6443/healthz如果返回ok说明服务正常启动。然后再检查apiserver日志里有没有证书相关报错journalctl -u kube-apiserver -n 50 --no-pager3.5 替换controller-manager与scheduler的kubeconfigcontroller-manager和scheduler通常不直接指定pem文件而是通过kubeconfig连接apiserver。最简单的方式是重新生成一份kubeconfig文件名保持和原来一致这样systemd启动参数不用改。假设新签发的控制器证书是controller-manager.pem和controller-manager-key.pem可以用kubectl命令重建kubeconfigkubectl config set-cluster kubernetes \ --serverhttps://192.168.1.10:6443 \ --certificate-authority/etc/kubernetes/ssl/ca.pem \ --embed-certstrue \ --kubeconfig/etc/kubernetes/controller-manager.kubeconfig kubectl config set-credentials system:kube-controller-manager \ --client-certificate/etc/kubernetes/ssl/controller-manager.pem \ --client-key/etc/kubernetes/ssl/controller-manager-key.pem \ --embed-certstrue \ --kubeconfig/etc/kubernetes/controller-manager.kubeconfig kubectl config set-context kubernetes \ --clusterkubernetes \ --usersystem:kube-controller-manager \ --kubeconfig/etc/kubernetes/controller-manager.kubeconfig kubectl config use-context kubernetes --kubeconfig/etc/kubernetes/controller-manager.kubeconfigscheduler同理只是用户名和文件路径换成scheduler。替换后重启两个服务systemctl restart kube-controller-manager kube-scheduler然后查看对应服务的日志重点看是否有Unauthorized、certificate has expired这类关键词。4. 最容易翻车的环节kubelet、kube-proxy与admin kubeconfig4.1 kubelet证书的双重身份kubelet的证书在二进制部署里最容易混淆因为它承担了两个职责作为服务端证书监听10250端口负责响应kubectl logs、kubectl exec、metrics采集等请求。作为客户端证书让kubelet连接apiserver完成注册、心跳和Pod状态上报。如果只有客户端证书过期你会看到节点状态变成NotReady但kubectl logs可能仍然能工作因为logs走的是另一个方向的认证。反之如果只有服务端证书过期节点状态可能正常但执行kubectl logs会报x509: certificate has expired or is not yet valid。更新时需要同时确保证书文件本身、kubelet.kubeconfig里的嵌入证书、以及kubelet启动参数指向的路径三者一致。很多人的kubelet配置里同时存在--client-ca-file用于验证apiserver访问kubelet时携带的客户端证书和--tls-cert-file、--tls-private-key-file这三项都要检查。4.2 worker节点的替换顺序先kubelet后kube-proxyworker节点上涉及更新的是kubelet和kube-proxy。建议顺序是先kubelet再kube-proxy。原因是kubelet负责节点的心跳和Pod生命周期管理如果 kube-proxy先更新重启而kubelet还没恢复期间新创建的Service规则可能无法及时同步影响面比单节点重启稍大。具体操作时先在worker节点上确认新证书已经分发到位ls -l /etc/kubernetes/ssl/kubelet.pem /etc/kubernetes/ssl/kubelet-key.pem /etc/kubernetes/kubelet.kubeconfig然后重启kubeletsystemctl restart kubelet回到master节点用kubectl get nodes看该节点是否重新变为Ready。这里有个经验如果节点没有恢复正常先看kubelet日志里有没有证书报错journalctl -u kubelet -n 100 --no-pager | grep -i cert不要反复重启服务而不看日志纯属浪费时间。kubelet正常后再替换kube-proxy的kubeconfig并重启systemctl restart kube-proxy4.3 重建admin kubeconfigadmin的kubeconfig一般在~/.kube/config或/etc/kubernetes/admin.conf里。管理员自己用的这份证书过期后最明显的症状是执行kubectl get nodes直接报Unable to connect to the server: x509: certificate has expired or is not yet valid。其实服务端本身可能还活着只是客户端证书失效了。重建admin kubeconfig和上面controller-manager的方式一致只是用户名和证书不同。通常admin的CN是adminO字段是system:masters这样才能拥有集群管理员权限。kubectl config set-cluster kubernetes \ --serverhttps://192.168.1.10:6443 \ --certificate-authority/etc/kubernetes/ssl/ca.pem \ --embed-certstrue \ --kubeconfig/root/.kube/config kubectl config set-credentials admin \ --client-certificate/etc/kubernetes/ssl/admin.pem \ --client-key/etc/kubernetes/ssl/admin-key.pem \ --embed-certstrue \ --kubeconfig/root/.kube/config kubectl config set-context kubernetes \ --clusterkubernetes \ --useradmin \ --kubeconfig/root/.kube/config kubectl config use-context kubernetes --kubeconfig/root/.kube/config重建后直接验证kubectl get nodes如果这一步能正常列出节点说明整个管理链路的证书已经打通。4.4 更新后的验证动作清单证书更新完不要只看一眼kubectl get nodes就收工。我习惯按下面这份清单逐项验证kubectl get nodes所有节点是否Ready有没有NotReady的kubectl get pod -A核心组件是否都Running特别是coredns、flannel/calico这类网络插件kubectl logs -n kube-system 某个apiserver pod因为是二进制部署没有apiserver pod而是systemd服务这一步改成看journalctlkubectl exec -it 某个业务pod -- sh验证kubelet服务端证书是否正常能否建立到kubelet的exec连接访问一个NodePort或Ingress暴露的服务验证网络链路是否完整在master节点执行curl --cacert /etc/kubernetes/ssl/ca.pem https://127.0.0.1:6443/healthz确认apiserver自身的健康检查通过每项验证都不费什么时间但能帮你提前暴露隐藏问题。尤其是kubectl exec很多证书问题在get nodes阶段看不出来一执行exec就报证书错误。5. 我实际踩过的坑顺序、SAN、VIP、备份一个都不能少5.1 先etcd后apiserver还是反过来顺序错了连坐一片第一次给集群更新证书时我先动了apiserver再动etcd。结果apiserver重启后直接起不来日志里全是etcdserver: request timed out。原因很简单apiserver启动时要连etcd做一系列初始化和数据读取而etcd那边还是旧证书在服务新证书和旧etcd证书不在同一个信任链上apiserver连不上etcd整个API服务就瘫了。所以顺序应该严格遵循依赖关系先etcd后apiserver再controller-manager和scheduler。etcd是整个集群的数据底座它先恢复正常apiserver启动后才能顺利拉取数据。etcd如果还是旧证书apiserver换成新证书后握手必然失败。5.2 SAN漏掉VIP后的症状有一次更新apiserver证书时我只把三台master节点的IP填进了SAN把keepalived的VIP给忘了。结果证书替换完kubectl get nodes能看到节点列表但每过一会儿节点就变成NotReady然后又恢复非常诡异。排查到最后发现kubelet的--server参数指向的是https://192.168.1.100:6443也就是VIP而新证书SAN里没有这个VIP。kubelet每次向VIP发起TLS握手时都因为SAN不匹配而失败心跳断断续续。解决问题也很简单把VIP加进SAN重新签发apiserver证书再替换一次。这里想借这个例子提醒大家证书更新时SAN列表一定要拿当初部署时的那份CSR模板做底稿不要凭印象重新抄写漏掉一个地址可能要用一整晚来排查。5.3 忘了备份回滚无从谈起证书更新的过程虽然理论上可逆但如果你没有备份出了问题就只能从头签一套新证书时间成本翻倍。现在我的习惯是任何证书替换前先把整个证书目录打包带上时间戳tar zcf /data/backup/kubernetes-ssl-$(date %F).tar.gz /etc/kubernetes/ssl /etc/etcd/ssl /etc/kubernetes/*.kubeconfig /root/.kube/config这个备份包放到集群外部的存储或者另一台机器上别放在本地硬盘万一误删了还有救。回滚时不需要全部恢复哪一步出问题就恢复哪些文件然后重启对应的服务。5.4 双网卡/多IP环境的连锁反应还有一次踩坑是在一张“双网卡”的机器上。那台master节点同时有内网IP和管理网IP部署时apiserver证书里只写了内网IP。后来管理网段重新规划管理网IP变了但证书没变导致外部通过新管理IP访问apiserver时始终报证书错误。如果你所在的网络环境有多块网卡或者未来有可能调整IP建议在签发apiserver证书时把所有网卡的IP、主机名都写进SAN。虽然看着不太“干净”但能避免日后网络调整带来的连锁故障。5.5 更新kubelet证书导致正在运行的Pod中断kubelet重启会短暂中断该节点上Pod的心跳上报但不会主动杀掉容器。真正受影响的是正在进行的kubectl exec、kubectl logs -f这类长连接会在kubelet重启的瞬间断开。如果你在业务高峰期滚动更新worker节点用户侧可能会有“页面卡顿一下”的体感。所以worker节点证书更新尽量安排在低峰期并且采用逐台滚动方式。一次只更新一台等节点重新Ready并运行一段时间再操作下一台而不是所有worker并行重启。6. 把一次性操作变成例行巡检脚本化与Checklist6.1 一个简单的证书到期巡检脚本与其等到证书真的过期那天被报警吵醒不如写个脚本定期巡检。下面这个脚本可以放到cron里每月执行一次输出所有证书的到期时间#!/bin/bash echo /etc/kubernetes/ssl 下的证书 for cert in /etc/kubernetes/ssl/*.pem; do [ -f $cert ] || continue echo $cert : openssl x509 -in $cert -noout -dates -subject 2/dev/null done echo echo /etc/etcd/ssl 下的证书 for cert in /etc/etcd/ssl/*.pem; do [ -f $cert ] || continue echo $cert : openssl x509 -in $cert -noout -dates -subject 2/dev/null done如果想检查kubeconfig里嵌入的证书可以把上面的逻辑扩展到遍历所有.kubeconfig文件提取base64字段再调用openssl。更进一步可以在脚本里用date -d计算到期天数当天数小于30时输出告警小于7天时通知值班群。具体告警方式可以根据团队情况选择邮件、钉钉、企业微信都行关键是别让它闲置。6.2 规划更合理的有效期长期CA加短期组件经过几次手动续期后我越来越觉得证书有效期规划很重要。现在的做法是CA一次性签10年组件证书签2到3年。CA有效期长意味着未来很长一段时间内不需要动集群的信任根每次更新只要用现有CA重新签发组件证书即可操作面小、风险低。组件证书的有效期也不用太长。太短会增加更新频率太长又容易让人放松警惕。两年一个周期每年巡检时记录一次到期情况时间上比较从容。需要调整有效期时改ca-config.json里的expiry字段就行但要注意新签发的证书有效期不能超过CA的剩余有效期。6.3 每次证书更新的Checklist更新的过程步骤多容易漏所以我一般会维护一份Checklist每次按表操作阶段操作是否完成前置备份所有证书目录和kubeconfig前置确认CA未过期确认组件证书列表签发用现有CA重新签发各组件证书检查SAN和有效期etcd逐节点替换etcd证书并重启确认集群healthapiserver替换证书并重启curl healthz验证控制面重建controller-manager/scheduler的kubeconfig并重启worker逐节点更新kubelet、kube-proxy确认节点Ready管理员重建admin kubeconfigkubectl get nodes验证检查核心Pod、exec、NodePort、网络插件收尾再次备份更新巡检记录我个人实际操作的体会是证书更新这件事本身不算复杂真正考验人的是操作顺序和细节。按上面的顺序走一遍顺利的话整个集群的证书更新在半小时到一个小时内就能完成。最忌一上来就蛮干把etcd、apiserver的证书全换完再统一重启那样一旦出问题排查范围立刻失控。
返回列表