SAP BTP Cloud Foundry 路由映射实践:从入门到避坑指南
2026/9/24 18:34:13 网站建设 项目流程

在 SAP BTP Cloud Foundry 上部署应用,很多人 push 成功之后就以为万事大吉,结果打开控制台输出的 URL,迎面一个 404,或者干脆 DNS 解析失败。问题多半出在 route 映射上。这篇东西就是聊透 SAP BTP Cloud Foundry 里的路由映射——route 到底是什么、怎么把路由映射到应用、映射策略怎么选、以及我在实际项目里踩过的各种坑。内容适合刚接触 BTP 的开发者,也适合已经在生产环境维护 CF 应用、想进一步规范路由管理的平台团队。

先说明一下,这里的 route 不是网络工程师电脑上敲的ip route,也不是route print输出的系统路由表,更不是 ThinkPHP 里那个做地址跳转的 route 配置。Cloud Foundry 里的 route 是一个外部可访问的 URL 入口,只有把路由映射到应用,这个 URL 才会变成真正的访问入口,否则它就是个没人接听的电话号码。

1. 先搞清楚 Route 在 Cloud Foundry 里到底是什么角色

1.1 从三种“路由”的对比说起

我见过太多人一听到 route 就懵。因为在不同语境下,route 的含义差了十万八千里。

传统网络里的路由(比如ip routeroute print)解决的是数据包从哪个网卡、哪条链路走的问题,它决定的是“下一跳”往哪转发。Web 框架里的路由(比如 ThinkPHP 的route地址跳转配置)解决的是 HTTP 请求路径对应到哪个控制器方法的问题,它决定的是“这个 URL 由哪段代码处理”。

而 Cloud Foundry 里的 route,解决的则是“外部请求通过什么 URL 找到我部署的应用”。在这个语境下,route 就是一个由域名、主机名、路径组合而成的完整 URL 描述。它本质上是外部流量进入应用的“门牌号”。

我经常用一个快递的类比:你有一个房子(应用),快递员(外部请求)要找到你,手里得有一张写了完整地址的快递单(route)。快递单上只有街道和城市(域名)、没有门牌号(主机名)或者没写收件人(应用),快递就送不到。CF 里的逻辑一模一样——一个 route 如果没映射到任何应用,访问它只会得到一个 404。

1.2 一个完整 Route 的三要素

在 SAP BTP Cloud Foundry 环境里,一个 route 由三部分组成:

  • Domain(域):域名的后半部分,相当于城市和街道。比如 SAP BTP 子账户默认的共享域是cfapps.eu10.hana.ondemand.com(区域不同,前缀不同)。
  • Hostname(主机名):域名前面的那一截,相当于门牌号。比如my-app
  • Path(路径):URL 中域名后面的路径部分,相当于“小区里的第几栋楼”,比如/api

三者组合起来就是一个完整 route:

https://my-app.cfapps.eu10.hana.ondemand.com/api

其中my-app是 hostname,cfapps.eu10.hana.ondemand.com是 domain,/api是 path。

需要注意的点是,在 SAP BTP Cloud Foundry 环境中,“应用名”和“路由的主机名”没有必然联系。你 push 一个名为my-backend-service的应用,如果不对路由做任何配置,它默认生成的主机名可能跟应用名相同,但这只是约定的惯例,不是强制规则。你完全可以手动创建一条主机名为api-gateway的路由,映射到my-backend-service应用上。这点理解不透彻,后面设计路由策略的时候就容易绕晕。

1.3 Route 映射背后发生了什么

当你执行cf map-route命令时,平台做的事情远不止“写一条记录”这么简单。它会通知 Cloud Foundry 的 Control Plane,把这个 route 注册到 Gorouter 的路由表中。

Gorouter 是 Cloud Foundry 内置的 HTTP 负载均衡器和请求路由器。所有进入平台的外部 HTTP 请求都会先打到 Gorouter,Gorouter 根据请求的 Host 头和 URL 路径,在路由表中找到对应的应用实例,然后把请求转发过去。

所以“路由映射”这个动作,本质上就是在 Gorouter 的路由表里建立一条“URL 地址 → 应用实例”的对应关系。没有这条对应关系,请求到了 Gorouter 那一步就直接返回 404。

实际运维中,我遇到过有人在cf push之后发现应用访问不了,第一反应是查应用日志。但如果是路由问题,应用日志里什么都查不到,因为请求压根没到应用那层。这个排查思路上的误区很常见,后面我会专门展开讲。

