Skip to content

第10章 CI/CD 与自动化(Git / Jenkins / Ansible) ​

对应简历:技能第13条「熟悉 Git、Jenkins CI/CD 持续集成交付流程与 Ansible 自动化批量配置管理」/ 项目二职责「完成全节点标准化初始化与集群部署:containerd 运行时(Systemd cgroup)、内核参数调优、NTP 时间同步」「构建"公网拉取 → 内网私有仓库托管 → 集群认证拉取"的镜像双轨体系」

诚实口径(全章总基调):简历动词是"熟悉"——CI/CD 部分按课程项目实操 + 理解讲(GitLab+Jenkins 流水线我在课程里从零搭过一遍);生产上的关联点是我配套过业务发布(项目二镜像双轨体系、业务系统发布支持),被问"生产上 CI/CD 跑了多少条流水线"就答课程项目口径 + 生产配套,不编造。术语:Kubernetes(下文简写 K8s)。

10.1 Git 常用命令与三个区域 【必背】 ​

标准回答: 先说结论:Git 是分布式版本控制系统,每个开发者本地都有完整的仓库包括全部历史,不依赖中央服务器也能提交、分支和回退,这是它和 SVN 那种集中式最大的区别。理解 Git 的钥匙是三个区域:我在工作区改文件,git add 把改动放进暂存区,git commit 把暂存区的内容提交成本地仓库的一个版本,最后 git push 推到远程仓库——工作区、暂存区、本地仓库、远程仓库,一条线。日常命令我按使用频率背:主干是 clone、pull、status、add、commit、push 六个;分支操作是 branch、checkout -b 建并切换、merge 合并;后悔药是 reset 和 revert;临时切走改一半的代码用 stash;查历史用 log。还有一个理解层面的点:pull 等于 fetch 加 merge,想让历史保持线性就用 pull --rebase。这些不只是课程里学的,我自己日常就用 Git 管理部署脚本和方案文档——初始化脚本、Prometheus 配置都进仓库,任何配置变更前先 commit 一次,出了问题 git diff 一下就能看出改了什么,这是我处理"最近变更类"故障的固定习惯。

追问链:

  • 追问1:reset 和 revert 有什么区别?什么时候用哪个? → 答:reset --hard 是把分支指针直接挪回旧提交,后面的历史相当于消失了,只适合处理还没 push 出去的本地提交;revert 是生成一个新的反向提交去抵消目标提交,历史完整保留、每个人都能看到发生过什么。远程共享分支上只能用 revert——如果 reset 之后强推上去,会把别人基于旧历史的提交全部搞乱,这是团队协作的大忌。
  • 追问2:怎么撤销还没 commit 的修改? → 答:分三种情况:改动还没 add,用 git checkout -- 文件 直接还原;已经 add 了,先 git reset HEAD 文件 把它退出暂存区再还原;如果改到一半要先切到别的分支处理紧急事情,就用 git stash 把现场存起来,回来 git stash pop 恢复,半成品不会丢也不会挡路。
  • 追问3:pull 的时候冲突了怎么处理? → 答:Git 会把冲突文件里标出 <<<<<<<、=======、>>>>>>> 三段记号,打开文件把两边内容看清楚,手工保留正确的内容删掉记号,然后 git add 这个文件标记冲突已解决,最后 git commit 完成合并;如果是 rebase 过程中的冲突,解决后要 git rebase --continue。预防冲突的办法就是勤拉勤推、小步提交,别攒一大坨。

关联话术:我的部署脚本和方案文档全部进 Git 仓库管理,移动云项目的配置基线每次变更前先提交,事后能精确 diff 出"哪次改了什么"——这套习惯直接迁移到团队里就是最朴素的配置版本管理。

实操模板(命令组,可直接背):

bash
git clone -b dev http://内网GitLab地址/group/project.git   # 克隆指定分支
git config --global user.name "ops" && git config --global user.email "ops@corp.com"
git status -s                    # 简洁看变更
git add . && git commit -m "feat: 更新初始化脚本"   # 提交(遵循 类型: 描述 规范)
git pull --rebase && git push    # 先拉再推,保持线性历史
git branch -b feature/init       # 建功能分支(-b 建并切换)
git merge feature/init           # 合回主干
git log --oneline --graph -5     # 图形化看最近5条
git stash push -m "临时现场" && git stash pop
git revert HEAD                  # 安全撤销最近一次已推送的提交

10.2 分支模型与环境对应 【理解】 ​

标准回答: 我们的做法是分支对应环境:dev 分支对应开发环境,开发在自己机器上写完合到 dev 做自测;test 分支对应测试环境,部署给测试人员做验收;master 分支对应生产环境,是受保护分支,不允许任何人直接 push,只能通过合并请求、经过评审之后合入。发布的时候从 test 验证通过合到 master,再打一个 tag 比如 v1.2.0 作为发布版本,这个 tag 就是回滚和追溯的锚点。再完整一点的模式是 GitFlow,它在这个基础上加了 hotfix 分支——生产环境出了紧急缺陷,从 master 拉一个 hotfix 分支出去修复,修完同时合回 master 和 dev,保证两个环境都拿到修复。这套模型的核心思想一句话:主干永远保持可发布,功能在分支上开发,环境靠分支隔离——开发再怎么折腾功能分支,master 上的代码随时可以拿去上线,这是发布信心的来源。

追问链:

  • 追问1:master 分支怎么防止被人直接 push 坏代码上去? → 答:在 GitLab 里配 Protected Branches 保护分支:把 master 设为保护分支,只有 Maintainer 角色能执行合入操作,并且强制要求走 Merge Request、至少一个人 review 通过才能合;Developer 角色只能推自己的功能分支。这样从平台层面杜绝了"绕过评审直接上主干"的可能。
  • 追问2:为什么非要走 Merge Request,直接 push 不是更快吗? → 答:MR 带来三件 push 给不了的东西:一是代码评审,第二双眼睛能挡住大量低级问题;二是 CI 自动触发,合入前自动跑构建和测试,不过就拦下来;三是审批记录,谁在什么时候合了什么变更全程留痕,出问题能追溯。快的那几分钟和这些保障比起来不值一提,特别是生产相关的主干分支。

10.3 GitLab 是什么与权限模型 【理解】 ​

标准回答: GitLab 是基于 Git 的私有代码托管和 DevOps 一体化平台,它和 GitHub 最大的差异是支持整体私有化部署——社区版 CE 免费而且功能够用,所以企业内网基本都用它,代码不出内网,这一点对金融类客户几乎是硬性要求。核心功能我说四块:代码仓库和分支保护,就是我们刚才讲的保护分支那套;Merge Request 评审流程;内置的 CI 能力,根目录放一个 .gitlab-ci.yml 就能跑流水线;还有 Wiki 和 Issue 做项目协作和任务管理。权限模型分五级,从低到高:Guest 只能提 issue 看不了代码;Reporter 能克隆看代码但不能改,适合测试和产品同学;Developer 能推功能分支、提 MR;Maintainer 能合入保护分支、管理成员;Owner 是项目全权。分配的原则就是最小权限:测试给 Reporter,开发给 Developer,只有技术负责人拿 Maintainer,权限范围和职责范围对齐,既安全又清晰。

