精读笔记(RHCA 英文教材)· DO447 Chapter 3 Managing Task Execution
大约 13 分钟
精读笔记(RHCA 英文教材)· DO447 Chapter 3 Managing Task Execution
教材原文:RHCA 官方英文教材(教材第 71~134 页)(OCR 整书版已从本站移除,本页为章节精读) 关联知识:
02-08-Ansible自动化(RH294DO447)、RHCE9精读笔记(第 25~33 章 Shell/Ansible/Docker/K8S);本系列配套00-RHCA教材精读-导航与学习法.md章节结构:3.1 控制特权提升 → 3.2 控制任务执行 → 3.3 用 tags 选择任务 → 3.4 优化执行速度 → LabManaging Task Execution
Chapter Goal / Objectives(原文+译)
- GOAL: Control how Ansible runs tasks — privilege escalation, execution order, tag-based selection, and speed optimization.(控制 Ansible 任务的执行方式:提权、执行顺序、按标签选择、提速优化)
- OBJECTIVES(本章总目标): ① 控制自动特权提升,并把提权限定到必要作用域(play/role/block/task/连接变量);② 控制 play 内 pre_tasks/roles/tasks/post_tasks 与 handlers 的执行顺序,用
listen一个通知触发多个 handler;③ 用tags只执行或跳过指定任务;④ 提升 playbook 执行效率(关 facts、加大 forks、包一次装一批、SSH 复用/管道化、用回调插件剖析耗时)。
3.1 Controlling Privilege Escalation(控制特权提升)
提权指令与命令行选项(directives ↔ CLI)
- 提权由 become 体系控制,可在 play、role、block、task 四级设置 4 个指令:
become、become_user、become_method、become_flags;默认行为:become_user=root、become_method=sudo。 - 配置方式对照表: | 配置/剧本指令 | 命令行选项 | | --- | --- | |
become|--become或-b| |become_method|--become-method=BECOME_METHOD| |become_user|--become-user=BECOME_USER| |become_password|--ask-become-pass或-K| - 配置文件
[privilege_escalation]段把become设为 true 时,所有 play 默认提权(用当前 become_method 切到 become_user)。 - 易错点:配置文件写
become: false,但命令行给了-b→ 命令行生效,配置文件被忽略;反之未给选项时跟随配置文件默认。
play / task / block / role 各层写法
- play 级:
become: true(本 play 全部提权)或become: false(即使配置文件/命令行开了也不提权);不写则跟随默认。教材示例 3 个 play 分别 true / false / 默认,用debug: var: ansible_user_id看受管端实际执行用户。 - task 级:个别任务需要(或不需要)提权时写在任务上,覆盖 play 设置。
- block 级:一批任务统一提权,覆盖 play 设置;也常配合
become_user以应用普通用户身份(如数据库用户)跑子集任务。示例:playbecome: false+ block 内两任务提权 + block 外 uri 测试任务不提权:- name: Deploy web services hosts: webservers become: false tasks: - block: - name: Ensure httpd is installed package: { name: httpd, state: installed } - name: Start and enable webserver service: { name: httpd, state: started, enabled: yes } become: true - name: Test website from itself, do not become uri: url: http://{{ ansible_host }} return_content: yes register: webpage failed_when: webpage.status != 200 - role 级:① 角色内部自带提权设置(角色文档说明);② 在调用处按角色单独设(类似 task/block):
roles: - role: role-name become: true
连接变量方式(inventory 侧,优先级最高)
| 剧本/配置指令 | 连接变量 |
|---|---|
become | ansible_become |
become_method | ansible_become_method |
become_user | ansible_become_user |
become_password | ansible_become_pass |
- IMPORTANT:连接变量覆盖配置文件以及 play/task/block/role 里的 become 设置 → 优先级极高,慎用(一旦配错整组行为都变)。
- 适用场景:大组里个别主机需要不同提权方法/用户(逐台配太繁琐,只给特例主机配);写在 YAML 清单
vars:或组/主机变量里。play 的vars:也可写ansible_become: true(走变量优先级,同样压过 inventory 与 become 指令)。 - 用法小结:建议把“是否提权”(become)放 playbook 里控制、把“用什么方式提权”(ansible_become_method 等)放 inventory 变量里控制。
如何选择提权方案(决策要点)
- 两大权衡:① Keep It Simple(playbook 简洁是 Ansible 最佳实践首要原则);② least privilege 最小权限(避免剧本错误导致受管端被越权破坏)。
- 新手通病:全剧本默认提权常开 → 不需要 root 的任务也以 root 跑,风险增大。
- 不推荐直连 root 账号:跑剧本者都得有 root 凭据、难以审计“哪次变更是谁执行的”。
- 推荐做法:需要提权的任务/段单独开(play 开 + 个别任务
become: false关,或按“需/不需提权”拆成两个 play);跨主机提权方式不同 → inventory 变量配ansible_become_method,playbook 用become控制开关。 - 配置文件开 become 只在“剧本必须提权但你又不能改它”时才需要。
Guided Exercise 1 要点(lab: task-escalation)
lab task-escalation start → git clone task-escalation 仓库 → 把“默认全部提权”改成“仅对需要的 play/role/block/task 提权”,其余保持最小权限 → 用 ansible_user_id 验证执行用户 → commit/push → lab task-escalation finish。
3.2 Controlling Task Execution(控制任务执行)
play 内固定执行顺序(核心考点)
- play 各段固定执行顺序:
pre_tasks- pre_tasks 中通知的 handlers
rolestasks- roles 与 tasks 中通知的 handlers
post_tasks- post_tasks 中通知的 handlers
- 顶层指令顺序不影响执行顺序(play 是 YAML 字典,Ansible 按固定顺序解析):即使把
tasks写在roles前面,角色任务仍先执行;为可读性建议按 pre_tasks→roles→tasks→post_tasks 书写,handlers 放最后。 - handlers 的冲刷时机:pre_tasks 后、roles+tasks 后、post_tasks 后各一次 → 同一个 handler 若在多段被 notify,可以在一场 play 里执行多次。
- meta: flush_handlers:在任务中途强制立刻跑完已通知的 handler。典型场景:更新配置文件 → notify
Restart api server→meta: flush_handlers→ 再调 API(否则 API 还在用旧配置):tasks: - name: Deploying the configuration file template: src: api-server.cfg.j2 dest: /etc/api-server.cfg notify: Restart api server - name: Running all notified handlers meta: flush_handlers - name: Asking the API server to rebuild its internal cache uri: { url: "https://{{ inventory_hostname }}/rest/api/2/cache/", method: POST, status_code: 201 } - handler 具有 play 全局作用域:play 可通知角色内 handler,角色之间、角色与 play 之间可互相 notify;已通知的 handler 按 handlers 段中的定义顺序执行(不是通知顺序)。
include_role 与 import_role(把角色当任务用)
| 特性 | import_role(静态导入) | include_role(动态包含) |
|---|---|---|
| 解析时机 | 启动时解析并插入 play | 执行到该任务时才解析 |
| 角色语法错误 | 启动即报错,不开始执行 | 执行到才中止 |
when 条件为假 | 仍然解析 | 直接跳过、不解析 |
- 用途:把角色夹在普通任务之间(先跑普通任务 → include/import 角色 → 再跑任务);缺点:不细看剧本不易发现用到哪些角色。
pre_tasks / post_tasks 的典型场景
- 教科书例子(应用发布防误报):pre_tasks 停 Nagios 监控(nagios action disable_alerts + delegate_to: nagios-srv)→ roles 部署新版本 → tasks 重启 memcached、用 uri 打
/healthz校验(failed_when: "'OK' not in result.content")→ post_tasks 恢复监控。注意验证放 tasks 里,说明“tasks 在 roles 与 post_tasks 之间”,可用 URL 探活代替 sleep。
handlers 的 listen(一次通知、多个响应)
- 两种批量通知方式:
- 任务
notify一串 handler 名字(逐一列名); - 任务只
notify一个事件名,多个 handler 用listen: 事件名订阅同一个通知。
- 任务
listen优点:一组 handler 被多个任务复用时,增删 handler 只改 handlers 段、不改任务;任务侧只需发一个通知。缺点/注意:handler 名字必须唯一(不能两个同名);用 listen 时也建议给 handler 起独立名字,避免“名字恰好等于事件名”造成的阅读混乱。- listen 特别适合角色协作:角色 notify“服务需重启”事件,play 里外部 handler
listen该事件做附加动作(通知监控、重启依赖服务、证书过期后重启 httpd 等)。 - 报错场景:任务 notify 的名字既不在主 handlers 列表、也不在任何 listening handlers 列表 → 报
ERROR! The requested handler 'xxx' was not found in either the main handlers list nor in the listening handlers list(debug + changed_when: true 也能触发)。
控制主机执行顺序(order 指令)
- Ansible 2.4 起默认按 inventory 中列出顺序执行 play。
order取值: | 值 | 含义 | | --- | --- | |inventory| 清单顺序(默认) | |reverse_inventory| 清单逆序 | |sorted| 字母序(数字排在字母前) | |reverse_sorted| 字母逆序 | |shuffle| 每次运行随机 |- 易错:任务并行执行,
ansible-playbook输出显示的是“完成顺序”而非“执行顺序”,即使按字母序执行,输出仍可能 www1 最后完成。
Guided Exercise 2 要点(lab: task-execution)
lab task-execution start → ~/DO447/labs/task-execution/ → 用 pre_tasks/post_tasks 控制 haproxy 部署前后任务 → 用 listen 让一个通知触发多个 handler → lab task-execution finish。
3.3 Running Selected Tasks(tags 选择任务)
给 5 类资源打标签
- task 级(最常见):
tags: [install]; - play 级:整个 play 一个标签;
include_tasks引用文件:标签写在 include_tasks 任务上 → 全局作用于该文件全部任务;- role:
roles: - { role: databases, tags: ['production', 'staging'] }; - block:整个块共享标签。
- IMPORTANT:打在 roles/include_tasks 上的标签是“给其中所有任务统一附加”,不是用来剔除文件内某些任务的机制。
选择 / 跳过 / 列出
| 命令 | 作用 |
|---|---|
ansible-playbook main.yml --tags webserver | 只运行带 webserver 标签的任务/play |
ansible-playbook main.yml --tags install,setup | 多个标签逗号分隔 |
ansible-playbook main.yml --skip-tags webserver | 跳过带该标签的,只跑未打标任务 |
ansible-playbook main.yml --list-tags | 列出 playbook 中全部标签(不执行) |
- 注意:
--tags/--skip-tags不会关掉 Gathering Facts(setup 默认仍跑,除非剧本 gather_facts: no)。
特殊标签
always:总是执行(即使不在 --tags 列表里);唯一例外是--skip-tags always显式跳过。never:默认不执行;仅当--tags never或同时命中该任务其它标签时才执行。tagged:只运行所有带显式标签的资源;untagged:只运行没有显式标签的资源;all:全部任务(默认行为)。
Guided Exercise 3 要点(lab: task-tagging)
lab task-tagging start → clone task-tagging 仓库 → 给部署 playbook 各任务打标签 → 用 --tags/--skip-tags 验证只执行部分任务 → commit/push → finish。
3.4 Optimizing Execution for Speed(优化执行速度)
基础设施层面
- 用较新版本 Ansible(核心与内置模块持续优化);控制节点与受管节点网络“靠近”(Ansible 重度依赖网络传输,高延迟/低带宽直接拖慢执行)。
关掉事实收集(性价比最高)
- 每个 play 开头有一个隐藏的 facts 任务(setup 模块)→ 不用
ansible_facts就把gather_facts: False。 - 教材实测(3 台):开 6.171s vs 关 1.336s。
- 需要主机名时用 magic 变量
inventory_hostname/inventory_hostname_short,别用依赖 facts 的ansible_facts['hostname']/ansible_hostname。 - 确实需要 facts 的 play 单独保留,或在该 play 里手动加 setup 模块任务,收集后供后续 play 使用。
提高并行度(forks)
forks控制同时活动连接数,默认 5:100 台主机也按每批 5 台推进,全部主机跑完当前任务才进下一任务。- ansible.cfg
[defaults] forks = 100可显著提速;连接数增多后注意控制节点文件句柄上限(ulimit)。
避免无谓循环(包管理一次装一批)
- yum/dnf 模块的
name接受列表 → 单次模块调用、一次事务装完(等价一条yum install httpd mod_ssl ...),只做一次依赖解析:- name: Ensure the packages are installed yum: name: - httpd - mod_ssl - httpd-tools - php - php-mysqlnd state: present - 用 loop 逐包装等价跑 N 次 yum、N 次依赖解析,慢且低效。
- service 模块
name只收单值 → 多服务必须 loop(name: "{{ item }}"+loop: [httpd, mariadb])。 - 不确定参数是否收列表 →
ansible-doc yum/ansible-doc service查看参数类型。 - 批量改同一文件的多个行:
lineinfile配 loop 低效易错 → 用template/copy整体覆盖(src: httpd.conf.j2 → dest);整目录分发用synchronize(rsync 封装)而不是逐文件 copy。
SSH 连接复用与管道化
ControlMaster:多条 SSH 会话复用同一条网络连接(首条建连、后续复用);ControlPersist:最后一条会话结束后连接仍在后台保活 N 秒供下次复用。- ansible.cfg 默认:
[ssh_connection] ssh_args = -o ControlMaster=auto -o ControlPersist=60s。 - 主机多或任务超 60s → 调大 ControlPersist(如 300s);与 forks 配合考虑控制节点并发连接数与文件句柄。
pipelining = True([ssh_connection]段):模块数据走既有 SSH 通道、减少建连次数;前提是受管端 sudo 关闭requiretty:visudo→Defaults !requiretty(RHEL 8 默认已关,其它系统可能开)。
回调插件剖析性能
- 启用:ansible.cfg
[defaults] callback_whitelist = timer, profile_tasks, profile_roles。 - 各插件输出:
timer总耗时;profile_tasks每个任务耗时、结束时按耗时倒序打印(例:yum 30.18s → firewalld 10.43s → gather_facts 2.85s …);profile_roles每个角色耗时倒序。 cgroup_perf_recap:用 cgroups 统计 CPU/内存/pids 峰值:先cgcreate建控制组并写 ansible.cfg[callback_cgroup_perf_recap] control_group=ansible_profile,再cgexec -g cpuacct,memory,pids:ansible_profile ansible-playbook ...。- 列出/查文档:
ansible-doc -t callback -l、ansible-doc -t callback 插件名。 - 注意:Ansible Tower 会解析 ansible-playbook 输出做作业日志,改动输出的回调插件在 Tower 环境慎用。
Guided Exercise 4 + 总 Lab 要点(lab: task-speed / task-review)
- task-speed:剖析 deploy_webservers.yml 的慢任务 → 关 gather_facts、yum 列表合并、copy/template 优化 → 对比耗时明显下降。
- task-review(总 Lab):① 把默认提权改为更安全(默认不提权,firewall/haproxy/apache/webapp 各角色按需提权,整段提权放 block,handler 不进 block);② 加任务钩子与 handlers 改变行为;③ 打标签控制执行;④ 剖析并优化。评分注意:先
lab task-review finish触发 handlers 重跑、再执行 site.yml、最后lab task-review grade。
命令速查表
| 场景 | 命令/写法 |
|---|---|
| play 级提权 | become: true / become_user: webapp / become_method: su |
| 命令行提权 | ansible-playbook site.yml -b -K(-b 开提权,-K 询问提权密码) |
| 组内特例提权方式 | YAML 清单 vars: { ansible_become_method: su } |
| 中途立即跑 handler | meta: flush_handlers |
| 一对多通知 | 任务 notify: My handlers + handler listen: My handlers |
| 角色当任务 | include_role: { name: role2 } / import_role: { name: role2 } |
| 标签筛选/跳过/列出 | --tags webserver / --skip-tags webserver / --list-tags |
| 关 facts / 调并发 | gather_facts: False;[defaults] forks = 50 |
| SSH 复用/管道 | [ssh_connection] ssh_args = -o ControlPersist=300s、pipelining = True |
| 性能剖析 | [defaults] callback_whitelist = timer, profile_tasks, profile_roles |
| 实验 | `lab task-escalation / task-execution / task-tagging / task-speed / task-review start |
核心词汇表
| 英文 | 中文速记 |
|---|---|
| privilege escalation | 特权提升(become 体系) |
| become / become_user / become_method / become_flags | 提权开关 / 目标用户 / 方法(sudo/su/pbrun…) / 附加参数 |
| least privilege | 最小权限原则 |
| connection variable | 连接变量(ansible_become 系列,优先级最高) |
| pre_tasks / post_tasks | 角色前 / 常规任务与 handlers 后执行的段 |
| flush_handlers | 立即冲刷已通知的 handler |
| include_role / import_role | 动态包含 / 静态导入角色 |
| listen | handler 订阅事件名,一个通知触发多个 handler |
| global scope (handlers) | handler 的 play 全局作用域 |
| order | 主机执行顺序(inventory/sorted/reverse_*/shuffle) |
| tags / --tags / --skip-tags / --list-tags | 标签及选择/跳过/列出 |
| always / never / tagged / untagged | 特殊标签(all 为默认) |
| gather_facts | 事实收集任务(可关闭提速) |
| forks | 并行主机数(默认 5) |
| ControlMaster / ControlPersist | SSH 连接复用 / 后台保活时长 |
| pipelining | SSH 管道化(requiretty 需关闭) |
| callback plugin | 回调插件(timer/profile_tasks/profile_roles/cgroup_perf_recap) |
| cgroup / cgexec | Linux 控制组 / 在控制组中运行命令(资源剖析) |
本章自测
- 提权可在哪几级设置?4 个指令与命令行选项的对应关系?
- 为什么连接变量优先级最高?什么场景才值得用?
- play 内各段固定执行顺序?tasks 写在 roles 前为什么仍先跑 roles?
- flush_handlers 解决什么问题?举一个必须使用它的场景。
- import_role 与 include_role 在“语法检查时机”和“when 为假时”的行为差异?
- listen 相对逐个 notify 的优点?notify 找不到匹配 handler 会怎样?
- order 有哪 5 个取值?为什么输出顺序常与执行顺序不一致?
- --tags / --skip-tags / --list-tags 各做什么?always / never / tagged / untagged / all 的语义?
- 提速手段至少列 6 条;为什么 yum 传列表比 loop 快?service 为何必须 loop?
- timer / profile_tasks / profile_roles / cgroup_perf_recap 各自输出什么?为什么 Tower 环境要慎用回调插件?
