Traefik 子路径路由:一个域名挂多个应用

这个域名下挂着三套完全独立的东西:根路径是 WordPress,/qrcode 是一个 Nuxt 应用(二维码与文件分享),/tools 是另一个 Nuxt 应用(在线图片工具)。它们没有共用任何代码,只是恰好共用一个域名。

让这件事成立的是 Traefik 的 Ingress 路径匹配。

路径规则

Ingress 里按前缀分流,一个 host 下三条规则:

rules:
  - host: www.mazter.cn
    http:
      paths:
        - path: /qrcode
          pathType: Prefix
          backend:
            service:
              name: svc-qrcode-app
              port:
                number: 80
        - path: /tools
          pathType: Prefix
          backend:
            service:
              name: svc-tools-app
              port:
                number: 80
        - path: /
          pathType: Prefix
          backend:
            service:
              name: svc-wordpress
              port:
                number: 80

匹配规则是最长前缀优先/tools/compress 先匹配 /tools 而不是 /。所以顺序写在前面后面其实不影响结果,但把具体的写在前面更符合直觉。

要不要 stripPrefix

这是最容易踩的一个决策点。所谓 stripPrefix,是指 Ingress 把 /qrcode 这段前缀剥掉再转给后端,后端收到的就是 /

我没有用它,理由是:现代前端框架(Nuxt、Next、Vite)都要求在构建期就知道自己的 base 路径。如果让 Ingress 偷偷把前缀剥掉,前端生成的资源链接和实际访问路径就会对不上,出现”页面能打开但 JS/CSS 全 404″。

做法是让应用自己知道前缀,构建时注入:

# Nuxt 构建期指定 baseURL
nuxt.config.ts 中:
  app: {
    baseURL: '/qrcode/'
  }

于是后端收到的请求路径仍然是 /qrcode/xxx,应用自己也按 /qrcode 生成链接,两边对得上。

Middleware 的 API 组陷阱

Traefik v2 和 v3 的 CRD 分属不同的 API 组,写错的话资源能创建但完全不生效,而且不报错:

# v2(当前集群是 v2.11.18)
apiVersion: traefik.containo.us/v1alpha1

# v3
apiVersion: traefik.io/v1alpha1

判断方法很简单,看集群里实际装了哪些 CRD:

kubectl get crd | grep traefik

哪个组存在就用哪个。这个坑的表现是”我明明配了跳转 HTTPS,为什么不生效”,而 YAML 看起来毫无问题。

证书:一个 host 只有一张

三个应用共用一个 host,所以也只能有一张 TLS 证书。它们复用同一个 Secret(这里是 wp-tls),不需要为每个应用单独签发。cert-manager 按 host 签发,不是按路径。

这意味着新增子路径应用时,证书部分什么都不用做——这是共用域名的另一个便利。

什么时候该换子域名

子路径方案的前提是所有应用”彼此信任、共用一套 Cookie 域”。我的两个应用确实需要共享登录态(同一个 mazter_token Cookie 挂在根路径上),所以子路径是加分项。

如果某个应用需要完全独立的 Cookie、独立的安全策略,或者要交给别人维护,那就该拆成子域名。判断标准不是技术难度,是信任边界