Kubernetes TLS自动化:cert-manager+Traefik+Let‘s Encrypt实战
2026/6/23 8:45:28 网站建设 项目流程

1. 项目概述:为什么在 Kubernetes 里搞 TLS 不能只靠“配个证书”就完事?

你是不是也经历过——辛辛苦苦把服务部署进 Kubernetes,用kubectl apply -f一气呵成,前端访问却卡在NET::ERR_CERT_INVALID?浏览器地址栏赫然挂着一个红色叉号,点开一看:“此连接不是私密连接”,甚至直接拒绝加载。更糟的是,有些内部服务调用(比如微服务间 gRPC、Prometheus 抓取指标、GitLab CI 连接 Harbor)突然报错:x509: certificate signed by unknown authorityfailed to create SSL/TLS secure channel。这时候翻日志,满屏都是tls handshake failedcertificate verify failedinternal 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.13+,Ubuntu 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 的核心价值,在于它把“证书”本身定义成了 CRD(CustomResourceDefinition):CertificateIssuerClusterIssuerCertificateRequest。你只需声明“我要一个域名app.example.com的证书,由 Let’s Encrypt 生产”,cert-manager 就会自动创建对应 Secret,并确保其内容始终与证书状态一致。这就像你声明一个 Deployment,K8s 控制器自动保证 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(集群级)统一管理颁发机构,用Certificateusages字段控制证书用途(server auth,client auth),用durationrenewBefore精确控制有效期和续期时机。一套 YAML,通过kustomizepatch 或 Helm values 就能适配所有环境。

注意:不要在生产环境用letsencrypt-stagingIssuer 长期运行。Staging 环境证书不被浏览器信任,仅用于功能验证。正式上线前务必切换到letsencrypt-prod,并确认 Let’s Encrypt 生产环境速率限制(每周 50 张新证书,每域名每周 5 张)是否满足业务需求。

2.2 为什么选 Traefik 而非 Nginx Ingress 或 APISIX?

Kubernetes Ingress 生态里,Nginx Ingress Controller 是事实标准,APISIX 因其动态路由和插件生态越来越火。但在这个 TLS 自动化场景下,Traefik 有三个不可替代的优势。

第一个优势是原生深度集成 cert-manager。Traefik 的 IngressRoute CRD(替代原生 Ingress)直接支持tls字段引用Secret,且其控制器内置了对 cert-managerCertificate资源的 watch 机制。当你创建一个Certificate,Traefik 不需要额外配置就能感知并自动绑定。而 Nginx Ingress 需要你在Ingress资源里显式指定spec.tls.secretName,且 Nginx 控制器本身不监听Certificate事件——你得靠 cert-manager 的ingress-shim功能,它会自动为你生成一个Ingress资源,再由 Nginx 控制器去读。这多了一层间接,出问题时排查链路更长(CertificateIngressSecretNginx)。Traefik 的路径是CertificateSecretIngressRoute,更扁平。

第二个优势是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: true,cert-manager 会自动提供 OCSP 响应数据。而 Nginx 需要手动配置ssl_staplingssl_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 或自建 CA?

