ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SAP BTP Cloud Foundry 路由映射实践:从入门到避坑指南

SAP BTP Cloud Foundry 路由映射实践:从入门到避坑指南 在 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 route、route 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。三者组合起来就是一个完整 routehttps://my-app.cfapps.eu10.hana.ondemand.com/api其中my-app是 hostnamecfapps.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 请求都会先打到 GorouterGorouter 根据请求的 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 主机名规划的实践经验主机名是最容易被忽略、但后期最难改的部分。我见过很多项目的主机名就是随便起比如test1、app、demo。等应用多了、环境多了再回头看整个路由表一团乱麻。我的建议是遵循一个统一的命名规范比如环境维度dev-myapp、test-myapp、prod-myapp应用维度myapp-api、myapp-web、myapp-worker两者结合dev-myapp-api、prod-myapp-web这么做的原因很简单路由一旦正式使用就很难改。因为所有调用方都记住了这个 URL改路由意味着通知所有下游更新配置成本极高。所以前期多花十分钟想清楚命名规范后面能省好几个小时的返工时间。还有一点测试环境不要依赖--random-route。这个参数确实能避免路由冲突但每次 push 生成的 URL 都不一样如果你是一个团队在协作其他人根本不知道去哪里访问配置文件的回调地址也会天天变。随机路由只适合个人开发环境验证代码用一旦多人协作或者对接了其他系统一定要用固定的、有明确含义的路由。2.3 路径映射用同一域名区分不同版本路由映射不只是主机名域名的组合还可以加路径。这是很多人忽略的一个能力。比如你有两个版本的 APIv1 和 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 蓝绿发布与路由切换路由映射最经典的高级应用场景就是蓝绿发布。原理非常简单两个应用实例蓝色版本和绿色版本共享同一条生产路由但同一时刻只有其中一个映射到这条路由。流程是这样蓝色版本myapp-blue当前映射着生产路由prod.myapp.com正常对外服务。新版本代码部署成绿色版本myapp-green先不映射生产路由用一个内部测试路由验证。验证通过后执行cf map-route myapp-green prod.myapp.com让绿色版本也接收生产流量。执行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 routescf 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 CLIv8会直接报错旧版行为不一致。所以规范做法是统一使用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.comGorouter 收到请求后根据Host: api.example.com找到对应路由。第三步上传 TLS 证书。这一步最容易出错。证书必须是完整的证书链包含服务器证书和中间证书。如果你用的是 Lets 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 路由冲突多个应用争抢同一个入口路由冲突是生产环境最容易出事故的问题之一。场景是这样的两个应用通过某种方式映射到了同一个 routeGorouter 就会把流量交替转发给两个应用具体行为可能因平台配置而异但结果都是不可控的。一个容易引发冲突的操作是用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.crt、intermediate.crt、root.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 routes、cf app name、cf logs name --recent确认路由映射等健康检查通过查应用日志DNS 解析失败CNAME 配置错误、TTL 未生效nslookup、dig检查 DNS 记录和生效时间路由冲突流量不稳定多个应用映射到同一 routecf routes用cf 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输出现在去看一眼也许会发现几个意想不到的惊喜。
返回列表