追问链:

  • 追问1:GitLab 和 GitHub 怎么选? → 答:定位不同。GitHub 偏开源社区和 SaaS 服务,代码托管在它那边;GitLab 偏企业私有化 DevOps 平台,自带 CI Runner、完整的权限体系和 MR 流程,可以整体部署在公司内网。对个人和开源项目 GitHub 更合适;对企业特别是有数据安全合规要求的,GitLab 私有部署是标准答案,我们课程项目也是在内网搭的 GitLab。
  • 追问2:GitLab 怎么触发 Jenkins?触发失败了怎么排查? → 答:在 GitLab 项目的 Settings 里找到 Webhooks,配 Jenkins 的回调地址和 token,之后 push 或者 MR 事件就会自动通知 Jenkins 触发对应任务。排查触发失败就看 Webhook 的请求历史:返回 403 多半是 token 权限没配对,超时多半是两台机器之间网络策略没放行,对着返回码定位非常快。这个坑我在课程项目里真实排查过。

10.4 CI/CD 概念与五大阶段(提拉编测查) 【必背】 ​

标准回答: CI 是持续集成:开发人员频繁地把代码提交合入主干,每次提交都自动触发构建和测试,让问题在很小、很早的阶段就暴露出来,解决的是"大家各写各的、最后合到一起全是冲突"的集成地狱。CD 有两个含义:持续交付是构建产物自动流转到测试甚至准生产环境,随时可以上线,但最后发布生产那一下留人工确认;持续部署则是测试通过后全自动上生产、零人工干预——绝大多数企业做到持续交付就停了,因为生产发布必须留人工卡点。一条典型的发布流水线,我总结成五个字"提、拉、编、测、查":提,开发把代码 push 到 GitLab,Webhook 触发流水线;拉,Jenkins 拉取指定分支的代码;编,执行 mvn 或 npm 构建,或者 docker build 打镜像;测,跑单元测试和接口测试,不过就中断流水线、通知开发;查,代码质量扫描加部署后的健康检查,全绿才算这次发布成功。这套东西的价值一句话:把人重复干的活交给机器流水线,效率高、出错少、每次发布都可追溯。我在移动云项目里做的镜像双轨体系——公网拉取、内网私有仓库托管、集群 ImagePullSecret 认证拉取——就是这条流水线里"镜像制品分发"那一环的生产版,所以流水线前后两端我都有真实体感。

追问链:

  • 追问1:持续交付和持续部署到底差在哪? → 答:只差最后一公里的人工确认。持续交付是自动化做到"随时可上线"的状态,发布时人点一下按钮确认;持续部署是测试全通过后直接自动上生产,没有任何人工环节。差的这一公里恰恰是风险控制:生产环境发布影响真实用户,绝大多数公司要求有人在环里看一眼、有个审批动作,所以企业实践里持续交付是主流形态。
  • 追问2:为什么要容器化之后再部署,直接传 jar 包不行吗? → 答:传包有三个解决不掉的问题:一是环境一致性,jar 依赖目标机器的 JDK 版本和配置,"我这能跑你那不能跑"就是这么来的,镜像把代码加依赖加运行环境整体打包,彻底根治;二是回滚,传包模式回滚要重新传旧包重启,镜像模式切版本就是改一个 tag,秒级;三是弹性,K8s 的滚动更新、健康探针、副本伸缩都是围绕镜像设计的,传包模式享受不到这些能力。
  • 追问3:流水线怎么区分环境、防止测试代码误发到生产? → 答:三分开加一个卡点:GitLab 分支对应环境,dev、test、master 各自独立;Jenkins 按环境建不同任务,生产任务单独授权,只有指定的运维人员能触发;目标机器在 Ansible 的 inventory 里按环境分组、严格隔离,test 组和 prod 组物理上就是不同的清单。最后生产发布前加一步 input 人工确认,确认信息无误才继续执行。这几层防线叠起来,误发生产的概率基本归零。

关联话术:课程项目里我搭的是"提交到部署"的全流水线;生产上我做的是两端的配套——制品分发端(镜像双轨体系)和部署回滚端(kubectl 滚动更新与 rollout undo)。两端都亲手做过,中间调度环节理解到位,这个组合是我的诚实口径。

10.5 Jenkins 架构 【理解】 ​

标准回答: Jenkins 是开源的 CI/CD 调度服务器,Java 写的。架构三块:第一块是 Master 主节点,跑 Web 管理界面和任务调度中心,它管编排但不干重活;第二块是 Agent 构建节点,真正执行编译、打包这些构建任务的机器,通过 SSH 或者 JNLP 的方式连到 Master,按 label 标签认领任务,构建压力大了就横向加 Agent 节点;第三块是插件体系,Jenkins 几乎所有能力都是插件提供的——Git 插件负责拉代码,Credentials 插件管凭据,Pipeline 插件定义流水线,Docker 插件打镜像,装什么插件它就有什么能力。运维必须记住的三个核心概念:Job 是一个任务单元;Credentials 是加密存储的凭据,Git 的 token、镜像仓库的账号密码都放这里,不落在代码和脚本里;JENKINS_HOME 是数据主目录,RPM 安装默认在 /var/lib/jenkins,所有配置、任务定义和构建历史都在这个目录里,所以迁移和备份 Jenkins 本质上就是备份这个目录——先停服务再 rsync 整目录过去,改环境变量指向新路径再启动,不停服务直接搬会损坏数据。

追问链:

  • 追问1:Jenkins 主目录到底在哪?怎么迁移? → 答:看安装方式:RPM 包安装默认 /var/lib/jenkins,root 手动跑 war 包的话是 /root/.jenkins,最终由 JENKINS_HOME 环境变量决定,systemctl cat jenkins 一眼能看到。迁移的标准流程是停服务、rsync 整个 JENKINS_HOME 到新路径或者新机器、改环境变量、起服务验证,四个动作一气呵成。
  • 追问2:构建太慢,每次都全量编译,怎么优化? → 答:三个手段叠加。第一,Maven 本地仓库缓存复用依赖,用 -Dmaven.repo.local 指到共享目录,依赖只下载一次;第二,Dockerfile 分层把依赖安装层放前面、源码层放后面,改一行代码只有最后一层重建,这是分层缓存的铁律;第三,流水线里加 when { changeset "src/**" } 条件,只有相关代码变了才跑对应阶段。三个加起来,常规构建时间能压下来一大截。
  • 追问3(深挖):几百台服务器,Jenkins 怎么分发部署? → 答:Jenkins 不直接管几百台——它编排"部署动作",真正的大规模批量执行交给 Ansible:Jenkins 的流水线里调 ansible-playbook 去批量执行,或者 K8s 场景下调 kubectl 做滚动更新,各干各的擅长事。这块我在课程项目里就是 Jenkins 加 Ansible 联动的模式;生产上我配套的体系是 Jenkins 思想加 K8s 滚动发布。至于 Jenkins 自己的多 Agent 分布式构建集群,我理解到方案层面,没有实际搭过,可以会后补。

10.6 freestyle vs Pipeline 与 Jenkinsfile 结构 【必背】 ​