2. 路由映射的策略:怎么设计才稳定又灵活

2.1 共享域还是自定义域

SAP BTP 每个子账户都会有一个默认的共享域,格式一般是cfapps.<region>.hana.ondemand.com。比如我的环境在 EU10 区域的子账户,共享域就是cfapps.eu10.hana.ondemand.com

用共享域的好处是省事,零配置就能用。坏处也很明显:URL 又长又丑,而且所有 BTP 用户都在同一个根域下面,你只能靠主机名去区分彼此。对生产环境来说,这既不专业,也存在一定的安全风险——一旦某个主机名因为误配置被其他子账户占用,就可能出现路由冲突。

这里我强烈建议,生产环境一定要配置自定义域。所谓自定义域,就是你自己注册的域名,比如api.example.com。配置路径是先把自定义域添加到子账户,然后在 DNS 服务商那边配置 CNAME 记录,把自定义域指向 BTP 默认域,再上传 TLS 证书,最后创建 route 时指定这个自定义域。

可能有人觉得配置自定义域很麻烦,但一旦配置好,后续的所有路由映射都基于自定义域来做,整个 URL 体系会清爽得多。而且企业级应用的对外接口,用https://api.example.com明显比https://my-app.cfapps.eu10.hana.ondemand.com更可信。

2.2 主机名规划的实践经验

主机名是最容易被忽略、但后期最难改的部分。我见过很多项目的主机名就是随便起,比如test1appdemo。等应用多了、环境多了,再回头看,整个路由表一团乱麻。

我的建议是遵循一个统一的命名规范,比如:

  • 环境维度dev-myapptest-myappprod-myapp
  • 应用维度myapp-apimyapp-webmyapp-worker
  • 两者结合:dev-myapp-apiprod-myapp-web

这么做的原因很简单:路由一旦正式使用,就很难改。因为所有调用方都记住了这个 URL,改路由意味着通知所有下游更新配置,成本极高。所以前期多花十分钟想清楚命名规范,后面能省好几个小时的返工时间。

还有一点,测试环境不要依赖--random-route。这个参数确实能避免路由冲突,但每次 push 生成的 URL 都不一样,如果你是一个团队在协作,其他人根本不知道去哪里访问,配置文件的回调地址也会天天变。随机路由只适合个人开发环境验证代码用,一旦多人协作或者对接了其他系统,一定要用固定的、有明确含义的路由。

2.3 路径映射:用同一域名区分不同版本

路由映射不只是主机名+域名的组合,还可以加路径。这是很多人忽略的一个能力。

比如你有两个版本的 API,v1 和 v2,在同一个应用实例中同时提供服务,那你完全可以用一条路由:

https://api.example.com/v1 → 你的应用 https://api.example.com/v2 → 还是你的应用

也就是说,同一个应用可以绑定多条带不同 path 的 route。这比开两个应用、配两个域名来得简单,因为共享了同一个主机名和证书,外部调用方只需要改路径前缀就能切换版本。

但需要注意:路径映射需要应用自己处理路径前缀的区分。CF 的 Gorouter 会把完整路径转发给应用,如果你的应用框架默认从根路径开始匹配路由,那/v1/v2都需要在应用代码里做配置。举例来说,Spring Boot 应用可以把server.servlet.context-path设置成/v1/v2,但同一个应用实例只能有一个 context-path,所以如果要通过不同 path 区分版本,通常需要部署两个实例,分别用不同 context-path,再映射不同的 path 路由。

这里有个更容易踩坑的细节:如果你用cf map-route给应用增加了/v1路径,但应用本身没有对/v1做任何处理,Gorouter 会把请求转发过去,应用却返回 404。这种“路由已映射、应用未处理”的情况,排查起来比“路由未映射”更费劲,因为从路由层面看一切正常,问题出在应用自身。映射路径之前,务必确认应用能正确处理这个路径前缀

2.4 蓝绿发布与路由切换

路由映射最经典的高级应用场景就是蓝绿发布。原理非常简单:两个应用实例(蓝色版本和绿色版本)共享同一条生产路由,但同一时刻只有其中一个映射到这条路由。

流程是这样:

  1. 蓝色版本myapp-blue当前映射着生产路由prod.myapp.com,正常对外服务。
  2. 新版本代码部署成绿色版本myapp-green,先不映射生产路由,用一个内部测试路由验证。
  3. 验证通过后,执行cf map-route myapp-green prod.myapp.com,让绿色版本也接收生产流量。
  4. 执行cf unmap-route myapp-blue prod.myapp.com,把蓝色版本摘掉。

