AKS中统一Helm Chart部署Cert-Manager与Nginx证书签发失败
本人接触Kubernetes约1个月,目前正在将业务从Azure App Service迁移至AKS,遇到Nginx Ingress Controller与cert-manager协同异常、HTTP-01挑战持续失败导致证书无法签发的问题,怀疑故障与DNS记录切换时机、组件安装方式有关。
部署架构分为两类Helm Chart:
- networking Chart:作为集群基础网络组件,仅需部署1个实例
- application Chart:对应业务应用,可部署多个实例分别承载staging、QA、生产等多环境
networking Chart配置
该Chart无自定义模板文件,相关配置如下:
Chart.yaml
apiVersion: v2 name: networking description: A Helm chart for Kubernetes type: application version: 0.0.1 appVersion: "1.1.0" icon: <<自有图标地址>> dependencies: - name: nginx-ingress version: 0.14.0 repository: https://helm.nginx.com/stable alias: nginx-ingress - name: cert-manager version: 1.8.2 repository: https://charts.jetstack.io
values.yaml
replicaCount: 1 cert-manager: installCRDs: true nginx-ingress: controller: service: annotations: "service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path": /healthz
application Chart配置
application Chart按部署环境配置对应命名空间的Issuer与Ingress资源,配置如下:
ingress.yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress {{ if eq .Values.environment "Release" }} namespace: release {{ else if eq .Values.environment "ReleaseQA" }} namespace: release-qa {{ else if eq .Values.environment "ReleaseProd" }} namespace: release-prod {{ else }} {{ required "value for .Values.environment is not as expected" .Values.environment }} {{ end }} annotations: nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/ssl-redirect: "false" # ingress.kubernetes.io/ssl-redirect: "false" # 已尝试过该配置项 cert-manager.io/issuer: letsencrypt-nginx # acme.cert-manager.io/http01-ingress-class: "nginx-cert-controller" # 已尝试过该配置项 spec: tls: {{ if eq .Values.environment "Release" }} - hosts: - core.staging.foo.com secretName: core-cert-nginx - hosts: - portal.staging.foo.com secretName: portal-cert-nginx - hosts: # 其余环境的TLS定义省略 # 后续为路由规则配置 ingressClassName: nginx rules: {{ if eq .Values.environment "Release" }} - host: core.staging.foo.com http: paths: - pathType: Prefix path: "/" backend: service: name: core-service port: number: 80 - host: portal.staging.foo.com http: paths: - pathType: Prefix path: "/" backend: service: name: portal-service port: number: 80
issuer.yaml
apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: letsencrypt-nginx {{ if eq .Values.environment "Release" }} namespace: release {{ else if eq .Values.environment "ReleaseQA" }} namespace: release-qa {{ else if eq .Values.environment "ReleaseProd" }} namespace: release-prod {{ else }} {{ required "value for .Values.environment is not as expected" .Values.environment }} {{ end }} spec: acme: email: <<有效ACME注册邮箱>> server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-nginx-private-key solvers: # 使用HTTP-01挑战提供者 - http01: ingress: class: nginx
当前集群内所有内部服务、应用Pod均已创建且运行健康,故障表现如下:
- HTTP-01挑战持续失败,通过kubectl describe查看Challenge资源,发现cert-manager创建的挑战端点无法访问,切换不同配置时会返回302、404、502状态码,或请求被直接路由到业务应用
- 存在偶发现象:如果首次挑战未触发失败,则可正常完成签发,后续也可通过cert-manager kubectl插件正常续期
- 操作背景:使用外部域名服务商,为尽可能降低流量切换的停机时间,在域名解析记录切换到AKS负载均衡新IP、DNS传播未完全完成时就会触发证书签发,此阶段挑战可能会多次失败
- 平台:Azure AKS
- Kubernetes版本:1.23.5
- nginx-ingress Chart版本:0.14.0
- cert-manager Chart版本:1.8.2
- 业务负载:2个静态React网站、4个.NET Core后端API及其他类型服务
1. 解决rewrite-target全局注解导致的路由错误
你在业务Ingress上全局配置了nginx.ingress.kubernetes.io/rewrite-target: /,cert-manager生成的临时挑战Ingress和业务Ingress共享同域名路由规则时,/.well-known/acme-challenge/开头的挑战请求会被直接重写到业务服务根路径,打到React或者.NET服务上,自然返回404、业务首页响应等错误,这是请求被路由到业务应用的核心原因。
修复方式:修改Issuer的solver配置,给cert-manager自动生成的临时挑战Ingress单独注入注解,覆盖rewrite规则:
solvers: - http01: ingress: class: nginx annotations: nginx.ingress.kubernetes.io/rewrite-target: "/.well-known/acme-challenge/" nginx.ingress.kubernetes.io/ssl-redirect: "false"
更彻底的修复是把业务侧的rewrite-target从Ingress全局注解移到具体路径的配置项里,不要对所有路径生效,仅对需要重写的业务路径配置规则。
2. 调整DNS切换与证书签发的顺序
你提到的“首次挑战成功后续续期正常”现象,完全是DNS切换时机不对导致的:
- HTTP-01挑战由Let's Encrypt从全球多个边缘节点发起请求,你在DNS记录刚修改、全球传播未完成时触发签发,部分区域的请求会解析到旧的Azure App Service地址,旧地址没有对应的挑战响应路径,必然返回失败
- cert-manager默认的重试逻辑在连续多次失败后会拉长重试间隔,很容易卡在持续失败的状态,而首次签发成功后,后续续期时证书对应的Secret已经存在,Ingress路由规则长期生效,不会再受DNS切换过程的影响
正确操作流程: - 提前将AKS负载均衡IP配置到临时测试域名,先完成所有证书的首次签发,确认证书Secret正常生成
- 正式切换业务域名前,把域名TTL调到60秒,等原有TTL过期后再把记录指向AKS负载均衡IP
- 切换后等待10-15分钟全球DNS传播完成,再做业务可用性校验,不要在传播过程中触发首次签发
3. 校验Ingress类与LB健康状态
- 执行
kubectl get ingressclass确认集群内只有一个名为nginx的IngressClass对应你部署的Nginx Ingress Controller,避免其他Ingress控制器(比如AKS自带的应用网关Ingress)抢占路由 - 在Azure门户查看负载均衡的后端池健康状态,确认你配置的
/healthz探针可以正常访问Nginx Ingress的80端口,后端实例不健康时LB会直接返回502,请求到不了Ingress控制器 - 检查Nginx Ingress Controller的全局配置,确认没有配置全局强制HTTPS跳转规则拦截80端口的挑战请求,如果开启了全局HTTPS跳转,要把
/.well-known/acme-challenge/路径加入跳转白名单
内容的提问来源于stack exchange,提问作者Márton Péntek