标准回答: freestyle 是老式任务形态,在网页上点选配置,优点是简单直观,缺点是流程固化在页面里、没法版本化,改流水线要去点页面,没有 review 也没有历史。Pipeline 是用代码定义流水线:把整条流水线写在一个 Jenkinsfile 里,随代码一起提交进仓库,这就是 Pipeline as Code——改流程就是改代码,有评审、有版本历史、可以回退,是现在的绝对主流。Pipeline 写法分两种:声明式结构化程度高,pipeline、stages、steps 层级清晰,语法检查严格,官方推荐;脚本式就是直接写 Groovy,自由灵活但容易写飞,一般只在声明式搞不定的复杂逻辑里用。声明式 Jenkinsfile 的五要素我按顺序背:agent 指定在哪个节点执行;environment 定义环境变量和注入凭据;parameters 做参数化构建,比如让发布的人选目标环境;stages 是各个阶段的编排,每个 stage 里面是具体的 steps;post 定义流水线结束后的动作,成功通知、失败告警、永远清理工作空间。另外两个常用件:失败重试用 retry(3),生产发布前的人工卡点用 input。

追问链:

  • 追问1:从零写一条流水线的完整过程说一下? → 答:六步:装好 Git、Docker、Pipeline 这几个必要插件;在系统管理的 Credentials 里存好 GitLab 的访问令牌和镜像仓库的账号;新建 Pipeline 任务,配置里选"Pipeline script from SCM",指向代码仓库里的 Jenkinsfile;在 GitLab 项目里配 Webhook 指向这个 Jenkins 任务;push 代码验证自动触发;之后就是按 stages 逐阶段跑,失败了看 Console Output 逐行定位。这套我在课程里完整搭过一遍。
  • 追问2:凭据为什么要放 Credentials,直接写在脚本里不行吗? → 答:绝对不行。Jenkinsfile 是随代码进 Git 仓库的,明文密码写进去等于把密码发给所有有仓库读权限的人,而且会永远留在历史里。Credentials 是加密存储的,流水线里用 credentials('id') 按标识引用,日志输出时还自动打码,从机制上保证密钥不落地——这和 AI 平台 API 密钥统一管理是同一个安全思想:密钥永远不进代码。

实操模板(Jenkinsfile 骨架,可直接背):

groovy
pipeline {
  agent any
  parameters { choice(name: 'ENV', choices: ['dev','test','prod'], description: '部署环境') }
  environment {
    REGISTRY = '内网私有仓库地址'
    IMAGE    = "${REGISTRY}/project/app:${env.BUILD_ID}"
    // 引用 Credentials:GIT_TOKEN、DOCKER_USER/PASS
  }
  stages {
    stage('拉取代码') { steps { checkout scm } }
    stage('构建镜像') { steps { sh 'docker build -t $IMAGE .' } }
    stage('推送仓库') { steps { sh 'echo $DOCKER_PASS | docker login $REGISTRY -u $DOCKER_USER --password-stdin && docker push $IMAGE' } }
    stage('部署K8s')   {
      when { expression { return params.ENV == 'test' || params.ENV == 'prod' } }
      steps { input '确认发布?'; sh 'kubectl set image deploy/app app=$IMAGE -n web && kubectl rollout status deploy/app -n web' }
    }
  }
  post {
    failure { echo "构建失败: ${currentBuild.durationString}" }   // 实际接钉钉/邮件通知
    always  { cleanWs() }
  }
}

10.7 一条典型发布流水线完整讲法(课程项目口径) 【必背】 ​

标准回答(60–90 秒标准讲法): 这条流水线是我在课程项目里从零搭的,我完整讲一遍。第一步触发:开发把代码 push 到 GitLab 的 dev 分支,GitLab 通过 Webhook 自动触发 Jenkins 上对应的任务,不需要任何人手动点构建。第二步拉代码:Jenkins 用 Credentials 里存的访问令牌拉取该分支的最新代码。第三步构建:先 mvn clean package 编译出 jar 包,再用 Dockerfile 把 jar 打成镜像,Dockerfile 的分层设计让依赖层可以吃缓存,构建很快。第四步测试:跑单元测试和接口冒烟测试,测试不通过流水线直接中断并通知开发,坏代码走不到下一站。第五步推仓库:镜像打上 BUILD_ID 的标签推送到私有镜像仓库——我课程里用的是 Harbor,生产对应物是移动云的内网私有仓库。第六步部署:Jenkins 执行 kubectl set image 触发 K8s 里 Deployment 的滚动更新,再用 kubectl rollout status 等所有新 Pod 就绪,readiness 探针通过之后发布完成。第七步回滚预案:一旦发现问题,kubectl rollout undo 一条命令切回上一个镜像版本。搭的过程中我踩过一个典型坑:Jenkins 拉 GitLab 代码一直报权限不足,排查发现两个原因叠加——GitLab 的访问令牌没有给 api 权限,加上服务器之间防火墙没放行通信端口,把令牌权限补齐、端口放行之后就通了。这个坑让我把"凭据权限加网络策略"这条排查线走熟了。

追问链:

  • 追问1:这套流水线是你生产搭的还是课程搭的?被问"你们生产有几条流水线"怎么答? → 答(诚实口径):流水线整套是课程项目实操,从 GitLab、Jenkins 到 K8s 部署是我自己从零搭的;生产上我做的是配套环节——移动云集群的私有镜像仓库建设、ImagePullSecret 认证拉取、业务发布的滚动更新和回滚。所以我不虚报生产流水线数量,我讲的是"流水线两端我都有真实体感,中间的调度环节我完整搭过课程版",这个组合对应届社招的 3 年经验定位是匹配的。
  • 追问2:镜像 tag 用 latest 行不行? → 答:生产坚决不行。latest 有两个致命问题:一是不可追溯,出了问题你说不清线上跑的到底是哪次构建;二是缓存策略会出"同一个 tag 前后内容不一样",节点上缓存了旧的 latest 就不会重新拉,发布了等于没发,回滚更是无从谈起。正确做法是用 BUILD_ID 或者 git commit 短哈希做 tag,一次构建对应一个唯一不可变的版本,发布和回滚才有精确的依据。

10.8 制品库概念 【理解】 ​

标准回答: 制品就是构建的产出物——jar、war 包、前端 dist 目录、Docker 镜像都算。制品库就是统一存放这些产物的仓库,核心价值有两个:一是按版本可追溯、可复用,要找上周三构建的那个包一查就有,不会出现"构建一次到处找不到那个包";二是作为流水线的中转枢纽,CI 构建完把制品推上去,CD 从库里按版本拉下来部署,构建和部署解耦。常见制品库按存的东西分:Nexus 和 Artifactory 主要存 jar 和 npm 包,Nexus 还能做 Maven 私服代理——内网机器都从它拉依赖,既加速又保证某个依赖哪天从上游删了也不影响自己;Harbor 存容器镜像,在官方 registry 之上加了项目管理、RBAC 权限、镜像漏洞扫描、签名和复制同步,是企业自建私有仓库的主流选择。我在移动云项目里的对应物是云厂商的私有镜像仓库,我搭的是"公网拉取到内网仓库托管、集群用 ImagePullSecret 认证拉取"的镜像双轨体系——这就是镜像制品分发链路的生产实践。

