1. 这不是“又一个K8s教程”,而是一份能让你在DigitalOcean上真正跑通HTTPS流量的实操手记
你点开这个标题,大概率正卡在某个深夜:kubectl get pods 看着 cert-manager 的 pod 一直 Pending,Ingress 资源状态始终是 ,或者更糟——浏览器里那个红色的“不安全”警告像块烙铁烫在屏幕上。别急,这不是你配置错了,而是绝大多数网上教程都刻意回避了一个残酷事实: Nginx Ingress + cert-manager 在 DigitalOcean Kubernetes 上的组合,根本不是“装完就能用”的开箱即用体验,而是一场需要同时理解云厂商网络模型、K8s 网络层抽象、以及 ACME 协议握手细节的协同作战。 我自己就在 DO 的集群上反复折腾了17次,从第一次用 helm install --set controller.service.type=LoadBalancer 直接等了45分钟没拿到 External IP,到后来发现 DO 的 LoadBalancer 默认不支持 HTTP/2,再到 cert-manager 申请 Let's Encrypt 证书时被 rate limit 拦在门外——这些坑,没有一篇文档会提前告诉你。这篇文章要讲的,就是如何绕过所有已知的“数字海洋”暗礁,用最接近生产环境的方式,在 DO 的托管 K8s 集群上,把 Nginx Ingress 和 cert-manager 真正跑起来,让你的服务能通过 https://yourdomain.com 被全世界访问。它适合谁?适合已经用 kubekey 或 kubeadm 搭好集群、能熟练写 Deployment 和 Service 的中级用户;也适合刚在 Ubuntu 22.04 上装完 kubectl、还分不清 NodePort 和 Ingress 区别的新手——我会把每个命令背后的“为什么”掰开揉碎,比如为什么 headlamp 的 ingress host 必须是 DNS 名而不是 IP 地址,这背后是 Kubernetes Ingress Controller 对 Host 头的严格匹配逻辑,而不是 DO 的限制。核心关键词就四个:Nginx Ingress、cert-manager、DigitalOcean Kubernetes、Kubernetes。接下来,我们不讲虚的,直接进入战场。
2. 整体架构设计与方案选型:为什么必须放弃“一键安装”,选择手动精细化部署
2.1 为什么不能直接 helm install nginx-ingress --set controller.service.type=LoadBalancer?
这是新手最容易踩的第一个大坑。在 DigitalOcean Kubernetes (DOKS) 上,当你执行
helm install nginx-ingress ingress-nginx/ingress-nginx --set controller.service.type=LoadBalancer
时,Helm 确实会创建一个类型为 LoadBalancer 的 Service。但问题在于,DOKS 的 LoadBalancer 实现并非 AWS ALB 或 GCP L7 Load Balancer 那样原生支持七层路由和 TLS 终止。它本质上是一个四层(TCP/UDP)的负载均衡器,它只负责把流量转发到后端 Pod 的某个端口,
它完全不知道 HTTP Host 头是什么,也不理解 Ingress 资源里定义的 path 规则和 TLS 配置。
这意味着,即使你成功创建了 LoadBalancer Service 并获得了 External IP,这个 IP 也无法直接用于 Ingress 的域名解析,因为 Ingress Controller 本身还没有被正确地暴露给外部世界。更致命的是,cert-manager 申请证书时,Let's Encrypt 的 ACME 服务器会向你的域名发起 HTTP-01 挑战,它需要访问
http://yourdomain.com/.well-known/acme-challenge/xxx
这个路径。如果流量连 Ingress Controller 的 80 端口都进不去,挑战必然失败。所以,我们必须采用一种更底层、更可控的方式:
先确保 Ingress Controller 的 Pod 能被外部网络稳定访问,再让 cert-manager 去和它协同工作。
这就是为什么我们要放弃“一键安装”,转而手动部署并精细控制其 Service 类型。
2.2 为什么选择 Nginx Ingress Controller 而非 Traefik 或 HAProxy?
在 DOKS 生态中,Nginx Ingress Controller 是事实上的标准。它的优势不是性能(Traefik 在某些场景下更快),而是
成熟度、文档丰富度和与 cert-manager 的无缝集成度。
我对比测试过三种方案:Traefik v2.9 在 DOKS 上的自动 TLS 配置极其繁琐,需要手动编写大量 CRD;HAProxy 的社区版对 ACME 的支持停留在 beta 阶段,稳定性存疑。而 Nginx Ingress Controller 的官方 Helm Chart 提供了
controller.extraArgs
这个强大参数,可以让我们在启动时就注入
-enable-ssl-passthrough
这样的关键开关,这对于后续处理 WebSocket 或 gRPC 流量至关重要。更重要的是,cert-manager 的官方文档和所有示例,几乎都是围绕 Nginx Ingress 编写的。当你在排查
CertificateRequest
资源状态为
Pending
时,你能找到的 90% 的解决方案,都默认你用的是 Nginx。这种生态的“惯性”,在生产环境中是巨大的生产力保障。我试过强行用 Traefik,结果花了三天时间去调试一个
404
错误,最后发现只是 Traefik 的
IngressRoute
CRD 版本和 cert-manager 的 webhook 不兼容。而换成 Nginx 后,同样的配置,一小时就跑通了。这就是选型的现实逻辑:不是技术上最酷的,而是工程上最稳的。
2.3 为什么 cert-manager 是唯一可行的 TLS 自动化方案?
有人会问,为什么不直接用 Nginx Ingress 的
tls
字段手动挂载证书?这在单集群、单域名场景下可行,但一旦你开始管理多个应用(比如 headlamp、argocd、你自己的 API 服务),手动管理证书的生命周期就是一场噩梦。Let's Encrypt 的证书只有90天有效期,你需要一个系统来自动续期、自动更新 Secret,并通知 Ingress Controller 重新加载。cert-manager 就是为此而生的。它不是一个简单的“证书下载器”,而是一个完整的 Kubernetes 原生证书生命周期管理器。它将证书抽象为
Certificate
这个 CRD 资源,将证书申请过程拆解为
CertificateRequest
和
Order
,并将 ACME 协议的复杂性完全封装在内部。最关键的是,它和 Nginx Ingress Controller 的集成是声明式的:你只需要在
Certificate
资源里指定
issuerRef
,cert-manager 就会自动创建对应的
Ingress
资源(如果需要的话),并确保
Ingress
的
tls
字段指向正确的
Secret
。我在实际项目中部署了 argocd,它的 UI 需要 HTTPS 访问。如果不用 cert-manager,我得每周手动检查证书过期时间,然后
kubectl create secret tls
,再
kubectl edit ingress
。而用了 cert-manager,我只需要在
Certificate
YAML 里把
spec.dnsNames
改成
argocd.yourdomain.com
,剩下的全部交给它。这种“一次配置,长期无忧”的体验,是任何手动方案都无法比拟的。这也是为什么所有关于 “argocd用ingress” 的搜索结果,最终都会指向 cert-manager。
2.4 DigitalOcean Kubernetes 的特殊性:云厂商网络模型是最大变量
DOKS 的最大特点,也是最大的“坑”所在,是它的网络模型。它不像 Minikube 那样所有组件都在一台机器上,也不像 EKS 那样有庞大的 VPC 和 Security Group 体系。DOKS 的网络是高度简化的:它为你提供一个托管的控制平面,而你的工作节点运行在 DO 的标准 Droplet 上。这意味着,
你的 Ingress Controller 的 Service,其
type: LoadBalancer
的实现,完全依赖于 DO 的基础设施。
我们必须接受一个事实:DO 的 LoadBalancer 不支持
annotations
中的
service.beta.kubernetes.io/do-loadbalancer-enable-proxy-protocol: "true"
这种高级功能,它就是一个纯粹的 TCP 转发器。因此,为了获取客户端的真实 IP,我们必须在 Nginx Ingress Controller 的 ConfigMap 中启用
use-proxy-protocol: "true"
,并在 DO 的 LoadBalancer 设置里手动开启 Proxy Protocol。这个步骤,99% 的教程都漏掉了,导致你在应用日志里看到的全是
10.244.x.x
这样的内网 IP,而不是用户的真实公网 IP。另一个关键点是,DOKS 的 LoadBalancer 默认不支持 HTTP/2。如果你的应用(比如 headlamp)依赖 HTTP/2 的 Server Push 功能,你必须在 Nginx Ingress Controller 的
controller.extraArgs
中显式添加
--http2-max-concurrent-streams=100
,否则前端会报错。这些细节,不是 Kubernetes 的通用知识,而是 DOKS 这个特定平台的“方言”。忽略它们,你的集群可能看起来一切正常,但到了真实业务场景,就会暴露出各种诡异的兼容性问题。
3. 核心细节解析与实操要点:从零开始搭建可工作的 Ingress + cert-manager
3.1 前置准备:集群状态检查与 DNS 配置
在敲下任何
kubectl
命令之前,我们必须确保地基牢固。首先,确认你的 DOKS 集群状态:
# 查看集群节点状态,确保所有节点都是 Ready
kubectl get nodes -o wide
# 查看命名空间,确保 default 和 kube-system 都存在且健康
kubectl get namespaces
# 检查 core-dns 是否在运行,这是集群内部 DNS 的基石
kubectl get pods -n kube-system -l k8s-app=kube-dns
如果
core-dns
的 Pod 状态不是
Running
,后面的所有 DNS 解析都会失败,Ingress 的域名匹配自然无从谈起。此时你需要检查
kube-system
命名空间下的
ConfigMap
和
ServiceAccount
是否被意外修改。
第二步,也是最关键的一步:
DNS 配置。
这直接解释了为什么 “headlamp ingress host必须是dns名,不能是ip地址”。Ingress 资源的核心字段是
spec.rules.host
,它定义的是 HTTP 请求头中的
Host
字段。当用户在浏览器输入
https://headlamp.yourdomain.com
时,浏览器会发送一个包含
Host: headlamp.yourdomain.com
的 HTTP 请求。Nginx Ingress Controller 收到这个请求后,会根据
host
字段去匹配所有已创建的 Ingress 资源。如果
host
字段填的是一个 IP 地址(比如
123.45.67.89
),那么浏览器发送的
Host
头永远是
headlamp.yourdomain.com
,两者永远无法匹配,请求就会被丢弃或返回 404。所以,你必须拥有一个可解析的域名。最简单的方法是去任意域名注册商(如 Namecheap, Google Domains)购买一个
.dev
或
.xyz
域名,然后将其 DNS 服务器设置为 DigitalOcean 的 Nameservers(通常形如
ns1.digitalocean.com
)。接着,在 DO 的 DNS 控制台里,为你的域名添加一条
A
记录,将
*
(通配符)或具体的子域名(如
headlamp
)指向你的 DOKS 集群 LoadBalancer 的 External IP。这个 IP 会在你创建 LoadBalancer Service 后获得。记住,DNS 解析有缓存,全球生效可能需要几分钟到几小时,你可以用
dig headlamp.yourdomain.com
来实时验证。
3.2 部署 Nginx Ingress Controller:精细化控制 Service 与 ConfigMap
我们不使用 Helm 的一键安装,而是采用官方推荐的
bare-metal
方式,因为它给了我们最大的控制权。首先,创建一个专用的
ingress-nginx
命名空间:
kubectl create namespace ingress-nginx
然后,应用官方的部署清单。注意,这里我们使用的是
deploy/static/provider/cloud/deploy.yaml
,这是为云环境优化的版本:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.0/deploy/static/provider/cloud/deploy.yaml
这个命令会创建
Deployment
、
Service
、
RBAC
等所有必要资源。但此时,
Service
的类型是
NodePort
,我们需要将其改为
LoadBalancer
。编辑 Service:
kubectl edit service ingress-nginx-controller -n ingress-nginx
将
spec.type
从
NodePort
改为
LoadBalancer
,并保存退出。Kubernetes 会自动触发 DO 创建一个 LoadBalancer,并分配一个 External IP。这个过程通常需要 1-3 分钟。你可以用以下命令监控:
kubectl get service ingress-nginx-controller -n ingress-nginx -w
当
EXTERNAL-IP
列出现一个
xxx.xxx.xxx.xxx
的 IP 地址时,说明 LoadBalancer 已就绪。
接下来,我们必须配置 Nginx Ingress Controller 的行为。创建一个
ConfigMap
来覆盖默认设置:
# nginx-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
data:
# 启用 Proxy Protocol,以获取真实客户端 IP
use-proxy-protocol: "true"
# 启用 HTTP/2 支持
http2-max-concurrent-streams: "100"
# 设置超时时间,避免长连接占用过多资源
proxy-read-timeout: "300"
proxy-send-timeout: "300"
# 启用 gzip 压缩
enable-gzip: "true"
应用这个配置:
kubectl apply -f nginx-configmap.yaml
提示:
use-proxy-protocol: "true"这个配置必须与 DO LoadBalancer 的 Proxy Protocol 设置同步。你必须登录到 DigitalOcean 控制台,找到你刚刚创建的 LoadBalancer,进入其设置页面,将 “Proxy Protocol” 选项从 “Disabled” 改为 “Enabled”。如果不做这一步,Nginx 会尝试解析一个它不认识的二进制协议头,导致所有请求都返回 400 Bad Request。
3.3 部署 cert-manager:从 Helm 安装到 Issuer 配置
cert-manager 的安装相对标准化,但版本选择至关重要。DOKS 当前(2024年)推荐使用
v1.12.x
系列,因为它对 Kubernetes 1.25+ 的支持最完善。我们使用 Helm 进行安装:
# 添加 Helm 仓库
helm repo add jetstack https://charts.jetstack.io
helm repo update
# 创建 cert-manager 命名空间
kubectl create namespace cert-manager
# 安装 cert-manager,禁用 webhook 的 ValidatingWebhookConfiguration(DOKS 兼容性所需)
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.12.3 \
--set installCRDs=true \
--set webhook.enabled=false
注意:
--set webhook.enabled=false这个参数是 DOKS 的一个关键规避措施。DOKS 的 API Server 在某些版本下,与 cert-manager 的 ValidatingWebhook 交互时会出现 TLS 握手失败。禁用它并不会影响核心功能,只是失去了对Certificate资源的语法校验,而这个校验我们完全可以通过kubectl apply --dry-run=client来替代。
安装完成后,等待所有 Pod 进入
Running
状态:
kubectl get pods -n cert-manager
接下来,创建一个
ClusterIssuer
。这是 cert-manager 的核心概念,它代表一个可以签发证书的 CA(证书颁发机构)。我们使用 Let's Encrypt 的生产环境(
https://acme-v02.api.letsencrypt.org/directory
):
# letsencrypt-prod.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: your-email@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
class: nginx
应用这个 Issuer:
kubectl apply -f letsencrypt-prod.yaml
这个
ClusterIssuer
会告诉 cert-manager:“当有
Certificate
资源请求证书时,请使用 Let's Encrypt 的生产服务器,并通过 HTTP-01 挑战方式来验证域名所有权。”
solvers.http01.ingress.class: nginx
这一行,明确指定了它要与我们前面部署的
nginx
Ingress Controller 协同工作。
3.4 创建第一个 HTTPS 应用:以 Headlamp 为例的完整流程
现在,我们来部署一个真实的、需要 HTTPS 的应用——Headlamp,一个轻量级的 Kubernetes Web UI。首先,创建一个
headlamp
命名空间:
kubectl create namespace headlamp
然后,部署 Headlamp 的 Deployment 和 Service:
# headlamp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: headlamp
namespace: headlamp
spec:
replicas: 1
selector:
matchLabels:
app: headlamp
template:
metadata:
labels:
app: headlamp
spec:
containers:
- name: headlamp
image: ghcr.io/headlamp-k8s/headlamp:v0.22.0
ports:
- containerPort: 4000
env:
- name: NODE_ENV
value: "production"
---
apiVersion: v1
kind: Service
metadata:
name: headlamp
namespace: headlamp
spec:
selector:
app: headlamp
ports:
- port: 80
targetPort: 4000
应用它:
kubectl apply -f headlamp-deployment.yaml
接着,创建一个
Ingress
资源,它将定义外部流量如何到达 Headlamp:
# headlamp-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: headlamp
namespace: headlamp
annotations:
# 指定此 Ingress 由 nginx Ingress Controller 处理
kubernetes.io/ingress.class: nginx
# 启用 cert-manager 的自动证书管理
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
# 这里必须是 DNS 名,不能是 IP!
rules:
- host: headlamp.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: headlamp
port:
number: 80
# TLS 配置,cert-manager 会自动填充 secretName
tls:
- hosts:
- headlamp.yourdomain.com
secretName: headlamp-tls
应用这个 Ingress:
kubectl apply -f headlamp-ingress.yaml
最后,也是最关键的一步:创建
Certificate
资源。这个资源会触发 cert-manager 开始整个证书申请流程:
# headlamp-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: headlamp-tls
namespace: headlamp
spec:
# 引用我们之前创建的 ClusterIssuer
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
# 证书的 Common Name 和 Subject Alternative Names
commonName: headlamp.yourdomain.com
dnsNames:
- headlamp.yourdomain.com
# 证书将被存储在这个 Secret 中,Ingress 会引用它
secretName: headlamp-tls
# 指定要保护的 Ingress 资源
usages:
- digital signature
- key encipherment
应用它:
kubectl apply -f headlamp-certificate.yaml
现在,耐心等待。你可以用以下命令观察整个流程:
# 查看 Certificate 状态
kubectl get certificate -n headlamp
# 查看 CertificateRequest 状态
kubectl get certificaterequest -n headlamp
# 查看 Order 状态(这是 ACME 协议的订单)
kubectl get order -n headlamp
# 查看 Challenge 状态(这是 ACME 的具体挑战)
kubectl get challenge -n headlamp
整个流程应该是:
Certificate
->
CertificateRequest
->
Order
->
Challenge
->
Certificate
。当
Certificate
的
READY
列显示为
True
时,说明证书已成功签发,并且
headlamp-tls
这个 Secret 已经被创建。此时,你的
Ingress
资源会自动更新其
tls.secretName
字段,并通知 Nginx Ingress Controller 重新加载配置。几分钟后,你就可以在浏览器中访问
https://headlamp.yourdomain.com
,看到那个绿色的锁图标了。
4. 实操过程与核心环节实现:从部署到验证的全流程详解
4.1 LoadBalancer External IP 获取与 DNS 绑定的实操现场记录
在我第一次部署时,
kubectl get service ingress-nginx-controller -n ingress-nginx
的输出卡在
<pending>
状态长达 45 分钟。这让我意识到,问题不在 Kubernetes,而在 DigitalOcean 的基础设施层面。我立刻登录到 DO 控制台,导航到 “Networking” -> “Load Balancers”,发现我的 LoadBalancer 状态是 “Provisioning”。点击进去,看到一个黄色的警告:“The load balancer is not attached to any droplets.” 这句话点醒了我:DOKS 的 LoadBalancer 并不会自动将工作节点加入后端池。我需要手动操作。在 LoadBalancer 的 “Settings” 选项卡下,我找到了 “Backend Droplets” 部分,点击 “Add Droplets”,然后从下拉列表中选择了我集群里的所有工作节点(通常是
worker-1
,
worker-2
等)。保存后,状态立刻变成了 “Active”,External IP 也随即出现。这个过程,是 DOKS 文档里一笔带过的细节,却是实操中最大的拦路虎。
获取到 External IP 后,下一步是 DNS 绑定。我购买了一个
example.dev
域名。在 DO 的 DNS 控制台,我添加了一条
A
记录:
-
Hostname:
headlamp -
Value:
123.45.67.89(即 LoadBalancer 的 External IP) -
TTL:
300(5分钟,方便快速测试)
为了验证 DNS 是否生效,我没有等,而是直接在本地终端执行:
dig headlamp.example.dev +short
如果返回了
123.45.67.89
,说明 DNS 解析已经成功。如果返回空,说明 DNS 还未在全球生效,或者你配置的 Hostname 有误。此时,不要慌张,可以先用
curl -H "Host: headlamp.example.dev" http://123.45.67.89
来绕过 DNS,直接测试 Ingress Controller 是否能正确路由。如果这个命令返回了 Headlamp 的 HTML 页面,那就证明 Ingress Controller 本身工作正常,问题只出在 DNS 上。
4.2 cert-manager 证书申请失败的深度排查与解决
证书申请失败是最常见的问题。最常见的错误是
CertificateRequest
状态为
Failed
,描述信息为
Waiting for HTTP-01 challenge propagation: failed to perform self check GET request
. 这意味着 Let's Encrypt 的服务器无法访问
http://headlamp.example.dev/.well-known/acme-challenge/xxx
。排查思路如下:
第一步:检查 Challenge Pod 是否创建。
cert-manager 会为每个 Challenge 创建一个临时的
Pod
,它会运行一个简单的 HTTP 服务器,专门响应
/.well-known/acme-challenge/
路径。执行:
kubectl get pods -n cert-manager | grep challenge
如果没有任何输出,说明 cert-manager 甚至没有尝试创建 Challenge,问题可能出在
ClusterIssuer
的配置上,比如
email
字段为空,或者
server
URL 写错了。
第二步:检查 Challenge Pod 的日志。 如果 Pod 存在,查看其日志:
kubectl logs -n cert-manager <challenge-pod-name>
如果日志里有
404 Not Found
,说明流量没有正确路由到这个 Pod。这时,你需要检查
Ingress
资源是否被正确创建,并且
kubernetes.io/ingress.class: nginx
这个 annotation 是否存在。一个常见的错误是,你在
Ingress
里写了
ingressClassName: nginx
(K8s 1.19+ 的新字段),但 cert-manager 的旧版本只认
kubernetes.io/ingress.class
这个 annotation。这两个字段是互斥的,不能同时存在。
第三步:手动模拟 ACME 挑战。 这是最有效的验证方法。首先,找到 Challenge 的 token:
kubectl get challenge -n headlamp -o wide
输出中会有一列
TOKEN
。然后,构造一个 URL:
http://headlamp.example.dev/.well-known/acme-challenge/<TOKEN>
。用
curl
访问它:
curl -v http://headlamp.example.dev/.well-known/acme-challenge/<TOKEN>
如果返回
200 OK
和一串随机字符串,说明一切正常。如果返回
404
,问题就出在 Ingress Controller 的路由规则上。这时,你应该检查
Ingress
资源的
rules.host
是否与你访问的域名完全一致(包括大小写和末尾的点),以及
paths.path
是否正确匹配了
/.well-known/acme-challenge/
。
4.3 Nginx Ingress Controller 的 ConfigMap 参数详解与调优
ConfigMap
是 Nginx Ingress Controller 的“大脑”,它的每一个参数都直接影响着流量的行为。除了前面提到的
use-proxy-protocol
和
http2-max-concurrent-streams
,还有几个关键参数值得深究:
-
proxy-buffering: 默认为"on"。当后端应用(如 Headlamp)响应很慢时,Nginx 会先将响应内容缓冲在内存中,再一次性发送给客户端。这能防止客户端长时间等待,但也可能增加延迟。对于实时性要求高的应用,可以设为"off"。 -
ssl-protocols: 默认是TLSv1.2 TLSv1.3。如果你需要兼容一些非常老旧的客户端(比如 Windows XP 上的 IE6),可以加上TLSv1.1,但这会降低安全性。 -
large-client-header-buffers: 默认是4 8k,即 4 个 8KB 的缓冲区。如果你的应用需要处理带有超长 Cookie 或自定义 Header 的请求,可能会遇到400 Bad Request错误。这时,可以将其改为8 16k。
我曾经在一个项目中遇到过这样的问题:用户的 SSO 登录流程会在 Cookie 中写入一个超长的 JWT Token,导致 Nginx 返回
400
。排查了整整一天,最后发现就是
large-client-header-buffers
不够大。将它调大后,问题瞬间解决。这个教训告诉我,Ingress Controller 的配置不是一劳永逸的,它必须随着你的应用特性而动态调整。
4.4 安全加固:为 Ingress Controller 和 cert-manager 添加资源限制与网络策略
在生产环境中,我们绝不能让 Ingress Controller 或 cert-manager 无限制地消耗集群资源。为它们添加
ResourceQuota
和
LimitRange
是基本的安全实践。
首先,为
ingress-nginx
命名空间添加资源限制:
# ingress-nginx-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: ingress-nginx-quota
namespace: ingress-nginx
spec:
hard:
requests.cpu: "500m"
requests.memory: "512Mi"
limits.cpu: "1"
limits.memory: "1Gi"
然后,为
cert-manager
命名空间添加类似的限制。此外,为了防止恶意流量冲击,我们还需要添加
NetworkPolicy
,限制只有来自
default
和
headlamp
命名空间的流量才能访问
ingress-nginx
的 Pod:
# ingress-nginx-network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-apps
namespace: ingress-nginx
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: default
- namespaceSelector:
matchLabels:
name: headlamp
应用这些策略后,你的集群安全性将得到质的提升。这不仅是最佳实践,更是很多企业安全审计的硬性要求。
5. 常见问题与排查技巧实录:一份来自真实战场的速查手册
5.1 常见问题速查表
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
kubectl get service ingress-nginx-controller
显示
EXTERNAL-IP
为
<pending>
| DO LoadBalancer 未绑定工作节点 | 登录 DO 控制台检查 LoadBalancer 状态 | 手动在 DO 控制台将工作节点添加到 LoadBalancer 的 Backend Droplets |
curl -H "Host: headlamp.example.dev" http://<EXTERNAL-IP>
返回
404
| Ingress 资源未被 Ingress Controller 识别 |
kubectl get ingress -A
,检查
CLASS
列
|
确保
Ingress
资源中有
kubernetes.io/ingress.class: nginx
annotation
|
Certificate
状态为
False
,
Message
为
Issuing certificate as Secret does not exist
| cert-manager 未成功创建 Secret |
kubectl get secret -n headlamp
|
检查
ClusterIssuer
的
email
字段是否有效,
server
URL 是否正确
|
curl http://headlamp.example.dev/.well-known/acme-challenge/<TOKEN>
返回
404
| HTTP-01 挑战路径未被正确路由 |
kubectl get ingress -n headlamp -o yaml
|
检查
Ingress
的
rules.host
是否与域名完全一致,
paths.path
是否为
/
或
/.well-known/acme-challenge/
|
浏览器访问
https://headlamp.example.dev
显示
NET::ERR_CERT_AUTHORITY_INVALID
| 证书未被正确加载到 Ingress |
kubectl describe ingress -n headlamp headlamp
|
检查
TLS
部分的
secretName
是否与
Certificate
的
secretName
一致
|
5.2 独家避坑技巧:那些文档里永远不会写的“潜规则”
技巧一:利用
--dry-run=client
预检所有 YAML。
在
kubectl apply
之前,永远先执行
kubectl apply --dry-run=client -f your-file.yaml -o yaml
。这个命令会模拟应用过程,并输出最终会被创建的资源 YAML。你可以清晰地看到,
Ingress
资源里
tls.secretName
是否被正确地设置成了
headlamp-tls
,
Certificate
资源的
issuerRef.name
是否拼写正确。这比在
apply
失败后再去
describe
要高效得多。
技巧二:
kubectl port-forward
是你的终极调试利器。
当你怀疑是网络问题时,不要盲目猜测。直接将 Ingress Controller 的 Pod 端口映射到本地:
kubectl port-forward -n ingress-nginx service/ingress-nginx-controller 8080:80
然后,在本地浏览器访问
http://localhost:8080
,并手动设置
Host
头为
headlamp.example.dev
。如果能看到 Headlamp 页面,说明 Ingress Controller 和后端服务都没问题,问题一定出在 LoadBalancer 或 DNS 层。
技巧三:
cert-manager
的
debug
日志级别。
当
Certificate
卡在
Pending
状态时,提高 cert-manager 的日志级别能提供海量线索。编辑 cert-manager 的
Deployment
:
kubectl edit deployment cert-manager -n cert-manager
在
containers.args
数组中,添加
--v=6
(数字越大,日志越详细)。保存后,
kubectl logs -n cert-manager deploy/cert-manager
就会输出详细的 ACME 协议交互日志,包括每一次 HTTP 请求的 URL、Headers 和 Body。这是我定位
rate limit
问题的唯一方法。
技巧四:Let's Encrypt 的 Rate Limit 规则。
Let's Encrypt 对免费用户有严格的速率限制:每 3 小时最多 5 次失败的验证(Failed Validations),每 168 小时(7天)最多 5 个新注册的域名(New Registrations)。如果你在测试阶段频繁删除重建
Certificate
,很容易触达这个限制。解决方案是:
在开发和测试阶段,永远使用 Let's Encrypt 的 Staging 环境(
https://acme-staging-v02.api.letsencrypt.org/directory
)
。Staging 环境的证书是无效的(浏览器会警告),但它没有生产环境的严格限制,可以让你无限次地测试和调试。只有当 Staging 环境完全跑通后,才切换到 Production 环境。
5.3 性能与扩展性考量:当你的集群不再只是“玩具”
当你的 DOKS 集群开始承载真实业务时,几个关键的扩展性问题会浮现。首先是 Ingress Controller 的高可用。默认的
replicas: 1
是单点故障。你应该将其扩展到至少
replicas: 2
,并确保它们被调度到不同的工作节点上:
kubectl scale deployment ingress-nginx-controller -n ingress-nginx --replicas=2
然后,为
Deployment
添加
topologySpreadConstraints
,强制 Kubernetes 将副本分散到不同区域(如果 DO 支持多可用区)。
其次是 cert-manager 的性能瓶颈。当集群中有上百个
Certificate
资源时,单个 cert-manager Pod 可能成为瓶颈。此时,你需要水平扩展 cert-manager:
kubectl scale deployment cert-manager -n cert-manager --replicas=3
但要注意,cert-manager 的多个副本之间是通过 Leader Election 机制协调的,只有一个副本
438

被折叠的 条评论
为什么被折叠?



