Kubernetes 中使用 Gateway API 暴露服务与 HTTPS 证书绑定实战
摘要
本文围绕 Kubernetes Gateway API 服务暴露展开,重点说明如何使用 Gateway、HTTPRoute、cert-manager 与 Let's Encrypt 实现 HTTP/HTTPS 入口管理、TLS 证书自动签发与续期,并补充可直接复用的 YAML 配置、上线步骤与排障方法。适合正在从 Ingress 迁移到 Gateway API,或者准备统一管理 Kubernetes 南北向流量入口的团队参考。
在 Kubernetes 里做服务暴露,很多人第一反应还是 Service + Ingress。但随着业务复杂度上升,传统 Ingress 的几个问题会越来越明显:
- 网关能力强依赖控制器实现,标准不统一;
- 路由、监听、网关基础设施耦合较重;
- 多团队协作时,平台侧和应用侧边界不够清晰;
- 对 TCP、TLS、跨命名空间授权等高级场景表达力有限。
而 Gateway API 的目标,就是把这些能力做成更清晰、更标准、更适合平台治理的 Kubernetes 原生 API。
这篇文章我会从实战角度说明:
Gateway API到底解决了什么问题;- 如何在 Kubernetes 中用
Gateway + HTTPRoute暴露服务; - 如何结合
cert-manager给 Gateway 绑定 HTTPS 证书; - 给出一套可以直接改造落地的案例;
- 总结上线和排障时最容易踩坑的点。
本文示例选择:
- Gateway Controller:
Envoy Gateway - 证书管理:
cert-manager - 公网证书签发:
Let's Encrypt HTTP-01
这是比较通用的一套组合,适合自建 Kubernetes、测试环境以及大多数需要“标准 Gateway API + 自动签证书”能力的场景。
1. Gateway API 是什么
Gateway API 是 Kubernetes SIG Network 推进的一组网络 API 标准,用来替代单体化的 Ingress 表达方式。
它把“流量入口基础设施”和“路由规则”拆成了多个资源:
GatewayClass:由具体网关实现提供的“网关类型”Gateway:实际的入口实例,定义监听端口、协议、TLS 等HTTPRoute:HTTP 路由规则,定义域名、路径、Header 与后端服务映射TCPRoute/TLSRoute/GRPCRoute:面向更多协议的路由能力ReferenceGrant:跨命名空间引用授权
和 Ingress 相比,可以把它理解成:
Gateway更像“负载均衡器 / 网关实例”Route更像“挂到这个网关上的流量规则”
这种拆分有两个直接好处:
- 平台团队可以维护
Gateway,控制公网入口、监听端口、证书和暴露范围; - 业务团队只维护自己的
HTTPRoute,按约定接入,不必争抢一份巨大的 Ingress。
2. Gateway API 暴露服务的核心链路
一个最基本的服务暴露链路如下:
Client --> LoadBalancer / Gateway IP --> Gateway Listener --> HTTPRoute --> Service --> Pod其中最关键的两个资源是:
2.1 Gateway
Gateway 定义入口监听,比如:
- 监听
80端口的 HTTP - 监听
443端口的 HTTPS - 指定
hostname - 指定
tls.certificateRefs - 指定哪些命名空间的 Route 可以挂上来
2.2 HTTPRoute
HTTPRoute 定义流量如何转发,比如:
example.com转发到web-svcfoo.example.com/login转发到foo-svc- 满足某个 Header 时,路由到 canary 服务
- 也可以做 redirect、rewrite、流量拆分等
3. 为什么推荐用 Gateway API
在实际项目中,Gateway API 最值得用的地方主要有四个:
3.1 角色边界更清晰
- 平台团队负责
GatewayClass、Gateway - 应用团队负责
HTTPRoute
这比所有人都去改一个 Ingress 清晰得多。
3.2 表达能力更强
除了普通 HTTP 暴露,还能更自然地表达:
- TLS 终止
- TLS Passthrough
- TCP/UDP/gRPC 暴露
- Header / Query / Method 级路由
- 跨命名空间授权
3.3 更标准,不被单一实现绑死
Ingress 在不同控制器之间差异很大,很多高级能力都得靠 annotation。Gateway API 把很多常用能力上升成了标准字段,迁移和治理会更稳。
3.4 更适合多租户平台
通过 allowedRoutes、parentRefs、ReferenceGrant 等机制,可以更明确地控制:
- 哪些业务命名空间可以接入某个网关
- 哪些对象可以跨命名空间引用
- 证书 Secret 能不能被别的 namespace 使用
4. 前置条件
开始之前,请先准备好以下环境:
- 一个可正常访问的 Kubernetes 集群;
- 本地已安装并配置
kubectl、helm; - 有一个可解析到公网 LB IP 的域名,例如
demo.example.com; - 集群出方向可以访问 Let’s Encrypt;
- 你选择的 Gateway Controller 已支持 Gateway API v1。
注意两点:
Gateway API只是标准 CRD,不自带实际数据面,必须安装控制器,例如 Envoy Gateway、NGINX Gateway Fabric、Istio、云厂商 Gateway 实现等。- 如果你使用的是云厂商托管 Gateway,
gatewayClassName和证书绑定方式可能略有差异。本文示例使用的是比较通用的Envoy Gateway + cert-manager。
5. 安装 Gateway API 与 Envoy Gateway
5.1 安装 Gateway API CRDs
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml验证:
kubectl get crd | grep gateway.networking.k8s.io你应该能看到类似下面这些资源:
gatewayclasses.gateway.networking.k8s.iogateways.gateway.networking.k8s.iohttproutes.gateway.networking.k8s.ioreferencegrants.gateway.networking.k8s.io
5.2 安装 Envoy Gateway
helm install eg oci://docker.io/envoyproxy/gateway-helm \ --version v1.5.4 \ -n envoy-gateway-system \ --create-namespace验证控制器状态:
kubectl get pods -n envoy-gateway-systemkubectl get gatewayclass通常会看到一个由 Envoy Gateway 管理的 GatewayClass,其控制器名为:
gateway.envoyproxy.io/gatewayclass-controller6. 部署一个示例应用
下面创建一个简单的 Web 应用,稍后通过 Gateway API 暴露出去。
apiVersion: v1kind: Namespacemetadata: name: demo-app---apiVersion: apps/v1kind: Deploymentmetadata: name: web namespace: demo-appspec: replicas: 2 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: web image: nginxdemos/hello:plain-text ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: web-svc namespace: demo-appspec: selector: app: web ports: - name: http port: 80 targetPort: 80执行:
kubectl apply -f app.yamlkubectl get pod,svc -n demo-app7. 通过 Gateway + HTTPRoute 暴露 HTTP 服务
这里我建议把网关资源放到一个独立命名空间,比如 infra-gateway,这样更贴近生产治理习惯。
7.1 创建 Gateway
apiVersion: v1kind: Namespacemetadata: name: infra-gateway---apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: public-gateway namespace: infra-gatewayspec: gatewayClassName: envoy-gateway listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: All应用:
kubectl apply -f gateway-http.yamlkubectl get gateway -n infra-gatewaykubectl get gateway public-gateway -n infra-gateway -o yaml重点观察状态字段:
Accepted=TrueProgrammed=Truestatus.addresses
拿到入口地址:
export GATEWAY_IP=$(kubectl get gateway public-gateway -n infra-gateway -o jsonpath='{.status.addresses[0].value}')echo $GATEWAY_IP7.2 创建 HTTPRoute
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: web-route namespace: demo-appspec: parentRefs: - name: public-gateway namespace: infra-gateway sectionName: http hostnames: - demo.example.com rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: web-svc port: 80应用:
kubectl apply -f httproute.yamlkubectl get httproute -n demo-appkubectl get httproute web-route -n demo-app -o yaml如果 HTTPRoute 成功挂到 Gateway,通常会在状态里看到:
Accepted=TrueResolvedRefs=True
7.3 验证 HTTP 暴露
如果域名还没有解析,可以先通过 Host 头验证:
curl -H 'Host: demo.example.com' http://${GATEWAY_IP}/如果已经解析好了 DNS,则可以直接:
curl http://demo.example.com/到这里,Gateway API 的基础服务暴露就完成了。
8. 给 Gateway 绑定 HTTPS 证书的两种常见思路
在 Gateway API 中做 HTTPS,核心是给 Gateway listener 增加 TLS 配置:
listeners:- name: https protocol: HTTPS port: 443 hostname: demo.example.com tls: mode: Terminate certificateRefs: - kind: Secret group: "" name: demo-example-com-tls这里的 certificateRefs 指向 Kubernetes Secret,Gateway Controller 会读取这个 Secret 中的证书和私钥来完成 TLS 终止。
常见绑定方式有两种:
8.1 手工创建 TLS Secret
适合内网、离线环境或已有企业 CA 的场景:
kubectl -n infra-gateway create secret tls demo-example-com-tls \ --cert=tls.crt \ --key=tls.key然后在 Gateway 的 HTTPS listener 里引用该 Secret 即可。
8.2 使用 cert-manager 自动签发和续期
这更适合长期运行的生产环境。
典型流程是:
- 在
Gateway上增加 cert-manager annotation; - cert-manager 识别到 HTTPS listener;
- 根据
hostname + certificateRefs.secretName自动创建Certificate; - 签发完成后把证书写入目标 Secret;
- Gateway Controller 读取 Secret,完成 HTTPS 加载。
本文下面采用的就是这套方式。
9. 安装 cert-manager,并开启 Gateway API 支持
9.1 安装 cert-manager
helm repo add jetstack https://charts.jetstack.iohelm repo update
helm upgrade --install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --set crds.enabled=true \ --set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \ --set config.kind="ControllerConfiguration" \ --set config.enableGatewayAPI=true验证:
kubectl get pods -n cert-managerkubectl wait --for=condition=Available deployment -n cert-manager --all --timeout=180s如果你的 Gateway API CRD 是在 cert-manager 之后安装的,建议重启一下 cert-manager:
kubectl rollout restart deployment cert-manager -n cert-manager10. 创建 Let’s Encrypt ClusterIssuer
先用 staging 环境验证,成功后再切到生产环境,避免因为调试频繁触发速率限制。
10.1 Staging Issuer
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: letsencrypt-stagingspec: acme: email: you@example.com server: https://acme-staging-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-staging-account-key solvers: - http01: gatewayHTTPRoute: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: public-gateway namespace: infra-gateway应用:
kubectl apply -f clusterissuer-staging.yamlkubectl get clusterissuer letsencrypt-staging10.2 Production Issuer
确认流程完全打通后,再切换成正式签发:
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: letsencrypt-prodspec: acme: email: you@example.com server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-prod-account-key solvers: - http01: gatewayHTTPRoute: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: public-gateway namespace: infra-gateway11. 给 Gateway 增加 HTTPS Listener 并让 cert-manager 自动签发证书
这里是最关键的一步。
11.1 更新 Gateway
apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: public-gateway namespace: infra-gateway annotations: cert-manager.io/cluster-issuer: letsencrypt-stagingspec: gatewayClassName: envoy-gateway listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: All - name: https protocol: HTTPS port: 443 hostname: demo.example.com tls: mode: Terminate certificateRefs: - kind: Secret group: "" name: demo-example-com-tls allowedRoutes: namespaces: from: All应用:
kubectl apply -f gateway-https.yaml11.2 更新 HTTPRoute,让业务挂到 HTTPS Listener
最简单的方式是把 parentRefs.sectionName 指到 https,也可以同时挂到 http 与 https 两个 listener。
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: web-route namespace: demo-appspec: parentRefs: - name: public-gateway namespace: infra-gateway sectionName: https hostnames: - demo.example.com rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: web-svc port: 8011.3 验证 cert-manager 是否自动创建证书对象
kubectl get certificate -Akubectl get certificaterequest -Akubectl get order,challenge -Akubectl describe gateway public-gateway -n infra-gatewaykubectl describe certificate demo-example-com-tls -n infra-gatewaykubectl get secret demo-example-com-tls -n infra-gateway如果一切正常,cert-manager 会自动创建名为 demo-example-com-tls 的 Certificate 和同名 Secret。
12. DNS 与 ACME HTTP-01 校验注意事项
在 Let’s Encrypt 正式签发之前,下面几件事必须满足:
- 域名
demo.example.com已解析到 Gateway 的公网 IP; - 外网可以访问你的
80端口; - cert-manager 可以在校验时临时创建用于 ACME challenge 的
HTTPRoute; - Gateway 对这个 HTTP challenge 流量可达。
检查公网地址:
kubectl get gateway public-gateway -n infra-gateway -o jsonpath='{.status.addresses[0].value}'检查 DNS:
dig +short demo.example.com两者必须一致,或者域名必须正确指向该 LB 地址。
13. 案例:为 demo.example.com 开启 HTTPS 暴露
下面把整套案例串起来看一遍。
Step 1:应用服务上线
demo-appnamespace 中运行webDeploymentweb-svc暴露 80 端口
Step 2:创建公网 Gateway
infra-gateway/public-gateway- 暴露
80和443 - 允许所有 namespace 的 Route 接入
Step 3:创建 HTTPRoute
demo-app/web-route- 绑定到
public-gateway hostnames: [demo.example.com]- 后端指向
web-svc:80
Step 4:配置 cert-manager
- 安装 cert-manager
- 开启
enableGatewayAPI=true - 创建
letsencrypt-stagingClusterIssuer
Step 5:在 Gateway 上声明 TLS
- 给
Gateway加cert-manager.io/cluster-issuerannotation - 新增
httpslistener tls.mode: TerminatecertificateRefs.name: demo-example-com-tls
Step 6:等待签发完成
kubectl get certificate -n infra-gatewaykubectl get secret -n infra-gateway demo-example-com-tlskubectl get gateway -n infra-gateway public-gateway -o yamlStep 7:验证 HTTPS
如果 DNS 已生效:
curl -Iv https://demo.example.com/如果还没做正式解析,也可以先本地强制指定 Host 进行验证:
curl -Iv --resolve demo.example.com:443:${GATEWAY_IP} https://demo.example.com/正常情况下你会看到:
- 证书链由 Let’s Encrypt 签发;
HTTP/1.1 200或HTTP/2 200;- 后端返回
nginxdemos/hello页面内容。
14. 可选优化:把 HTTP 强制跳转到 HTTPS
很多生产环境会要求 80 端口只做跳转。
可以额外创建一个绑定到 http listener 的 HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: http-to-https-redirect namespace: demo-appspec: parentRefs: - name: public-gateway namespace: infra-gateway sectionName: http hostnames: - demo.example.com rules: - filters: - type: RequestRedirect requestRedirect: scheme: https statusCode: 301应用后,访问:
curl -I http://demo.example.com/应返回 301 或 302 跳转到 HTTPS 地址。
15. 生产实践中的几个关键细节
15.1 Secret 通常要和 Gateway 在同一命名空间
证书 Secret 是给 Gateway listener 用的,而 cert-manager 基于 Gateway annotation 自动创建证书时,默认也是在 Gateway 所在 namespace 里生成 Secret。
所以一个简单、稳定的实践是:
Gateway放在infra-gateway- 证书 Secret 也放在
infra-gateway - 业务
HTTPRoute可以在各自 namespace
15.2 hostname 不能为空
对于 cert-manager 基于 Gateway 自动签发证书的场景,listener 的 hostname 非常重要。没有 hostname,cert-manager 无法正确生成证书的 dnsNames。
15.3 TLS 模式要用 Terminate
如果你希望在 Gateway 上终止 HTTPS,那么 listener 必须类似这样:
tls: mode: Terminate如果使用 Passthrough,则 TLS 不会在 Gateway 终止,而是原样透传给后端,此时证书处理逻辑完全不同。
15.4 先用 staging,再切 production
这是强烈建议。
因为 DNS 未生效、80 端口不通、Route 未绑定成功、Gateway 未就绪等问题,在首次调试中都非常常见。先用 staging 可以避免过快触发 Let’s Encrypt 限流。
15.5 云厂商实现可能有自己的证书集成方式
如果你使用的是 GKE、AKS、EKS 上的托管 Gateway / Load Balancer 产品,通常会有更云原生的证书接入方式,例如:
- 直接引用云证书资源
- 使用厂商管理证书
- 与私有 CA / 证书服务集成
这时 Gateway API 的路由方式仍然成立,但具体证书绑定字段、注解或控制器行为需要看对应实现文档。
16. 常见故障排查
16.1 Gateway 没有地址
检查:
kubectl get gateway -Akubectl describe gateway public-gateway -n infra-gatewaykubectl get pods -n envoy-gateway-system常见原因:
- Gateway Controller 没安装好;
gatewayClassName写错;- 云资源创建失败;
- LB 权限不足。
16.2 HTTPRoute 没有绑定成功
检查:
kubectl describe httproute web-route -n demo-app重点看:
AcceptedResolvedRefsParentRefs
常见原因:
parentRefs.name或 namespace 写错;- listener 不允许该 namespace 的 Route 接入;
- 后端 Service 名称或端口错误。
16.3 证书一直不签发
检查:
kubectl get certificate,certificaterequest,order,challenge -Akubectl describe certificate demo-example-com-tls -n infra-gatewaykubectl logs deploy/cert-manager -n cert-manager常见原因:
enableGatewayAPI=true没开启;- Gateway API CRD 安装顺序不对;
- 域名未解析到 Gateway 地址;
- 80 端口未对外开放;
- listener 没有
hostname; certificateRefs.name未配置;- Gateway 与 Secret namespace 不符合控制器要求。
16.4 HTTPS 可以通,但证书不对
检查:
openssl s_client -connect demo.example.com:443 -servername demo.example.com重点确认:
- 服务出的证书 CN / SAN 是否包含目标域名;
- 是否命中了错误 listener;
- 是否还在用旧 Secret;
- 是否存在多个证书与 hostname 冲突。
17. 总结
如果你现在还在用单一 Ingress 做所有服务暴露,那么在下面这些场景里,Gateway API 值得优先考虑:
- 多团队共享入口;
- 要做更清晰的平台治理;
- 需要更强的七层路由表达;
- 希望把 HTTPS、流量规则、命名空间授权拆开治理;
- 后续还要支持 TCP、gRPC、TLS Passthrough 等扩展能力。
落地时,可以记住下面这条最小链路:
- 安装 Gateway API CRD;
- 安装 Gateway Controller;
- 创建
Gateway; - 创建
HTTPRoute; - 安装 cert-manager 并开启 Gateway API 支持;
- 在
Gateway上声明 HTTPS listener 与certificateRefs; - 配置
ClusterIssuer; - 做 DNS 解析和证书验证;
- 最后加上 HTTP -> HTTPS redirect 与监控告警。
如果你只是想先跑通,我建议直接按本文的 Envoy Gateway + cert-manager + Let's Encrypt staging 方案先做一遍。它足够标准、足够通用,而且后续切换到生产证书也很顺滑。
等这套跑通之后,再结合你的云平台实现、WAF、CDN、私有 CA 或多环境发布策略继续增强,会轻松很多。