追问链:

  • 追问1:Harbor 和 Docker 官方 registry 差在哪? → 答:官方 registry 只提供最基本的存和拉能力;Harbor 在它上面加了一整套企业特性:按项目隔离镜像、用户和角色的 RBAC 管控、内置 Trivy 漏洞扫描、镜像签名验真、标签保留和垃圾回收策略、跨 Harbor 的复制同步。生产环境多团队共用仓库时这些能力都是刚需,所以自建私有仓库基本都是选 Harbor。
  • 追问2:K8s 集群怎么拉私有仓库的镜像? → 答:两步:先用 kubectl create secret docker-registry 创建一个包含仓库地址和账号密码的认证 Secret,然后在 Deployment 的 spec 里通过 imagePullSecrets 引用这个 Secret,这样 Pod 调度到任何节点都能用这份凭证安全地拉取私有镜像。这正是我在移动云项目里落地的 ImagePullSecret 分发,凭证统一管理、不散落在节点上。

10.9 蓝绿 / 灰度 / 金丝雀一句话口径 【必背】 ​

标准回答: 这三个都是发布策略,目的都是把发布风险切片。蓝绿部署:准备两套完全一样的环境,旧的叫蓝、新的叫绿,新版本部署到绿环境充分验证后,把流量一次性整体从蓝切到绿,切换是瞬时的,回滚就是把流量切回蓝环境,代价是要维持双倍的资源。灰度发布:新旧版本同时在线,按比例逐步放量——先放百分之五的流量到新版本,观察指标没问题再加到百分之五十、再到百分之百,有问题随时停在中间状态,影响面始终可控。金丝雀发布:本质是灰度的最小形态,先放一台或者一小撮真实流量到新版本上当"金丝雀"探路,观察核心指标确实无异常,再扩大范围,名字就来自矿工带金丝雀下井探毒气的典故。三者的共同逻辑一句话:让错误只影响一小部分流量,且能快速退回。落到 K8s 里,Deployment 的 maxSurge 和 maxUnavailable 参数控制的滚动更新天然具备灰度特性,配合 readiness 探针保证新 Pod 没就绪不接流量;要做到按百分比精确切流量的精细灰度,就要上 Ingress 的权重注解或者服务网格那一层了。

追问链:

  • 追问1:你们实际用的哪种? → 答:课程项目里用的是滚动更新,通过 maxSurge 控制新老并存的比例、readiness 探针保证旧 Pod 不提前摘、新 Pod 不提前接流量;生产上业务发布我也是 K8s 滚动更新模式。蓝绿和金丝雀的精细流量控制在我们的演进规划里有——往服务网格方向走,目前我对它们的理解在原理和适用场景这一层,没有生产实操,这个我如实说明。
  • 追问2:滚动更新和蓝绿回滚速度差在哪? → 答:蓝绿切换是改流量指向,一条规则的事,回滚理论上秒级而且新旧环境都完整保留着;滚动更新回滚是 rollout undo,按旧模板再滚动一轮替换,秒级到分钟级,取决于副本数和 Pod 启动速度。蓝绿用资源换速度,滚动用时间换资源,这是两者的本质取舍。

10.10 回滚策略 【必背】 ​

标准回答: 回滚的前提是"可回滚",三个条件缺一不可:镜像有唯一版本 tag 而不是 latest,配置全部进版本管理随时能取回旧版,数据库变更是向前兼容的。分两层讲回滚手段。应用层:K8s 环境一条命令,kubectl rollout undo deployment/app,指定历史版本加 --to-revision=2,把镜像切回上一版秒级生效;虚拟机或者物理机场景,用 Ansible playbook 部署时保留上一版本的制品目录,回滚就是把软链切回旧目录再重启服务,我们课程项目就是这么设计的。数据层的原则是"回滚应用不回滚数据":应用回滚是换二进制,代价小;数据回滚意味着丢掉回滚点之后产生的用户数据,代价大得多。所以配套的纪律是:数据库变更前必须先备份,变更脚本里新字段只增不删保证旧代码兼容,大型的危险 DDL 安排停机窗口做。我把这套总结成一句话:发布不是终点,能在五分钟内回滚的发布才是合格的发布——发布方案里没有回滚预案,就不应该被批准执行。

追问链:

  • 追问1:滚动更新进行到一半发现新版本有 bug,一半新一半旧,这时候怎么办? → 答:直接执行 rollout undo。undo 的机制是按旧版本的模板再做一轮滚动,把已经更新的那部分 Pod 换回旧镜像,期间新旧两种版本并存,靠 Service 的负载均衡把请求兜住,对用户表现为短暂的比例变化而不是服务中断。这正是滚动模式相比"先停旧再起新"的优势——任何中间状态都是可服务的,也都是可退的。
  • 追问2:为什么数据库不能随便跟着应用一起回滚? → 答:因为两者回滚的代价完全不对称。应用回滚换的是程序,没有数据损失;数据库回滚意味着把用户已经产生的数据抹掉——比如用户下的单、付的款。所以规范是应用层随时可回滚、数据层不轻易动:变更前必备份、DDL 向前兼容设计、危险操作走窗口,这是发布设计里必须有的一条边界意识。

10.11 Ansible 架构与 SSH 免密 【必背】 ​

标准回答: Ansible 是无 Agent 的自动化配置管理工具。所谓无 Agent,就是被管理的机器上不需要安装任何客户端,只要有 SSH 服务和 Python 就行——控制节点装 Ansible,通过 SSH 把要执行的模块代码推到目标机器上运行、拿回结果。这是它和 SaltStack、Puppet 最大的区别,也是运维最喜欢它的一点:零侵入、不改变被管机的软件形态,不存在"Agent 要升级、Agent 挂了连不上"这类自找的运维问题。执行链路三步:控制节点读 inventory 主机清单确定目标机器,SSH 推送模块代码过去执行,收集结果统一汇总展示。使用前的两个关键动作:第一做 SSH 免密,ssh-keygen 生成密钥对,ssh-copy-id 把公钥逐台推到被管节点,之后连接不再要密码;第二处理首次连接的交互确认,批量执行时加 -o StrictHostKeyChecking=no 参数,或者在 ansible.cfg 里配 host_key_checking=False,否则每台新机器都要手工敲一次 yes。这两步做完,ansible all -m ping 全绿,就具备了批量管理的能力。我在移动云项目做的 7 台 ECS 全节点标准化初始化,思路和这个完全同构——统一定义期望状态、所有节点一致执行,只是当时用脚本加文档沉淀的形式做的。