这个方案的好处是可以做到秒级切换,因为 Gorouter 的路由表更新很快,不需要重新部署任何东西。而且发现问题后回滚也快,把路由重新映射回蓝色版本就行。

我强烈建议把这一套流程脚本化,不要靠手工敲命令。因为生产环境的蓝绿切换必须又快又准,手工操作在紧张情况下很容易漏掉某个unmap-route,导致两个版本同时接收生产流量,一部分用户走到新版本、一部分用户走到旧版本,出问题的时候排查起来非常痛苦。

3. 实操记录:从零到一完成路由映射

3.1 环境准备与基础命令

在动手之前,先确认 cf CLI 已经安装并登录成功。登录命令如下:

cf login -a https://api.cf.eu10.hana.ondemand.com -u <你的用户名>

执行后会提示输入密码,然后选择 Org 和 Space。这里提醒一句:登录时不要图省事直接回车选默认 org 和 space,尤其当你同时有好几个客户的子账户时,选错了环境,后面所有操作都会落到错误的地方。

登录成功后,先做两个基础检查:

cf domains cf routes

cf domains会列出当前空间可用的所有域名,包括共享域和通过cf create-domain创建的自定义域。cf routes会列出当前空间下已经存在的所有路由,以及每条路由当前映射到了哪个应用。

这个习惯非常重要。我曾经在一个共享平台上排查问题,发现有人创建了一条路由但没映射任何应用,还有一条路由映射到了一个已经删除的应用上,残留的路由占着主机名不放,导致别人想用同一个名字却创建不了。所以动手之前先看一眼现有的路由清单,能省掉很多不必要的麻烦。

3.2 创建并映射路由:三种方式对比

映射路由的方式有三种,我平时会根据场景选择。

第一种:push 时自动创建

cf push的时候,如果没有指定任何路由相关参数,平台会自动创建一条主机名为应用名的路由,映射到当前应用,域名用默认共享域。这是最省事的方式,适合快速验证。

cf push my-app

这条命令执行完,访问https://my-app.cfapps.eu10.hana.ondemand.com就能打开应用。

第二种:用命令显式创建路由

如果你不想用共享域,或者路由的主机名与应用名不一致,可以手动创建:

cf create-route <space> <domain> --hostname <hostname> cf map-route <app> <domain> --hostname <hostname>

比如我想把生产路由api.example.com映射到my-app应用:

cf create-route dev cfapps.eu10.hana.ondemand.com --hostname api cf map-route my-app cfapps.eu10.hana.ondemand.com --hostname api

这里要特别注意,cf create-route<space>是路由所属的空间,而cf map-route不需要指定空间,默认映射当前登录空间的应用。如果你创建路由的空间和应用所在的空间不一致,映射会失败。

第三种:manifest.yml 统一管理

生产环境我推荐这种方式。把路由写在manifest.yml里,跟随应用代码一起版本化,团队成员只要 push 代码就能保证路由配置一致:

applications: - name: my-app memory: 512M instances: 2 routes: - route: my-app.cfapps.eu10.hana.ondemand.com - route: my-app.cfapps.eu10.hana.ondemand.com/api

这个配置会创建两条路由:一条根路径https://my-app.cfapps.eu10.hana.ondemand.com,一条/api路径。应用启动后两条地址都能访问。

关于 manifest 里的路由写法有一个坑:不要在同一个应用配置里同时使用routes和旧版的host/domain字段。如果你写了routes,又写了host,旧版本的 cf CLI 可能会报错,或者routes里的配置被host覆盖。我实测下来,新版 cf CLI(v8+)会直接报错,旧版行为不一致。所以规范做法是统一使用routes

3.3 自定义域映射:DNS 与证书的完整流程

自定义域映射说起来不复杂,但每一步都有坑。

第一步,在子账户中把自定义域加进去:

cf create-domain <org> example.com

第二步,去 DNS 服务商那里配置一条 CNAME 记录。关键点来了:CNAME 的指向,是 SAP BTP 给你分配的默认域,比如cfapps.eu10.hana.ondemand.com。也就是说,让api.example.com这个域名解析到cfapps.eu10.hana.ondemand.com,Gorouter 收到请求后,根据Host: api.example.com找到对应路由。

