agents24/agents cicd-automation 插件高级部署策略参考:GitHub Actions / GitLab CI / Azure Pipelines 多平台流水线与金丝雀及数据库回滚实战
本文是 agents24/agents 插件市场中 cicd-automation 插件 deployment-pipeline-design 技能的进阶参考指南,主题聚焦于部署流水线的“高级纵深”:三大主流 CI/CD 平台(GitHub Actions、GitLab CI、Azure Pipelines)的完整生产级流水线配置、多区域金丝雀推广、Argo Rollouts 高级流量模式(基于实验的 A/B 分析、基于 Header 的内测分流)、数据库迁移回滚策略(Expand/Contract、Flyway Undo、零停机索引)、蓝绿切换与数据库共舞的完整示例,以及部署冻结自动化与通知脚本。读完本文,你将能直接复刻这些配置,为自己的多环境、多区域 Kubernetes 服务设计出“质量门禁 + 渐进式发布 + 快速可逆”的生产交付链路。
deployment-pipeline-design 技能采用“渐进式披露”分层组织文档:核心模式与决策表位于 SKILL.md,常规模式详解与工作示例位于 details.md,而本文所讲的正是其中的“Advanced Topics”层——平台定制化配置与高级回滚策略。本文由该参考文档(plugins/cicd-automation/skills/deployment-pipeline-design/references/advanced-strategies.md)为骨架展开,可配合仓库内同插件的 github-actions-templates、gitlab-ci-patterns、secrets-management 等技能一并查阅,技能如何被 Agent 挂载与激活可参考 docs/agent-skills.md。
前置定位:一份“参考层”文档在技能三层结构中的位置
在动手阅读 YAML 之前,先明确这份参考文档的定位。deployment-pipeline-design 技能解决的问题域是:设计带审批门禁、安全检查与部署编排的多阶段 CI/CD 流水线,尤其当需要零停机部署、金丝雀/蓝绿发布、多环境推广或多区域发布时使用。其主文档 SKILL.md 给出了五类核心交付物:流水线阶段定义、部署策略选择(带注释的权重/切换参数)、健康检查设计(浅/深探测与冒烟脚本)、门禁定义(自动化指标阈值 + 人工审批)、回滚计划。它还维护了四类常见故障的排查指引(深健康检查、inconclusiveLimit 卡死、生产环境审批未配置、Docker 层缓存失效、回滚后数据库迁移错位)。
本文档在 SKILL.md 中通过 references/advanced-strategies.md 被引用,负责补齐“扩展 YAML 示例、平台定制配置、多区域金丝雀与数据库回滚策略”。因此如果你要落地一套“build → test → security → staging → e2e → production”的完整链路,details.md 提供各阶段拼图,而本文提供可直接整体照搬的“整条流水线”。
GitHub Actions:一条覆盖安全扫描与 Argo Rollouts 金丝雀的全生产流水线
仓库内 github-actions-templates 技能覆盖 GitHub Actions 实现模式,而本参考文档给出的是一条“从 push 到生产金丝雀、再到失败自动回滚”的端到端流水线。整体阶段依赖为:build → (security-scan ‖ test) → deploy-staging → e2e-tests → deploy-production。这段配置可以直接保存为 .github/workflows/production.yml:
# .github/workflows/production.yml
name: Production Pipeline
on:
push:
branches: [main]
workflow_dispatch:
inputs:
skip_tests:
type: boolean
default: false
permissions:
contents: read
id-token: write # for OIDC auth to cloud providers
jobs:
build:
runs-on: ubuntu-latest
outputs:
image_tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha,prefix=,format=short
- name: Build and push (with layer cache)
uses: docker/build-push-action@v5
with:
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
security-scan:
needs: build
runs-on: ubuntu-latest
steps:
- name: Run Trivy vulnerability scan
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: ghcr.io/${{ github.repository }}:${{ needs.build.outputs.image_tag }}
exit-code: 1
severity: CRITICAL,HIGH
- name: SAST with Semgrep
uses: semgrep/semgrep-action@v1
with:
config: auto
test:
needs: build
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
steps:
- uses: actions/checkout@v4
- name: Run test suite
run: make test-ci
env:
DATABASE_URL: postgres://postgres:test@localhost/test
deploy-staging:
needs: [test, security-scan]
environment:
name: staging
url: https://staging.example.com
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/deploy-staging
aws-region: us-east-1
- name: Deploy to EKS staging
run: |
aws eks update-kubeconfig --name my-cluster-staging
kubectl set image deployment/my-app \
app=ghcr.io/${{ github.repository }}:${{ needs.build.outputs.image_tag }}
kubectl rollout status deployment/my-app --timeout=5m
e2e-tests:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Playwright E2E suite
run: npx playwright test --reporter=github
env:
BASE_URL: https://staging.example.com
deploy-production:
needs: e2e-tests
environment:
name: production
url: https://app.example.com
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/deploy-production
aws-region: us-east-1
- name: Deploy canary to production
run: |
aws eks update-kubeconfig --name my-cluster-prod
kubectl argo rollouts set image my-app \
app=ghcr.io/${{ github.repository }}:${{ needs.build.outputs.image_tag }}
- name: Monitor canary promotion
run: |
kubectl argo rollouts status my-app --timeout=30m
- name: Rollback on failure
if: failure()
run: kubectl argo rollouts abort my-app
这条配置里值得拆解的要点:
- OIDC 联邦而非长期密钥:
permissions.id-token: write与aws-actions/configure-aws-credentials@v4的role-to-assume配合,用工作负载身份直接换取临时 AWS 凭证,staging 与 production 使用不同的 IAM 角色,从根上避免了在仓库 Secrets 中长期存放云厂商密钥。 - 一次构建、镜像标签贯穿全程:
buildjob 通过outputs.image_tag(来自docker/metadata-action的type=sha,format=short)把镜像标签暴露给后续所有 job。后续 staging、production 部署引用的都是同一个标签,这符合 details.md 中“Artifact promotion——build once,promote the same artifact”的最佳实践。 - 构建层缓存:
cache-from: type=gha与cache-to: type=gha,mode=max把镜像层缓存放到 GitHub Actions 缓存中,避免每次从零构建。这一点恰好呼应 SKILL.md 的排障条目——若COPY . .出现在依赖安装之前,任何源码改动都会使依赖层缓存失效。 - 安全门禁“卡死”流水线:
security-scanjob 中 Trivy 配置了exit-code: 1且只对CRITICAL,HIGH级别生效,一旦镜像存在高危漏洞,扫描直接以非零码退出,后续deploy-staging(needs: [test, security-scan])不会启动。 - staging 是生产的“试金石”:E2E 测试只在部署到 staging 之后运行,且通过环境变量把
BASE_URL指向 staging。也就是说,Playwright 套件验证的是“真实运行中的暂存版本”,而不是本地模拟。 - 生产使用 Argo Rollouts 而非原生 Deployment:
deploy-production用kubectl argo rollouts set image触发金丝雀,kubectl argo rollouts status my-app --timeout=30m阻塞等待自动晋级或失败,而if: failure()的kubectl argo rollouts abort my-app在监控超时/失败时强制中止金丝雀、回落稳定版本——这就是一个标准的“自动化回滚触发器”。
值得注意:示例依赖 kubectl argo rollouts 插件与 EKS 集群配置,使用前请确保 runner 上安装 Argo Rollouts kubectl 插件、并存在对应的 Rollout 资源与 AnalysisTemplate(见下文“高级 Argo Rollouts 模式”一节)。
GitLab CI:动态环境 + 按需启停的 staging 环境
GitLab CI 示例对应同插件 gitlab-ci-patterns 技能。它展示了一个与 GitHub Actions 不同但同样重要的能力——动态环境(Dynamic Environments):每次部署到 staging 都可以创建/销毁独立环境,生产部署则以人工触发方式把关。完整 .gitlab-ci.yml 如下:
# .gitlab-ci.yml
stages:
- build
- test
- staging
- production
variables:
DOCKER_DRIVER: overlay2
IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $IMAGE .
- docker push $IMAGE
- docker tag $IMAGE $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
test:unit:
stage: test
image: $IMAGE
script:
- make test
coverage: '/coverage: \d+\.\d+%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
test:security:
stage: test
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH $IMAGE
deploy:staging:
stage: staging
environment:
name: staging
url: https://staging.example.com
on_stop: stop:staging
script:
- kubectl apply -f k8s/staging/
- kubectl set image deployment/my-app app=$IMAGE -n staging
- kubectl rollout status deployment/my-app -n staging
only:
- main
stop:staging:
stage: staging
environment:
name: staging
action: stop
script:
- kubectl delete namespace staging --ignore-not-found
when: manual
only:
- main
deploy:production:
stage: production
environment:
name: production
url: https://app.example.com
script:
- kubectl set image deployment/my-app app=$IMAGE -n production
- kubectl rollout status deployment/my-app -n production --timeout=10m
when: manual
only:
- main
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
allow_failure: false
可关注的细节:
- 用当前提交 SHA 打镜像标签:
IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA,保证流水线内所有 job 引用同一不可变标签;同时把latest作为后续构建的--cache-from缓存基准,形成“上次构建即缓存”的滚动优化。 - 用构建产物自身跑测试:
test:unit使用image: $IMAGE,直接在刚构建出的镜像里执行make test,避免测试环境与运行环境漂移;还能通过coverage: '/coverage: \d+\.\d+%/'正则从日志中解析覆盖率并生成 Cobertura 报告制品。 - Trivy 容器内扫描:
test:security使用entrypoint: [""]覆盖 Trivy 镜像默认入口,使其可以作为普通 job 运行,并对CRITICAL,HIGH级别的漏洞以--exit-code 1阻断流水线。 on_stop动态环境回收:deploy:staging的environment.on_stop: stop:staging声明了配套的停止 job,stop:staging设置action: stop并通过kubectl delete namespace staging --ignore-not-found在人工触发时销毁整个 staging 命名空间。这是 GitLab 动态环境“按需存在、用完即焚”的标准写法。- 生产门禁三件套:
deploy:production同时使用when: manual与rules(allow_failure: false)——只有 main 分支会出现该 job、必须人工点击触发、且人工 job 不允许被忽略;与之对比 details.md 给出了“时间窗口式”人工审批(when: delayed; start_in: 30 minutes),两种门禁可按团队节奏选用。
Azure Pipelines:内置 canary 策略与人工审批的部署 job
Azure Pipelines 示例把“人工审批”与“金丝雀自动放量”封装进 deployment job 的 strategy 之中,是三种平台里对渐进式发布原生支持最直观的一种。完整 azure-pipelines.yml 如下:
# azure-pipelines.yml
trigger:
branches:
include:
- main
variables:
imageRepository: 'myapp'
containerRegistry: 'myregistry.azurecr.io'
tag: '$(Build.BuildId)'
stages:
- stage: Build
displayName: 'Build & Test'
jobs:
- job: BuildAndTest
pool:
vmImage: ubuntu-latest
steps:
- task: Docker@2
displayName: Build image
inputs:
command: build
repository: $(imageRepository)
dockerfile: Dockerfile
tags: $(tag)
- task: Docker@2
displayName: Push image
inputs:
command: push
repository: $(imageRepository)
tags: $(tag)
- script: make test
displayName: Run tests
- stage: Staging
displayName: 'Deploy to Staging'
dependsOn: Build
jobs:
- deployment: DeployStaging
environment: staging
pool:
vmImage: ubuntu-latest
strategy:
runOnce:
deploy:
steps:
- task: KubernetesManifest@0
inputs:
action: deploy
manifests: k8s/staging/*.yaml
containers: $(containerRegistry)/$(imageRepository):$(tag)
- stage: Production
displayName: 'Deploy to Production'
dependsOn: Staging
jobs:
- deployment: DeployProduction
environment:
name: production
resourceType: Kubernetes
pool:
vmImage: ubuntu-latest
strategy:
canary:
increments: [10, 25, 50]
preDeploy:
steps:
- task: ManualValidation@0
inputs:
notifyUsers: 'release-managers@example.com'
instructions: 'Verify staging metrics. Approve to start canary.'
onTimeout: reject
deploy:
steps:
- task: KubernetesManifest@0
inputs:
action: deploy
manifests: k8s/production/*.yaml
containers: $(containerRegistry)/$(imageRepository):$(tag)
postRouteTraffic:
steps:
- script: ./scripts/verify-deployment.sh https://app.example.com
on:
failure:
steps:
- task: KubernetesManifest@0
inputs:
action: reject
要点解读:
- 阶段依赖与镜像 tag:
Build → Staging → Production通过dependsOn串行;镜像 tag 使用$(Build.BuildId)(Azure 全局唯一构建号),保证每个 stage 拉取同一个不可变镜像。 - deployment job 才是“会部署”的 job:普通
job与deploymentjob 的区别在于后者绑定environment、支持生命周期 hook 与发布策略。staging 用runOnce,production 用canary。 - canary 策略三段式 hook:
preDeploy:放量前的人工验证门禁。ManualValidation@0通知release-managers@example.com检查 staging 指标,onTimeout: reject保证长时间无人审批时自动拒绝而非无限挂起——对应 SKILL.md 中“staging 成功但 production 不启动,通常是环境保护规则/审批人未配置”的排查点。deploy+increments: [10, 25, 50]:KubernetesManifest@0分三次放量 10% → 25% → 50%,由 Azure Pipelines 在路由层面切流。postRouteTraffic:每次放量后执行./scripts/verify-deployment.sh做健康校验(该脚本模式见 details.md 的 Post-Deployment Verification Script)。on.failure:任一步骤失败即对发布执行action: reject,自动拒绝金丝雀。
- 参考文档中该配置的 staging 环境未额外声明
resourceType,production 声明为 Kubernetes,表示生产环境资源由 Kubernetes 集群承载——KubernetesManifest@0会使用这些manifests指向的清单进行部署/拒绝操作。
多区域金丝雀推广:先导区域验证,再并行铺满其余区域
当服务需要部署到多个 AWS 区域时,最安全的顺序是“单点突破、再横向扩散”。参考文档给出的模式分为两步:
deploy-pilot:先只向us-east-1这一个“试点区域”发布金丝雀,并阻塞等待其完成自动晋级(--timeout=20m);deploy-secondary:needs: deploy-pilot确认试点成功后才启动,用 GitHub Actions 的strategy.matrix对[us-west-2, eu-west-1, ap-southeast-1]三个区域并行执行相同的发布流程,并通过environment: production-${{ matrix.region }}让每个区域拥有独立的环境保护规则。
# deploy-multiregion.yml (GitHub Actions)
jobs:
deploy-pilot:
environment: production-us-east-1
runs-on: ubuntu-latest
steps:
- name: Deploy canary to pilot region
run: |
aws eks update-kubeconfig --name cluster-us-east-1 --region us-east-1
kubectl argo rollouts set image my-app app=$IMAGE
kubectl argo rollouts status my-app --timeout=20m
deploy-secondary:
needs: deploy-pilot
strategy:
matrix:
region: [us-west-2, eu-west-1, ap-southeast-1]
environment: production-${{ matrix.region }}
runs-on: ubuntu-latest
steps:
- name: Deploy to ${{ matrix.region }}
run: |
aws eks update-kubeconfig --name cluster-${{ matrix.region }} \
--region ${{ matrix.region }}
kubectl argo rollouts set image my-app app=$IMAGE
kubectl argo rollouts status my-app --timeout=20m
这个“试点-扩散”拓扑的价值在于:把故障爆炸半径限制在单一区域。若 pilot 区域的金丝雀未能通过分析(如错误率超标),deploy-secondary 根本不会启动,其余区域不受影响;同时各区域有各自的 environment,便于在 GitHub Actions 的 Environment 设置中配置按区域划分的审批人。注意这段片段中 $IMAGE 是占位变量,真实落地时应替换为上节“GitHub Actions”示例里 build job 输出的 image_tag,确保所有区域使用完全相同的制品版本。
高级 Argo Rollouts 模式
当滚动发布需要“用真实流量验证版本好坏”时,Deployment 与简单金丝雀已经不够用。参考文档给出两种高阶用法。
基于实验的金丝雀(A/B 分析):stable 与 canary 同台对比
普通金丝雀只能看到“新版跑得怎样”;实验式金丝雀则让基线(stable)与金丝雀(canary)同时承载流量并接受同一个分析模板对比打分,从而获得具备统计意义的 A/B 结论。对应 Rollout 清单如下:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 20
strategy:
canary:
canaryService: my-app-canary
stableService: my-app-stable
trafficRouting:
istio:
virtualService:
name: my-app-vsvc
analysis:
templates:
- templateName: success-rate
- templateName: latency-p99
startingStep: 1
args:
- name: service-name
value: my-app-canary
steps:
- setWeight: 5
- experiment:
templates:
- name: baseline
specRef: stable
- name: canary
specRef: canary
analyses:
- templateName: ab-test
requiredForCompletion: true
- setWeight: 20
- pause: { duration: 10m }
- setWeight: 50
- pause: { duration: 15m }
- setWeight: 100
结构逐段拆解:
- 流量路由层:
trafficRouting.istio.virtualService声明由名为my-app-vsvc的 Istio VirtualService 负责切流;canaryService/stableService分别对应新旧版本的 Kubernetes Service。 - 分析即门禁:
analysis.templates挂载success-rate与latency-p99两个模板,startingStep: 1表示分析从第 1 步起即开始运行。模板的具体指标定义(成功条件/失败条件/inconclusiveLimit)参考 details.md 的 Automated Metric Gate 示例与 SKILL.md 的“canary 永不晋级”排障(Prometheus 无数据时分析会一直 inconclusive,应设置inconclusiveLimit让它快速失败而非无限挂起)。这里给analysis传入了参数service-name=my-app-canary,供分析模板的 PromQL 精确圈定金丝雀服务的指标范围。 - 实验步骤:先
setWeight: 5放 5% 试探,然后进入experiment步骤:同时拉起基于stable的baseline与基于canary的副本,运行ab-test分析,且requiredForCompletion: true表明该实验分析必须出结论才算完成。只有当 A/B 结论通过后,才依次setWeight: 20(暂停 10 分钟)→50(暂停 15 分钟)→100完成晋级。若 ab-test 判定金丝雀劣于基线,Rollout 自动回退。
基于 Header 的金丝雀:切流之前先做“内部人”验证
在生产流量按权重拆分之前,团队往往希望先让自己人、QA 或内部测试流量“定向”访问新版本。基于 Header 的路由让带有 X-Canary: true 请求头的用户固定打到金丝雀 Pod,其余流量仍按 90/10 权重在 stable/canary 间分配:
# Route users with X-Canary: true header to canary pods
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: my-app-vsvc
spec:
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: my-app-canary
- route:
- destination:
host: my-app-stable
weight: 90
- destination:
host: my-app-canary
weight: 10
这段配置与前文 Rollout 中的 trafficRouting.istio.virtualService.name: my-app-vsvc 是配套关系:Rollout 声明“用这个 VirtualService 切流”,此 VirtualService 定义“怎么切”。两类规则并列存在:
- match 优先:凡是
x-canary: exact: "true"的请求 100% 进入my-app-canary。通常由浏览器插件、调试代理或内部入口网关统一附加该 Header,实现“员工内测金丝雀”; - 兜底权重:无该 Header 的普通请求 90% 到 stable、10% 到 canary,仍然保留小流量真实用户体验。
两个模式结合,即构成“Header 定向内测 → 确认无误后进入权重放量 → 分析通过后全量”的完整渐进链路。
数据库迁移回滚策略
应用层可以“秒级回滚”,数据库却无法秒级“回滚”,这是所有高可用发布中最危险的部分。参考文档在此给出三套互补策略。
Expand/Contract:用三个发布周期换取永不破坏向后兼容
核心约束一句话:永远不要在同一个发布周期内破坏向后兼容性。迁移被拆成三个独立发布窗口:
Release N: Add nullable column (expand)
Release N+1: Backfill data, deploy new code that reads new column
Release N+2: Drop old column (contract) — safe because no code references it
对应的两条迁移脚本:
# Release N migration — backward compatible
cat > migrations/V20240315__expand_add_email_v2.sql <<'EOF'
ALTER TABLE users ADD COLUMN email_v2 VARCHAR(255);
CREATE INDEX CONCURRENTLY idx_users_email_v2 ON users(email_v2);
EOF
# Release N+2 migration — contract (only after old code retired)
cat > migrations/V20240415__contract_drop_email.sql <<'EOF'
ALTER TABLE users DROP COLUMN email;
ALTER TABLE users RENAME COLUMN email_v2 TO email;
EOF
- Release N 只“加列”(
email_v2可空、不阻塞),这是加性变更,旧代码依旧读email,互不干扰; - Release N+1 回填数据、部署读新列的新代码;
- Release N+2 才真正
DROP COLUMN email并RENAME——此时旧代码已从所有环境退役,删列不会引发 schema/code 错位。
这正好呼应 SKILL.md 中“回滚后数据库迁移仍停留在旧代码”的排障建议:服务回滚而不回滚迁移会引发 schema/code 不匹配,务必让迁移在至少一个发布周期内保持向后兼容(仅加性),并把 undo 脚本与迁移一并版本化。
Flyway Undo 脚本:向前迁移、向后可撤
Flyway 是版本化数据库迁移工具,其 undo 能力提供结构化回滚路径:
# Forward migration
flyway migrate
# Undo the most recent migration (requires Flyway Teams)
flyway undo
# Undo a specific version
flyway undo -target=20240315
配套的命名约定:正向迁移用 V 前缀,回退脚本用 U 前缀,同一版本号一一对应:
Undo script naming convention: `U20240315__expand_add_email_v2.sql` (prefix `U` instead of `V`).
需要如实说明的限制:flyway undo 是 Flyway Teams(商业版)能力;且 undo 只适合“最近一次、可安全逆转”的变更,破坏性变更的兜底仍是 Expand/Contract + 充分的前向兼容窗口。
零停机索引创建(PostgreSQL)
在在线系统上建索引时,普通 CREATE INDEX 会持有表锁、阻塞读写:
-- Blocking (avoid in production):
CREATE INDEX idx_users_email ON users(email);
-- Non-blocking (safe for live systems):
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
结论:所有生产迁移都应使用 CREATE INDEX CONCURRENTLY,并且把它放进“部署应用之前”的预部署迁移步骤执行。以三发布 Expand/Contract 为例,索引创建就应放在 Release N 的预部署阶段,让新索引在应用切换读写前先就绪。
蓝绿切换与数据库的完整配套示例
蓝绿部署(blue-green)本身切换极快、回滚瞬时,但一旦涉及数据库,就必须把“向前兼容的迁移”编排进绿环境的启动流程。参考文档给出的完整步骤序列值得逐行阅读:
# blue-green-deploy.yml
steps:
- name: Deploy green environment
run: |
kubectl apply -f k8s/green/
- name: Run database migrations (forward, backward-compatible)
run: |
kubectl exec -n production deploy/migration-job -- flyway migrate
- name: Smoke test green
run: |
kubectl port-forward -n production svc/my-app-green 8080:80 &
sleep 3
curl -sf http://localhost:8080/health/ready
- name: Switch traffic to green
run: |
kubectl patch service my-app \
-p '{"spec":{"selector":{"slot":"green"}}}'
- name: Verify green is live
run: ./scripts/verify-deployment.sh https://app.example.com
- name: Scale down blue
run: |
kubectl scale deployment my-app-blue --replicas=0
- name: Rollback to blue on failure
if: failure()
run: |
kubectl patch service my-app \
-p '{"spec":{"selector":{"slot":"blue"}}}'
kubectl scale deployment my-app-blue --replicas=5
时序中的关键决策点:
- 先部署、后迁移、再切流:绿环境(
slot: green)先就绪,紧接着通过migration-job执行flyway migrate——这里的迁移必须“只前向、且向后兼容”(即上文的加性变更),因为此刻蓝环境还在继续承载全部流量; - 冒烟探测真实依赖:
port-forward到绿环境后 curl/health/ready,探测的是“包含数据库等真实依赖”的深健康端点,而非只返回 200 的浅/ping(深浅健康检查的对比见 details.md); - 切流是“改 Service selector”而不是改代码:
kubectl patch service my-app把 selector 从slot: blue改成slot: green,流量瞬时翻转,这是蓝绿回滚速度“Instant”的由来(对应 details.md 决策表中 Blue-Green 的“零停机、瞬时回滚、2x 临时基础设施成本”); - 失败即一键倒回:任一步失败都会执行反向 patch(selector 指回
slot: blue)并把蓝环境扩容回--replicas=5——因为迁移是向后兼容的,旧代码在旧 schema 上仍能正常运行。
部署冻结自动化:用脚本在流水线入口挡住“节假日发布”
大型团队常在感恩节、年末等高风险窗口冻结生产发布。参考文档给出一个轻量方案:用一个 Python 脚本作为流水线首个检查步骤,命中冻结窗口即失败退出。脚本维护一个冻结窗口表,每个窗口以 (月, 起始日, 结束日, 描述) 表示:
#!/usr/bin/env python3
# scripts/check-freeze-window.py
import sys
from datetime import datetime, timezone
FREEZE_WINDOWS = [
# (month, day_start, day_end, description)
(11, 25, 30, "US Thanksgiving"),
(12, 20, 31, "Year-end freeze"),
(1, 1, 2, "New Year"),
]
now = datetime.now(timezone.utc)
month, day = now.month, now.day
for fm, d_start, d_end, label in FREEZE_WINDOWS:
if fm == month and d_start <= day <= d_end:
print(f"BLOCKED: Deployment freeze active — {label}")
print("Override with FORCE_DEPLOY=true environment variable if critical.")
if not os.environ.get("FORCE_DEPLOY"):
sys.exit(1)
print("No active freeze window — deployment allowed.")
可复制性提示:以上片段依赖
os.environ,但原文未显式import os;实际运行前请补上import os,否则冻结窗口判断会因NameError失败。
接入流水线时把它作为首个执行步骤,并将仓库级变量 FORCE_DEPLOY 注入环境变量,让应急发布可以显式覆盖冻结:
- name: Check deployment freeze window
run: python scripts/check-freeze-window.py
env:
FORCE_DEPLOY: ${{ vars.FORCE_DEPLOY }}
设计要点:脚本以 UTC 计算“当前时刻”,规避不同时区 runner 造成的窗口判断漂移;默认策略是“命中即拦”,FORCE_DEPLOY 是刻意需要人工配置的逃生舱——对应 details.md 最佳实践中“Deployment windows——优先低流量窗口,用门禁策略强制变更冻结期”。
通知模板:把发布结果以结构化信息推给团队
发布链路闭环的最后一块是“让正确的人第一时间知道结果”。参考文档的 Slack 通知脚本把部署结果封装为带颜色与字段的 Slack attachment:
#!/usr/bin/env bash
# scripts/notify-slack.sh
STATUS="${1:?pass 'success' or 'failure'}"
WEBHOOK="${SLACK_WEBHOOK:?SLACK_WEBHOOK not set}"
REPO="${GITHUB_REPOSITORY:-unknown}"
SHA="${GITHUB_SHA:-unknown}"
ACTOR="${GITHUB_ACTOR:-unknown}"
if [ "$STATUS" = "success" ]; then
COLOR="good"
EMOJI=":white_check_mark:"
TEXT="Production deploy succeeded"
else
COLOR="danger"
EMOJI=":red_circle:"
TEXT="Production deploy FAILED — rollback triggered"
fi
curl -sf -X POST "$WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"attachments\": [{
\"color\": \"$COLOR\",
\"text\": \"$EMOJI $TEXT\",
\"fields\": [
{\"title\": \"Repo\", \"value\": \"$REPO\", \"short\": true},
{\"title\": \"SHA\", \"value\": \"${SHA:0:7}\", \"short\": true},
{\"title\": \"Deployed by\", \"value\": \"$ACTOR\", \"short\": true}
]
}]
}"
使用要点:
- 脚本前置守卫
"${1:?...}"与"${SLACK_WEBHOOK:?...}"保证缺少必传参数或环境变量时立即失败并给出可读错误,属于防御式 Bash 的典型写法; - 成功时绿色
good+ 白勾,失败时红色danger+ 红圈,字段里同时带上 Repo、截断到 7 位的 SHA 与触发人$ACTOR; - 它默认读取 GitHub Actions 注入的
GITHUB_REPOSITORY/GITHUB_SHA/GITHUB_ACTOR环境变量并带:-unknown兜底,因此也可复用于本地或其他平台的手动执行; - 在流水线中把
STATUS传成success/failure、SLACK_WEBHOOK从 CI/CD 平台的加密 Secret 读取(不应硬编码),即完成部署通知闭环。该脚本与 details.md 中“Notify on success”步骤及同插件 secrets-management 技能强调的“绝不明文硬编码 Secret”原则完全一致。
在本仓库中如何被 Agent 使用
以上策略并非孤立文本,而是 agents24/agents 这一 multi-harness Agentic 插件市场中 cicd-automation 插件的组成部分。整套资料按“命令 → Agent → Skill”三层组织:
- Skill 是知识层:
deployment-pipeline-design(含本文档、SKILL.md、details.md)供 Agent 在接到“设计零停机发布、实现金丝雀、搭建多环境推广”类任务时按需读取; - Agent 是决策层:同插件提供 cloud-architect.md、deployment-engineer.md、kubernetes-architect.md、devops-troubleshooter.md、terraform-specialist.md 等专职 Agent,在生成交付物时会组合本文的平台配置与回滚方案;
- Command 是触发层:workflow-automate.md 是流水线自动化专家指令,当用户描述需要自动化的部署/发布诉求时由该命令承载。
参考文档的开篇即注明“核心模式与决策表位于 SKILL.md”,因此无论由 Claude Code、Codex、Cursor、OpenCode、GitHub Copilot 还是 Google Antigravity 接入本插件市场,推荐的检索路径都是:先读 SKILL.md 决定策略(滚动/蓝绿/金丝雀/重建/特性开关),再查 details.md 补常规编排,最后按需翻开本文档照搬生产级多平台配置与高级回滚方案。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00