追问链:

  • 追问1:无 Agent 为什么反而是优点?有没有代价? → 答:优点是被管机零依赖:不需要装客户端就不存在客户端自身的版本、故障、安全补丁问题,SSH 通道是每台 Linux 机器现成有的,上手成本极低。代价是每次执行都要建立 SSH 连接,有连接开销,执行速度不如有 Agent 的方案;在几千台规模、要求秒级推送的场景,SaltStack 那种 ZeroMQ 消息总线加 Agent 的架构更快。我们是百台以内的规模,Ansible 的能力和易用性完全够用,这个取舍很清楚。
  • 追问2:Ansible 怎么知道要管哪些机器? → 答:靠 inventory 主机清单文件,默认在 /etc/ansible/hosts,也可以用 -i 参数指定自定义文件。清单里可以写 IP、主机名,更关键的是分组:用 [webservers] 这样的组名把机器按角色归类,组和组之间还能用 children 嵌套组合,执行时按组名批量操作,比如 ansible webservers -m ping 一次探测整组。分组管理是 inventory 的灵魂。

实操模板(初始化免密,可直接背):

bash
ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -N ""
for ip in 192.168.1.11 192.168.1.12 192.168.1.13; do
  ssh-copy-id -i ~/.ssh/id_rsa.pub root@$ip
done
ansible all -i hosts -m ping          # 连通性验证,全绿再往下做

10.12 inventory 主机清单 【理解】 ​

标准回答: inventory 就是 Ansible 的"花名册",默认路径 /etc/ansible/hosts,更常见的做法是用 -i 参数指定项目自己的清单文件,跟 playbook 放在一起做版本管理。核心写法三件事:按环境和角色分组,组内列 IP 或者主机名,可以带 ansible_user、ansible_port 这样的连接变量;用 [组名:children] 做组嵌套,比如 webservers 和 dbservers 组合成一个 prod 大组;用 [组名:vars] 定义组级变量,整组共用。我最强调的实践是分环境:生产环境和测试环境严格分成两个组、甚至两个独立的 inventory 文件,执行 playbook 时必须显式指定组和清单,从入口层面杜绝"本想在测试跑、结果打到了生产"这种最疼的事故——这和 Jenkins 生产任务单独授权是同一个纵深防御思想,一层不够就多铺几层。

实操模板:

ini
[webservers]
192.168.1.11 ansible_user=root
192.168.1.12 ansible_user=root

[dbservers]
192.168.1.21

[prod:children]
webservers
dbservers

[prod:vars]
ansible_port=22

10.13 常用模块 【理解】 ​

标准回答: Ansible 的所有操作都是模块完成的,我按用途分六组记。命令类:command 是默认模块,执行单条命令但不经过 shell 所以不支持管道;shell 走 /bin/sh,支持管道和重定向,复杂命令用它,还可以用 args 指定工作目录;script 把控制端的脚本整个推到目标机执行。包管理类:yum 和 dnf 用于 CentOS 系,apt 用于 Ubuntu 系,state 参数控制目标状态——present 装上、absent 卸掉、latest 升到最新。配置文件类:copy 推送静态文件;template 推 jinja2 模板并渲染变量,是批量生成配置文件的主力;file 管权限、属主和软链;lineinfile 精确改单行,比如改内核参数;blockinfile 插入多行配置块。服务类:service 和 systemd 模块,started、stopped、restarted 控制状态,enabled=yes 设开机自启。系统类:user 和 group 建账号、cron 管计划任务、hostname 改主机名、firewalld 管防火墙。信息类:setup 采集被管机全部 facts 信息,主机名、IP、内存这些都能在 playbook 里直接引用;debug 输出变量辅助调试。这套模块组合下来,日常批量运维的绝大多数场景都有幂等的正规做法,不需要写裸脚本。

追问链:

  • 追问1:command、shell、raw 三个有什么区别? → 答:command 是默认模块,执行单条命令,不走 shell 所以管道、变量替换、重定向都不支持,但胜在安全可控;shell 通过 /bin/sh 执行,管道重定向都支持,需要复杂命令时用它;raw 是最原始的,直接把命令甩过去执行,连 Python 都不需要,专门用于给没装 Python 的老系统做引导——先 raw 装上 Python 再谈其他。日常优先 command,要管道再 shell,raw 只干引导这一件事。
  • 追问2:临时要给 20 台机器改一个内核参数,最快怎么做? → 答:用 ad-hoc 临时命令一行搞定:ansible webservers -m sysctl -a "name=vm.swappiness value=0 state=present",或者用 lineinfile 改配置文件再用 command 执行 sysctl -p。原则是:一次性的批量小操作用 ad-hoc 直接敲;会重复执行、需要固化的操作写成 playbook 进版本库,临时命令不沉淀,脚本化的知识才沉淀。

实操模板(ad-hoc 临时操作命令组,可直接背):

bash
ansible webservers -m ping                          # 全连通性
ansible all -m setup -a "filter=ansible_hostname"   # 采集指定 fact
ansible webservers -m apt -a "name=nginx state=present" --become   # 批量装包(CentOS 换 yum)
ansible webservers -m service -a "name=nginx state=restarted" --become   # 批量重启
ansible webservers -m shell -a "df -h | grep -v tmpfs"    # 批量查磁盘(shell 支持管道)
ansible dbservers -m copy -a "src=my.cnf dest=/etc/mysql/ backup=yes" --become   # 推配置自动备份原文件

10.14 playbook 结构与 roles 【必背】 ​

标准回答: playbook 是 Ansible 的"剧本",用 YAML 描述"对哪组主机、以什么身份、按什么顺序执行哪些任务"。核心要素五个:hosts 指定目标主机组;vars 定义变量;tasks 是任务列表,每个任务调用一个模块带参数,name 写清楚这个任务干什么;handlers 是被动的触发器,被前面任务的 notify 通知后才会执行,典型如"配置文件变了才重启服务";template 和 copy 负责分发文件。roles 是 playbook 的工程化拆分:把部署一个服务所需的东西按固定目录结构组织——tasks 放任务、templates 放 jinja2 模板、vars 放变量、handlers 放触发器、files 放静态文件——一个 role 对应一个服务,比如 nginx role、docker role,playbook 里用 roles 一引用就行。价值是复用和标准化:同一个 nginx role,传不同的变量就能部署测试环境和生产环境,做到"一次编写、处处部署",配置以代码的形式被 review、被版本管理,这就是 IaC 基础设施即代码的落地形态。手写部署 Nginx 的 playbook 我可以现场写:装包、template 分发配置并 notify reload、file 建目录、service 启动加自启、uri 做部署后健康检查,五步结构完整。

追问链:

  • 追问1:handlers 为什么不直接写成普通 task? → 答:handlers 有三重智能:第一,只有被 notify 且前面任务确实发生了变更(状态是 changed)时才执行,配置没改就不会白重启服务;第二,同一个 handler 被 notify 多次也只执行一次,不会重复触发;第三,统一在 play 结束时执行,把"重启"这种有代价的动作收敛到最后必要的一刻。普通 task 每次都执行,做不到"没变化就跳过",这正是幂等设计在流程控制上的体现。
  • 追问2:变量可以在哪些地方定义?优先级如何? → 答:常见定义位置:role 的 defaults 目录(默认值)、role 的 vars、inventory 的 group_vars 组变量、playbook 里的 vars、执行命令时 -e 临时传入。优先级是 -e 最高、role defaults 最低,中间按刚才说的顺序。实践原则:通用默认值放 role defaults,各环境有差异的值放 group_vars,执行时偶尔覆盖用 -e,各得其所。

实操模板(部署 Nginx 的 playbook 完整示例,可直接背诵手写):

