Gemini实战:用AI写CI/CD脚本提升研发效能## 一个手动部署受害者的自白我永远记得那个周五的晚上。那是2025年12月的一个周五,下午5点47分。我已经收拾好东西准备下班了,产品经理突然冲过来:“这个支付功能的hotfix今天必须上,有个客户投诉说付款一直失败。“行吧。打开电脑,开始走部署流程:1. 本地切到hotfix分支,跑build2. build失败了,因为有个同事改了某个依赖的版本但没更新package-lock3. 修好依赖,重新build4. 把dist目录scp到服务器5. SSH登录服务器6. 备份当前版本(手动cp -r)7. 解压新版本8. 重启PM2进程9. 看日志,报错了——环境变量少了一个10. 找运维要环境变量,加上11. 重启,看日志,终于正常了12. 发消息给产品经理:“上了"13. 产品经理:“客户说还是不行"14. 查日志,发现是数据库连接串指向了测试库15. 改配置,重启16. 客户说好了17. 看了一眼时间,晚上8点23分这三个小时里,我做了至少7个纯手工操作,每一步都可能出错。而且最让人崩溃的是——这不是第一次了。几乎每次部署都是一场冒险,你永远不知道哪个环节会出幺蛾子。那天晚上回家后我躺在沙发上想:这事不能再这么搞下去了。## 一、问题的全貌我是公司一个6人小团队的技术负责人,负责一个电商SaaS平台。技术栈不算复杂:Node.js后端 + React前端 + MySQL + Redis + 阿里云ECS。但部署流程烂得不像话。花了一周时间梳理了一下现状:| 部署环节 | 当前方式 | 耗时 | 风险点 ||---------|---------|------|--------|| 代码构建 | 本地npm run build | 8-12min | 依赖版本不一致 || 产物传输 | scp手动上传 | 3-5min | 网络不稳定时可能中断 || 服务器操作 | SSH手动执行 | 10-15min | 命令记错/漏步骤 || 配置管理 | .env文件手动编辑 | 5min | 容易改错/漏改 || 进程重启 | PM2手动重启 | 2min | 重启期间服务中断 || 健康检查 | 肉眼看日志 | 3-5min | 容易漏看错误 || 回滚 | 手动恢复备份 | 10-15min | 备份可能不完整 || 总计 | | 41-57min | 7个手工操作点 |每次部署平均45分钟,每周部署2-3次,光是部署就消耗了团队每周约3个小时。但这还不是最大的问题——最大的问题是不可靠。我统计了过去3个月的部署记录:| 月份 | 部署次数 | 一次成功 | 需要修复后重试 | 失败需回滚 | 成功率 ||------|---------|---------|--------------|-----------|--------|| 10月 | 11次 | 6次 | 3次 | 2次 | 55% || 11月 | 13次 | 7次 | 4次 | 2次 | 54% || 12月 | 9次 | 5次 | 3次 | 1次 | 56% |成功率只有55%左右。也就是说,接近一半的部署会出问题。这个问题不解决,团队效率永远上不去。## 二、为什么选择Gemini来辅助决定搞CI/CD之后,我面临一个选择:自己从头学GitHub Actions/GitLab CI的配置语法,还是用AI辅助?说实话,我不是DevOps工程师,对CI/CD的配置文件并不熟悉。自己写当然可以,但学习曲线陡峭,而且容易写出有问题的配置。选择Gemini的原因有几个:1. 上下文窗口大:Gemini 1.5 Pro支持100万token的上下文窗口,我可以把整个项目的README、Dockerfile、package.json都扔给它,让它理解全貌后再生成配置2. 免费额度够用:Google AI Studio的免费额度对于个人使用完全够了3. 对Google Cloud生态熟悉:我们的部分服务跑在GCP上,Gemini对GCP相关的配置理解更准确4. 多模态能力:可以截图架构图给它看,让它理解部署拓扑当然,Copilot和Claude也能做这件事。选Gemini不是因为它碾压其他工具,而是因为它在我这个场景下够用、免费、上下文够大。## 三、第一阶段:从手动到半自动### 3.1 让Gemini理解项目全貌我没有直接让Gemini写配置,而是先让它"认识"我的项目。我在Google AI Studio里开了一个新的对话,把以下信息都喂给了它:项目背景:- 电商SaaS平台,Node.js + React + MySQL + Redis- 代码托管在GitHub(私有仓库)- 服务器在阿里云ECS(2台,Nginx负载均衡)- 前端构建产物放到OSS + CDN- 后端用PM2管理Node进程当前部署流程(手动):1. 本地 git pull origin main2. npm install && npm run build3. scp dist/ root@server:/app/dist4. SSH登录服务器,pm2 reload all5. 手动检查日志期望目标:- push到main分支自动触发部署- 先跑测试,测试通过才部署- 前端部署到OSS,后端部署到ECS- 支持回滚Gemini的回应让我比较惊喜——它没有直接甩一堆yaml给我,而是先问了我几个问题:1. 阿里云的AccessKey准备好了吗?需要配置到GitHub Secrets2. ECS上是否已经安装了Docker?是否考虑容器化?3. 数据库迁移(migration)是怎么做的?需要在部署时执行吗?4. 是否需要分环境(staging/production)?这些问题问得好。如果它直接生成配置,大概率会因为缺少上下文而不准确。我逐一回答后,它给出了一个分阶段的迁移方案:- 第一阶段:先搞定自动化构建和测试- 第二阶段:搞定前端自动部署到OSS- 第三阶段:搞定后端自动部署到ECS- 第四阶段:加入蓝绿部署和回滚### 3.2 第一阶段的配置Gemini生成的第一阶段配置(GitHub Actions):yaml# .github/workflows/ci.ymlname: CI - Build and Teston: push: branches: [main, develop] pull_request: branches: [main]jobs: build-and-test: runs-on: ubuntu-latest services: mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: testpass MYSQL_DATABASE: test_db ports: - 3306:3306 options: >- --health-cmd="mysqladmin ping -h 127.0.0.1" --health-interval=10s --health-timeout=5s --health-retries=3 redis: image: redis:7-alpine ports: - 6379:6379 options: >- --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Run lint run: npm run lint - name: Run database migration env: DATABASE_URL: mysql://root:testpass@127.0.0.1:3306/test_db run: npm run migrate - name: Run tests env: DATABASE_URL: mysql://root:testpass@127.0.0.1:3306/test_db REDIS_URL: redis://127.0.0.1:6379 NODE_ENV: test run: npm run test:coverage - name: Build run: npm run build - name: Upload coverage uses: codecov/codecov-action@v4 if: github.ref == 'refs/heads/main' with: file: ./coverage/lcov.info这个配置有几个亮点是Gemini自己加的,我没要求:- 用GitHub Actions的 services 起了一个MySQL和Redis容器做测试环境,这样测试在一个干净的环境里跑,不受本地环境影响- 加了健康检查(--health-cmd),确保MySQL和Redis完全启动后才跑测试- coverage只在main分支上传,避免PR时重复上传但也有需要修改的地方。比如我们的migration脚本实际上需要一个 --env=test 参数,Gemini不知道这个细节。这类"需要人工微调"的地方在后面会反复出现。### 3.3 第一阶段效果配置上线后,效果立竿见影:| 指标 | 手动时期 | CI自动化后 | 变化 ||------|---------|-----------|------|| 构建一致性 | 经常不一致 | 100%一致 | 彻底解决 || 测试执行率 | 约60%(经常跳过) | 100%(强制执行) | +40% || 构建耗时 | 8-12min | 4min(缓存加速) | -60% || 人工干预次数 | 每次都需要 | 几乎为0 | -95% |以前经常出现"我这能跑你那不能跑"的问题,现在大家都一样——都在GitHub Actions的同一个环境里跑,要么都通过要么都不通过。## 四、第二阶段:前端自动部署到OSS### 4.1 配置前端部署相对简单:build产物上传到阿里云OSS,CDN自动刷新。Gemini生成的配置:yaml# .github/workflows/deploy-frontend.ymlname: Deploy Frontendon: push: branches: [main] paths: - 'frontend/**' - '.github/workflows/deploy-frontend.yml'jobs: deploy: needs: build-and-test # 依赖CI job通过 runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install and build run: | cd frontend npm ci npm run build - name: Upload to Aliyun OSS uses: tvrcgo/oss-action@master with: key: ${{ secrets.OSS_KEY_ID }} secret: ${{ secrets.OSS_KEY_SECRET }} region: oss-cn-hangzhou bucket: my-app-prod assets: frontend/dist/:dist/ - name: Refresh CDN cache run: | aliyun cdn RefreshObjectCaches --ObjectPath "https://cdn.myapp.com/*" \ --ObjectType Directory env: ALIYUN_ACCESS_KEY_ID: ${{ secrets.OSS_KEY_ID }} ALIYUN_ACCESS_KEY_SECRET: ${{ secrets.OSS_KEY_SECRET }}### 4.2 踩坑:OSS路径问题Gemini生成的配置有一个路径问题。assets: frontend/dist/:dist/ 这行的意思是把 frontend/dist/ 下的文件上传到OSS的 dist/ 目录下。但我们的OSS bucket结构是直接把文件放在根目录,不需要 dist/ 前缀。我跟Gemini说明了这个问题,它很快给出了修正:yamlassets: frontend/dist/:/:dist/ 改成 :/ 就行了。这种小问题在AI辅助配置的过程中很常见——AI理解了大致逻辑,但具体路径、命名这些细节需要人工确认。### 4.3 前端部署前后对比| 指标 | 手动scp上传 | CI自动部署到OSS ||------|-----------|----------------|| 部署耗时 | 5min(含手动操作) | 90秒(全自动) || 文件遗漏风险 | 高(偶尔漏传文件) | 0(自动上传整个目录) || CDN刷新 | 手动在控制台点 | 自动执行 || 回滚 | 手动传旧版本 | 重新触发上一次成功的workflow |## 五、第三阶段:后端自动部署到ECS这是最难的一步。后端部署比前端复杂得多,因为涉及到:- 数据库migration执行- PM2进程重启(重启期间服务会短暂中断)- 健康检查- 回滚机制### 5.1 初版配置Gemini给的初版方案是通过SSH连接ECS执行部署命令:yaml# .github/workflows/deploy-backend.ymlname: Deploy Backendon: push: branches: [main] paths: - 'backend/**' - '.github/workflows/deploy-backend.yml'jobs: deploy: needs: build-and-test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' environment: production steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install and build run: | cd backend npm ci --production=false npm run build - name: Create deployment package run: | cd backend tar -czf ../deploy-package.tar.gz dist/ package.json package-lock.json prisma/ ecosystem.config.js - name: Upload package to server uses: appleboy/scp-action@v0.1.7 with: host: ${{ secrets.ECS_HOST }} username: ${{ secrets.ECS_USER }} key: ${{ secrets.ECS_SSH_KEY }} source: "deploy-package.tar.gz" target: "/tmp/" - name: Deploy on server uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.ECS_HOST }} username: ${{ secrets.ECS_USER }} key: ${{ secrets.ECS_SSH_KEY }} script: | cd /app # 备份当前版本 BACKUP_NAME="backup-$(date +%Y%m%d%H%M%S)" cp -r dist /tmp/$BACKUP_NAME # 解压新版本 tar -xzf /tmp/deploy-package.tar.gz -C /app/ # 安装生产依赖 npm ci --production # 执行数据库迁移 npx prisma migrate deploy # 重启PM2 pm2 reload ecosystem.config.js --env production # 清理 rm /tmp/deploy-package.tar.gz - name: Health check run: | sleep 10 for i in {1..6}; do RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://api.myapp.com/health) if [ "$RESPONSE" = "200" ]; then echo "Health check passed!" exit 0 fi echo "Attempt $i: Health check returned $RESPONSE, retrying in 10s..." sleep 10 done echo "Health check failed!" exit 1这个配置已经比较完整了,包含了备份、部署、迁移、重启、健康检查五个步骤。但实际跑起来还是遇到了一些问题。### 5.2 踩坑记录坑1:PM2 reload导致短暂的服务中断pm2 reload 虽然是"零停机"重启,但实际上在reload的那一瞬间,正在处理的请求可能会被中断。我们有个支付接口在reload时正好有个用户在支付,请求中断了,用户体验很差。解决方案:Gemini建议改成蓝绿部署。即同时部署到两台ECS,先部署A,等A健康检查通过后再部署B。这样总有一台在服务。坑2:数据库migration失败导致服务不可用有一次 prisma migrate deploy 失败了(因为新migration有个语法错误),但脚本继续执行了 pm2 reload,导致服务重启后连数据库都连不上。解决方案:在migration后面加一个错误检查:bashnpx prisma migrate deployif [ $? -ne 0 ]; then echo "Migration failed! Rolling back..." cp -r /tmp/$BACKUP_NAME dist/ pm2 reload ecosystem.config.js --env production exit 1fiGemini帮我补上了这段逻辑,但这种"失败后回滚"的思路其实应该一开始就有。这也是AI辅助的一个特点——你不去问,它可能不会主动考虑异常路径。坑3:SSH超时有时候服务器响应慢,SSH连接会超时导致部署中断。解决方案是给scp和ssh都加上超时参数,并且设置重试逻辑。### 5.3 最终版:蓝绿部署经过几轮迭代,最终的部署配置实现了蓝绿部署。核心思路是利用Nginx的upstream切换:| 步骤 | 操作 | 说明 ||------|------|------|| 1 | 确定当前活跃服务器 | 通过Nginx配置判断A还是B在服务 || 2 | 部署到备用服务器 | 在非活跃服务器上执行部署 || 3 | 执行migration | 在备用服务器上跑数据库迁移 || 4 | 健康检查 | 对备用服务器发请求验证 || 5 | 切换Nginx upstream | 把流量切到新部署的服务器 || 6 | 等待旧服务器处理完请求 | grace period 30秒 || 7 | 健康检查通过 → 完成 | 失败 → 自动切回旧服务器 |蓝绿部署的核心脚本(Nginx切换部分):bash#!/bin/bash# switch_upstream.sh - 切换Nginx upstream# 用法: ./switch_upstream.sh [server_a|server_b]TARGET=$1NGINX_CONF="/etc/nginx/conf.d/upstream.conf"if [ "$TARGET" = "server_a" ]; then cat > $NGINX_CONF << 'EOF'upstream backend { server 10.0.1.10:3000 weight=10; server 10.0.1.11:3000 weight=0 down;}EOFelif [ "$TARGET" = "server_b" ]; then cat > $NGINX_CONF << 'EOF'upstream backend { server 10.0.1.10:3000 weight=0 down; server 10.0.1.11:3000 weight=10;}EOFfinginx -t && nginx -s reloadecho "Switched to $TARGET"这个脚本不是我写的,是Gemini生成的,但逻辑很清晰——通过修改Nginx的upstream配置来实现流量切换,weight=0 down 表示不往这台服务器发请求。## 六、第四阶段:测试自动化CI/CD搞到一半的时候,我发现一个问题:虽然部署自动化了,但测试还是不够。如果测试覆盖不够,CI通过不代表代码没问题,那自动部署反而可能更快地把Bug推到线上。### 6.1 测试分层跟Gemini讨论后,我们设计了一个分层测试策略:| 测试层 | 执行时机 | 覆盖范围 | 目标 ||--------|---------|---------|------|| 单元测试 | 每次PR | 单个函数/模块 | 快速反馈,秒级完成 || 集成测试 | merge到main前 | API端到端 | 验证模块间协作 || E2E测试 | 部署到staging后 | 完整用户流程 | 验证业务流程正确 || 烟雾测试 | 生产部署后 | 核心API | 确保生产环境可用 |### 6.2 用Gemini生成测试这一步我大量使用了Gemini来生成测试用例。比如有一个订单创建的接口:javascript// backend/src/routes/orders.jsrouter.post('/orders', authMiddleware, async (req, res) => { const { items, shippingAddress, paymentMethod } = req.body; const userId = req.user.id; try { // 验证商品库存 const stockCheck = await checkStock(items); if (!stockCheck.available) { return res.status(400).json({ error: 'INSUFFICIENT_STOCK', items: stockCheck.unavailable }); } // 计算价格 const pricing = await calculatePricing(items, userId); // 创建订单 const order = await createOrder({ userId, items, shippingAddress, paymentMethod, totalAmount: pricing.total, discountAmount: pricing.discount, }); // 锁定库存 await lockStock(items, order.id); // 发起支付 const payment = await initiatePayment(order.id, paymentMethod, pricing.total); res.status(201).json({ order, payment }); } catch (err) { logger.error('Order creation failed', { error: err, userId }); res.status(500).json({ error: 'ORDER_CREATION_FAILED' }); }});我把这个接口的代码贴给Gemini,让它生成集成测试。它的回应是这样的:javascript// backend/tests/integration/orders.test.jsimport request from 'supertest';import { app } from '../../src/app';import { prisma } from '../../src/lib/prisma';import { createTestUser, createTestProducts } from '../helpers';describe('POST /orders', () => { let authToken; let testProducts; beforeEach(async () => { // 清理测试数据库 await prisma.order.deleteMany(); await prisma.product.deleteMany(); await prisma.user.deleteMany(); // 创建测试数据 const user = await createTestUser({ tier: 'gold' }); authToken = generateToken(user); testProducts = await createTestProducts([ { id: 'p1', name: '商品A', price: 100, stock: 10 }, { id: 'p2', name: '商品B', price: 200, stock: 5 }, { id: 'p3', name: '商品C', price: 50, stock: 0 }, // 缺货商品 ]); }); test('正常创建订单', async () => { const res = await request(app) .post('/orders') .set('Authorization', `Bearer ${authToken}`) .send({ items: [ { productId: 'p1', quantity: 2 }, { productId: 'p2', quantity: 1 }, ], shippingAddress: { city: '上海', address: 'xxx路1号' }, paymentMethod: 'alipay', }); expect(res.status).toBe(201); expect(res.body.order).toBeDefined(); expect(res.body.payment).toBeDefined(); expect(res.body.order.totalAmount).toBe(400); // 100*2 + 200*1 }); test('库存不足时返回400', async () => { const res = await request(app) .post('/orders') .set('Authorization', `Bearer ${authToken}`) .send({ items: [{ productId: 'p3', quantity: 1 }], shippingAddress: { city: '上海', address: 'xxx路1号' }, paymentMethod: 'alipay', }); expect(res.status).toBe(400); expect(res.body.error).toBe('INSUFFICIENT_STOCK'); expect(res.body.items).toContain('p3'); }); test('未登录时返回401', async () => { const res = await request(app) .post('/orders') .send({ items: [{ productId: 'p1', quantity: 1 }], shippingAddress: { city: '上海', address: 'xxx路1号' }, paymentMethod: 'alipay', }); expect(res.status).toBe(401); }); test('商品不存在时返回400', async () => { const res = await request(app) .post('/orders') .set('Authorization', `Bearer ${authToken}`) .send({ items: [{ productId: 'nonexistent', quantity: 1 }], shippingAddress: { city: '上海', address: 'xxx路1号' }, paymentMethod: 'alipay', }); expect(res.status).toBe(400); }); test('请求参数缺失时返回400', async () => { const res = await request(app) .post('/orders') .set('Authorization', `Bearer ${authToken}`) .send({ items: [{ productId: 'p1', quantity: 2 }], // 缺少 shippingAddress 和 paymentMethod }); expect(res.status).toBe(400); });});Gemini生成的测试有几个值得注意的点:- 它自动创建了缺货商品(stock: 0)来测试库存不足的场景- 它测试了未认证的请求(401)- 它测试了不存在的商品(400)- 它测试了参数缺失(400)这些都是我会写的测试,但它替我省了大量时间。从描述需求到拿到完整测试代码,大概花了3分钟。如果我自己写,至少要30分钟。### 6.3 烟雾测试部署后的烟雾测试是最重要的一道防线。我们在CI/CD流水线的最后加了一步:yaml- name: Smoke test run: | ENDPOINTS=( "GET https://api.myapp.com/health" "GET https://api.myapp.com/api/products?limit=1" "POST https://api.myapp.com/api/auth/login" "GET https://api.myapp.com/api/orders" ) for endpoint in "${ENDPOINTS[@]}"; do METHOD=$(echo $endpoint | awk '{print $1}') URL=$(echo $endpoint | awk '{print $2}') STATUS=$(curl -s -o /dev/null -w "%{http_code}" \ -X $METHOD $URL \ -H "Authorization: Bearer ${{ secrets.SMOKE_TEST_TOKEN }}") if [ "$STATUS" = "200" ] || [ "$STATUS" = "201" ]; then echo "✅ $METHOD $URL → $STATUS" else echo "❌ $METHOD $URL → $STATUS" exit 1 fi done echo "All smoke tests passed!"烟雾测试很简单——就是检查几个核心API能不能正常响应。如果任何一个返回非2xx状态码,部署就会被标记为失败,触发回滚。## 七、效能提升数据经过4个月的迭代,整个CI/CD体系终于搭建完成。来看最终的数据对比:### 7.1 部署效率| 指标 | 手动部署时期 | CI/CD完成后 | 变化 ||------|------------|-----------|------|| 部署频率 | 2-3次/周 | 每天可部署 | +300% || 单次部署耗时 | 45min | 6min(全自动) | -87% || 人工操作时间 | 45min | 2min(确认触发) | -96% || 部署成功率 | 55% | 94% | +71% || 平均回滚时间 | 15min(手动) | 90秒(自动) | -90% || 凌晨紧急部署 | 需要 | 不需要 | — |### 7.2 质量提升| 指标 | CI/CD前 | CI/CD后 | 变化 ||------|---------|---------|------|| 测试覆盖率 | 52% | 81% | +56% || 线上Bug数(月均) | 12个 | 4个 | -67% || P1事故数(季度) | 5次 | 1次 | -80% || 回滚次数(月均) | 2.3次 | 0.5次 | -78% |### 7.3 团队效率| 指标 | CI/CD前 | CI/CD后 | 变化 ||------|---------|---------|------|| 部署相关工时/周 | 9h | 1.5h | -83% || 加班时长/周 | 12h | 4h | -67% || 团队满意度 | 5.2/10 | 8.1/10 | +56% |## 八、跟Gemini协作的经验总结用了4个月Gemini来辅助搭建CI/CD,我总结了一些跟AI协作写配置文件的经验。### 8.1 喂够上下文Gemini最强大的能力是处理长上下文。不要只给它一个模糊的需求,要把项目的全貌告诉它:- 项目结构(目录树)- 技术栈和版本- 现有的部署方式(哪怕是手动的)- 服务器的环境信息- 你期望达到的目标喂的上下文越充分,生成的配置越准确。### 8.2 分步骤推进不要一次让AI生成完整的CI/CD配置。一个包含测试、构建、部署、回滚的完整workflow可能有200多行yaml,一次性生成大概率有问题。更好的方式是分步骤:1. 先生成测试部分 → 跑通2. 再加构建部分 → 跑通3. 再加部署部分 → 跑通4. 最后加回滚和健康检查 → 跑通每一步都验证通过后再进入下一步,这样出了问题容易定位。### 8.3 异常路径要主动提AI有一个倾向:它会很好地处理"正常流程”,但对"异常流程"的考虑不够。比如:- 部署失败了怎么办?- 健康检查不通过怎么办?- 数据库migration失败了怎么办?- SSH连接超时了怎么办?这些异常路径需要你主动跟AI提出来,让它加上对应的处理逻辑。不能假设它会自己想到。### 8.4 AI生成的不一定是最优解有一次Gemini给我生成了一个部署脚本,用了一个比较冷门的GitHub Action(某个第三方的scp action)。我去那个action的仓库一看,已经6个月没更新了,最近还有未解决的issue。我换了一个更主流的方案(appleboy/scp-action),稳定性好得多。AI推荐的工具和库不一定是最优的,它可能推荐了一个它训练数据中出现过但已经不再维护的方案。拿到推荐后,自己去验证一下。### 8.5 安全检查CI/CD配置里会涉及大量secrets(API Key、SSH Key、数据库密码等)。Gemini生成的配置里,有时候会把secrets的值直接写在注释里作为示例。这本身不是大问题,但如果直接复制粘贴不改,就可能把真实密钥泄露。重要原则:所有敏感信息必须通过GitHub Secrets引用,绝不能硬编码在配置文件里。AI生成的配置里如果有任何硬编码的凭据,必须删除。## 九、整个CI/CD流水线的最终架构最终我们的流水线长这样:| 阶段 | 触发条件 | 执行内容 | 耗时 | 失败处理 ||------|---------|---------|------|---------|| CI | PR提交 | Lint + 单元测试 + Build | 3min | 阻止merge || 集成测试 | merge到main | 集成测试 + 覆盖率检查 | 5min | 通知开发者 || 前端部署 | CI+集成通过 | Build → 上传OSS → 刷新CDN | 2min | 保留旧版本 || 后端部署 | CI+集成通过 | 打包 → 上传ECS → migration → 蓝绿切换 | 6min | 自动回滚 || 烟雾测试 | 部署完成 | 核心API健康检查 | 1min | 触发回滚+告警 || 总计 | | | ~12min | |从提交代码到生产上线,全流程12分钟,人工操作只有"确认merge"这一步。## 十、那些Gemini没帮我搞定的部分说了这么多Gemini的好处,也该说说它做不到的事情了。AI辅助不是万能药,有些环节它确实帮不上忙,甚至还会添乱。第一件事:网络架构和安全组的配置我们的ECS在阿里云VPC里,蓝绿部署需要Nginx能同时访问两台ECS。一开始两台ECS的安全组规则只开了80和443端口,Nginx跟ECS之间的3000端口不通,蓝绿部署根本跑不起来。这种问题Gemini帮不了你——它不知道你的VPC是怎么划分的,安全组规则是什么样的。你得自己去阿里云控制台检查网络拓扑,确认端口开放情况。我花了一个下午才把网络问题排查清楚,这期间Gemini只能帮我理解一些网络概念,但实际的配置操作得自己来。第二件事:数据库migration的兼容性问题有一次部署到生产环境,prisma migrate deploy 报了一个语法错误。原因是开发环境的MySQL是8.0版本,但生产环境是5.7版本,有个SQL语法在5.7里不支持。Gemini生成的migration脚本在开发环境跑得好好的,到了生产就挂了。这事的教训是:开发环境和生产环境的版本必须一致。我们后来用Docker统一了数据库版本,开发环境也用MySQL 5.7镜像,彻底杜绝了版本不一致的问题。Gemini可以帮你写migration脚本,但它不会主动提醒你检查数据库版本兼容性——除非你问了。第三件事:GitHub Actions的并发控制有一次两个开发者几乎同时往main分支push了代码,两个workflow同时触发,同时往ECS部署。结果两台ECS上跑的是不同的版本,Nginx负载均衡把请求分到了两个不同版本的后端,数据格式不兼容,直接报错。Gemini最初生成的配置里没有考虑并发控制。我后来加了一个concurrency配置:yamlconcurrency: group: deploy-production cancel-in-progress: false这个配置的意思是:同一时间只允许一个部署workflow运行,如果有新的部署被触发,它会排队等待,而不是同时执行。这种生产环境的"坑"往往需要踩过一次才会想起来加防护。第四件事:Secret管理的安全审计CI/CD配置里有大量的secrets——阿里云AccessKey、SSH私钥、数据库密码、Slack Webhook URL。Gemini帮我生成了配置,但配置里引用了哪些secrets、这些secrets的权限范围是什么、是否需要定期轮换,这些安全管理问题AI不会主动帮你梳理。我们后来做了一次安全审计,发现有个Secret是半年前创建的API Key,权限是管理员级别——其实部署只需要OSS的读写权限就够了。我们回收了这个Key,换了一个最小权限的。如果不去审计,这个过大的权限可能一直存在下去。## 十一、对中小团队的建议如果你是一个类似我们这样的中小团队(10人以内,没有专职DevOps),想用AI辅助搭建CI/CD,我有几条具体的建议:建议一:先用GitHub Actions的现成模板GitHub Actions的marketplace里有大量现成的Action,很多常见场景(Node.js部署、Docker构建、OSS上传)都有成熟的模板。先找一个最接近你需求的模板,再用AI帮你改——比自己从零写或者让AI从零生成要靠谱得多。建议二:staging环境必须有我们一开始没有staging环境,直接从CI部署到生产。后来出了几次问题后,加了一个staging环境——CI通过后先部署到staging,跑一遍烟雾测试,确认没问题后再手动触发生产部署。多了一步,但安全得多。建议三:把部署日志存下来GitHub Actions的日志默认保留90天。但我们把关键部署的日志额外存了一份到OSS上,永久保留。万一线上出了问题需要回溯是什么时候引入的,有完整的部署日志可以查。**建议四:定期做"部署演练”**CI/CD搭好了不等于万事大吉。我们每个月做一次"部署演练”——故意制造一个需要回滚的场景,测试回滚流程是否正常工作。有一次演练发现回滚脚本有个路径写错了,回滚后服务直接起不来。幸亏是演练时发现的,不是真出事时发现的。## 十二、写在最后从那个周五晚上8点23分手动部署到崩溃,到现在12分钟全自动部署,这个过程花了4个月。不是因为技术上有多难,而是因为每一步都需要验证、调整、踩坑、再修正。Gemini在这个过程中扮演的角色,不是一个"帮你写完所有代码的AI”,而是一个"帮你少走弯路的pair programming伙伴"。它替我省了翻文档的时间、写样板代码的时间、查语法的时件,但架构设计、流程规划、异常处理这些需要思考的部分,还是得靠人。如果你也在为部署流程头疼,我的建议是:别等"准备好了"再开始,从最小的一步做起。先搞定自动测试,再搞定自动构建,最后搞定自动部署。每一步都让团队感受到效率提升,后面的推进就会越来越顺。CI/CD不是一个一蹴而就的项目,而是一个持续优化的过程。我们的流水线现在也还在迭代——下一步打算加入性能基准测试和自动化的安全扫描。最后说一句:如果你也有用AI辅助搭建CI/CD的经验或者踩坑故事,欢迎在评论区聊聊。工具在变,但"让部署变得可靠"这个目标不会变。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