第三步,上传 TLS 证书。这一步最容易出错。证书必须是完整的证书链,包含服务器证书和中间证书。如果你用的是 Let's Encrypt 或者企业 CA 签发的证书,通常需要把 CA 签发的中间证书内容拼接到服务器证书后面,形成一个 PEM 文件,再上传。

cf create-service-key <service> <key> cf create-certificate <cert-name> --cert <path-to-cert.pem> --key <path-to-private-key.pem>

第四步,创建 route 时指定自定义域:

cf map-route my-app example.com --hostname api

映射完成后,https://api.example.com就能访问到应用了。

需要多说一句,自定义域的 DNS 解析需要时间,CNAME 配置完成后不要立刻测试,通常等几分钟到几小时不等(取决于 TTL 设置)。我遇到过配置完 CNAME 立刻测试,解析失败,以为配置错了,结果过半小时再看就通了。所以做这一步的时候耐心一点,先用nslookup确认 DNS 解析生效了,再测 HTTPS 访问。

3.4 蓝绿切换的完整命令序列

这里给一套可以直接抄的蓝绿发布命令序列,假设生产路由是prod.myapp.com

# 1. 部署绿色版本,用独立测试路由 cf push myapp-green -f manifest-green.yml # 2. 先用测试路由验证 curl -I https://myapp-green.cfapps.eu10.hana.ondemand.com # 3. 确认测试路由返回 200 后,把生产路由映射到绿色版本 cf map-route myapp-green example.com --hostname prod # 4. 再次验证生产路由 curl -I https://prod.example.com # 5. 确认无误后,摘掉蓝色版本的生产路由 cf unmap-route myapp-blue example.com --hostname prod # 6. 清理:如果蓝色版本不再需要,可以删除应用和测试路由 cf delete myapp-blue -f cf delete-route example.com --hostname prod -f

这里有一个非常关键的细节:映射完绿色版本后,不要马上执行第 5 步。中间至少要留出验证时间,确认绿色版本在生产路由上响应正常(比如连续请求几次,检查返回内容和响应时间),再发起切换。有些团队为了“快”,map 完立刻 unmap 旧版本,结果绿色版本因为配置缺失在生产流量下挂了,又没有旧版本可以回滚,只能紧急重新 push 旧代码,整个过程反而更慢。

另外,两个应用实例的资源配置要尽量一致。如果蓝色版本是 2 实例、1G 内存,绿色版本部署时写成 1 实例、512M 内存,切换后流量进来,性能表现可能天差地别。部署前检查一下 manifest 或者启动参数,避免环境差异。

4. 常见问题与排查技巧实录

4.1 路由映射后返回 404:先判断请求停在哪一层

我之前说过,404 是最常见的路由问题,但 404 的原因可以分好几层。

第一层,DDoS/WAF 层直接拦截,请求压根没到 BTP。这个跟路由配置无关,需要检查是不是有防火墙、API 网关在中间拦截了请求。

第二层,DNS 解析失败。访问域名解析不到 IP 地址,浏览器直接报错。用nslookup <domain>或者dig <domain>看解析结果。

第三层,Gorouter 层 404。请求到了 BTP,但路由表里没有对应记录。这是最典型的“路由未映射”场景。用cf routes检查主机名,用cf app <app-name>检查应用状态。

第四层,应用层 404。路由映射成功,请求也转发到了应用,但应用自己的代码返回 404。这个时候查看应用日志,cf logs <app-name> --recent,能看到请求的实际处理结果。

判断的关键技巧是:先看cf routes的输出,确认 route 确实存在并且映射到了正确的应用。如果 route 存在但映射的应用不对,或者映射到了多个应用(路由冲突),问题就出在映射配置上。

4.2 路由冲突:多个应用争抢同一个入口

路由冲突是生产环境最容易出事故的问题之一。场景是这样的:两个应用通过某种方式映射到了同一个 route,Gorouter 就会把流量交替转发给两个应用(具体行为可能因平台配置而异,但结果都是不可控的)。

一个容易引发冲突的操作是:cf push重新部署时,正好另一个团队也在部署同名应用。如果两个团队在同一个 space 工作,应用名相同,路由就可能互相抢占。

排查方法很简单:

cf routes

输出里会列出每条路由对应的应用。如果某个 route 后面跟着多个应用名,就说明存在路由冲突。

解决方法是先想清楚哪个应用应该占用这个路由,然后cf unmap-route把其他应用摘掉。摘的时候要确认当前没有流量打到该应用上,否则摘掉瞬间可能会有请求丢失。