yaml
---
- name: 部署 Nginx
  hosts: webservers
  become: yes
  vars:
    nginx_port: 80
    server_name: web.example.local

  tasks:
    - name: 安装 Nginx
      apt: name=nginx state=present update_cache=yes
      # CentOS 改为: yum: name=nginx state=present

    - name: 分发站点配置(模板渲染变量)
      template:
        src: templates/web.conf.j2
        dest: /etc/nginx/conf.d/web.conf
        mode: '0644'
      notify: reload nginx

    - name: 创建站点根目录
      file: path=/var/www/html state=directory mode='0755'

    - name: 启动并开机自启
      service: name=nginx state=started enabled=yes

    - name: 部署后健康检查
      uri: url="http://127.0.0.1:{{ nginx_port }}" status_code=200

  handlers:
    - name: reload nginx
      service: name=nginx state=reloaded

配套模板 templates/web.conf.j2(jinja2 变量):

nginx
server {
    listen {{ nginx_port }};
    server_name {{ server_name }};
    location / { root /var/www/html; index index.html; }
}

执行:ansible-playbook -i hosts deploy_nginx.yml;检查模式先演练:--check --list-tasks。

10.15 幂等性 【理解】 ​

标准回答: 幂等性的定义是:同一个操作执行一次和执行 N 次,结果完全一致。这是 Ansible 能拿来管配置的根基。它的模块大多内建幂等:包管理模块 state=present,装过了再跑就是 ok、changed 为 0,不会重复安装;template 模块推送配置前会先比对目标文件内容和渲染结果,一致就不动,不一致才覆盖并标记 changed、触发 notify。带来的实际价值是 playbook 可以放心地重复执行:环境配置漂移了,把剧本再跑一遍,机器自动收敛回期望状态——这就是"声明期望状态"和"执行命令"的本质区别。对比裸 shell 脚本就很清楚:脚本里写 apt install,敲十遍它执行十遍,脚本没有"期望状态"的概念,只有"动作";而且脚本中途失败重跑,前面的步骤会再来一遍,可能出问题。Ansible 每一步都知道"当前状态"和"目标状态",只做必要的变更,这就是它比裸脚本高级的地方,也是我在移动云项目把节点初始化沉淀成"标准化清单"背后的思想来源。

追问链:

  • 追问1:怎么验证一个 playbook 是幂等的? → 答:最直接的办法:连续执行两次,看第二次的输出——所有任务都是 ok、changed 等于 0,就是幂等的;哪个任务第二次还是 changed,它就不幂等,需要处理。不幂等的任务一般两类改法:换用幂等模块,比如把重启服务改成 service 模块带状态声明;或者用 changed_when、creates 参数手工控制变更判定,比如给 command 加 creates=某个文件,文件存在就跳过。
  • 追问2:哪些场景幂等会失效?怎么防? → 答:三类:command、shell、raw 模块本身不管幂等,执行就是执行,需要自己加 creates 之类的防护条件;依赖执行次数或者顺序的操作,比如数据库里跑一个自增初始化脚本,跑两遍数据就错,这类要单独设计成只跑一次的逻辑;还有外部状态不在 Ansible 视野里的操作,比如调一个 API,它改没改 Ansible 判断不了。原则是:能幂等模块解决的绝不用裸命令,必须用裸命令的必须加防护。

10.16 与其他自动化工具对比 【了解】 ​

标准回答: 一句话定位各家:SaltStack 是 Master 加 Minion 的 Agent 架构,走 ZeroMQ 消息总线,推送速度快,适合超大规模和实时性要求高的场景;Puppet 是老牌企业级,C/S 强模型、声明式 DSL,学习曲线陡,适合超大型组织做高度标准化的配置管理;Chef 用 Ruby DSL,偏开发文化重的团队;Fabric 本质是个 Python 的 SSH 批量执行库,算"批量脚本",没有状态管理和幂等的概念。选型逻辑就一条:看规模和团队。我们这种百台以内的规模、追求零侵入和低上手门槛,Ansible 是最合适的选择;到了上万台、要秒级推送的场景才需要 SaltStack 那种有 Agent 的架构。面试被问"为什么选 Ansible",我就按"规模匹配 + 零侵入 + 生态成熟"三点答,不贬低其他工具。

追问链:

  • 追问1:如果公司已经有 Puppet,你怎么办? → 答:先不急着换。存量环境的迁移成本是真实存在的,正确姿势是共存过渡:新的自动化需求用 Ansible 做增量,跑稳之后评估是否逐步替换。工具是手段,配置管理"期望状态、版本化、幂等收敛"这些思想是共通的,我在一个体系里建立的方法论可以平移到另一个体系。

10.17 Terraform 与声明式 IaC 一句话 【了解】 ​

标准回答: Terraform 是 HashiCorp 家的声明式基础设施即代码工具,用 HCL 语言描述"我要什么样的基础设施"——几台云主机、什么规格、什么网络、什么安全组,写成一个配置文件。执行分两步走:先 terraform plan 做预览,它会算出"为了达到这个状态,需要新建什么、修改什么、销毁什么"给你看;确认没问题再 terraform apply,云上的实际状态就收敛到你描述的状态。它最大的价值是跨云通用——移动云、阿里云都有官方 provider,同一套写法换云只换 provider 配置。它和 Ansible 的分工一句话讲清:Ansible 管配置,管的是机器里面的软件和文件;Terraform 管资源,管的是机器本身和相关云资源的创建销毁,两者经常配合使用。我在移动云项目里做的是资源评估加控制台创建的传统路径,Terraform 属于我理解了原理、列入下一步学习计划的方向——当资源交付需要更快复制、环境需要反复重建时,它就是正解。

追问链:

  • 追问1:声明式和命令式的差别到底体现在哪? → 答:命令式是告诉机器"怎么做"——一步一步的命令,执行者关心过程;声明式是告诉机器"要什么"——只描述终态,工具自己决定怎么做。区别的实感在重复执行和出错重试上:声明式的 plan/apply 任何时候重跑都会向终态收敛,中途失败修好再跑就行;命令式脚本中断后重跑要自己处理"前面哪些已经做过了"。Ansible 的 playbook、K8s 的 YAML、Terraform 的 HCL 都是声明式,这也是云原生时代工具的主流形态。

10.18 发布窗口与变更管理(运维视角的 CD) 【理解】 ​

标准回答: 工具链解决"怎么自动发",变更管理解决"敢不敢发、出了事找谁"——这是 CD 落地的另一半,而且是最容易被忽视的一半。我们的实践有五个要素:第一,发布窗口,生产发布集中在业务低峰窗口比如工作日晚间,重大变更避开业务高峰和节前,给可能的回滚留足时间;第二,变更单和审批留痕,发布前写清四要素——改什么、影响面是什么、怎么验证、怎么回滚——有记录才可复盘,行业里六成的故障和变更相关,这是铁律;第三,灰度顺序,先测试环境后生产、先小流量后全量,绝不一步到位;第四,验证与观察期,发布完成后盯监控大盘和错误率一个观察期,比如三十分钟,确认无异常再离场,很多问题是发布后十几分钟才暴露的;第五,回滚预案先行,回滚命令在发布前就准备好放在手边,出问题第一时间执行而不是现场开会讨论。我在银行项目执行华为原厂版本升级、在移动云项目做故障切换演练,都是严格按这套节奏走的——金融环境把这套流程焊死在制度里,云环境我是自己给自己立的规矩,本质一样:变更的可控性不靠胆量,靠流程。

