当开发 Agent 能在短时间内连续修改多个分支时,传统的共享测试环境很快会变成瓶颈:配置互相覆盖、测试状态彼此污染,日志也难以对应到某一次改动。Worker Previews 的核心变化,是为每个分支提供独立的 URL、配置、状态和可观测性,让开发者与 Agent 可以并行验证改动,而不触碰生产环境。
隔离的不只是访问地址
“每个分支一个 URL”只是预览环境最直观的部分。真正有价值的是四个维度同时隔离:
- URL:每次改动都有明确入口,可以交给自动化测试、产品人员或另一个 Agent 检查。
- 配置:环境变量、功能开关和依赖地址不会与其他分支共用。
- 状态:某个分支写入的测试数据不会改变其他预览或生产环境。
- 可观测性:日志、指标和错误可以追溯到具体分支,而不是全部混在共享测试环境里。
这几个条件缺一不可。只有独立 URL、却仍然连接共享数据库的环境,并不是真正隔离;同样,如果日志中没有分支或预览标识,并行运行多个 Agent 后仍然很难定位故障。
对于 Agent 工作流,这种隔离尤其重要。Agent 不只生成代码,还可能执行部署、调用接口、写入状态并根据结果继续修改。如果所有任务共享同一套环境,一个任务产生的数据就可能误导另一个任务。
从“提交代码”转向“提交可验证环境”
接入预览环境后,一次变更的交付物不再只有 commit 或 diff,还应该包括一个可以访问和诊断的运行实例。典型流程可以设计成:
- Agent 在独立分支上完成修改。
- CI 为该分支创建预览环境,并返回唯一 URL。
- 自动化测试针对这个 URL 执行接口、页面或回归检查。
- Agent 根据独立日志和测试结果继续修复。
- 人工评审通过后合并代码,并销毁预览环境。
这里的关键是让预览 URL 成为流水线中的一等输出。例如,CI 可以把它写入 Pull Request 评论、任务记录或 Agent 上下文:
PREVIEW_URL="https://fix-login-timeout.preview.example.com"
curl --fail --show-error --silent \
"${PREVIEW_URL}/health"
curl --fail --show-error --silent \
-H 'Content-Type: application/json' \
-d '{"email":"preview-user@example.com"}' \
"${PREVIEW_URL}/api/session/check"
实际使用时,需要把域名和接口路径替换为项目自己的地址,并确保测试账号与生产账号完全分离。
用 Kubernetes 模拟分支级隔离
Worker Previews 的具体平台接口并未在摘要中展开。为了理解其运行模型,可以在 Kubernetes 中用“每个分支一个 Namespace”模拟相似的隔离方式。下面示例假设:
- 集群已经安装 Ingress Controller;
- 应用镜像监听
8080端口; - DNS 支持
*.preview.example.com; - 应用把临时状态写入
/data。
将以下内容保存为 preview.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: preview-${SLUG}
labels:
app.kubernetes.io/part-of: branch-preview
preview.branch: "${SLUG}"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: preview-${SLUG}
data:
APP_ENV: preview
PREVIEW_ID: "${SLUG}"
FEATURE_EXPERIMENTAL_UI: "true"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-state
namespace: preview-${SLUG}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: preview-${SLUG}
labels:
app: preview-app
preview.branch: "${SLUG}"
spec:
replicas: 1
selector:
matchLabels:
app: preview-app
template:
metadata:
labels:
app: preview-app
preview.branch: "${SLUG}"
spec:
containers:
- name: app
image: ${IMAGE}
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
volumeMounts:
- name: state
mountPath: /data
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: state
persistentVolumeClaim:
claimName: app-state
---
apiVersion: v1
kind: Service
metadata:
name: app
namespace: preview-${SLUG}
spec:
selector:
app: preview-app
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
namespace: preview-${SLUG}
spec:
rules:
- host: ${SLUG}.preview.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80
可以使用下面的命令从当前 Git 分支生成安全的预览标识并部署:
export SLUG="$(git branch --show-current \
| tr '[:upper:]' '[:lower:]' \
| sed -E 's/[^a-z0-9-]+/-/g; s/^-+|-+$//g' \
| cut -c1-40)"
export IMAGE="registry.example.com/my-app:$(git rev-parse --short HEAD)"
envsubst < preview.yaml | kubectl apply -f -
kubectl rollout status \
deployment/app \
-n "preview-${SLUG}"
echo "Preview: https://${SLUG}.preview.example.com"
日志也可以按预览环境单独读取:
kubectl logs \
-n "preview-${SLUG}" \
deployment/app \
--all-containers=true \
--follow
变更合并或分支关闭后,删除整个 Namespace 即可回收该预览的计算、配置和状态:
kubectl delete namespace "preview-${SLUG}"
这不是 Worker Previews 官方接口,而是一个可改造的隔离模型。实际平台如果原生提供分支 URL、状态和日志,就不需要自行维护这些 Kubernetes 资源。
落地时最容易遗漏的边界
独立预览环境并不自动等于安全环境。接入 Agent 与 CI 时,至少要检查以下事项:
- 密钥分级:预览环境只能使用测试密钥,不应获得生产数据库或支付系统凭证。
- 外部依赖隔离:邮件、短信、Webhook 和队列可能仍指向真实系统,应使用沙箱、模拟服务或明确的预览前缀。
- 数据库迁移策略:破坏性迁移不应从预览环境直接作用于共享数据库。
- 访问控制:分支 URL 可能包含尚未发布的功能和数据,需要身份认证或短期访问令牌。
- 资源回收:为关闭的分支设置自动销毁和最大存活时间,避免 Agent 高频创建环境导致成本失控。
- 统一关联标识:在日志、指标、Trace 和测试报告中写入同一个 preview ID,才能形成完整诊断链路。
- 环境一致性:预览环境应尽量复用生产构建产物和启动方式,但数据与权限必须保持隔离。
Worker Previews 的价值,不只是省去手工部署测试分支的步骤。它把每次代码修改变成一个独立、可访问、可验证、可回收的运行单元。对并行工作的开发者和 Agent 来说,这正是避免环境争用、状态污染和错误归因的基础设施。