Let’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),确认无误后再切到production(URL: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 +short(Linux/macOS)或nslookup app.example.com(Windows)在本地和集群节点上双重验证。特别注意:Kubernetes 集群内部 DNS(CoreDNS)默认会缓存查询结果,TTL 为 30 秒。如果刚改完 DNS 就急着部署,cert-manager 可能拿到过期的 IP,导致 HTTP01 挑战请求发到旧服务器而失败。解决方案:在集群节点上执行sudo systemd-resolve --flush-caches(Ubuntu 22.04)或重启 CoreDNS Pod(kubectl delete pod -n kube-system -l k8s-app=kube-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注册表项,确保MaxCacheEntryTtlLimitMaxNegativeCacheTtlLimit值合理(建议设为 300)。否则,即使 DNS 已更新,Windows 节点上的 cert-manager Pod 仍可能用缓存 IP 发起挑战。

提示:用curl -v http://app.example.com/.well-known/acme-challenge/test从集群外(如你本地电脑)和集群内(kubectl run -it --rm debug --image=curlimages/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-manager(Helm 方式)

# 添加 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 installCRDs=true \ --set webhook.timeoutSeconds=30 \ --set prometheus.enabled=true \ --set prometheus.serviceMonitor.enabled=true \ --set image.repository=quay.io/jetstack/cert-manager-controller \ --set image.tag=v1.13.2 \ --set webhook.image.repository=quay.io/jetstack/cert-manager-webhook \ --set webhook.image.tag=v1.13.2 \ --set cainjector.image.repository=quay.io/jetstack/cert-manager-cainjector \ --set cainjector.image.tag=v1.13.2

参数深挖与避坑

  • --set installCRDs=true:这是必须的。cert-manager 的CertificateIssuer等 CRD 必须在控制器启动前安装,否则控制器会报错退出。Helm Chart 的installCRDs选项会自动下载并安装对应版本的 CRD YAML。切勿手动kubectl applyCRD,因为版本不匹配会导致Unknown field "status"等诡异错误。
  • --set webhook.timeoutSeconds=30:Webhook 是 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看状态,缺乏历史趋势和告警能力。

创建 ClusterIssuer(Let’s Encrypt Production)

# issuer-prod.yaml apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod # ClusterIssuer 是集群级资源,无需 namespace spec: acme: # Let's Encrypt 生产环境 ACME 服务器 URL server: https://acme-v02.api.letsencrypt.org/directory # 用于存储 ACME 账户私钥的 Secret 名称 privateKeySecretRef: name: letsencrypt-prod # 邮箱地址,Let's Encrypt 会用它发送到期提醒和重要通知 email: admin@example.com # HTTP01 挑战配置 solvers: - http01: ingress: class: traefik # 指定使用 Traefik IngressClass # 如果 Traefik 不在 default 命名空间,需指定 ingress.namespace # namespace: traefik

关键配置解析

  • privateKeySecretRef.name:cert-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作为 DNS,cert-manager Pod 可能因/etc/resolv.conf配置问题无法解析域名。解决方案是在 Helm install 时添加--set extraArgs="{--dns-resolver=1.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.port=80 \ --set ports.websecure.port=443 \ --set ports.metrics.port=9100 \ --set service.type=ClusterIP \ --set deployment.kind=DaemonSet \ --set ports.web.hostPort=80 \ --set ports.websecure.hostPort=443 \ --set ports.metrics.hostPort=9100 \ --set additionalArguments="{--log.level=INFO,--accesslog,--api.insecure=true,--providers.kubernetescrd,--providers.kubernetesingress}" \ --set metrics.prometheus.enabled=true \ --set securityContext.capabilities.add="{NET_BIND_SERVICE}" \ --set securityContext.runAsUser=0 \ --set rbac.enabled=true

参数深挖与避坑

  • ports.web.hostPort=80ports.websecure.hostPort=443:这是 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.insecure=true开启 Traefik Dashboard(仅限内网访问,生产环境建议关闭或加认证)。
  • securityContext.capabilities.add="{NET_BIND_SERVICE}":Linux Capabilities 机制,允许容器绑定 1024 以下端口(80/443),而无需runAsUser=0(root)。这是比直接用 root 更安全的做法。但某些精简版 OS(如某些 Windows WSL2 发行版)可能不支持,此时才启用runAsUser=0

创建全局 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 主机名列表(SAN,Subject Alternative Name) dnsNames: - app.example.com - www.app.example.com # 有效期(默认 90 天,可覆盖) duration: 2160h # 90 天 # 续期提前期(默认 720h=30天,可覆盖) 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 default

describe输出中,你会看到类似这样的 Events:

Events: 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,就要看CertificateRequestChallenge的状态。

深入排查 Challenge

# 获取最新的 CertificateRequest kubectl get certificaterequest -n default # 查看具体 Challenge(HTTP01 挑战的详情) kubectl describe challenge <challenge-name> -n default

describe输出中的Status字段是黄金线索:

  • State: valid:挑战成功,证书即将签发。
  • State: pending:挑战已发出,但 Let’s Encrypt 还未验证。
  • State: invalid:挑战失败,Reason字段会明确告诉你原因,如Invalid response from http://app.example.com/.well-known/acme-challenge/xxx(HTTP 返回非 200)或DNS problem: NXDOMAIN looking up TXT for _acme-challenge.app.example.com(DNS TXT 记录不存在)。

实操心得:在 Ubuntu 22.04 上,如果Challenge状态一直是pending,大概率是节点防火墙(ufw)阻止了 80 端口入站。执行sudo ufw status verbose查看,确保80/tcpAnywhere规则中。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. 关闭 swap(K8s 强制要求) 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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询