CI/CD实战:从手动部署到自动化流水线

每次发布都要手动打包、上传、重启服务。 出了bug,回滚也是手动操作。 这样的流程,效率低,还容易出错。CI/CD就是来解决这个问题的。

什么是CI/CD?

CI:持续集成(Continuous Integration)

开发者频繁地把代码合并到主分支。

每次合并都自动构建、测试。

及早发现问题,避免集成地狱。

CD:持续交付/部署(Continuous Delivery/Deployment)

持续交付:代码随时可以发布到生产环境。

持续部署:代码自动发布到生产环境。

区别在于最后一步是手动还是自动。

为什么需要CI/CD?

提高效率

自动化代替手动操作。

原来要半小时的部署,现在几分钟搞定。

减少错误

手动操作容易出错。

自动化流程标准化,减少人为失误。

快速反馈

代码提交后,几分钟就知道有没有问题。

不用等到集成时才发现bug。

频繁发布

有了CI/CD,可以一天发布多次。

快速迭代,快速响应用户需求。

CI/CD工具

市面上有很多工具。

Jenkins

老牌工具,功能强大,插件丰富。

但配置复杂,界面老旧。

GitLab CI

和GitLab集成,配置简单。

用YAML文件定义流水线。

GitHub Actions

GitHub自带的CI/CD。

和GitHub无缝集成,免费额度够用。

其他

  • CircleCI:云端CI/CD

  • Travis CI:开源项目免费

  • Azure DevOps:微软的解决方案

本文以GitHub Actions为例。

GitHub Actions实战

基本概念

  • Workflow:工作流,一个自动化流程

  • Job:任务,一个workflow包含多个job

  • Step:步骤,一个job包含多个step

  • Action:动作,可复用的步骤

第一个Workflow

在项目根目录创建.github/workflows/ci.yml:

name: CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run tests run: npm test - name: Build run: npm run build

这个workflow做了什么?

  1. 当代码push到main分支或创建PR时触发

  2. 在Ubuntu环境运行

  3. 检出代码

  4. 安装Node.js 18

  5. 安装依赖

  6. 运行测试

  7. 构建项目

提交这个文件,GitHub就会自动运行。

添加代码检查

  • name: Lint run: npm run lint - name: Type check run: npm run type-check

在测试前加上代码检查和类型检查。

确保代码质量。

缓存依赖

每次都安装依赖很慢。

