--- id: devops.cicd.interview title: CI/CD 面试题 type: interview domain: devops stack: [cicd] tags: [CI/CD, 发布, 回滚, 面试] level: intermediate related: [devops.cicd.pipeline, devops.cicd.release-rollback] week: [14, 16] --- # CI/CD 面试题 ## 基础 ### 【基础】CI 和 CD 分别是什么? **参考答案**:CI(持续集成)是开发者频繁合并代码,每次合并自动执行检查、测试和构建,尽早发现集成问题;CD 包括持续交付(每次变更都自动产出可发布的产物,发布由人触发)和持续部署(自动发布到生产)。 **追问**:持续交付和持续部署有什么区别?→ 持续交付:每次合并后都能随时发布,但上线需要人工确认;持续部署:通过所有检查后自动上线。 ### 【基础】一条典型的流水线包括哪些阶段? **参考答案**:代码检查(格式、lint、类型检查)→ 单元测试和集成测试 → 构建(编译、打包、构建镜像)→ 安全扫描(依赖、镜像、密钥)→ 部署到测试环境 → (审批)→ 部署生产 → 部署后验证(健康检查、冒烟测试)。 **追问**:单元测试和集成测试放在流水线的哪个位置?→ 快的检查放前面(格式、静态检查、单元测试),失败尽早停止;集成测试和镜像扫描放在后面,可以并行。 ### 【基础】流水线里的密钥怎么管理? **参考答案**:存放在 CI 平台的 Secrets 或专门的密钥管理系统中,以环境变量注入,不写进仓库和镜像;按环境和最小权限划分;不要在日志里打印;来自外部 fork 的合并请求默认拿不到密钥;泄露后立即轮换。 **追问**:第三方 Action / 插件有什么安全风险?→ 可能读取到流水线的密钥。固定到具体的提交哈希或可信版本,最小化令牌权限,优先使用 OIDC 短期凭证。 ## 进阶 ### 【进阶】蓝绿发布、滚动发布、金丝雀发布有什么区别? **参考答案**:滚动发布逐台替换实例,资源占用少,但新旧版本会同时在线,回滚也要逐台;蓝绿发布准备一整套新环境,验证后一次性切换流量,回滚即切回,但需要双倍资源;金丝雀发布先把小比例流量导到新版本,观察错误率等指标后逐步扩大,风险最小,需要流量控制和监控能力。 **追问**:金丝雀发布怎么判断能否继续放量?→ 对比新旧版本的错误率、延迟、关键业务指标,设定阈值;异常时自动或人工回滚。 ### 【进阶】怎么让数据库变更也能回滚? **参考答案**:迁移脚本版本化,由工具执行;每次变更保持向后兼容,使新旧代码都能在新表结构上运行;破坏性变更拆成多次发布:先扩展(加字段 / 表),再迁移数据和切换读写,最后确认不会回滚后再收缩(删旧字段)。大表变更用在线 DDL 工具,并提前在预发环境演练。 **追问**:删除字段为什么要分多次发布?→ 旧版本代码还在读这个字段,回滚时也需要它;先让代码不再使用,确认稳定后再删除字段(扩展 - 收缩模式)。 ### 【进阶】流水线太慢,怎么优化? **参考答案**:缓存依赖(按锁文件哈希作为 key);任务并行;按改动路径只运行相关部分;Docker 构建利用层缓存;把慢的端到端测试放到合并后或定时执行;拆分测试并行运行;用更快的执行机器。 **追问**:依赖缓存的 key 怎么设计?→ 用锁文件的哈希(`go.sum`、`uv.lock`、`package-lock.json`),依赖变化时缓存自动失效。 ## 场景题 ### 【场景题】新版本上线后错误率升高,你怎么处理? **参考答案**:先止血:确认是否与本次发布相关(时间点吻合),是的话立即回滚到上一个镜像标签,或关闭对应的特性开关;同时保留日志和错误信息。恢复后再定位原因,补充测试,复盘为什么没有在测试阶段发现。前提是平时就有一键回滚的能力,并且数据库变更向后兼容。 **追问**:回滚之后还要做什么?→ 确认指标恢复;保留现场(日志、版本、变更内容);复盘根因,补充测试或告警,防止再次发生。 ### 【场景题】你们团队的发布流程是怎样的?(如果之前是前端,可以怎么回答) **参考答案**:按真实情况讲流程:合并请求触发 lint / 测试 / 构建,评审通过且检查通过后合并;合并后自动构建产物并部署到测试环境;生产发布由负责人触发,发布后观察监控,有问题回滚。前端可以讲:构建产物上传 CDN、`index.html` 不缓存、带哈希的资源长缓存、通过切换入口文件回滚。再补充你在转岗项目里实现的后端流水线(本专题的示例)。不要编造没做过的经历。 **追问**:没有参与过发布流程怎么办?→ 如实说明前端的发布方式(构建、上传静态资源、CDN 刷新、回滚),再讲在学习项目中搭建的后端流水线和自己的设计考虑。 ### 【场景题】线上运行的是哪个版本的代码?怎么确认? **参考答案**:镜像标签使用提交哈希或语义化版本,部署记录里保存每次部署的标签;服务在健康检查或版本接口里返回构建时注入的版本号和提交哈希(Go 用 `-ldflags "-X main.version=..."`);日志和错误上报里带上版本号。这样出问题时能准确定位到代码。 **追问**:镜像标签为什么不用 `latest`?→ `latest` 会变化,无法确定线上具体是哪个版本,回滚也不可靠;用 Git 提交哈希或版本号做标签。