精读笔记(RHCA 英文教材)· DO447 Chapter 5 Coordinating Rolling Updates
大约 8 分钟
精读笔记(RHCA 英文教材)· DO447 Chapter 5 Coordinating Rolling Updates
教材原文:RHCA 官方英文教材(教材第 199~228 页)(OCR 整书版已从本站移除,本页为章节精读) 关联知识:
02-08-Ansible自动化(RH294DO447)、RHCE9精读笔记\精读-第32章-Docker容器(镜像更新可对照滚动发布思路);本系列配套00-RHCA教材精读-导航与学习法.md章节结构:5.1 委派任务与 facts(delegate_to / delegate_facts)→ 5.2 滚动更新管理(serial / max_fail_percentage / run_once)→ LabCoordinating Rolling Updates
Chapter Goal / Objectives(原文+译)
- GOAL: Use advanced features of Ansible to manage rolling updates in order to minimize downtime, and to ensure maintainability and simplicity of Ansible projects.(用 Ansible 高级特性管理滚动更新:把停机时间降到最低,同时保证项目的可维护性与简洁性)
- OBJECTIVES:① 让一个任务替当前主机在“另一台主机”上执行,并控制该任务收集的 facts 归到谁名下;② 用
serial把主机分批整 play 推进;③ 失败主机超过阈值时中止 play(max_fail_percentage);④ 让任务“每批只跑一次”或“整 play 只跑一次”(run_once)。
5.1 Delegating Tasks and Facts(委派任务与 facts)
delegate_to:把任务挪到别的机器执行
- 场景:配置 A 主机时,需要顺带在 B 机器上做动作——如登录网络设备改 DHCP、保证 AD 域里存在某些组、调用受管端没有工具的 API。
delegate_to指定“代替当前目标主机真正执行任务”的主机;最常见的委派目标是localhost(控制节点):服务 API 从控制节点可达、受管端不可达时。- 示例 1(基本):每台被管端先
command: uname -a并 register,再delegate_to: localhost跑一次 uname 并 register——第二个任务在控制节点上“替每台”执行。 - 示例 2(实用:滚动摘除节点):
- name: Remove the server from HAProxy haproxy: state: disabled host: "{{ ansible_facts['fqdn'] }}" socket: /var/lib/haproxy/stats delegate_to: "{{ item }}" loop: "{{ groups['lbservers'] }}" - name: Make sure Apache HTTPD is stopped service: { name: httpd, state: stopped } - 委派后的变量上下文(易错点):委派任务仍使用“当前受管主机”(inventory_hostname)的变量与 facts——上例
ansible_facts['fqdn']是受管端 FQDN,不是被委派机(负载均衡器)的 FQDN。这通常正是我们想要的(把“当前这台”从 LB 摘除)。
delegate_facts:控制委派任务 facts 的归属
- 默认:委派任务产生的 facts/变量记到“当前受管主机”名下(hostvars[当前主机])。
- 需要记到“被委派主机”名下时,任务加
delegate_facts: True:- hosts: localhost gather_facts: no tasks: - name: Set a fact in delegated task on servera set_fact: myfact: Where am I set? delegate_to: servera.lab.example.com delegate_facts: True - name: Display the facts from servera.lab.example.com debug: msg: "{{ hostvars['servera.lab.example.com']['myfact'] }}" - 上例:play 跑 localhost,任务委派到 servera;
delegate_facts: True让 myfact 落到hostvars['servera'],而不是默认的hostvars['localhost']。
Guided Exercise 1 要点(lab: update-delegation)
lab update-delegation start → clone update-delegation 仓库 → inventory web_servers: server[a:f].lab.example.com → 补全 query_times.yml:① task1 在每台 web 上 shell: date 取时间并 register(加 changed_when: false);② task2 把每台主机名+时间 append 写入 workstation 的 /tmp/times.txt(delegate_to: localhost + shell: "echo ... >> /tmp/times.txt")→ ansible-playbook query_times.yml → cat /tmp/times.txt 验证 6 行 → lab update-delegation finish。
5.2 Managing Rolling Updates(管理滚动更新)
概念与默认行为
- 滚动更新 = 把部署“分批交错”推进(stagger),实现零停机;意外发生时立即停住,错误只波及当前批次;配合测试/监控可做:回滚本批配置、隔离坏主机、给干系人发通知。
- 默认(无 serial):所有主机一个批,Ansible 逐个任务推进;某主机某任务失败会被“丢出”play,但其余主机继续跑——play 只在本批全部主机都失败时才停。
serial:控制批次大小
| 写法 | 行为 |
|---|---|
serial: 2 | 每批 2 台,整批跑完 play 再进下一批;末批可不足 2(200 台=100 批) |
serial: 25% | 每批 = 总主机数 × 25%(20 台或 200 台都是 4 批);小数向下截断,截断为 0 按 1 台;余数进最后小批 |
serial: [1, 10%, 100%] | 第 1 批 1 台试跑;第 2 批 = 总数 10%(按总组数算,不是按剩余数);之后 100% 收尾剩余主机 |
- 百分比举例(25%):3 台 → 0.75 截断为 0 → 每批 1 台(3 批);13 台 → 3.25→3(批次 3/3/3/3/1);19 台 → 4.25→4(4/4/4/4/3)。
- 列表用尽仍有未处理主机时,最后一个批次大小重复执行直到处理完。例:100 台
serial: [1, 10%, 25%]→ 1 + 10 + 25 + 25 + 25 + 14 = 100(三批 25 后余 14 进末批)。 - ansible_play_batch:Ansible 用该变量保存“当前批仍活跃的主机”列表;主机任务失败后即被移除(每个任务后更新)→ 后续任务只对仍活跃主机执行。
失败行为与 max_fail_percentage
- 只设 serial:当前批“全部失败”才中止,且会阻止启动后续批次(已成功主机保留,未处理主机不受影响)。例:serial: 2,第一批 1 成 1 败 → 继续第二批;第二批两台都失败 → 整个 play 中止,最终 1 台成功、3 台可能异常、其余未动。
max_fail_percentage: 30%:当前批失败主机占比超过阈值即中止(不是整批全挂)。例:100 台 +serial: [2, 10%, 100%]:首批 2 台 → 30%×2=0.6,1 台失败即停;第二批 10 台 → 失败超过 3 台才停。- fail fast:
max_fail_percentage: 0→ 任何主机失败立即中止整个 play。 - 官方小结(IMPORTANT):
- serial 与 max_fail_percentage 都不设 → 一批跑完,全部失败才失败;
- 只设 serial → 多批跑,任一批整批失败即失败;
- 设 max_fail_percentage → 批内失败占比超标即失败;
- 任一 play 失败,playbook 中剩余 plays 全部中止。
run_once:每批只跑一次 / 整 play 只跑一次
- 任务默认对每台主机各跑一次;加
run_once: yes后:有 serial 时每批跑一次,无 serial 时整 play 跑一次。 - 典型组合:
run_once+delegate_to把“批量汇总动作”丢到一台机器执行,例如把当前批成功主机列表交给激活脚本:- name: Reactivate Hosts shell: /sbin/activate.sh {{ active_hosts_string }} run_once: yes delegate_to: monitor.example.com vars: active_hosts_string: "{{ ansible_play_batch | join(' ') }}" - 只想“整个 play 只跑一次”(即使分多批):
run_once: yes+when: inventory_hostname == ansible_play_hosts[0](条件限定 play 内第一台)。
Guided Exercise 2 + Lab 要点(lab: update-management / update-review)
- GE2(update-management):在现有 haproxy 集群上跑“批次不等的更新”playbook:serial 控制批次、失败中止、任务 run_once(每批一次的动作)。
- 总 Lab(update-review):clone update-review.git 并先跑 site.yml 部署前端 LB + 后端 web 池,然后改 update_webapp.yml 实现滚动更新: ①
pre_tasks:用 haproxy 模块把每台 web(host:{{ inventory_hostname }})从负载均衡摘除,delegate_to: "{{ groups['lb_servers'][0] }}"; ② 把post_tasks里的冒烟测试改为从负载均衡器发起请求(委派到 lb),真正覆盖网络链路而非本机回环; ③ 冒烟测试后post_tasks把每台 web 重新 enable 回 haproxy; ④ 批次:serial: [5%, 35%, 100%](保证不超过 3 批完成全部更新); ⑤ 加max_fail_percentage: 0(任何主机失败即中止——fail fast); ⑥ 运行 update_webapp.yml → commit/push →lab update-review grade→lab update-review finish。
命令速查表
| 场景 | 命令/写法 |
|---|---|
| 委派到控制节点 | delegate_to: localhost |
| 委派到清单组内每台 | delegate_to: "{{ item }}" + loop: "{{ groups['lb_servers'] }}" |
| 委派到组内第一台 | delegate_to: "{{ groups['lb_servers'][0] }}" |
| 委派 facts 归被委派主机 | delegate_facts: True |
| 固定批次 | serial: 2 |
| 百分比批次 | serial: 25% |
| 渐进批次列表 | serial: [1, 10%, 100%] |
| 失败容忍/秒停 | max_fail_percentage: 30%(0 = 一败即停) |
| 每批一次 | run_once: yes(配 delegate_to 到 monitor/activator) |
| 整 play 一次 | run_once: yes + when: inventory_hostname == ansible_play_hosts[0] |
| 当前批活跃主机 | {{ ansible_play_batch }}(配 join(' ') 拼参数) |
| 实验 | lab update-delegation / update-management / update-review start|grade|finish |
核心词汇表
| 英文 | 中文速记 |
|---|---|
| delegate_to | 委派指令:任务改到指定主机执行 |
| delegate_facts | 委派任务 facts 归属开关(默认归当前主机) |
| rolling update | 滚动更新(分批交错部署,零停机) |
| batch | 批次(serial 决定每批主机数) |
| serial | 批次大小指令(整数 / 百分比 / 列表) |
| ansible_play_batch | 当前批仍活跃的主机列表(失败即移除) |
| max_fail_percentage | 失败占比阈值(超了中止;0=fail fast) |
| fail fast | 快速失败策略 |
| run_once | 每批(或整 play)只执行一次 |
| ansible_play_hosts | 本 play 全部目标主机列表 |
| smoke test | 冒烟测试(部署后验证) |
| load balancer / drain | 负载均衡器 / 摘除节点(滚动更新前先摘除) |
| localhost (delegation) | 控制节点作为委派目标 |
本章自测
- delegate_to 解决什么问题?委派后任务里引用变量/facts 用的是谁的值?
- delegate_facts: True 与默认行为有什么差别?给出一个非它不可的场景。
- 无 serial 时主机失败会怎样?只设 serial 时“整批失败”与“批内个别失败”行为差异?
- serial: 25% 对 3 台 / 13 台 / 19 台各是几批?截断为 0 时怎么办?
- serial: [1, 10%, 100%] 与 [1, 10%, 25%](100 台)各批怎么分?为什么最后一个值会重复?
- ansible_play_batch 是什么?何时更新?
- max_fail_percentage 的计算基准是什么(批内还是全场)?怎么实现“一败即停”?
- run_once 与“整 play 只跑一次”的写法差异?run_once 任务通常配什么一起用?
- 滚动更新里“摘除节点 → 更新 → 冒烟测试 → 放回”分别放在 pre_tasks/tasks/post_tasks 的哪一段?为什么要从负载均衡器发冒烟请求?
- SUMMARY 五条分别对应本章哪几个考点?
