最开始那个月,我每天手动ssh上去git pull、npm build、pm2 restart,重复机械劳动到怀疑人生。直到有天凌晨3点部署把生产环境搞崩了,才决心搞CI/CD。刚开始以为这玩意儿很简单,结果连踩三个坑:权限配置搞了2天、流水线跑了6次全红、部署脚本把数据库干瘫痪了。
先说说为什么你需要CI/CD
0,.08);”
loading=”lazy” width=”800″ height=”500″>
假设你每天部署1次,每次手动操作15分钟。一年就是15*365=5475分钟≈91小时。这还不算半夜爬起来修bug、回滚失败、环境不一致导致的排查时间。我团队去年统计的数据:手动部署平均耗时3.2小时(包含环境准备+测试+回滚预案),而CI/CD自动部署平均0.8分钟——从3.2小时降到0.8分钟,效率提升240倍。
另一个坑:你以为”就我一个人写代码,用不着自动化”。但当你第5次忘记执行测试就push代码,或者第10次把开发环境的配置带到生产环境时,就会明白——CI/CD不是给团队用的,是给未来的自己用的。
核心概念:流水线到底长什么样
先看一个最简单的CI/CD流水线骨架:
“yaml
.gitlab-ci.yml 最小可行配置
stages:
- build
- test
- deploy
before_script:
- npm ci --prefer-offline # 用ci代替install,速度提升40%
build-job:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/ # 把构建产物传给后续阶段
expire_in: 1 hour
test-job:
stage: test
script:
- npm run lint
- npm run test:unit
needs: ["build-job"] # 显式依赖,比默认顺序更快
deploy-job:
stage: deploy
script:
- scp -r dist/* user@server:/var/www/app/
only:
- main # 只有main分支才触发部署
`
这个设计真的反人类——我第一次写的时候以为stages是顺序执行的,结果发现同一stage的job是并行的。官方文档这段文档不够清晰,后来才搞明白:默认同一个stage的job并行,不同stage串行。
为什么要这么写?因为:
让构建产物在job间传递,避免重复构建打破默认顺序依赖,比如测试不需要等构建完才下载依赖限制部署范围,防止feature分支误部署真实踩坑记录:Jenkins配置血泪史
我们团队一开始用Jenkins,因为"业界标准"。结果配置第一周就崩溃了——插件装了40个,每次重启要5分钟。后来发现一个真相:Jenkins适合CI/CD专家,不适合想快速上手的团队。
如果你非要用Jenkins,记住这个救命配置:
`groovy
// Jenkinsfile 生产可用的管道脚本
pipeline {
agent any
environment {
DOCKER_IMAGE = 'myapp:latest'
DEPLOY_SERVER = 'prod-server-01'
}
stages {
stage('Checkout') {
steps {
checkout scm
// 这个参数能节省30%检出时间
checkout([$class: 'GitSCM',
branches: [[name: '*/main']],
extensions: [[$class: 'CloneOption',
depth: 1,
shallow: true]]])
}
}
stage('Build') {
steps {
sh '''
# 用缓存目录避免每次都下载全部依赖
if [ -d /var/cache/node_modules ]; then
cp -r /var/cache/node_modules ./node_modules
fi
npm run build
'''
}
}
stage('Test') {
parallel {
stage('Unit Test') {
steps { sh 'npm run test:unit' }
}
stage('Lint') {
steps { sh 'npm run lint' }
}
}
}
stage('Deploy') {
when {
branch 'main'
// 增加手动审批,防止手滑
expression {
input message: '确认部署到生产环境?',
ok: '部署'
return true
}
}
steps {
sshagent(['prod-ssh-key']) {
sh "scp dist/* ${DEPLOY_SERVER}:/var/www/"
}
}
}
}
post {
failure {
// 失败时自动通知,省得半夜查邮件
slackSend(
channel: '#ops-alert',
message: "部署失败:${env.BUILD_URL}",
color: 'danger'
)
}
success {
// 成功时记录版本号,方便回滚
sh "echo ${env.BUILD_NUMBER} > version.txt"
}
}
}
`
这个配置花了我3天调试。最坑的是sshagent插件——官方文档说"自动管理SSH密钥",结果我配置了把私钥明文写在脚本里,被安全审计查到直接扣绩效。正确做法是用Jenkins凭据管理(Credentials)存储密钥,然后在管道里用sshagent([‘凭证ID’])引用。
另一个坑:环境一致性
有次开发环境测试全绿,但生产部署后页面白屏。排查了2小时发现:开发机Node 18,生产机Node 16,某个新语法不支持。这就是典型的"在我电脑上能跑"问题。
解决方案:用Docker容器化构建环境。
`dockerfile
Dockerfile.ci 确保构建环境一致
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM nginx:alpine AS runner
只拷贝构建产物,减小镜像体积
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
`
然后在CI管道里用这个镜像:
`yaml`
build:
stage: build
image: node:18-alpine # 锁定Node版本
script:
- npm ci
- npm run build
这样一来,无论开发机是什么版本,构建环境永远一致。这个改动让我们的"环境不一致"bug从每月3次降到0次。
还有个技巧:增量部署
全量部署每次上传几百兆文件,慢得要死。后来我们改成增量部署——只上传变更的文件。
`yaml
在GitLab CI中实现增量部署
variables:
DEPLOY_DIR: /var/www/app
deploy-incremental:
stage: deploy
script:
- |
# 获取上次部署的commit
LAST_DEPLOY=$(cat $DEPLOY_DIR/LAST_COMMIT 2>/dev/null || echo "none")
if [ "$LAST_DEPLOY" == "none" ]; then
# 首次部署,全量
rsync -avz --delete dist/ user@server:$DEPLOY_DIR
else
# 增量:只传输变更文件
CHANGED_FILES=$(git diff --name-only $LAST_DEPLOY HEAD -- dist/)
if [ -n "$CHANGED_FILES" ]; then
rsync -avz $CHANGED_FILES user@server:$DEPLOY_DIR
fi
fi
# 记录当前commit
echo $CI_COMMIT_SHA > $DEPLOY_DIR/LAST_COMMIT
`
这个技巧把部署时间从平均2分钟降到8秒。但有个坑:如果文件被删除,增量更新不会自动删除远程文件。需要在rsync加上–delete参数,或者定期做全量同步。
生产环境必须加的防护
最后一次踩坑:同事不小心把feature分支合并到main,自动触发部署,结果数据库DDL没写回滚脚本,导致线上数据丢失2小时。从此我们加了这些防护:
`yaml
canary-deploy:
stage: deploy
script:
- |
# 金丝雀部署:先更新1台服务器
for server in canary-server-01; do
ssh $server "cd /app && git pull && npm ci && pm2 restart app"
done
# 等待5分钟观察
sleep 300
# 如果健康检查通过,更新剩余服务器
if curl -f http://canary-server-01/health; then
for server in prod-server-{01..10}; do
ssh $server "cd /app && git pull && npm ci && pm2 restart app"
done
else
# 自动回滚
echo "金丝雀失败,开始回滚"
exit 1
fi
“
总结一下,你可以立刻用的三个点
最后说个真实数据:我们团队实施CI/CD后,部署失败率从25%降到3%,部署时间从3.2小时降到0.8分钟,更重要的是——再也没有半夜被叫起来修bug了。如果你还在手动部署,找个周末花2小时搭个最小流水线,你会回来感谢自己的。