
1. 项目概述为什么在 Kubernetes 里搞 TLS 不能只靠“配个证书”就完事你是不是也经历过——辛辛苦苦把服务部署进 Kubernetes用kubectl apply -f一气呵成前端访问却卡在NET::ERR_CERT_INVALID浏览器地址栏赫然挂着一个红色叉号点开一看“此连接不是私密连接”甚至直接拒绝加载。更糟的是有些内部服务调用比如微服务间 gRPC、Prometheus 抓取指标、GitLab CI 连接 Harbor突然报错x509: certificate signed by unknown authority或failed to create SSL/TLS secure channel。这时候翻日志满屏都是tls handshake failed、certificate verify failed、internal error status 10013……这些错误看似零散但背后指向同一个根因TLS 不是“加个证书就安全了”而是一整套需要闭环管理的基础设施能力。我从 2018 年开始在生产环境跑 Kubernetes最早那会儿真就是手动 openssl 生成自签名证书base64 编码塞进 Secret再挂到 Ingress 上。结果呢证书过期前一周没人发现凌晨三点告警炸群换证书要改 YAML、删 Secret、滚动重启一次操作半小时测试环境和生产环境证书混用导致某次灰度发布后所有 iOS 客户端集体失联——因为苹果 ATS 强制校验证书链完整性而我们用的中间 CA 没配全。后来我们试过脚本自动续期但脚本跑在跳板机上和集群网络隔离每次都要人工同步也试过用 Helm chart 封装 cert-manager结果版本不兼容CertificateRequest卡在Pending状态三天没人能看懂状态机流转逻辑。真正让这件事“稳下来”的转折点是把 TLS 当作 Kubernetes 原生资源来对待——就像 Pod、Service、ConfigMap 那样声明式定义、自动生命周期管理、可观测可审计。而cert-manager Traefik Let’s Encrypt 的组合正是目前最成熟、最符合云原生哲学的落地路径。它不是简单拼凑三个工具而是形成了一条自动化流水线Traefik 作为边缘网关暴露 HTTP/HTTPS 入口 → cert-manager 监听 Ingress 或 HTTP01 资源变更 → 自动向 Let’s Encrypt 发起 ACME 协议挑战 → 成功后将签发的证书写入 Kubernetes Secret → Traefik 实时热加载该 Secret 并启用 TLS。整个过程无需人工干预证书自动续期失败自动重试状态一目了然。这个方案特别适合三类人一是刚搭好 K8s 集群、正为“怎么让外部能安全访问我的服务”发愁的新手比如用 kubekey 在 Ubuntu 22.04 上装完集群下一步就卡在这儿二是正在做企业级项目交付的工程师需要满足等保、ISO27001 对 TLS 版本、密钥强度、证书有效期的硬性要求三是运维或 SRE每天被“证书过期告警”追着跑想彻底从“证书救火员”转型为“证书管道建设者”。它不解决“Kubernetes 是什么”这种基础问题但能让你在真实生产中把 TLS 这个高频痛点变成一个几乎可以遗忘的后台服务。提示本文所有操作均基于 Kubernetes v1.24、Traefik v2.10、cert-manager v1.13Ubuntu 22.04 和 Windows 环境下的差异点会单独标注。文中所有 YAML、命令、参数均经过实测验证非网上拼凑的“理论可行”方案。2. 整体架构设计与核心组件选型逻辑2.1 为什么是 cert-manager而不是自己写脚本或用其他 ACME 客户端很多人第一反应是“不就是调 Let’s Encrypt API 吗我用 acme.sh 写个 cron job 不就行了”——这想法很朴素但放到 Kubernetes 生态里会立刻撞上三堵墙。第一堵墙是资源抽象缺失。acme.sh 生成的证书是文件而 Kubernetes 里服务要消费证书必须是Secret类型资源。你得自己写脚本把 PEM 文件转成 base64再kubectl create secret tls还要处理 Secret 名称、命名空间、标签对齐。一旦服务跨命名空间比如 ingress-nginx 在kube-system你的应用在default脚本就得硬编码 namespace失去声明式优势。而 cert-manager 的核心价值在于它把“证书”本身定义成了 CRDCustomResourceDefinitionCertificate、Issuer、ClusterIssuer、CertificateRequest。你只需声明“我要一个域名app.example.com的证书由 Let’s Encrypt 生产”cert-manager 就会自动创建对应 Secret并确保其内容始终与证书状态一致。这就像你声明一个 DeploymentK8s 控制器自动保证 Pod 数量而不是你手动kubectl run几十个 Pod。第二堵墙是状态机与重试逻辑复杂。ACME 协议不是发个请求就拿证书。它包含账户注册 → 订单创建 → 挑战HTTP01/DNS01→ 验证 → 证书签发 → 续期触发通常在到期前30天。任何一个环节失败比如 DNS 解析延迟、HTTP 挑战端口被防火墙拦截、Let’s Encrypt 限流都需要指数退避重试、状态持久化、失败告警。acme.sh 虽有重试但它的状态存在本地文件K8s 集群节点故障或 Pod 重建后状态就丢了。cert-manager 把所有中间状态都存为 Kubernetes 资源比如Challenge对象控制器崩溃重启后能从 etcd 中恢复进度这是生产环境不可妥协的可靠性保障。第三堵墙是多环境与策略治理难。企业往往有 dev/staging/prod 多套环境每套环境证书策略不同dev 可用staging环境速率限制宽松不计入限额prod 必须用production某些敏感服务要求ECDSA P-384密钥而非默认 RSA有的需要通配符证书*.example.com。如果用脚本你得维护 N 个配置文件、N 个 cron job。cert-manager 用Issuer命名空间级和ClusterIssuer集群级统一管理颁发机构用Certificate的usages字段控制证书用途server auth,client auth用duration和renewBefore精确控制有效期和续期时机。一套 YAML通过kustomizepatch 或 Helm values 就能适配所有环境。注意不要在生产环境用letsencrypt-stagingIssuer 长期运行。Staging 环境证书不被浏览器信任仅用于功能验证。正式上线前务必切换到letsencrypt-prod并确认 Let’s Encrypt 生产环境速率限制每周 50 张新证书每域名每周 5 张是否满足业务需求。2.2 为什么选 Traefik 而非 Nginx Ingress 或 APISIXKubernetes Ingress 生态里Nginx Ingress Controller 是事实标准APISIX 因其动态路由和插件生态越来越火。但在这个 TLS 自动化场景下Traefik 有三个不可替代的优势。第一个优势是原生深度集成 cert-manager。Traefik 的 IngressRoute CRD替代原生 Ingress直接支持tls字段引用Secret且其控制器内置了对 cert-managerCertificate资源的 watch 机制。当你创建一个CertificateTraefik 不需要额外配置就能感知并自动绑定。而 Nginx Ingress 需要你在Ingress资源里显式指定spec.tls.secretName且 Nginx 控制器本身不监听Certificate事件——你得靠 cert-manager 的ingress-shim功能它会自动为你生成一个Ingress资源再由 Nginx 控制器去读。这多了一层间接出问题时排查链路更长Certificate→Ingress→Secret→Nginx。Traefik 的路径是Certificate→Secret→IngressRoute更扁平。第二个优势是HTTP01 挑战的零配置穿透能力。Let’s Encrypt 的 HTTP01 挑战要求当它向http://app.example.com/.well-known/acme-challenge/xxx发起 GET 请求时你的服务器必须返回特定 token。Traefik 默认就将所有/.well-known/acme-challenge/路径的请求自动路由到 cert-manager 的challengesService由 cert-manager 自动部署。你完全不用改任何路由规则、不用配额外的 Ingress。而 Nginx Ingress 要实现同样效果得手动写一个Ingress规则把/.well-known路径 proxy_pass 到 cert-manager 的 Service稍有不慎就会和主应用路由冲突。在 Windows 环境下IIS 或其他 Web 服务器若占用了 80 端口HTTP01 挑战会直接失败而 Traefik 的自动路由规避了这个问题。第三个优势是对现代 TLS 特性的开箱即用支持。比如 TLS 1.3 是当前最佳实践但 Nginx Ingress 默认编译可能不带 OpenSSL 1.1.1需自行编译镜像而 Traefik v2.9 默认启用 TLS 1.3且支持minVersion: VersionTLS12强制最低版本。再如 OCSP Stapling在线证书状态协议装订能避免客户端直连 Let’s Encrypt 查询证书吊销状态提升首屏加载速度和隐私性。Traefik 只需在TLSOptions中设置ocspStapling: truecert-manager 会自动提供 OCSP 响应数据。而 Nginx 需要手动配置ssl_stapling、ssl_trusted_certificate等多个指令且依赖上游 CA 提供 OCSP 响应器地址配置极易出错。实操心得Traefik 的entryPoints.websecure.http.tls.options是全局 TLS 策略入口。我建议在集群初始化时就创建一个tls-options强制minVersion: VersionTLS12禁用TLS_RSA_WITH_AES_128_CBC_SHA等已知弱密码套件对应 CVE-2016-2183并开启preferServerCipherSuites: true。这样所有启用 TLS 的路由都自动继承避免每个IngressRoute单独配置遗漏。2.3 为什么是 Let’s Encrypt而不是商业 CA 或自建 CALet’s Encrypt 是免费、自动化、开放的证书颁发机构由互联网安全研究小组ISRG运营。它的核心价值不是“免费”而是“标准化”和“可编程”。首先它解决了证书信任链的普适性问题。Windows、macOS、Linux、Android、iOS 所有主流操作系统和浏览器都将 Let’s Encrypt 的根证书ISRG Root X1预置在信任库中。这意味着你用它签发的证书用户打开网页不会看到任何警告。而自建 CA比如用 cfssl 或 Vault签发的证书必须手动将根证书导入每一台客户端设备的信任库这对面向公众的网站完全不可行。商业 CA如 DigiCert、Sectigo虽也受信任但价格昂贵单域名年费数百元且自动化接口如 ACME支持参差不齐很多仍需人工审核。其次它的 ACME 协议是行业事实标准。cert-manager、Traefik、Caddy、Nginx 等几乎所有现代反向代理和证书管理器都原生支持 ACME v2 协议。这意味着你今天用 Let’s Encrypt明天想切到 ZeroSSL另一家免费 ACME CA只需改Issuer的 URL 和 API Key其余 YAML 和流程完全不变。这种解耦是商业 CA 无法提供的——它们的 API 各自为政迁移成本极高。最后它倒逼你建立健康的证书生命周期管理习惯。Let’s Encrypt 证书有效期只有 90 天这是刻意设计防止长期失效证书堆积。这迫使你必须依赖自动化工具cert-manager来续期而不是像过去那样签发 2 年期证书然后遗忘。90 天内cert-manager 默认在到期前 30 天即 60 天有效期时发起续期给你充足的缓冲期排查问题。如果你的证书因 DNS 配置错误续期失败cert-manager 会在Certificate资源的status.conditions中清晰标记Ready: False和原因比等用户投诉“网站打不开”再救火强百倍。注意Let’s Encrypt 对新注册账户有严格的速率限制Rate Limits。首次部署务必先用staging环境测试全流程URL:https://acme-staging-v02.api.letsencrypt.org/directory确认无误后再切到productionURL:https://acme-v02.api.letsencrypt.org/directory。否则频繁失败的请求会触发error creating new order :: too many failed authorizations导致账户被临时封禁数小时。3. 核心细节解析与实操要点3.1 域名与 DNS 准备这是 HTTPS 能否成功的物理前提再完美的 Kubernetes 配置如果域名解析没到位HTTPS 也永远是空中楼阁。这不是 Kubernetes 的问题而是互联网基础设施的硬约束。我见过太多人卡在这一步YAML 写得飞起kubectl apply全绿但浏览器就是打不开最后发现是 DNS 解析压根没生效。第一步确认域名所有权与解析权限。你必须能控制该域名的 DNS 记录。如果是公司主域名example.com找 DNS 管理员要权限如果是个人域名如阿里云万网、腾讯云 DNSPod登录控制台即可。关键点在于Let’s Encrypt 的 HTTP01 挑战要求app.example.com的 A/AAAA 记录必须指向你 Kubernetes 集群的入口节点通常是 Traefik 所在节点的公网 IP。如果你用的是云厂商负载均衡如 AWS ALB、阿里云 SLB则需将域名 CNAME 到该负载均衡的域名如k8s-traefik-123456789.cn-north-1.elb.amazonaws.com。第二步检查 DNS 传播与 TTL。DNS 修改不是秒生效。全球 DNS 服务器缓存时间TTL通常为 300 秒5分钟到 86400 秒24小时。部署前务必用dig app.example.com shortLinux/macOS或nslookup app.example.comWindows在本地和集群节点上双重验证。特别注意Kubernetes 集群内部 DNSCoreDNS默认会缓存查询结果TTL 为 30 秒。如果刚改完 DNS 就急着部署cert-manager 可能拿到过期的 IP导致 HTTP01 挑战请求发到旧服务器而失败。解决方案在集群节点上执行sudo systemd-resolve --flush-cachesUbuntu 22.04或重启 CoreDNS Podkubectl delete pod -n kube-system -l k8s-appkube-dns。第三步处理常见 DNS 陷阱。子域名通配符陷阱你想申请*.app.example.com但 DNS 只配置了app.example.com的 A 记录。通配符证书要求_acme-challenge.app.example.com的 TXT 记录必须存在且app.example.com本身必须能被解析即有 A/AAAA 记录。所以除了主域名你还得为app.example.com创建一条 A 记录。CDN 或 WAF 干扰如果你的域名前面挂了 Cloudflare、阿里云 WAF它们会拦截 HTTP01 挑战请求因为路径/.well-known/acme-challenge/不在你正常业务流量里。必须将该路径的流量绕过 CDN/WAF直通 Kubernetes 集群。Cloudflare 上叫 “Page Rule”设置app.example.com/.well-known/acme-challenge/*→ “Bypass”。Windows 环境特殊性Windows Server 的 DNS 客户端缓存更顽固。除了ipconfig /flushdns还需检查HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters注册表项确保MaxCacheEntryTtlLimit和MaxNegativeCacheTtlLimit值合理建议设为 300。否则即使 DNS 已更新Windows 节点上的 cert-manager Pod 仍可能用缓存 IP 发起挑战。提示用curl -v http://app.example.com/.well-known/acme-challenge/test从集群外如你本地电脑和集群内kubectl run -it --rm debug --imagecurlimages/curl -- sh -c curl -v http://app.example.com/.well-known/acme-challenge/test分别测试。如果集群内能通、集群外不通说明 DNS 解析或防火墙问题如果都不通大概率是 Traefik 入口没暴露或路由没配。3.2 cert-manager 部署与 Issuer 配置从零构建证书工厂cert-manager 的部署必须严格遵循官方 Helm Chart 的推荐方式切勿用kubectl apply直接拉 GitHub YAML那是给快速体验用的生产环境必踩坑。我们以 Helm 3 为例全程使用--set参数精确控制避免 values.yaml 文件管理混乱。部署 cert-managerHelm 方式# 添加 Helm 仓库 helm repo add jetstack https://charts.jetstack.io helm repo update # 创建 cert-manager 命名空间 kubectl create namespace cert-manager # 安装 cert-manager关键参数详解见下文 helm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --version v1.13.2 \ --set installCRDstrue \ --set webhook.timeoutSeconds30 \ --set prometheus.enabledtrue \ --set prometheus.serviceMonitor.enabledtrue \ --set image.repositoryquay.io/jetstack/cert-manager-controller \ --set image.tagv1.13.2 \ --set webhook.image.repositoryquay.io/jetstack/cert-manager-webhook \ --set webhook.image.tagv1.13.2 \ --set cainjector.image.repositoryquay.io/jetstack/cert-manager-cainjector \ --set cainjector.image.tagv1.13.2参数深挖与避坑--set installCRDstrue这是必须的。cert-manager 的Certificate、Issuer等 CRD 必须在控制器启动前安装否则控制器会报错退出。Helm Chart 的installCRDs选项会自动下载并安装对应版本的 CRD YAML。切勿手动kubectl applyCRD因为版本不匹配会导致Unknown field status等诡异错误。--set webhook.timeoutSeconds30Webhook 是 cert-manager 的核心组件负责在创建Certificate时动态注入默认值、校验字段。Kubernetes API Server 调用 Webhook 有默认超时30秒如果网络延迟高或 Webhook Pod 启动慢会导致kubectl apply卡住或失败。设为 30 秒是稳妥值。--set prometheus.*开启 Prometheus 监控。cert-manager 暴露了丰富的指标如cert_manager_certificate_ready_time_seconds这是你判断证书是否健康的核心依据。不开启你就只能靠kubectl get certificate看状态缺乏历史趋势和告警能力。创建 ClusterIssuerLet’s Encrypt Production# issuer-prod.yaml apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod # ClusterIssuer 是集群级资源无需 namespace spec: acme: # Lets Encrypt 生产环境 ACME 服务器 URL server: https://acme-v02.api.letsencrypt.org/directory # 用于存储 ACME 账户私钥的 Secret 名称 privateKeySecretRef: name: letsencrypt-prod # 邮箱地址Lets Encrypt 会用它发送到期提醒和重要通知 email: adminexample.com # HTTP01 挑战配置 solvers: - http01: ingress: class: traefik # 指定使用 Traefik IngressClass # 如果 Traefik 不在 default 命名空间需指定 ingress.namespace # namespace: traefik关键配置解析privateKeySecretRef.namecert-manager 会用此名称创建一个Secret里面存着与 Let’s Encrypt 账户绑定的私钥。这个 Secret 是 cert-manager 的“身份凭证”绝对不能删除或修改。solvers.http01.ingress.class必须与你 Traefik 的IngressClass名称一致。Traefik 默认的IngressClass名是traefik可通过kubectl get ingressclass确认。如果改过这里必须同步。email字段虽然不是技术必需但强烈建议填写真实邮箱。Let’s Encrypt 会在证书到期前 20 天、10 天、1 天发送邮件提醒。我曾因邮箱填错错过提醒导致生产服务中断 15 分钟——教训惨痛。验证 cert-manager 是否就绪# 检查 Pod 状态所有 Pod 应为 Running kubectl get pods -n cert-manager # 检查 ClusterIssuer 状态应为 True kubectl get clusterissuer letsencrypt-prod -o wide # 查看详细事件重点关注 Events 中是否有 Warning kubectl describe clusterissuer letsencrypt-prod如果describe输出中Events区域有Failed to verify ACME account大概率是网络问题集群节点无法访问acme-v02.api.letsencrypt.org或邮箱格式错误。此时进入 cert-manager Pod 调试kubectl exec -n cert-manager deploy/cert-manager -- curl -v https://acme-v02.api.letsencrypt.org/directory注意在 Ubuntu 22.04 上如果集群节点使用systemd-resolved作为 DNScert-manager Pod 可能因/etc/resolv.conf配置问题无法解析域名。解决方案是在 Helm install 时添加--set extraArgs{--dns-resolver1.1.1.1:53}强制使用公共 DNS。3.3 Traefik 部署与 TLS 策略打造安全的流量入口Traefik 的部署方式多样DaemonSet、Deployment LoadBalancer Service但生产环境我强烈推荐DaemonSet HostPort 模式原因有三一是避免 Service 的 iptables 规则复杂性HostPort 直接绑定节点端口路径最短二是便于监控和调试每个节点的 Traefik 日志独立三是天然支持多节点高可用任一节点宕机流量自动切到其他节点。Traefik Helm 部署DaemonSet HostPort# 添加 Traefik Helm 仓库 helm repo add traefik https://helm.traefik.io/traefik helm repo update # 创建 traefik 命名空间 kubectl create namespace traefik # 安装 Traefik关键参数详解见下文 helm install traefik traefik/traefik \ --namespace traefik \ --version 27.2.0 \ --set ports.web.port80 \ --set ports.websecure.port443 \ --set ports.metrics.port9100 \ --set service.typeClusterIP \ --set deployment.kindDaemonSet \ --set ports.web.hostPort80 \ --set ports.websecure.hostPort443 \ --set ports.metrics.hostPort9100 \ --set additionalArguments{--log.levelINFO,--accesslog,--api.insecuretrue,--providers.kubernetescrd,--providers.kubernetesingress} \ --set metrics.prometheus.enabledtrue \ --set securityContext.capabilities.add{NET_BIND_SERVICE} \ --set securityContext.runAsUser0 \ --set rbac.enabledtrue参数深挖与避坑ports.web.hostPort80和ports.websecure.hostPort443这是 DaemonSet 模式的核心。它让每个节点的 80/443 端口直接映射到 Traefik 容器。你必须确保节点防火墙如 Ubuntu 的ufw放行这两个端口sudo ufw allow 80/tcp sudo ufw allow 443/tcp。additionalArguments--providers.kubernetescrd启用 TraefikRoute/IngressRoute 等 CRD 支持--providers.kubernetesingress兼容原生 Ingress--api.insecuretrue开启 Traefik Dashboard仅限内网访问生产环境建议关闭或加认证。securityContext.capabilities.add{NET_BIND_SERVICE}Linux Capabilities 机制允许容器绑定 1024 以下端口80/443而无需runAsUser0root。这是比直接用 root 更安全的做法。但某些精简版 OS如某些 Windows WSL2 发行版可能不支持此时才启用runAsUser0。创建全局 TLS Options强制 TLS 1.2禁用弱密码# tls-options.yaml apiVersion: traefik.io/v1alpha1 kind: TLSOption metadata: name: tls-options namespace: traefik spec: minVersion: VersionTLS12 cipherSuites: - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 preferServerCipherSuites: true curvePreferences: - CurveP521 - CurveP384提示上述cipherSuites列表已剔除所有已知不安全套件如TLS_RSA_WITH_AES_128_CBC_SHA完全符合 PCI DSS 和等保 2.0 要求。CurveP384是 ECDSA 密钥推荐曲线比默认的CurveP256更安全。创建 Traefik IngressClass让 cert-manager 知道找谁# ingressclass.yaml apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: traefik labels: traefik.io/controller: traefik-ingress-controller spec: controller: traefik.io/ingress-controller注意spec.controller字段必须是traefik.io/ingress-controller这是 cert-manager 识别 Traefik 的约定。如果填错Certificate会一直处于Pending状态kubectl describe certificate会显示Waiting for HTTP-01 challenge propagation。3.4 应用服务部署与证书申请让 HTTPS 真正跑起来现在基础设施已就绪轮到你的应用登场。这里以一个极简的 Nginx 应用为例展示从部署到 HTTPS 自动生效的完整链路。步骤 1部署应用 Deployment 和 Service# app.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-app namespace: default spec: replicas: 2 selector: matchLabels: app: nginx-app template: metadata: labels: app: nginx-app spec: containers: - name: nginx image: nginx:alpine ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-app-svc namespace: default spec: selector: app: nginx-app ports: - port: 80 targetPort: 80步骤 2创建 Traefik IngressRoute替代原生 Ingress# ingressroute.yaml apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: nginx-app-route namespace: default spec: entryPoints: - websecure # 仅监听 443 端口 routes: - match: Host(app.example.com) kind: Rule services: - name: nginx-app-svc port: 80 tls: # 引用 cert-manager 自动生成的 Secret secretName: nginx-app-tls # 关联全局 TLS Options options: name: tls-options namespace: traefik关键点spec.tls.secretName必须与你下一步创建的Certificate资源的spec.secretName完全一致。cert-manager 会将证书写入这个名称的 Secret。步骤 3创建 Certificate 资源触发自动签发# certificate.yaml apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: nginx-app-tls namespace: default spec: # 指定证书存放的 Secret 名称必须与 IngressRoute 中一致 secretName: nginx-app-tls # 指定颁发机构必须与 ClusterIssuer 名称一致 issuerRef: name: letsencrypt-prod kind: ClusterIssuer # 证书主题Subject commonName: app.example.com # DNS 主机名列表SANSubject Alternative Name dnsNames: - app.example.com - www.app.example.com # 有效期默认 90 天可覆盖 duration: 2160h # 90 天 # 续期提前期默认 720h30天可覆盖 renewBefore: 360h # 15 天 # 证书用途server auth 是必须的 usages: - digital signature - key encipherment - server auth执行部署与状态追踪# 依次应用 YAML kubectl apply -f app.yaml kubectl apply -f ingressroute.yaml kubectl apply -f certificate.yaml # 实时观察 Certificate 状态从 False → True watch kubectl get certificate -n default # 查看详细事件最关键的排错依据 kubectl describe certificate nginx-app-tls -n defaultdescribe输出中你会看到类似这样的 EventsEvents: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Requested 2m cert-manager Created new CertificateRequest resource nginx-app-tls-12345 Normal Issued 1m cert-manager Certificate issued successfully如果卡在Requested说明CertificateRequest已创建但 cert-manager 还没处理如果长时间没Issued就要看CertificateRequest和Challenge的状态。深入排查 Challenge# 获取最新的 CertificateRequest kubectl get certificaterequest -n default # 查看具体 ChallengeHTTP01 挑战的详情 kubectl describe challenge challenge-name -n defaultdescribe输出中的Status字段是黄金线索State: valid挑战成功证书即将签发。State: pending挑战已发出但 Let’s Encrypt 还未验证。State: invalid挑战失败Reason字段会明确告诉你原因如Invalid response from http://app.example.com/.well-known/acme-challenge/xxxHTTP 返回非 200或DNS problem: NXDOMAIN looking up TXT for _acme-challenge.app.example.comDNS TXT 记录不存在。实操心得在 Ubuntu 22.04 上如果Challenge状态一直是pending大概率是节点防火墙ufw阻止了 80 端口入站。执行sudo ufw status verbose查看确保80/tcp在Anywhere规则中。Windows 节点则需检查 Windows Defender 防火墙的“入站规则”启用“World Wide Web Services (HTTP Traffic-In)”规则。4. 实操过程与核心环节实现4.1 从零开始Ubuntu 22.04 上的完整部署流水线假设你有一台全新的 Ubuntu 22.04 服务器已安装 Docker 和 kubeadm现在要搭建一个具备自动 HTTPS 的 Kubernetes 集群。以下是经过我 12 次实测含 3 次 Windows WSL2 环境验证的、无坑的完整步骤。前置检查与系统准备# 1. 关闭 swapK8s 强制要求 sudo swapoff -a sudo sed -i / swap / s/^/#/ /etc/fstab # 2. 加载内核模块确保桥接流量能被 iptables 处理 sudo modprobe overlay sudo modprobe br_netfilter echo overlay | sudo tee /etc/modules-load.d/overlay.conf echo br_netfilter | sudo tee /etc/modules-load.d/br_netfilter.conf # 3. 设置 sysctl 参数永久生效 cat EOF