4.3 路由刚映射完,访问还是超时

这种情况多半不是路由的问题,而是应用本身启动慢或者健康检查没通过

CF 的健康检查机制是:应用必须通过健康检查后,Gorouter 才会把流量转发给它。如果你的应用启动耗时超过健康检查的超时周期,路由虽然映射了,但请求依然会失败。

排查命令是:

cf app <app-name>

输出里有健康检查的状态。如果是starting或者down,说明应用还没就绪。这时候要做的是等应用完全启动,或者调整健康检查配置。

cf push my-app -c "node server.js" --health-check-type http --health-check-http-endpoint /health

这里指定了 HTTP 健康检查,端点是/health。应用必须在这个端点上返回200,且连续返回指定次数后,才会被认为是 healthy。

我自己的习惯是:健康检查的端点不要用根路径/,最好单独实现一个/health端点,只返回最简单的状态字,不依赖数据库、第三方 API 等外部资源。因为健康检查只是确认“进程活着、能响应 HTTP 请求”,不代表业务依赖全部就绪。如果健康检查端点去查了数据库,数据库一旦抖动,健康检查失败,Gorouter 就会把所有请求都拒掉,放大故障。

4.4 自定义域证书报错:证书链不完整

自定义域配置好之后,访问 https 报证书错误,最常见的原因是证书链不完整。

PEM 证书文件应该包含三部分:服务器证书、中间证书、根证书(可选但建议包含)。很多从 CA 下载的证书 zip 包会分成好几个文件:server.crtintermediate.crtroot.crt。上传 BTP 时,需要把这三个文件拼接成一个 PEM 文件:

cat server.crt intermediate.crt root.crt > fullchain.pem

拼接顺序不能乱:服务器证书在最前,根证书在最后。顺序反了,某些客户端校验会失败。

还有一个小细节:私钥文件不能设密码保护。如果你的私钥是用openssl req -newkey rsa:2048 -passout pass:xxx生成的,上传到 BTP 之后应用启动或路由映射可能会报错,因为平台没法自动输入密码。解决办法是生成时不加密,或者用openssl rsa -in encrypted.key -out decrypted.key解密。

4.5 常见问题速查表

现象可能原因排查命令/工具处理方法
访问 URL 返回 404路由未映射、应用未就绪、应用代码 404cf routescf app <name>cf logs <name> --recent确认路由映射,等健康检查通过,查应用日志
DNS 解析失败CNAME 配置错误、TTL 未生效nslookupdig检查 DNS 记录和生效时间
路由冲突,流量不稳定多个应用映射到同一 routecf routescf unmap-route摘掉多余应用
证书错误证书链不完整、私钥加密、域名不匹配浏览器查看证书链拼接完整证书链,解密私钥
路由映射成功但应用 404应用未处理对应路径curl 应用日志检查应用路由/前缀配置
路由删除失败路由仍被应用占用cf routes先 unmap 再 delete

这个表格我建议保存下来,排查问题的时候先对照表格缩小范围,比盲目试命令高效得多。

5. 一些进阶想法:把路由做成团队的基础设施

路由映射做到后面,就不只是“把 URL 指向应用”那么简单了。当应用多了、环境多了、版本发布频繁了,路由映射的管理方式会直接影响整个团队的交付效率和稳定性。

我的经验是,把路由配置代码化。所有应用的manifest.yml都纳入 Git 仓库管理,路由变更走代码评审流程。这样每次路由变更都有记录,出了问题可以追溯是谁在什么时间改了什么东西。

另外,定期做路由清理。每季度跑一次cf routes,把没有映射任何应用的冗余路由删掉。这些残留路由会占用主机名,导致后来者无法使用相同的主机名,还会增加 Gorouter 路由表的负载。删除前确认没有应用在映射状态:

cf unmap-route <app> <domain> --hostname <hostname> cf delete-route <domain> --hostname <hostname> -f

五年前我第一次在 CF 上做路由映射,也觉得这个功能简单——不就是创建个 URL 嘛。但真正跑过生产环境、经历过路由冲突、经历过蓝绿切换切了一半发现新版本不可用之后,才意识到路由映射是整个应用交付链路里最不起眼但最不能出错的一环。事后回头看,路由映射这件事,规划比操作重要得多。先把域名、主机名、路径策略定清楚,把发布流程脚本化,后面自然就顺了。如果你还没认真看过自己项目的cf routes输出,现在去看一眼,也许会发现几个意想不到的惊喜。

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

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

立即咨询