4. Ingress与Route基础

在OpenShift中,把集群内部的服务暴露给集群外部的客户端访问,有两条主要路径:一条是Kubernetes原生的Ingress资源,另一条是OpenShift特有的Route资源。理解这两者的关系、Route的三种TLS终止类型,以及HAProxy Ingress Controller如何选择路由,是排查“域名访问不通”“证书不匹配”“路径走错后端”等问题的基础。

Ingress与Route:同一目标的两套API

Ingress是Kubernetes社区定义的资源,它描述“哪个主机名和路径应该转发到哪个Service”,但Ingress本身只是一个声明,真正让它生效的是Ingress Controller。OpenShift默认部署的Ingress Controller基于HAProxy实现,通常运行在openshift-ingress命名空间中,以Deployment形式提供多个副本,并通过一个名为router-default的Service对外暴露80/443端口。

Route是OpenShift在Ingress之前就存在的资源,功能与Ingress高度重叠,但表达能力更强,尤其是TLS终止方式、路径匹配、权重分流等。OpenShift的Ingress Controller同时监听Ingress和Route两类对象:当你创建Ingress时,控制器会把它转换成内部的Route逻辑;当你直接创建Route时,控制器直接使用它。因此,在OpenShift里更推荐直接使用Route,能获得更完整的特性支持。

一个典型的Route定义如下,它把app.example.com的流量转发到web-svc的8080端口:

apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: web-route
spec:
  host: app.example.com
  to:
    kind: Service
    name: web-svc
  port:
    targetPort: 8080
  tls:
    termination: edge

创建后,HAProxy Ingress Controller会为该主机名生成对应的前端(frontend)和后端(backend)配置,并重新加载HAProxy。此时用oc get route web-route可以看到HOST/PORT列显示app.example.com,ADMITTED为True表示已被控制器接受。

Route的三种TLS终止类型

Route的spec.tls.termination字段决定TLS在链路的哪一段被处理,这是最容易混淆也最影响证书配置的地方。

  • edge(边缘终止):客户端到HAProxy之间是HTTPS,HAProxy解密后以HTTP转发给后端Pod。证书配置在Route上,后端不需要证书。这是最常用的方式,适合后端只提供HTTP服务的场景。此时后端Pod看到的请求是明文HTTP,如果需要知道原始协议,可依赖X-Forwarded-Proto头。
  • passthrough(透传):HAProxy不解密,直接把TLS字节流转发给后端Pod,由后端自己完成TLS握手和解密。证书必须配置在后端Pod上,Route上不配置证书。适合后端必须自己处理TLS的场景,例如需要端到端加密或后端使用SNI做多租户路由。此时HAProxy无法读取HTTP层信息,基于路径的路由和HTTP头改写都不可用。
  • re-encrypt(重新加密):客户端到HAProxy是HTTPS,HAProxy解密后再用另一套证书重新加密转发给后端。Route上配置前端证书,同时通过destinationCACertificate指定后端证书的CA,用于校验后端。适合既要在边缘做统一入口,又要求集群内网段也加密的场景。

三者的链路差异可以用下面的信号链图表示,注意TLS终止点的位置不同,证书的放置位置也随之不同。

Route 三种 TLS 终止类型的信号链 edge 客户端 HTTPS HAProxy 解密 HTTP 后端 Pod 证书在 Route passthrough 客户端 TLS 字节流透传(不解密) HAProxy 仅转发 后端 Pod 解密 证书在后端 re-encrypt 客户端 HTTPS HAProxy 解密再加密 HTTPS 后端 Pod 证书在两端 TLS 终止点 不解密转发 二次加密

HAProxy Ingress Controller的工作方式

控制器持续watch Ingress、Route、Service、EndpointSlice和Secret等对象。当发现变化时,它把当前所有路由规则渲染成一份HAProxy配置文件,写入Pod内的/var/lib/haproxy/conf/haproxy.config,然后触发HAProxy平滑重载。每个Route对应一个frontend(按主机名和路径匹配)和一个backend(指向Service的Endpoints)。

默认情况下,控制器使用共享的VIP和端口,通过SNI和Host头区分不同Route。如果某个Route需要独立IP,可以设置spec.host之外再配合oc expose或自定义Service,但大多数场景共享入口即可。

控制器的健康状态可通过以下命令观察:

oc get pods -n openshift-ingress
oc logs -n openshift-ingress deploy/router-default --tail=50
oc get route -A -o wide

如果Route创建后ADMITTED为False,通常是因为主机名与已有Route冲突,或证书Secret不存在。日志中会出现host already used或secret not found之类的提示。

路由选择与优先级

当多个Route声明了相同的主机名时,HAProxy Ingress Controller需要决定用哪一个。选择顺序大致如下:

  1. 先比较主机名,精确匹配优先于通配符匹配。例如app.example.com优先于*.example.com。
  2. 主机名相同时,比较路径长度,路径更长的优先。例如/api/v1优先于/api。
  3. 路径也相同时,比较Route的创建时间,更早创建的优先;若时间相同,则按名称字典序。
  4. 如果Route设置了spec.alternateBackends和权重,则按权重把流量分摊到多个后端。

路径匹配默认是前缀匹配(PathPrefix),即/api会匹配/api、/api/v1、/api/v1/users。如果需要精确匹配,可在Ingress中使用pathType: Exact,Route本身不直接暴露该字段,但可通过注解或Ingress转换实现。

在同一个命名空间内,如果两个Route使用相同host和path,后创建的会被拒绝,ADMITTED为False。跨命名空间时,控制器按上述优先级全局选择,因此建议为不同团队分配不同子域名,避免隐式冲突。

一个可复现的验证示例

假设已有Service web-svc监听8080,我们创建两个Route:一个edge终止,一个passthrough,分别用不同主机名,观察证书来源和访问结果。

# edge 路由,证书来自 Secret
oc create route edge web-edge --service=web-svc --host=web-edge.example.com \
  --cert=server.crt --key=server.key

# passthrough 路由,证书在后端
oc create route passthrough web-pass --service=web-svc --host=web-pass.example.com

# 查看结果
oc get route web-edge web-pass -o custom-columns=NAME:.metadata.name,HOST:.spec.host,TERM:.spec.tls.termination,ADMITTED:.status.ingress[0].conditions[0].status

预期输出中,web-edge的TERM为edge,ADMITTED为True;web-pass的TERM为passthrough,ADMITTED为True。用curl -k https://web-edge.example.com应能拿到后端响应,且证书是Route上配置的那张;用curl -k https://web-pass.example.com时,证书由后端Pod提供,若后端未配置TLS则会握手失败。这就是判断终止类型是否配置正确的直接依据。

掌握Ingress与Route的对应关系、三种TLS终止的链路差异,以及HAProxy Ingress Controller的选择顺序,就能在遇到域名冲突、证书错误或路径走错后端时,快速定位是资源定义问题还是控制器选择问题。