
前端转 AI 第 28 天CI/CD - GitHub Actions 自动化部署昨日回顾Day 27 把项目 Docker 化了docker compose up -d就能跑。但还有个问题——每次改代码都要手动做这些事# 每次提交后手动跑pytest# 跑测试dockerbuild-tai-assistant.# 构建镜像dockerpush registry/ai-api# 推镜像sshserver# 登服务器dockercompose pull# 拉镜像dockercompose up-d# 重启服务一次部署 10 分钟还容易忘步骤。今天上CI/CD——把这一串全自动化。Day 28 目标写出生产级 GitHub Actions 工作流从 push 到部署全自动。一、CI / CD 是什么前端同学对这个词可能有点陌生但用过 Vercel / Netlify 的一定懂git push之后Vercel 自动构建 部署1 分钟后线上就是新版本。这就是 CI/CD。1. 两个概念缩写全称含义CIContinuous Integration持续集成每次提交自动跑测试CDContinuous Delivery / Deployment持续交付/部署测试通过后自动部署实际流程开发者 push 代码 ↓ CI自动跑测试 lint ↓通过 CD自动构建镜像 部署 ↓ 线上更新2. 为什么必须上 CI/CD手动部署的问题问题后果忘了跑测试上线才发现有 bug忘了步骤部署失败服务中断不同人操作不同环境不一致无法回滚出问题只能手动恢复CI/CD 解决标准化流程写在代码里谁跑都一样快速反馈push 后几分钟知道有没有问题可追溯每次部署有记录可回滚出问题回退到上一个版本前端类比Vercel / Netlify 的自动部署就是 CI/CD。GitHub Actions 是更通用的版本——不只能部署前端也能部署后端、跑测试、发通知、做任何事。3. 主流 CI/CD 工具工具特点推荐度GitHub Actions和 GitHub 深度集成免费额度多⭐⭐⭐⭐⭐GitLab CIGitLab 用户首选⭐⭐⭐⭐Jenkins老牌自建⭐⭐⭐CircleCI独立服务强大⭐⭐⭐Travis CI老牌逐渐淡出⭐⭐我们用 GitHub Actions——开源项目免费个人项目 2000 分钟/月免费。二、GitHub Actions 核心概念4 个关键词概念含义前端类比Workflow一个完整的工作流YAML 文件npm run ci一条命令Job工作流里的一个任务npm run test/npm run buildStepJob 里的一步一条 shell 命令Action可复用的步骤社区提供npm 包关系Workflow (.github/workflows/ci.yml) ├── Job 1: test │ ├── Step 1: checkout │ ├── Step 2: setup-python │ └── Step 3: pytest └── Job 2: build (依赖 Job 1) ├── Step 1: checkout ├── Step 2: docker build └── Step 3: docker push所有 Workflow 放在.github/workflows/目录下。三、第一个 GitHub Actions1. 最简单的 Workflow创建.github/workflows/hello.ymlname:Helloon:push:branches:[main]jobs:hello:runs-on:ubuntu-lateststeps:-name:打招呼run:echo Hello,GitHub Actions!提交后 pushGitHub 就会自动跑。打开 GitHub 仓库 → Actions 标签能看到运行记录。2. 逐行拆解name:Hello# Workflow 名字显示在 UIon:# 触发条件push:branches:[main]# push 到 main 时触发jobs:# 所有 Jobhello:# Job ID唯一标识runs-on:ubuntu-latest# 运行环境Ubuntu 最新版steps:# 步骤列表-name:打招呼# 步骤名run:echo Hello# 执行命令3. 常用触发条件on:# push 时触发push:branches:[main,develop]paths:# 只在某些文件变化时触发-src/**-pyproject.toml# PR 时触发pull_request:branches:[main]# 手动触发Actions 页面点按钮workflow_dispatch:# 定时触发cron 语法UTC 时间schedule:-cron:0 2 * * *# 每天凌晨 2 点# 打 tag 时触发push:tags:-v*# v1.0.0 / v1.2.34. 常用 ActionGitHub 官方和社区提供了大量预构建的 Action直接用Action作用actions/checkoutv4拉取代码actions/setup-pythonv5安装 Pythonactions/setup-nodev4安装 Nodeactions/cachev4缓存依赖actions/upload-artifactv4上传构建产物docker/build-push-actionv5构建并推送 Docker 镜像用法steps:-uses:actions/checkoutv4# 用别人做好的 Action-uses:actions/setup-pythonv5with:# 传参数python-version:3.11usesvsrunuses调用现成的 Actionrun执行 shell 命令四、给项目加 CI核心每次 push 自动跑测试。1. 创建.github/workflows/ci.ymlname:CIon:push:branches:[main,develop]pull_request:branches:[main]jobs:test:name:测试runs-on:ubuntu-lateststrategy:matrix:python-version:[3.11,3.12]steps:-name:拉取代码uses:actions/checkoutv4-name:安装 Python ${{matrix.python-version}}uses:actions/setup-pythonv5with:python-version:${{matrix.python-version}}-name:缓存 pip 依赖uses:actions/cachev4with:path:~/.cache/pipkey:${{runner.os}}-pip-${{hashFiles(pyproject.toml)}}restore-keys:|${{ runner.os }}-pip--name:安装依赖run:|python -m pip install --upgrade pip pip install -e .[dev]-name:代码风格检查run:ruff check src/ tests/-name:类型检查run:mypy src/||true# 先允许失败-name:运行测试run:pytest--covsrc/ai_assistant--cov-reportxml-name:上传覆盖率uses:codecov/codecov-actionv4if:matrix.python-version 3.11with:file:./coverage.xmlfail_ci_if_error:false2. 关键点拆解1.strategy.matrix多版本测试strategy:matrix:python-version:[3.11,3.12]同时测两个 Python 版本保证兼容性。执行时Job 1: Python 3.11Job 2: Python 3.12两个并行跑都通过才算成功前端类比类似 Jest 测 Node 16 / 18 / 20 多版本。2. 缓存加速 10 倍-uses:actions/cachev4with:path:~/.cache/pipkey:${{runner.os}}-pip-${{hashFiles(pyproject.toml)}}第一次跑装依赖 30 秒第二次跑缓存命中3 秒hashFiles(pyproject.toml)是关键——依赖文件变了缓存就失效自动重新装。这就是精准缓存——既不浪费也不出错。3.ruff/mypy代码质量-name:代码风格检查run:ruff check src/ tests/ruff是 Python 的 ESLint——检查代码风格、潜在 bug。速度极快Rust 写的。加上pyproject.toml配置[tool.ruff] line-length 100 target-version py311 [tool.ruff.lint] select [E, F, I, N, W, B] ignore [E501] # 行长度交给 formattermypy是 Python 的类型检查器——对应前端的tsc[tool.mypy] python_version 3.11 strict false ignore_missing_imports true4. 覆盖率Codecov 集成-uses:codecov/codecov-actionv4with:file:./coverage.xml上传覆盖率到 CodecovREADME 可以显示覆盖率徽章[](https://codecov.io/gh/yourname/repo)面试官看到项目有覆盖率徽章会加分不少。3. 运行效果push 代码后GitHub Actions 页面显示✓ test (3.11) 1m 23s ✓ test (3.12) 1m 25s失败时✗ test (3.11) 1m 15s └─ 运行测试 ❌ FAILED tests/api/test_chat.py::TestChat::test_chat_creates_new_session点进去能看到完整日志。PR 页面也会显示检查通过与否。五、CD自动构建和推送 Docker 镜像CI 通过后构建 Docker 镜像推送到镜像仓库。1. 用什么镜像仓库仓库优点免费额度GitHub Container Registry (ghcr.io)和 GitHub 集成权限好管无限公开 / 500MB 私有Docker Hub老牌知名度高1 个私有仓库阿里云 ACR国内快有限免费推荐 GitHub Container Registry——和仓库绑定不用额外配置。2. 创建.github/workflows/docker.ymlname:Build and Push Dockeron:push:branches:[main]tags:[v*]env:REGISTRY:ghcr.ioIMAGE_NAME:${{github.repository}}jobs:build:runs-on:ubuntu-latestpermissions:contents:readpackages:write# 推送镜像需要steps:-name:拉取代码uses:actions/checkoutv4-name:设置 Docker Buildxuses:docker/setup-buildx-actionv3-name:登录到镜像仓库uses:docker/login-actionv3with:registry:${{env.REGISTRY}}username:${{github.actor}}password:${{secrets.GITHUB_TOKEN}}# 自动提供-name:生成镜像标签和元数据id:metauses:docker/metadata-actionv5with:images:${{env.REGISTRY}}/${{env.IMAGE_NAME}}tags:|typeref,eventbranch typesemver,pattern{{version}} typesemver,pattern{{major}}.{{minor}} typesha,prefixsha--name:构建并推送镜像uses:docker/build-push-actionv5with:context:.push:truetags:${{steps.meta.outputs.tags}}labels:${{steps.meta.outputs.labels}}cache-from:typeghacache-to:typegha,modemax3. 关键点拆解1.permissions权限声明permissions:contents:readpackages:writeGitHub Actions 默认权限很小要写镜像必须显式声明packages: write。2.secrets.GITHUB_TOKEN自动 TokenGITHUB_TOKEN是 GitHub 自动提供的不用手动配置。权限和permissions挂钩。其他密钥要用secrets.XXXpassword:${{secrets.DOCKER_HUB_PASSWORD}}在仓库 Settings → Secrets and variables → Actions 里配置。3.docker/metadata-action标签自动生成tags:|typeref,eventbranch # main → ghcr.io/user/repo:main typesemver,pattern{{version}} # v1.2.3 → ghcr.io/user/repo:1.2.3 typesemver,pattern{{major}}.{{minor}} # v1.2.3 → ghcr.io/user/repo:1.2 typesha,prefixsha- # sha-abc1234一个 commit 生成多个标签——灵活切换和回滚。4.cache-from/to: typegha构建缓存用 GitHub Actions 缓存加速 Docker 构建——第二次构建从 60 秒降到 10 秒。4. 运行效果push 后镜像自动推送到ghcr.io/yourname/ai-assistant。在 GitHub 仓库的 Packages 页面能看到ai-assistant ├── main ├── 1.2.3 ├── 1.2 └── sha-a3f9b2e任何服务器上dockerpull ghcr.io/yourname/ai-assistant:latestdockerrun-d-p8000:8000 ghcr.io/yourname/ai-assistant:latest部署变得无比简单。六、自动部署到服务器镜像构建好了接下来让服务器自动拉取更新。1. 部署方案对比方案优点缺点SSH 直连部署简单服务器暴露Webhook 服务器拉取无侵入要配 WebhookK8s ArgoCD生产级复杂Docker Swarm轻量集群生态小我们选 SSH 直连——最简单适合小项目。2. 服务器准备1. 服务器上装 Dockercurl-fsSLhttps://get.docker.com|shsudousermod-aGdocker$USER2. 创建docker-compose.prod.ymlDay 27 已写。3. 配置.env# /opt/ai-assistant/.envAPI_KEYyour-prod-keyPOSTGRES_PASSWORDstrong-password4. 首次手动部署cd/opt/ai-assistantdockercompose-fdocker-compose.prod.yml up-d确认服务能起再上自动部署。3. 生成 SSH 密钥本地生成ssh-keygen-ted25519-Cgithub-actions-f~/.ssh/github_actions把公钥放到服务器cat~/.ssh/github_actions.pub~/.ssh/authorized_keys在 GitHub Secrets 里配置SSH_HOST服务器 IPSSH_USERSSH 用户名SSH_KEY私钥内容cat ~/.ssh/github_actionsSSH_PORTSSH 端口默认 224. 创建.github/workflows/deploy.ymlname:Deployon:push:tags:[v*]# 只在大版本 tag 时部署workflow_dispatch:# 也支持手动触发inputs:environment:description:部署环境required:truedefault:productiontype:choiceoptions:-production-stagingjobs:deploy:name:部署到服务器runs-on:ubuntu-latestenvironment:productionsteps:-name:部署uses:appleboy/ssh-actionv1with:host:${{secrets.SSH_HOST}}username:${{secrets.SSH_USER}}key:${{secrets.SSH_KEY}}port:${{secrets.SSH_PORT||22}}script:|set -e cd /opt/ai-assistantecho 拉取最新代码 git pull origin main echo 拉取最新镜像 docker compose-f docker-compose.prod.yml pull echo 重启服务 docker compose-f docker-compose.prod.yml up-d echo 清理旧镜像 docker image prune-f echo 检查健康状态 sleep 5 curl-f http://localhost:8000/health/ready||exit 1 echo 部署完成 5. 关键点拆解1.tags: [v*]只在大版本部署日常 push 只触发 CI打 tag 才部署到生产——减少风险。流程# 日常开发gitpush# 只跑 CI# 要发版gittag v1.0.0gitpush--tags# 触发 CI Docker Deploy2.workflow_dispatch手动触发有时候需要手动部署比如修复小问题打开 GitHub → Actions → Deploy → Run workflow选环境 → Run3.appleboy/ssh-action社区最常用的 SSH Action——传命令到服务器执行。set -e让脚本遇错就停避免部分失败但显示成功。4. 部署脚本的步骤cd/opt/ai-assistantgitpull# 更新 compose 文件dockercompose pull# 拉新镜像dockercompose up-d# 重启dockerimage prune-f# 清理旧镜像省磁盘curl-fhealth/ready||exit1# 健康检查失败则退出最后一步最关键——健康检查。如果新版本起不来GitHub Actions 显示失败你能立刻知道。6. 失败自动回滚进阶更稳的方案部署失败自动回滚到上一个版本。-name:部署带回滚uses:appleboy/ssh-actionv1with:script:|set -e cd /opt/ai-assistant# 记录当前版本CURRENT$(docker compose-f docker-compose.prod.yml ps--format json|jq-r .[0].Image)# 部署新版本docker compose-f docker-compose.prod.yml pull docker compose-f docker-compose.prod.yml up-d# 健康检查sleep 10 if!curl-f http://localhost:8000/health/ready; then echo ❌ 新版本不健康回滚到 $CURRENT docker compose-f docker-compose.prod.yml down docker tag $CURRENT ai-assistant:rollback# ... 回滚逻辑exit 1 fi echo ✓ 部署成功真实生产环境用 K8s Argo Rollouts 做金丝雀发布但今天先理解基础思路。七、完整 CI/CD 流程1. 完整项目结构.github/ └── workflows/ ├── ci.yml # 测试 lint ├── docker.yml # 构建镜像 └── deploy.yml # 部署到服务器2. 各 Workflow 触发时机Workflow触发做什么ci.ymlpush / PR跑测试、lint、上传覆盖率docker.ymlpush main / tag构建镜像推 ghcrdeploy.ymltagv*/ 手动SSH 部署到服务器完整流程开发者 push 代码 ↓ [CI] 测试通过 ├── ❌ → 通知停止 └── ✓ → 继续 ↓ [Docker] 构建镜像推 ghcr ↓ 打 tag 时 [Docker] 重新构建带版本号的镜像 ↓ [Deploy] SSH 到服务器拉镜像重启 ↓ 健康检查通过 ↓ ✓ 上线完成3. 加一个通知失败时通过 Slack / 钉钉 / 飞书通知-name:失败通知if:failure()uses:8398a7/action-slackv3with:status:${{job.status}}webhook_url:${{secrets.SLACK_WEBHOOK}}或者用更通用的 curl-name:发送钉钉通知if:always()run:|STATUS${{ job.status }} if [ $STATUS success ]; then ICON✅ else ICON❌ ficurl-X POST ${{secrets.DINGTALK_WEBHOOK}} \-H Content-Type:application/json \-d {\msgtype\:\text\,\text\:{\content\:\$ICON 部署 $STATUS:${{github.repository}}(${{github.ref_name}})\}}if: always()让通知不管成功失败都发。if: failure()只在失败时发。八、前端视角GitHub Actions vs Vercel用过 Vercel 的你会觉得 GitHub Actions 有点原始环节VercelGitHub Actions触发git pushgit push / PR / tag / 手动 / 定时配置自动检测框架手写 YAML构建npm run build自己写步骤部署自动自己写 SSH / API预览每个 PR 有独立预览需要额外配置环境变量UI 配置secrets日志网页查看网页查看Vercel 是打包好的 CI/CD适合前端和 Next.js。GitHub Actions 是通用工具什么都能做部署后端服务构建 Docker 镜像发布 PyPI 包Day 20定时任务自动回复 Issue自动打标签…Vercel 的预览部署GitHub Actions 也能实现——每个 PR 部署到临时 URL。核心思想一样代码事件 → 自动动作。你前端的自动部署经验直接迁移。九、今日踩坑secrets名字拼错→ 读出来是空字符串不报错permissions没声明→ 推镜像时 403 权限不足actions/checkout没加→ 工作目录是空的cachekey 用了固定值→ 依赖更新了但缓存还在装的是旧包hashFiles路径写错→ 缓存 key 永远是同一个脏缓存SSH 部署命令没有set -e→ 中间步骤失败也返回成功密钥硬编码在 YAML 里→ 泄露所有密钥用secretsruns-on用了不存在的 runner→ 比如macos-14-xlarge要付费矩阵测试里变量引用错→${{ matrix.python-version }}记成${{ python-version }}失败了没通知→ 上线了才从用户那里知道生产环境用main分支触发部署→ 随便一个 push 就上线要打 tag 才部署忘了清理旧镜像→ 服务器磁盘 3 个月就满了免费额度用超→ 私有仓库 2000 分钟/月跑矩阵测试很吃时间部署脚本里cd写错路径→ 找不到项目目录if:条件写错→ 比如if: success()但想失败也执行要用always()十、今日总结今天核心收获三件事CI/CD 是代码事件驱动的自动化push 触发测试tag 触发部署GitHub Actions 三件套WorkflowYAML Job任务 Step步骤矩阵测试 缓存 健康检查是三大实用技巧多版本兼容、加速构建、保证部署成功一个感受CI/CD 的核心价值不是省事是可靠。手动部署 100 次99 次成功1 次忘了步骤就出事故自动部署 1000 次每次都按脚本执行可复现、可追溯、可回滚Vercel 让前端习惯了push 即部署GitHub Actions 让你对后端也有同样的体验。你的项目现在实现了全链路自动化✅ 代码 push → 自动测试✅ 测试通过 → 自动构建镜像✅ 打 tag → 自动部署到服务器✅ 部署失败 → 自动通知从手动运维到DevOps 工程师。Day 28 完成度✅明日预告Day 29Nginx 反代 HTTPSNginx 反向代理配置Let’s Encrypt 免费 SSL 证书HTTP → HTTPS 自动跳转域名绑定 DNS 配置Gzip 压缩 / 限流配置最后如果这篇对你有用点个赞让我知道有人在看。评论区互动你之前用过 CI/CD 吗GitHub Actions / GitLab CI / Jenkins踩过什么坑欢迎分享。有问题评论区见我们第 29 天继续。100 天第 29 天见。附录系列目录已完成 ✅阶段天数内容基础01-06Python 语法、数据结构、函数、OOP、文件、依赖管理网络07-08HTTP 请求、异步 asyncio工程09-12正则、装饰器、Pydantic、日志调试项目13-14命令行 AI 助手 v1 / v2复盘15阶段总结数据16-17SQLite 数据库、SQLAlchemy ORM测试18-19pytest 入门 进阶工具20CLI 打包与发布后端21-28FastAPI Docker CI/CD ✅进行中 阶段天数内容后端29-30Nginx 反代 部署上线大模型31-45LLM API Prompt 工程RAG46-60向量数据库 知识库Agent61-80LangChain / 工具调用实战81-100完整 Agent 项目上线100 天后见一起从切图仔变成 Agent 开发者。