可以缓存node_modules:

  • name: Cache dependencies uses: actions/cache@v3 with: path: ~/.npm key: {{ runner.os }}-node-{{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-

第二次运行会快很多。

自动部署

测试通过后,自动部署到服务器。

部署到服务器

deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3 - name: Deploy to server uses: appleboy/ssh-action@master with: host: {{ secrets.SERVER_HOST }} username: {{ secrets.SERVER_USER }} key: ${{ secrets.SSH_KEY }} script: | cd /var/www/myapp git pull npm install npm run build pm2 restart myapp

通过SSH连接服务器,执行部署命令。

敏感信息(服务器地址、密钥)存在GitHub Secrets里。

部署到Docker

docker: needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build Docker image run: docker build -t myapp:{{ github.sha }} . - name: Push to registry run: \| echo {{ secrets.DOCKER_PASSWORD }} | docker login -u {{ secrets.DOCKER_USERNAME }} --password-stdin docker push myapp:{{ github.sha }} - name: Deploy run: | ssh {{ secrets.SERVER_USER }}@{{ secrets.SERVER_HOST }} \ "docker pull myapp:{{ github.sha }} \&\& \\ docker stop myapp \|\| true \&\& \\ docker rm myapp \|\| true \&\& \\ docker run -d --name myapp -p 3000:3000 myapp:{{ github.sha }}"

构建Docker镜像,推送到仓库,然后在服务器上运行。

🚀 部署流程

构建 → 测试 → 打包 → 推送 → 部署

全自动化

多环境部署

通常有多个环境:开发、测试、生产。

环境分支策略

  • dev分支 → 开发环境

  • staging分支 → 测试环境

  • main分支 → 生产环境

on: push: branches: - dev - staging - main jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set environment run: | if [ "{{ github.ref }}" == "refs/heads/main" \]; then echo "ENV=production" \>\> GITHUB_ENV elif [ "{{ github.ref }}" == "refs/heads/staging" \]; then echo "ENV=staging" \>\> GITHUB_ENV else echo "ENV=development" >> GITHUB_ENV fi - name: Deploy run: \| echo "Deploying to {{ env.ENV }}" # 根据环境执行不同的部署命令

使用环境变量

  • name: Deploy to production if: env.ENV == 'production' run: | ssh {{ secrets.PROD_SERVER }} "cd /var/www/app \&\& ./deploy.sh" - name: Deploy to staging if: env.ENV == 'staging' run: \| ssh {{ secrets.STAGING_SERVER }} "cd /var/www/app && ./deploy.sh"

测试覆盖率

CI/CD不只是部署,还要保证质量。

  • name: Test with coverage run: npm test -- --coverage - name: Upload coverage uses: codecov/codecov-action@v3 with: files: ./coverage/lcov.info

生成测试覆盖率报告,上传到Codecov。

可以在PR里看到覆盖率变化。

通知和监控

Slack通知

  • name: Notify Slack if: always() uses: 8398a7/action-slack@v3 with: status: {{ job.status }} text: 'Deployment {{ job.status }}' webhook_url: ${{ secrets.SLACK_WEBHOOK }}

部署成功或失败,发送Slack通知。

邮件通知

  • name: Send email if: failure() uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com server_port: 465 username: {{ secrets.EMAIL_USERNAME }} password: {{ secrets.EMAIL_PASSWORD }} subject: 'CI/CD Failed' body: 'Build failed on ${{ github.ref }}' to: team@example.com

回滚策略

部署出问题了,要能快速回滚。

保留历史版本

  • name: Backup current version run: | ssh {{ secrets.SERVER }} \\ "cp -r /var/www/app /var/www/app.backup.(date +%Y%m%d%H%M%S)" - name: Deploy new version run: | # 部署新版本 - name: Health check run: | sleep 10 curl -f http://myapp.com/health || exit 1

部署前备份,部署后健康检查。

如果健康检查失败,自动回滚。

蓝绿部署

同时运行两个版本。

新版本测试通过后,切换流量。

  • name: Deploy to green run: | docker run -d --name app-green -p 3001:3000 myapp:new - name: Health check run: curl -f http://localhost:3001/health - name: Switch traffic run: | # 更新Nginx配置,把流量切到3001端口 # 停止旧版本

最佳实践

快速失败

把最容易失败的步骤放前面。

比如代码检查、单元测试。

这样可以更快发现问题,节省时间。

并行执行

多个job可以并行运行。

jobs: lint: runs-on: ubuntu-latest steps: - run: npm run lint test: runs-on: ubuntu-latest steps: - run: npm test build: needs: [lint, test] runs-on: ubuntu-latest steps: - run: npm run build

lint和test并行,都通过后再build。

保持流水线简单

不要把所有逻辑都写在workflow里。

复杂的逻辑写成脚本,workflow只负责调用。

  • name: Deploy run: ./scripts/deploy.sh ${{ env.ENV }}

版本控制

每次部署打上tag。

  • name: Create tag run: | git tag v$(date +%Y%m%d%H%M%S) git push --tags

方便追踪和回滚。

安全考虑

不要在代码里写密钥

用GitHub Secrets存储敏感信息。

在workflow里用${{ secrets.XXX }}引用。

限制权限

workflow的token权限要最小化。

permissions: contents: read deployments: write

审核部署

生产环境部署前,要人工审核。

deploy: runs-on: ubuntu-latest environment: name: production url: https://myapp.com steps: - name: Deploy run: ./deploy.sh

在GitHub设置里配置environment,需要审核才能部署。

总结

CI/CD是现代软件开发的标配。

它不只是工具,更是一种文化。

记住几个关键点:

  • 自动化一切可以自动化的

  • 快速反馈,及早发现问题

  • 保持流水线简单清晰

  • 做好测试和质量检查

  • 有回滚策略

  • 注意安全

最后,CI/CD不是一蹴而就的。

从简单开始,逐步完善。

先实现基本的构建和测试,再加部署,再加监控和通知。

一步一步来,不要贪多。

相关推荐
Snower_20221 小时前
CentOS 7 搭建 SVN 服务器(精简版教程)
linux·运维·服务器·svn·centos
这周也會开心1 小时前
Docker Compose容器化部署
运维·docker·容器
UP_Continue2 小时前
Linux--OS和认识进程
linux·运维·服务器
IT摆渡者2 小时前
Rocky Linux 10.1中找不到传统的 /etc/sysconfig/network-scripts 目录是正常现象。
linux·运维·服务器·网络·经验分享
无小道2 小时前
基于epoll的单进程Reactor服务器
运维·服务器·c++·网络编程·reactor·epoll
qq_395716172 小时前
服务器加硬盘应该用 RAID 几好?
运维·服务器
weixin_462446232 小时前
ubuntu / kali 将 /dev/sdb1 安全挂载为 /home 的完整实战指南(避坑版)
运维·安全·ubuntu
线束线缆组件品替网2 小时前
服务器高速互连测试中的 CableMAX 高速线缆实践
运维·服务器·人工智能·自动化·硬件工程·材料工程
破烂pan2 小时前
SGLang 服务器启动参数完整总结
运维·服务器·sglang