3. 环境命名与 URL 设计规范

Review Apps 能否被稳定访问,很大程度上取决于环境名与 URL 是否具备“可预测、可复现、可回收”三个特征。GitLab 在创建动态环境时,会依据 environment.name 生成环境记录,并依据 environment.url 生成可点击的访问入口。如果命名规则设计得随意,就会出现环境名冲突、URL 覆盖、DNS 记录无法自动清理等问题。本节围绕 CI_COMMIT_REF_SLUG 展开,说明如何生成唯一环境名,并给出自定义域名、路径路由与 DNS 策略的落地方法。

一、为什么以 CI_COMMIT_REF_SLUG 作为命名基元

GitLab 预定义变量 CI_COMMIT_REF_SLUG 是当前分支或标签名经过“小写化、非字母数字替换为连字符、截断至 63 字符”处理后的结果。例如分支 feature/login_UI 会被转换为 feature-login-ui。它天然满足 DNS 标签(label)的字符集要求,因此非常适合作为环境名和子域名的基元。

但仅用 CI_COMMIT_REF_SLUG 并不足以保证唯一性。原因有三:其一,不同项目可能使用相同分支名;其二,同一分支的多次流水线需要区分;其三,GitLab 环境名对大小写和特殊字符敏感,而 slug 已丢失原始信息。因此推荐采用“项目标识 + 分支 slug + 短哈希”的组合结构。

# 推荐的环境名生成方式(示例)
variables:
  ENV_SLUG: "${CI_PROJECT_NAME}-${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}"

# 示例输出(假设项目名为 shop-web,分支为 feature/login_UI,提交短哈希为 a1b2c3d4)
# shop-web-feature-login-ui-a1b2c3d4

其中 CI_COMMIT_SHORT_SHA 取提交哈希前 8 位,既保证同一分支不同提交的环境互不覆盖,又不会让名称过长。若希望同一分支的多次流水线复用同一环境(例如仅保留最新一次),可去掉短哈希,改用 CI_PIPELINE_IID 作为可选后缀,但需接受“后一次部署覆盖前一次”的语义。

二、环境名与 URL 的映射关系

环境名是 GitLab 内部标识,URL 是外部访问入口,两者应保持可推导的映射关系,便于排障时快速定位。常见做法是让 URL 的子域名等于环境名,例如:

environment:
  name: review/$CI_COMMIT_REF_SLUG
  url: https://$CI_PROJECT_NAME-$CI_COMMIT_REF_SLUG.$REVIEW_BASE_DOMAIN

这里 REVIEW_BASE_DOMAIN 是项目级或组级变量,例如 review.example.com。最终 URL 形如 https://shop-web-feature-login-ui.review.example.com。注意环境名中的 review/ 前缀是 GitLab 的文件夹式分组,用于在“运维 > 环境”页面按层级折叠展示,它不会出现在 URL 中。

下图展示了从分支名到环境名、再到 URL 与 DNS 记录的完整映射链路,以及各环节的关键约束。

分支 → 环境名 → URL → DNS 映射链路 分支 / 标签 feature/login_UI CI_COMMIT_REF_NAME Slug 化 feature-login-ui CI_COMMIT_REF_SLUG 环境名 review/feature-login-ui environment.name 访问 URL shop-web-feature- login-ui.review. example.com 约束: 仅小写字母、数字、连字符;长度 ≤ 63;不能以连字符开头或结尾;环境名区分大小写但 URL 不区分。 短哈希用于区分同一分支的不同提交,避免环境互相覆盖。 DNS 策略:通配符解析 + 按需记录 通配符 CNAME *.review.example.com → ingress-lb.example.com 按需 A / CNAME shop-web-feature-login-ui 由 CI 调用 DNS API 创建 环境停止时清理 on_stop 触发删除记录 避免 DNS 记录堆积 通配符适合快速起步;按需记录适合需要精确控制与审计的场景。

三、自定义域名与路径路由的选择

子域名方案(branch.review.example.com)的优点是隔离性好、Cookie 作用域天然分离、TLS 证书可用通配符证书覆盖。缺点是每个分支都需要一条 DNS 记录,若分支数量大,DNS 管理压力上升。此时可改用通配符 DNS:*.review.example.com 指向 Ingress 负载均衡器,由 Ingress 根据 Host 头路由到对应 Service。这样无需为每个分支单独创建 DNS 记录,只需保证 Ingress 规则随环境创建而更新。

路径路由方案(review.example.com/shop-web/feature-login-ui/)只需一条 DNS 记录和一个证书,适合分支数量多、域名资源紧张的场景。但它有两个明显代价:一是前端资源若使用绝对路径(如 /assets/app.js)会互相冲突,需要构建时注入 basePath;二是 Cookie 默认作用域为整个域名,不同分支的登录态可能互相干扰,需要为每个环境设置不同的 Cookie 名称或 Path。因此路径路由更适合纯 API 服务或已支持运行时 basePath 的前端框架。

维度 子域名路由 路径路由
DNS 记录 每条环境一条,或通配符一条 仅需一条
TLS 证书 通配符证书 *.review.example.com 单域名证书即可
前端资源冲突 无 需配置 basePath
Cookie 隔离 天然隔离 需手动设置 Path 或名称
适用场景 多分支、需强隔离 分支多、域名资源有限

四、DNS 策略与生命周期联动

无论选择哪种路由方式,DNS 记录都应与环境生命周期绑定。推荐在 .gitlab-ci.yml 中定义 on_stop 动作,当环境被停止(手动停止或分支删除触发)时,调用 DNS 服务商 API 删除对应记录。若使用通配符 DNS,则无需逐条删除,但仍需清理 Ingress 规则和命名空间,避免残留资源占用集群配额。

# 示例:环境停止时清理 DNS 记录(伪代码,需替换为实际 DNS API)
stop_review:
  stage: cleanup
  script:
    - ./scripts/dns-delete.sh "$CI_PROJECT_NAME-$CI_COMMIT_REF_SLUG"
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual
命名长度提醒:DNS 标签总长度不得超过 63 字符。若项目名与分支名都较长,建议对组合结果再做一次截断,并保留短哈希作为唯一性兜底。例如 shop-web-feature-login-ui-a1b2c3d4 共 36 字符,仍在安全范围内。
常见错误:直接使用 CI_COMMIT_REF_NAME 作为环境名。该变量保留原始大小写和特殊字符,例如 Feature/Login_UI,会导致 URL 中出现大写字母和下划线,部分 DNS 解析器和浏览器会将其视为非法或自动转换,从而出现“环境创建成功但无法访问”的现象。务必使用 CI_COMMIT_REF_SLUG。

综合来看,环境命名与 URL 设计的核心原则是:以 CI_COMMIT_REF_SLUG 为基元,叠加项目标识与短哈希保证唯一性;URL 与环境名保持可推导映射;根据分支规模在子域名与路径路由之间做取舍;DNS 记录随环境创建而生成、随环境停止而清理。把这四点固化为项目级规范后,Review Apps 才能从“偶尔能用”变成“每次合并请求都可稳定访问”的基础设施。