追问链:

  • 追问1:为什么说六成故障和变更相关? → 答:因为系统在稳态运行时很少自发故障,绝大多数异常都紧跟在一次发布、一次配置修改、一次扩缩容之后——新代码引入 bug、配置改错、资源参数不适配。所以运维排障的第一问永远是"最近改了什么",而变更留痕的价值就在这里:出了事,拿故障时间点对着变更清单一查,嫌疑对象立刻缩小,这是最快的破案路径,比漫无目的地翻日志高效得多。
  • 追问2:发布后的观察期具体看什么? → 答:四个核心指标:错误率(应用 5xx、接口失败率)有没有抬升;延迟(P95、P99 响应时间)有没有恶化;资源(CPU、内存、连接数)有没有异常爬升;业务指标(订单量、成功率)有没有掉。四个都平稳过完观察期才算这次发布闭环,任何一个报警先回滚再说——"先止损、后定位",发布期尤其如此。

10.19 Git 常见事故与急救(找回丢失的提交) 【理解】 ​

标准回答: Git 事故的急救我的核心心法是:只要提交过,数据大概率还在,先别慌。三类典型事故和急救动作:第一类"reset --hard 错了,提交还在吗"——在,git reflog 能看到 HEAD 的全部移动历史,包括被 reset 掉的那次提交的哈希,找到它然后 git reset --hard 那个哈希 或者 git checkout -b rescue 哈希 就找回来了——reflog 是 Git 的黑匣子,本地只要没被 gc 掉的提交都能追回。第二类"分支被删了"——同样走 reflog,找到该分支最后一次提交的哈希,git branch 分支名 哈希 重建。第三类"rebase 冲突搞乱了想放弃"——git rebase --abort 回到 rebase 开始前的状态,merge 冲突同理 git merge --abort,这两个 abort 是安全网。预防大于急救的三条纪律:重要分支配保护;大操作(rebase、filter-branch)前先打个 tag 或者记下当前哈希;push 之前的提交都在本地,出事先停手再查 reflog,千万别病急乱投医地继续操作把现场破坏了。我日常管理自己的脚本仓库就用这套纪律,reflog 救过我一次误 reset 的提交——那次之后"操作前记哈希"成了肌肉记忆。

追问链:

  • 追问1:reflog 和 log 的区别? → 答:log 显示的是当前分支可达的提交历史,被 reset、删分支"断开"的提交它看不到;reflog 记录的是 HEAD 本身的每一次移动——你做过什么它就记什么,跟提交是否可达无关。所以急救场景必须用 reflog:它是从"操作历史"里翻旧账,log 是从"数据结构"里顺着链走。一句话:log 看内容历史,reflog 看操作历史。
  • 追问2:cherry-pick 什么时候用? → 答:需要把某一个提交"摘"到另一个分支的时候。典型场景是 hotfix:修复提交合到了 dev 分支,生产分支也要这个修复但不要 dev 上的其他内容,就 cherry-pick 那个提交的哈希到 release 分支。注意它是复制不是移动——会产生新的提交哈希,两边内容一样但不是同一个提交,用多了历史会有重复感,所以它是精确手术刀,不是日常搬运工具。

10.20 Ansible 常见报错与排查 【理解】 ​

标准回答: Ansible 的报错我都按报错信息里的分类定位,讲五种最常见的。第一种 UNREACHABLE——SSH 连不上:先手动 ssh 一下目标机验证,常见原因是免密没配好、目标机防火墙、inventory 里的 IP 或端口写错。第二种 FAILED 说模块缺参数或者权限不够:权限问题的标志是报错里有 permission denied,加 --become 提权就好;裸 command 模块不支持管道这类是参数用法问题,换 shell 模块。第三种讲 YAML 语法错误——playbook 跑都不跑直接报:报错会给出行号,九成是缩进不对(YAML 用空格不用 tab,同级缩进必须一致)或者冒号后面缺空格。第四种 "could not find requested file":src 指定的模板或文件路径不对,注意相对路径是相对 playbook 所在目录的 roles 结构。第五种幂等失效类——不是报错但结果不对:command 模块每次都 changed,要加 creates 参数。排查的通用套路两步:先用 ansible 主机 -m ping 确认连通性没问题,再用 ansible-playbook --check --list-tasks 做干跑看任务清单——把"连不上"和"跑不对"两类问题分开,再对号入座。这套报错分类我在课程实验里全部真实撞过一遍。

追问链:

  • 追问1:playbook 卡在某台机器上不动,怎么处理? → 答:两个常见原因:一是 SSH 连接挂起——目标机网络不通但 TCP 没超时,可以配 ssh 的 ConnectTimeout 参数;二是任务里的命令本身在等交互输入(比如 yum 要确认),command 模块不响应交互就卡死,解决是命令里显式加非交互参数(yum -y)或改用幂等模块。控制手段是给任务加 async 超时或者 timeout 参数,批量任务别裸跑没超时的交互命令。
  • 追问2:怎么只对某一台机器重跑失败的任务? → 答:用 --limit 参数限定:ansible-playbook -i hosts site.yml --limit 192.168.1.12,只有这台执行;也可以在 inventory 里给故障机建个临时组。因为 Ansible 幂等,对已成功的机器重跑本来就无副作用,但 limit 能省时间也减少干扰面——生产变更影响面控制的习惯要带到自动化工具里。

10.21 Jenkins 权限与安全加固 【了解】 ​

标准回答: Jenkins 是个暴露在网上的高权限系统——它的凭据库里存着 Git token 和仓库密码、它的任务能执行任意脚本,所以安全加固是必做的,我按课程搭建加生产意识讲五条。第一,访问控制:装 Role-Based Authorization 插件做角色授权——开发只给各自任务的构建权限,配置管理权限只给运维,杜绝人人都是管理员;匿名访问必须关掉。第二,网络面收敛:Jenkins 尽量不暴露公网,放内网并配合安全组限制来源 IP,实在要走公网上反向代理加 HTTPS。第三,凭据纪律:所有密码 token 一律进 Credentials 加密存储,严禁写在 Jenkinsfile 或者 job 配置的明文框里,日志里注意别 echo 出敏感变量。第四,插件治理:插件是 Jenkins 最大的攻击面,只装必要的、定期升级安全补丁版本,长期不更新的废弃插件直接卸载。第五,JENKINS_HOME 的备份和权限:这个目录包含全部凭据和配置,备份要加密传输,目录权限收紧到 jenkins 用户。这套加固思想其实和我管 API 密钥、管服务器账号是同一套——高权限系统的安全就三板斧:入口收敛、最小权限、凭据不落地。

