最近又碰到一起真实的现场故障:一套用 Helm 安装的 Rancher 平台,访问域名一直正常,某天用户突然反馈浏览器打开就跳“不安全”,控制台里配的证书过期了。我去检查的时候发现,这台环境的 Ingress 证书更新流程走得很乱,有人在页面里换了 CA 签发的证书,结果底层 Ingress 指向的 Secret 还是老的,页面依然拿旧证书建连。折腾了半小时才把链路理清楚。
这篇文章就把这个场景完整梳理一遍:如何用 Helm 安装 Rancher,以及在此基础上安全更新 Ingress 证书。整个过程不复杂,但里面有几个“坑位”很容易踩,尤其是刚接触 Kubernetes 的同学,常常把更新 Ingress 证书理解成“申请一个新证书导入页面就行”,实际上 Ingress Controller 读的是 Secret,页面展示和链路建立是两回事。这篇文章把后面这层讲透。
1. 安装前的思路:先搞清证书在链路里的位置
1.1 为什么用 Helm 装 Rancher 而不是直接写 YAML
第一次给客户交付 Rancher 时,我也想过“直接拿官方文档里的 YAML 套一套不就行了”。后来发现这条路非常折腾。Rancher 本身由大量组件组成,除了最核心的 rancher Deployment,还有 cattle-cluster-agent、cattle-node-agent、Webhook、各种 CRD、ServiceAccount 和 ClusterRole。手写 YAML 的话,哪怕只是少一个 RBAC 对象,Rancher 启动后都会处于异常状态,排查起来特别费劲。
Helm 的价值在于把这一整套对象打包成一个 Release,用一条命令就能完成依赖安装、配置注入、升级回滚。最直接的收益是后续维护:当你需要调整 Rancher 参数,比如更换入口域名、修改 TLS 来源、调整资源限制时,不需要一个个对象去 patch,而是把 values 改好,重新跑一次 helm upgrade,Helm 会自动计算出需要新增和更新的资源。这个特性在前面的证书更新场景里也有价值,因为很多情况下更新 Ingress 证书不是单独改 Ingress 对象,而是需要整套配置保持一致。
不过 Helm 也不是没有“副作用”。它生成的资源名字带 release 名字,默认情况下如果按照官方文档安装,release 名为 rancher,命名空间为 cattle-system。一旦后续你手动改了某个资源,比如直接用 kubectl edit 修改了 Ingress,再执行 helm upgrade,就有可能出现配置覆盖或资源冲突。我建议从一开始就把“Helm 管理的资源”和“临时手工调整的资源”分清楚,能进 values 的就别在 YAML 里裸改。
1.2 证书更新的常见场景
Rancher 的证书更新一般来自三种需求:
- 平台安装初期使用的是自签名证书,浏览器一直报警,现在拿到了企业权威 CA 签发的正式证书,需要替换掉旧的临时证书。
- 已有证书即将过期,需要在证书到期前完成轮换,避免用户访问 Rancher 时出现证书错误。
- 更换域名或拆分环境,原先的证书域名范围不匹配,需要重新签一份包含新 SAN 的证书并绑定到 Ingress。
我遇到最多的是第一种和第二种。这里有个容易被忽略的前提:Rancher 并不强制要求证书必须由权威 CA 签发,它只要 Ingress 上配置的证书和域名匹配即可。所谓“更新 Ingress 证书”,本质上就是更新 Ingress 对象引用的 Secret 内容,或者让 Secret 名称指向一个全新的 Secret。
1.3 本文的实验环境
为了让你能直接照着操作,我先把本篇文章的实验环境说清楚。我用的是 Kubernetes 1.27 版本,Ingress Controller 使用常见的 Nginx Ingress Controller,Helm 版本为 3.13 左右。Rancher 版本选择当前稳定的主线版本。域名假设为 rancher.example.com。
需要注意的是,Rancher 对不同 Kubernetes 版本有兼容性要求,这个兼容性要求会直接影响安装是否正常。我在后面的部分会专门提到如何在安装前检查版本组合,别一股脑跑到最新版本。
2. Helm 安装 Rancher 的完整过程
2.1 准备工作:命名空间、仓库、cert-manager
安装 Rancher 前,有两件基础准备:一是创建独立的命名空间,二是安装 cert-manager。Rancher 默认的命名空间是 cattle-system,这个命名空间最好单独提前建好,不要和其他业务应用混在一起。
kubectl create namespace cattle-systemRancher 本身会用到 cert-manager 来管理自身的证书。初次安装时,如果你没有显式指定自己的证书,Rancher 会通过 cert-manager 生成一套自签 CA,再基于这套 CA 给 Ingress 签发证书。cert-manager 在 Rancher 的安装流程里几乎是标配,直接装好能避免后面很多证书解析问题。
添加 Helm 仓库:
helm repo add rancher-latest https://charts.rancher.io helm repo update还需要安装 cert-manager 的 CRD。cert-manager 的安装方式有两种,一种是直接 kubectl apply CRD 和组件,另一种也是通过 Helm。我习惯用 Helm 来装,因为后续升级 cert-manager 时更方便。执行命令如下:
helm repo add jetstack https://charts.jetstack.io helm repo update helm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --set installCRDs=true这里加了--set installCRDs=true,表示从 Chart 包里直接安装 CRD。如果没有这个参数,cert-manager 的 CRD 缺失时,Rancher 安装过程会反复报错。
安装完成后检查 cert-manager Pod 是否正常:
kubectl -n cert-manager get pod2.2 配置 ClusterIssuer 和 TLS 颁发策略
Rancher 安装时可以指定 TLS 的三种来源:rancher、secret、letsEncrypt。如果选择 rancher,Rancher 会自动创建一套自签 CA 来签发 Ingress 证书;如果选择 secret,则要求你事先准备好一个 TLS Secret;如果选择 letsEncrypt,Rancher 会尝试通过 cert-manager 从公共 CA 获取证书。
这个选择直接关系到你后面是否需要更新证书。生产环境我建议不要把敏感业务长期挂在自签证书上,最好在安装时就明确证书来源。如果你是首次部署,不确定哪张证书,可以先用 rancher 方式把平台跑起来,后面再按本文的步骤替换成正式证书。
helm install rancher rancher-latest/rancher \ --namespace cattle-system \ --set hostname=rancher.example.com \ --set bootstrapPassword=admin123456 \ --set ingress.tls.source=rancherbootstrapPassword 这里的值是你在 Rancher 初始化时需要输入的密码,建议强度高一点。日常维护中不要在命令行里直接明文写,可以用环境变量代替:
export RANCHER_BOOTSTRAP_PASSWORD='你的密码'如果公司内部有自己的 CA,也可以提前创建一个 ClusterIssuer,让 Rancher 的证书统一由这个 Issuer 签发。创建文件 cluster-issuer.yaml:
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: company-ca-issuer spec: ca: secretName: company-ca-secret这里 company-ca-secret 需要提前放到 cert-manager 所在命名空间或可以访问的 Secret 中。使用 ClusterIssuer 的好处是方便统一管理证书的签发和续期,后续哪怕域名变了,也只需要在 Certificate 对象上调整。
2.3 运行 Helm 命令安装 Rancher
执行 helm install 之前,先看一眼 Release 是否已经存在,避免重复安装:
helm list -n cattle-system如果第一次安装失败,建议先 helm uninstall 清理干净再重装,不要直接再跑一次 install,因为 Rancher 的部分资源是全局的,重复安装会产生冲突。
安装命令:
helm install rancher rancher-latest/rancher \ --namespace cattle-system \ --set hostname=rancher.example.com \ --set bootstrapPassword=${RANCHER_BOOTSTRAP_PASSWORD} \ --set ingress.tls.source=rancher \ --set replicas=3如果集群资源有限,可以把 replicas 调低,但 Rancher 高可用模式至少需要 3 个副本。单副本也能跑,只在测试环境建议使用,生产环境尽量保证 3 副本。
安装过程大约 2 到 5 分钟。期间需要关注 Pod 状态:
kubectl -n cattle-system get pod -w如果 Pod 一直处于 CrashLoopBackOff 或 ImagePullBackOff,优先查看对应 Pod 的日志。常见的原因是 cert-manager 没有正常安装,或者 helm install 时指定的 hostname 无法解析。
2.4 安装后的健康检查
安装完成之后,不要急着登录页面,先检查几个关键资源:
kubectl -n cattle-system get ingress kubectl -n cattle-system get secret kubectl -n cattle-system get certificate正常情况下,你应该看到一个名为 rancher 的 Ingress,并且 secret 列表中存在一个和 Ingress 关联的 TLS Secret。还需要检查 Rancher 的 Deployment 是否 Ready:
kubectl -n cattle-system rollout status deploy/rancher页面可以通过 https://rancher.example.com 访问。如果打开后提示证书不可信,这是正常的——因为你安装时选择了 rancher 作为 TLS 来源,它生成的是自签证书。接下来就是我重点要讲的更新过程。
3. 更新 Ingress 证书的核心原理
3.1 Ingress 证书的三要素:Secret、Ingress、Controller
很多人会把“配置证书”简单理解成“把证书放进服务器里”,但在 Kubernetes 里不是这样。Ingress Controller 在启动监听时,会把 Ingress 对象中的 host 和 TLS 配置读入内存。当浏览器请求到达 Ingress Controller 时,Controller 会根据域名找到对应的 TLS 配置,再从对应 Secret 中读取证书内容并返回给客户端。
所以一个 Ingress 证书要真正生效,必须同时满足三个条件:
- Secret 里存在可用的 tls.crt 和 tls.key。
- Ingress 对象的 spec.tls.hosts 和 secretName 指向这个 Secret。
- Ingress Controller 已经感知到以上变化,并完成内存配置重新加载。
这三个条件只要有一个不一致,浏览器拿到的响应就不是最新的证书。我排查过很多“证书更新失败”的案例,最后发现证书本身没有问题,Secret 的内容也换了,就是 Ingress 对象里 secretName 没改,或者指向的是同一个 Secret 但 Controller 没有重载配置。这三者务必一起检查。
3.2 确定你要更新的证书属于哪一跳
Rancher 域名访问链路中其实有两层证书:第一层是浏览器到 Ingress Controller 之间的证书,也就是用户直接看到的证书;第二层是 Ingress Controller 到后端 Rancher Pod 之间的证书,Rancher 默认监听 443 端口,Ingress Controller 转发时会通过 HTTPS 协议访问后端。
平时说的“更新 Rancher Ingress 证书”,通常指第一层,也就是用户浏览器看到的证书。但如果在 Rancher 页面内部也出现证书错误,那就需要检查第二层。这里有一个很实用的方法:在 Ingress 对象中,如果你的 service 端口是 443,而且 Ingress Controller 使用了 backend-protocol HTTPS 注解,Controller 在访问后端时也需要信任后端证书。这种情况下你需要在 Rancher 的 Secret 中额外配置为“自签证书的 CA 信息”,或者在 Rancher 页面里导入对应的 CA 证书。
所以更新证书不一定只改一个 Secret,有时候需要同时改两个地方。这个点很容易被忽略。
3.3 新旧证书怎么准备和校验
准备新证书时,建议先按照传统方式,把证书链和私钥准备好。如果你拿到的是 PEM 格式的证书链,确保所有中间证书都已经拼接在一起:
cat server.crt intermediate.crt root.crt > fullchain.crt私钥不能有密码。如果私钥文件有 passphrase,需要先转换成无加密私钥:
openssl rsa -in encrypted.key -out decrypted.key然后将证书和私钥分别保存为 tls.crt 和 tls.key。在生成 Secret 之前,先本地校验一下证书和私钥是否匹配:
openssl x509 -in fullchain.crt -noout -pubkey | openssl md5 openssl rsa -in decrypted.key -noout -pubkey | openssl md5两 N个输出的 MD5 值一致,说明证书和私钥是配套的。这一步很重要,因为很多更新失败不是流程问题,而是前端输错了文件,导致 Secret 中的密钥对不匹配。
4. 证书更新实操:从生成到生效
4.1 准备并导入证书 Secret
查看当前 Ingress 使用的 Secret 名称:
kubectl -n cattle-system get ingress rancher -o jsonpath='{.spec.tls[0].secretName}'这里会输出一个 Secret 名称,例如 tls-rancher-ingress。如果生产环境要求尽量不影响现有服务,我常用“新建 Secret 再切换”的方式,先创建一个新 Secret,确认内容没问题后再把 Ingress 切过去,避免在旧 Secret 上直接覆盖导致中间态证书不可用。
创建新的 Secret:
kubectl -n cattle-system create secret tls tls-rancher-ingress-new \ --cert=fullchain.crt \ --key=decrypted.key \ --dry-run=client -o yaml | kubectl apply -f -使用--dry-run=client -o yaml是一种常见做法,先本地生成 YAML 再 apply,这样即使目标环境没有安装 kubectl 插件,也不会因为 create 命令直接报错而半途而废。
创建完成后查看 Secret:
kubectl -n cattle-system get secret tls-rancher-ingress-new -o yaml确认 data.tls.crt 和 data.tls.key 都存在,且 base64 内容解码后能正常解析证书。
如果你不想新建 Secret,而是覆盖旧 Secret,可以直接使用:
kubectl -n cattle-system create secret tls tls-rancher-ingress \ --cert=fullchain.crt \ --key=decrypted.key \ --dry-run=client -o yaml \ | kubectl apply -f -更新式的方式也能完成替换,但通常我还是认为新建 Secret 的方式更安全一些。
4.2 修改 Ingress 对象指向新 Secret
接下来要把 Ingress 的 secretName 改成新 Secret 名称。如果 Ingress 对象只有一个 TLS 块,我习惯用 kubectl patch 快速修改:
kubectl -n cattle-system patch ingress rancher \ --type='json' \ -p='[{"op":"replace","path":"/spec/tls/0/secretName","value":"tls-rancher-ingress-new"}]'如果你好较完整地看一遍配置,也可以直接 edit:
kubectl -n cattle-system edit ingress rancher找到 spec.tls[0].secretName,把值改成新 Secret 名称后保存。
修改完成后,确认 Ingress 已经更新:
kubectl -n cattle-system get ingress rancher -o jsonpath='{.spec.tls[0]}'这里有一点需要注意:如果你后续还要执行 helm upgrade,Helm 可能会把 Ingress 重新拉回到安装时指定的 Secret 名称。要让这种变更持久化,最稳妥的办法是把 Ingress 的配置也写进 Rancher 的 values 文件里,或者通过 helm upgrade 一起应用。
4.3 触发 Controller 重新加载
在大多数 Nginx Ingress Controller 版本中,修改 Secret 内容后 Controller 会自动检测并重载配置,但有时候因为集群规模大、资源事件延迟等原因,重载并不及时。此时最直接的解决办法是滚动重启 Ingress Controller 的 Pod:
kubectl -n ingress-nginx rollout restart deployment/ingress-nginx-controller如果你用的不是 ingress-nginx 命名空间,换成实际命名空间即可。这条命令会让 Controller 重新读取所有 Ingress 和 Secret 数据,避免缓存导致的旧证书残留。
有些教程会建议删除 Ingress Controller Pod,让它强制重建,但滚动重启更安全,不会造成太长的中断时间。
重启完成后检查 Controller Pod 是否 Ready:
kubectl -n ingress-nginx get pod -l app.kubernetes.io/name=ingress-nginx4.4 验证与常见回滚
验证证书是否生效,最直接的方法是使用 curl 或 openssl。
先看证书签发对象:
curl -sv https://rancher.example.com -k 2>&1 | grep subject如果看到 subject 内容是新证书的域名,说明链路基本没问题。再用 openssl 查看证书有效期:
echo | openssl s_client -connect rancher.example.com:443 -servername rancher.example.com 2>/dev/null | openssl x509 -noout -dates -subject注意 curl 里的-k是不校验证书,这里主要用于排查。如果返回的证书还是旧的,回到前面三个要素检查:Secret 内容是否真替换、Ingress secretName 是否指向正确、Controller 是否已经重载。
回滚场景也经常出现。假如新证书导入后,发现证书链不完整,或者私钥匹配不上,你需要快速切回旧证书。如果之前是新建 Secret 并切换 secretName,回滚就是把 secretName 改成旧名称:
kubectl -n cattle-system patch ingress rancher \ --type='json' \ -p='[{"op":"replace","path":"/spec/tls/0/secretName","value":"tls-rancher-ingress-old"}]'这个操作可以在几秒内完成,比直接覆盖旧 Secret 再等 Controller 重载要快得多,也是我比较推荐“新建 Secret”这种更新方式的原因。
5. 更新 Ingress 证书时的典型坑位
5.1 更新后浏览器仍显示旧证书
这个问题最常见,原因不外乎三种:浏览器缓存了证书或 HSTS 信息;Ingress Controller 没有重新加载;Ingress 引用的 Secret 名称没改。
第一种容易判断,用 Chrome 无痕模式重新访问,或者 curl 验证证书内容。如果 curl 已经显示新证书,浏览器还显示旧证书,清理浏览器缓存或关闭 HSTS。
第二种需要确认 Controller Pod 是否滚动重启过。有些 Controller 短期内不会主动感知 Secret 变化,需要手动重启。
第三种是前面反复强调的,Ingress 对象里的 secretName 和实际要用的 Secret 不一致。检查方式很简单:
kubectl -n cattle-system get ingress rancher -o yaml | grep -A2 tls5.2 Rancher 后台报 invalid certificate
情况通常出现在使用自签 CA 证书作为 Ingress 证书时,Rancher 页面内某些组件需要通过 HTTPS 访问自身 API,当后端证书不是权威 CA 签发时,Rancher 就会在日志或页面上报出证书无效。
解决办法是把对应 CA 内容配置到 Rancher 的附加 CA Secret 中。Rancher 默认支持通过一个名为cattle-ca-additional的 Secret 加载额外的 CA 证书。你需要在 cattle-system 命名空间中创建这个 Secret,数据字段为ca-additional.pem。
kubectl -n cattle-system create secret generic cattle-ca-additional \ --from-file=ca-additional.pem=company-ca.pem创建完成后滚动重启 Rancher Deployment:
kubectl -n cattle-system rollout restart deploy/rancher这种方式适用于使用内部 CA 的场景,也在证书更新后经常被用到。
5.3 cert-manager 自动续期和手工证书冲突
如果你安装 Rancher 时选择的是 letsEncrypt 或使用了 cert-manager 的 ClusterIssuer,Rancher 的 Ingress 证书由 cert-manager 自动管理。此时你手动新建 Secret 并切换 secretName,可能过一段时间又被 cert-manager 覆盖回去。
原因在于 Credentials 对象。Rancher 或 cert-manager 根据 Certificate 对象来生成并反射 Secret。即使你手动改了 Ingress secretName,对应的 Certificate 对象还在公共名字空间里,到期后 cert-manager 会自动重新生成 Server,并覆盖当前 Secret。
遇到这种情况,建议先查看 Certificate 对象:
kubectl -n cattle-system get certificate如果发现这个 Ingress 确实被 cert-manager 管理,不要手工替换 Secret,而应该修改 Certificate 对象中的参数,比如更新签发域名、轮换私钥,或者把它关联到新的 ClusterIssuer。
另一种稳妥方案是:不需要 cert-manager 继续接管,删除相关 Certificate 对象,再把 Ingress 切换成手工管理的 Secret。这样可以避免之后每次到期后,发生类似“我明明更新了证书,没过多久又变回原样”的诡异问题。
5.4 升级 Rancher 时证书被重置
Rancher 在升级过程中,helm upgrade 会按照 values 里的 ingress.tls.source 重新计算相关配置。如果你在安装时选择了 rancher 来源,升级后各种 Secret 和 Ingress 配置可能会被 Helm 重新覆盖,甚至替换成新生成的证书。
避免这个问题的正确做法,是把“使用自定义证书”这个意图通过 values 告诉 Helm。安装或升级时指定:
--set ingress.tls.source=secret \ --set privateCA=true同时确保你已经提前创建了 Rancher 期望的 Secret,并且 Ingress 里的 secretName 能对应上。Rancher 官方安装文档中,如果使用外部证书,一般要求 Secret 名称为tls-rancher-ingress,这个名称在某些版本里是固定的。因此我强烈建议你升级前先备份当前 Ingress 和 Secret 的 YAML:
kubectl -n cattle-system get ingress rancher -o yaml > ingress-backup.yaml kubectl -n cattle-system get secret tls-rancher-ingress -o yaml > secret-backup.yaml备份后在升级时对比差异,发现问题可以快速回滚。
6. 实战排障速查与经验建议
6.1 排障命令速查表
我把平时排查 Ingress 证书问题时最常用的命令整理如下,基本上按顺序跑一遍就能定位问题:
| 故障现象 | 优先检查点 | 对应命令 |
|---|---|---|
| 浏览器打开示不安全 | 本地访问证书链是否完整 | curl -sv https://域名 |
| 浏览器显示旧证书 | Ingress 是否指向新 Secret | kubectl get ingress -n cattle-system -o yaml |
| 证书更新后马上失效 | 是否被 cert-manager 管理 | kubectl get certificate -n cattle-system |
| Controller 服务卡旧配置 | Ingress Controller 是否完成重新加载 | kubectl -n ingress-nginx rollout restart deploy/ingress-nginx-controller |
| Rancher 后台报证书错误 | 附加 CA 是否配置 | kubectl -n cattle-system get secret cattle-ca-additional |
| 私钥和证书不匹配 | 本地校验密钥对 | openssl x509 -in tls.crt -noout -pubkey | openssl md5 |
| 升级后证书被重置 | Helm values 中 TLS 来源是否正确 | helm get values rancher -n cattle-system |
这些命令不必死记,保存下来按场景查就行。定位问题时,先从最底层的数据源头开始,也就是 Secret,然后看 Ingress,最后看 Controller,这个顺序基本不会漏。
6.2 我习惯保留的几个操作技巧
第一个技巧是,更新证书前先看当前 Ingress 里 secretName 指向的是什么,不要想当然认为叫 tls-rancher-ingress。有些环境安装时自定义过名称,直接创建一个固定名字的 Secret 并不生效。
第二个技巧是,对生产环境而言,建议给证书到期时间设置主动监控,在证书到期前提前 30 天用脚本或定时任务提醒。Rancher 本身会监控证书到期,但最好还是在基础设施层面也做一道监控,避免从浏览器报告才知道。
第三个技巧是,尽量保持 Ingress 配置声明化。如果你的 Rancher 是通过 Helm 管理的,把证书相关的 Secret 和 Ingress 配置交给同一套自动化流程,不要一边用 Helm 一边用临时命令改资源。否则下一次 helm upgrade 就会破坏手改的内容,这是非常多见的事故来源。
第四个技巧是,所有涉及证书的变更都使用新建 Secret 再切换的方式,不要直接覆盖存在的 Secret。覆盖操作一旦失败,影响面是整个域名访问;而切换 Secret 的方式能在几秒内完成回滚,减小事故窗口。
6.3 说句实在话
我最开始在给客户更新证书时也走过弯路,总觉得页面里有个证书管理入口,更新应该是在 UI 上完成。后来才明白,Rancher 页面上的证书配置只解决 Rancher 自己分区信任的问题,而真正决定浏览器拿哪张证书的,始终是 Ingress Controller 和它背后的 Secret。这个认知如果不转变,后续不管遇到什么 K8s 应用,证书更新都会出问题。
现在每次再接到这类需求,我的习惯动作已经很固定:先查 Ingress 的 secretName,再查 Secret 内容,确认证书和私钥匹配,最后重载 Controller。这套流程基本能解决九成以上的证书更新问题。剩下的一成,往往是环境的特殊配置,比如多 IngressClass、自建 Controller 或者集群里的多个网关实例,但只要核心链路是清楚的,顺着链路排查总能找到原因。
希望这份梳理能让你在处理“Helm 安装 Rancher 更新 Ingress 证书”时少走一些弯路。如果你刚好正在为证书过期而头疼,按文章里的步骤从 Secret 开始检查,大概率能一次搞定。