CI/CD实战入门:从代码提交到自动部署的30分钟极速指南

*图片示意:一张CI/CD流水线示意图,从代码提交到构建、测试、部署的自动化流程*

为什么你需要CI/CD?——”在我机器上能跑”的终结者

1

0px rgba(0,0,0,.08);”
loading=”lazy” width=”800″ height=”500″>

我刚工作时,遇到过最崩溃的场景:周五下午5点,急着上线一个小功能。本地测试通过,push到服务器,重启服务,然后——页面500了。排查2小时,发现是线上环境少装了一个包。那一刻,我真想把”在我机器上能跑”这句话印在T恤上。

CI/CD(持续集成/持续交付)就是来终结这种故事的。它的核心就一句话:每次代码变更,自动帮你跑测试、构建、部署,确保代码在任何环境都能跑起来。

我用的经验是:新手别一开始就追求全量CI/CD,先解决”手动部署”这个最大痛点,再逐步加入测试、代码检查等环节。

第一步:选工具——别纠结,GitHub Actions就够了

市面上的CI/CD工具多如牛毛:Jenkins、GitLab CI、CircleCI、Travis CI…踩过坑后,我建议新手无脑选GitHub Actions。原因有三:

  • GitHub原生集成:你的代码在GitHub上,Actions直接在仓库里配,零额外成本
  • 免费额度够用:每月2000分钟免费,个人项目完全够
  • 生态丰富:别人写好的Action直接复用,像搭积木
  • 如果你用GitLab,GitLab CI也行,但配置逻辑类似。我以GitHub Actions为例,你掌握后迁移成本极低。

    第二步:写一个最简单的CI流水线——从0到1只需10分钟

    假设你有一个Node.js + Express项目,放在GitHub上。我们先实现:每次push代码,自动跑单元测试。

    2.1 准备工作

    确保你的项目根目录有package.json和测试脚本。我用的是Jest,你也可以用Mocha、Vitest等。

    json
    // package.json 部分内容
    {
    "scripts": {
    "test": "jest"
    },
    "devDependencies": {
    "jest": "^29.0.0"
    }
    }
    `

    2.2 创建第一个Workflow文件

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

    `yaml
    name: CI Pipeline

    触发条件:push到main或master分支

    on:
    push:
    branches: [ main, master ]
    pull_request:
    branches: [ main, master ]

    要执行的任务

    jobs:
    test:
    runs-on: ubuntu-latest # 运行环境:最新的Ubuntu

    steps:
    - uses: actions/checkout@v3 # 获取代码

    - name: 设置Node.js环境
    uses: actions/setup-node@v3
    with:
    node-version: '18' # 指定Node版本
    cache: 'npm' # 缓存依赖,加速后续运行

    - name: 安装依赖
    run: npm ci # 比npm install更快,且锁定版本

    - name: 运行测试
    run: npm test # 跑单元测试

    - name: 检查测试结果
    run: echo "测试完成!状态:${{ job.status }}"
    `

    踩过的坑:第一次配时,我用了npm install而不是npm ci。结果本地装的是^1.0.0版本,CI装的是1.0.1,导致测试失败。npm ci会严格按package-lock.json安装,杜绝这种问题。

    push这个文件到GitHub,你会在仓库的Actions标签页看到流水线正在运行:

    *图片示意:GitHub Actions界面,显示一个正在运行的workflow,包含steps日志*

    第三步:加入Docker构建——让环境不再是个谜

    光跑测试不够,我们还要确保构建产物在任何环境都能跑。Docker就是最佳选择——把应用和依赖打包成镜像,部署到哪都一样。

    3.1 项目根目录加Dockerfile

    `dockerfile

    使用官方Node.js镜像作为基础

    FROM node:18-alpine

    设置工作目录

    WORKDIR /app

    复制package.json和package-lock.json

    COPY package*.json ./

    安装依赖

    RUN npm ci --only=production

    复制源代码

    COPY . .

    暴露端口

    EXPOSE 3000

    启动命令

    CMD ["node", "server.js"]
    `

    3.2 更新CI文件,加入Docker构建和推送

    `yaml
    name: CI/CD Pipeline

    on:
    push:
    branches: [ main, master ]

    jobs:
    test-and-build:
    runs-on: ubuntu-latest

    # 定义环境变量
    env:
    IMAGE_NAME: my-app
    REGISTRY: ghcr.io # GitHub Container Registry

    steps:
    - uses: actions/checkout@v3

    - name: 设置Node.js
    uses: actions/setup-node@v3
    with:
    node-version: '18'
    cache: 'npm'

    - name: 安装依赖并测试
    run: |
    npm ci
    npm test

    - name: 登录到GitHub容器注册表
    uses: docker/login-action@v2
    with:
    registry: ${{ env.REGISTRY }}
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }} # GitHub自动生成的token

    - name: 构建并推送Docker镜像
    uses: docker/build-push-action@v4
    with:
    context: .
    push: true # 推送到注册表
    tags: |
    ${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}:latest
    ${{ env.REGISTRY }}/${{ github.repository }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
    `

    关键点说明

    • ${{ github.sha }}是当前commit的哈希值,用它作为镜像标签可以精确定位每个版本
    • secrets.GITHUB_TOKEN是GitHub自动生成的,不需要手动配置,安全又方便

    第四步:自动部署到服务器——最爽的一步

    现在测试通过、镜像也构建好了,就差最后一步:部署到服务器。我用的是最简单的方案:通过SSH远程执行命令。

    4.1 配置服务器秘钥

  • 在你的服务器上生成SSH密钥对:ssh-keygen -t ed25519 -C “github-actions”
  • 将公钥添加到服务器的~/.ssh/authorized_keys
  • 在GitHub仓库的Settings -> Secrets and variables -> Actions中,添加两个Secret:
  • - SSH_PRIVATE_KEY:私钥内容
    -
    SSH_HOST:服务器IP
    -
    SSH_USER:登录用户名

    4.2 在CI文件末尾加入部署步骤

    `yaml
    - name: 部署到服务器
    uses: appleboy/ssh-action@v0.1.5 # 社区流行的SSH Action
    with:
    host: ${{ secrets.SSH_HOST }}
    username: ${{ secrets.SSH_USER }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    script: |
    # 拉取最新镜像
    docker pull ghcr.io/${{ github.repository }}/my-app:latest

    # 停止旧容器
    docker stop my-app || true
    docker rm my-app || true

    # 启动新容器
    docker run -d \
    --name my-app \
    --restart unless-stopped \
    -p 3000:3000 \
    ghcr.io/${{ github.repository }}/my-app:latest

    # 清理旧镜像(保留最近5个)
    docker image prune -a --filter "until=24h" -f
    `

    建议:第一次部署时,先在服务器手动跑一遍这些命令,确认没问题再配置到CI里。我吃过亏:SSH密钥权限不对,导致部署失败,排查了半小时才发现是chmod 600没做。

    第五步:监控与回滚——给自己留条后路

    CI/CD跑起来后,你可能会遇到一个问题:自动部署的版本出bug了,怎么办?

    5.1 加入健康检查

    在部署步骤后,加一个健康检查:

    `yaml
    - name: 健康检查
    run: |
    sleep 10 # 等待容器启动
    curl -f http://${{ secrets.SSH_HOST }}:3000/health || exit 1
    `

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

    `yaml
    - name: 回滚(如果健康检查失败)
    if: failure()
    uses: appleboy/ssh-action@v0.1.5
    with:
    host: ${{ secrets.SSH_HOST }}
    username: ${{ secrets.SSH_USER }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    script: |
    # 获取上一个版本的镜像标签
    PREVIOUS_TAG=$(docker images --format "{{.Tag}}" | grep -v latest | sort | tail -2 | head -1)
    docker run -d --name my-app-rollback -p 3000:3000 ghcr.io/${{ github.repository }}/my-app:$PREVIOUS_TAG
    `

    5.2 监控部署状态

    在CI文件的最后,发送通知到企业微信或Slack:

    `yaml
    - name: 发送部署通知
    if: always()
    uses: slackapi/slack-github-action@v1.24.0
    with:
    payload: |
    {
    "text": "部署${{ job.status == 'success' && '成功' || '失败' }}:${{ github.repository }}@${{ github.sha }}"
    }
    env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
    `

    *图片示意:企业微信或Slack收到部署通知的截图,显示成功或失败的卡片消息*

    性能数据:CI/CD到底能省多少时间?

    我自己的项目经验:

    • 手动部署:平均每次10-15分钟(包括SSH连接、拉代码、装依赖、重启、验证)
    • CI/CD部署:平均2-3分钟(自动完成全部流程)
    • 部署失误率:从手动时的约15%降到接近0%

    按每天部署3次算,一年能省下约200小时——差不多是5周的工作时间。

    常见问题与避坑指南

    Q1:CI/CD跑得很慢怎么办?

    • 启用缓存:Docker构建时用–cache-from,Node依赖用actions/cache
    • 并行化:将测试拆分成多个job并行执行
    • 按需触发:只在特定分支或目录变更时触发

    Q2:测试通过了,部署还是跪了?

    这是最坑爹的情况。建议:

    • 在Dockerfile里用COPY . .前先COPY package*.json ./,利用Docker缓存层
    • 部署前在CI环境运行一个"冒烟测试":curl localhost:3000

    Q3:多环境怎么管理?

    我用的是分支策略:

    • dev分支:自动部署到测试环境
    • staging分支:手动触发部署到预发布环境
    • main分支:自动部署到生产环境(但经过code review)

    `yaml
    on:
    push:
    branches:
    - dev
    - main
    workflow_dispatch: # 手动触发
    branches:
    - staging
    `

    结语:从今天开始,告别手动部署

    CI/CD不是锦上添花,而是现代开发者的必备技能。它让你的代码从"在我机器上能跑"变成"在任何地方都能跑"。

    立刻行动

  • 今天下班前,给项目加上.github/workflows/ci.yml`,至少跑通测试
  • 这周末,配置Docker构建和自动部署,体验一把”push代码就自动上线”的快感
  • 当你第一次看到代码push后几分钟,应用已经自动更新到线上时,你会回来感谢自己的。


    滚动至顶部