追问链:

  • 追问1:为什么说 Jenkins 的插件是最大攻击面? → 答:因为插件生态庞大、质量参差、供应链环节多——历史上 Jenkins 的高危漏洞大量出在第三方插件上,而且插件能拿到 Jenkins 内部的完整权限。加固思路就是"最小安装加及时更新":每个插件问一句"没有它能不能干活",能就不装;装了的盯安全通告。这和服务器上"最小化安装软件包"是同一条安全原则。

10.22 本章案例 ​

  • 【课程项目】GitLab+Jenkins+Ansible 自动化部署平台(STAR,完整版约 300 字):背景是课程实战项目,目标是把开发提交代码到应用上线全流程自动化,替代手工传包部署。任务是我负责从零搭建整套平台。行动:第一步部署 GitLab 社区版做代码仓库,建立 dev/test/master 分支模型并配置 master 保护分支;第二步部署 Jenkins,安装 Git、Docker、Pipeline 插件,配置 Credentials 存访问令牌,编写 Jenkinsfile 流水线——拉代码、mvn 构建、docker build 打镜像、推 Harbor、kubectl 滚动更新到 K8s;第三步在 GitLab 配 Webhook 实现提交自动触发;第四步用 Ansible 做虚拟机场景的批量部署,inventory 按环境分组,playbook 含停止旧服务、分发制品、启动、健康检查。过程中解决两个典型问题:Jenkins 拉 GitLab 报权限不足,排查是访问令牌缺 api 权限加防火墙未放行端口,补齐后解决;部署后服务起不来,定位是旧服务未停导致端口占用,在 playbook 里增加"启动前强制停止旧服务加端口占用检测"后根治。结果是全流程跑通:提交到上线分钟级完成,回滚一条 rollout undo,沉淀了完整的 Jenkinsfile 和 playbook 模板。
  • 【真实项目】移动云集群标准化初始化(IaC 思想的生产落地,STAR):背景是 7 台 ECS 要构建生产集群,手工逐台配置必然产生环境漂移。任务是我负责全部节点的标准化初始化。行动:制定统一的初始化清单——关闭 swap、加载 br_netfilter 和 overlay 内核模块、sysctl 内调优、chrony 时间同步、containerd 统一配置(Systemd cgroup 驱动)、内核参数与 ulimit 基线——先在一台节点验证,再按清单批量执行到全部节点,并把清单沉淀成建设方案文档。结果:7 台节点环境完全一致,kubeadm 集群一次搭建成功;后来集群扩容和重建,按文档重放即可复制,资源交付从天级缩短到分钟级。这套"期望状态统一定义、所有节点收敛一致"的做法,就是 Ansible 剧本思想在我生产环境里的真实投影,也是我讲 IaC 时的亲身依据。
  • 【真实项目】镜像双轨体系支撑业务发布(STAR):背景是业务发布需要安全可控的镜像分发通道,节点直连公网仓库既慢又有安全隐患。任务是我负责构建镜像分发链路。行动:建立"公网拉取 → 内网私有仓库托管 → 集群认证拉取"的双轨体系:基础镜像经公网拉取后推送内网私有仓库托管,业务镜像直接进内网仓库;集群侧用 kubectl create secret docker-registry 创建认证 Secret,业务 Deployment 通过 imagePullSecrets 引用拉取。结果:集群节点不再直连公网仓库,出口统一收敛到 NAT 网关,镜像分发全程带认证可审计;配合 kubectl 滚动更新和 rollout undo,业务发布的部署与回滚两端都由我亲手落地。

10.23 章末自测清单 ​

  1. Git 三个区域? → 工作区 →(add)暂存区 →(commit)本地仓库 →(push)远程仓库。
  2. reset 和 revert 的区别? → reset 挪指针删历史(仅本地),revert 生成反向提交保留历史(远程用)。
  3. GitLab 五级权限? → Guest、Reporter、Developer、Maintainer、Owner。
  4. CI/CD 的 CD 两种含义? → 持续交付(人工确认后上线)、持续部署(全自动上线)。
  5. 五大阶段口诀? → 提(提交触发)、拉(拉代码)、编(编译构建)、测(测试)、查(质量扫描与健康检查)。
  6. freestyle 和 Pipeline 区别? → freestyle 网页点选不可版本化;Pipeline 用 Jenkinsfile 代码化、可 review 可版本管理。
  7. Jenkinsfile 声明式五要素? → agent、environment、parameters、stages、post。
  8. 蓝绿/灰度/金丝雀各自一句话? → 蓝绿双环境整体切换;灰度按比例逐步放量;金丝雀先放一小撮真实流量探路。
  9. K8s 回滚命令? → kubectl rollout undo deployment/xxx(--to-revision 指定版本)。
  10. Ansible 为什么无 Agent 是优点? → 被管机零依赖零侵入,复用 SSH,不存在 Agent 自身的运维负担。
  11. 部署 Nginx 的 playbook 用到哪些模块? → apt/yum、template、file、service、uri(+handlers reload)。
  12. 幂等性是什么、怎么验证? → 执行一次和 N 次结果一致;连跑两次第二次 changed=0。
  13. handlers 的触发条件? → 被 notify 且任务实际发生变更,play 结束统一执行一次。
  14. Ansible 和 SaltStack 一句话对比? → Ansible 无 Agent 走 SSH 适合百台级;SaltStack 有 Agent 走消息总线适合超大规模秒级推送。
  15. 生产镜像 tag 为什么不用 latest? → 不可追溯、缓存不可控,发布回滚失去版本依据。
  16. Terraform 和 Ansible 分工一句话? → Terraform 声明式管资源创建(机器),Ansible 管机器里的配置。
  17. 变更管理四要素? → 改什么、影响面、验证方式、回滚方案——发布前必须写清。
  18. reflog 和 log 的区别? → log 看分支可达的提交历史,reflog 记 HEAD 的操作历史——找回丢失提交用 reflog。
  19. rebase 搞乱了怎么放弃? → git rebase --abort 回到开始前(merge 同理 --abort)。
  20. Ansible UNREACHABLE 报错先查什么? → 手动 ssh 目标机验证——免密、防火墙、inventory 地址三个方向。
  21. Jenkins 安全三板斧? → 入口收敛(内网+限源)、最小权限(角色授权+关匿名)、凭据不落地(Credentials 加密)。

10.24 本章口径红线(面试前再读一遍) ​

  1. 诚实分层:Git 是日常重度使用(管自己的脚本与文档);Jenkins 流水线是课程项目实操;Ansible 是课程实操 + 生产同构实践(节点标准化初始化)。被问生产流水线数量时按这个口径答,不编数字。
  2. 生产关联点:镜像双轨体系(制品分发端)和滚动更新回滚(部署端)是真实生产经历,主动往这上面引,把"课程搭过全流程 + 生产做过两端"的组合讲成优势。
  3. 脱敏:内网 GitLab 地址、镜像仓库域名一律说"内网私有仓库",不报真实域名和账号。
  4. 不冒进:分布式构建集群、服务网格灰度、Terraform 生产化这些没实操过的,按"理解原理 + 会后补"的兜底话术答,绝不现编。
  5. 一句话人设:CI/CD 的完整链路我搭得出来、讲得清楚,生产上负责过其中最关键的分发和发布环节——工具会变,流程思想和变更纪律是通用的。