Ansible自动化(二十三) Ansible滚动更新策略
1 | 作者:李晓辉 |
Ansible 提供了滚动更新的功能,这种策略通过将服务器部署分批进行,帮助实现不中断服务的基础设施更新。
如果在更新过程中发生了意外问题,Ansible 可以暂停部署并将故障局限于某个特定批次的服务器。通过结合监控和测试,你可以设置 Playbook 执行以下操作:
- 对受影响的服务器批次进行配置回滚。
- 将出问题的服务器隔离,进行问题分析。
- 向相关方发送部署进度和状态通知。
控制批次⼤⼩
默认情况下,Ansible 会在所有主机上顺序执行任务。如果某个任务因错误无法成功,且 play 在所有主机上并行运行,那么当 Ansible 到达这个任务时,所有主机都可能失败并导致 play 中止。这样一来,所有主机都会受到影响,可能导致服务中断。
为了避免这种情况,你可以选择先在部分主机上执行 play,再逐步处理下一批主机。如果某一批主机出现问题,只有该批次的主机会受到影响,而不是所有主机都出问题。同时,你也可以设置 Ansible 在所有主机执行任务之前,如果出现过多失败的情况,提前停止整个 play,从而避免不必要的影响。
固定批次⼤⼩
如果你想分批处理主机,可以在 play 里用 serial 关键字来设置每批次处理的主机数量。Ansible 会先处理完当前批次的主机任务,然后才会开始下一个批次。如果当前批次的主机全都出问题,整个 play 就会停下来,不会继续执行后面的批次。
1 |
|
解释:
serial: 2
表示每次只会处理 2 台web_servers
上的任务。tasks
部分更新httpd
包并重启 Web 服务器。handlers
部分定义了如果需要重启 Nginx 服务的处理程序。
执行流程:
- Ansible 会先在前两台
web_servers
上执行更新任务。 - 如果这两台主机触发了
nginx
重启的通知,Ansible 会立即执行处理程序。 - 完成前两台主机的任务后,Ansible 会继续处理下一个批次的主机,直到所有主机都更新完毕。
按百分⽐划分批次
如果你想按百分比来分批处理主机,也就是每次处理一部分主机,你可以在 serial
里设置百分比。比如,你有 10 台主机,设置 serial: 20%
就会每次处理 2 台主机(10 台主机的 20%)。这种方法非常适合大规模更新,能有效避免一次性更新所有主机时出现问题,出错的影响也能更小。
1 |
|
解释:
serial: 20%
表示每次处理web_servers
列表中的 20% 主机。如果有 10 台主机,那么每次会处理 2 台。- 在
tasks
部分,我们更新httpd
包并重启 Web 服务器。 handlers
部分定义了,如果有主机触发重启nginx
服务的通知,Ansible 会自动执行处理程序。
执行流程:
- Ansible 会先处理前 20%(2 台)主机,执行更新和重启操作。
- 如果需要,处理程序会立即执行,比如重启
nginx
。 - 完成当前批次后,Ansible 会继续处理接下来的批次,直到所有主机都完成更新。
动态设置批次大小
你可以在 play 执行过程中灵活调整批次大小!比如,开始时用 1 台主机进行测试,如果成功了,你可以把批次大小增加到 10%,然后再增加到 50%,最后再处理剩余的主机。这种逐步增加批次大小的策略特别适合在进行大规模更新时,能有效地控制风险,逐步验证每个阶段的变化。
你可以通过将 serial
设置为一个值列表来实现这个目标。列表中的每个值代表一个阶段的批次大小,可以是整数也可以是百分比,并且它们会按顺序逐个应用。如果你使用了百分比,Ansible 会根据当前剩余的主机数量来计算批次大小,而不是基于整个主机组的总数。
1 |
|
解释:
serial
关键字设置为一个列表,逐步增加批次大小。首先处理 1 台主机,接着 10%(例如,10 台主机中的 1 台),然后是 50%,最后处理剩余的所有主机。- 在
tasks
部分,我们更新httpd
包并重启 Web 服务器。 handlers
部分定义了如果某台主机触发重启nginx
服务的通知,Ansible 会自动执行处理程序。
运行逻辑:
- 第一批: 先用 1 台服务器试水,如果有问题,就不会影响其他服务器。
- 第二批: 如果第一台服务器成功,下一批增加到 10%(假设 100 台服务器,那就是 10 台)。
- 第三批: 继续增加到 50%,加快更新速度。
- 第四批: 剩下的服务器一次性更新完。
这个方法特别适合 高可用集群的滚动更新,避免一次性把所有服务器都搞崩溃。
错误处理
默认情况下,Ansible 会尽可能多地调度主机来执行 play。如果某台主机在某个任务上失败,Ansible 会把它从 play 中移除,但不会影响其他主机的执行,除非所有主机都失败,play 才会终止。
不过,如果你使用了 serial
指令,把主机划分成不同的批次,那么失败的处理方式就不太一样了。如果某个批次里的所有主机都失败,Ansible 不会 继续执行下一个批次,而是会直接停止整个 play。这样可以防止继续尝试对可能存在相同问题的主机执行任务,避免影响更大范围的系统。
在 Ansible 里,每个批次的活跃主机列表都会存储在 ansible_play_batch
变量中。这个列表会在任务执行过程中动态更新,如果某台主机失败,它会被从 ansible_play_batch
里移除,确保后续任务不会再尝试对它执行。这样,Ansible 可以更灵活地管理批次中的主机状态,保证任务执行的稳定性。
避免全军覆没
你有没有遇到过这样的情况:用 Ansible 批量更新服务器,结果一不小心全军覆没?如果一次性对 100 台服务器执行任务,其中 99 台失败,只有 1 台成功,Ansible 还是会继续执行剩下的任务,但这个时候其实已经没啥意义了。
别担心,咱们可以用 serial
关键字,让 Ansible 按批次执行任务,这样就能更好地控制风险,防止所有服务器一起挂掉。
原始写法(所有服务器一起跑,风险大)
这个写法的问题是:如果 Step One
失败了 99 台,只有 1 台成功,那 Ansible 还是会继续跑 Step Two
,这就没什么意义了。
1 |
|
改进版(每次 2 台服务器批量执行)
运行逻辑:
- 先执行 2 台 服务器,如果成功,继续下一批。
- 如果某一批 2 台服务器全部失败,Ansible 停止 Playbook,不再执行剩下的服务器。
- 这样做能减少故障范围,及时发现问题。
1 |
|
指定容错率
默认情况下,Ansible 只有在 当前批次中的所有主机 都失败时才会停止 Playbook 运行。但在实际场景中,你可能更希望它更早终止,而不是等到整批失败才行动。
比如,如果 整个清单中的主机 超过一定比例失败,就应该立刻停止执行,而不是继续尝试剩下的服务器。甚至,你可能希望 只要有任何任务失败,就立即终止 Playbook,避免让问题进一步扩大。
这时候,max_fail_percentage
就派上用场了!
在 Playbook 里添加 max_fail_percentage
,可以让 Ansible 在 失败率超过设定值 时,自动中止执行,防止更多服务器受到影响。
1 |
|
max_fail_percentage: 20:如果 超过 20% 的服务器失败,Ansible 就会 停止 Playbook,不再继续执行。
- 这样可以防止大规模宕机,给管理员留出修复问题的时间。
- 例如:
- 如果有 10 台服务器,最多 允许 2 台失败。
- 如果有 100 台服务器,最多 允许 20 台失败。
- 超过这个失败比例,Playbook 直接终止,不会继续执行。
错误总结
Ansible 失败行为归纳
默认情况下(无
serial
和max_fail_percentage
)- 所有主机 在 同一批次 执行 play。
- 只有当所有主机都失败 时,play 才会失败。
- 否则,Ansible 继续执行剩余任务。
使用
serial
(分批执行)- 主机按
serial
指定的数量 分批 运行 play。 - 如果某个批次中所有主机都失败,则 play 立即失败,不会执行下一个批次。
- 但之前 成功的批次不会受影响,只影响当前批次及后续未执行的批次。
- 主机按
使用
max_fail_percentage
(失败率控制)- 允许设置 最大可接受的失败比例(如 20%)。
- 如果某个批次中失败的主机超过该比例,则 play 立即失败,不继续执行。
- 避免等所有主机都失败后才终止 play,提高容错性。
如果某个 play 失败,Ansible 的行为
- playbook 立即停止,不再执行后续的 play。
- 避免继续执行可能导致更严重的错误。
策略 | 失败行为 |
---|---|
默认(无 serial & max_fail_percentage ) | 只有所有主机都失败时,play 才会失败 |
使用 serial | 如果某个批次所有主机都失败,play 立即失败 |
使用 max_fail_percentage | 若某个批次失败主机超设定比例(如 20%),play 立即失败 |
play 失败时 | 停止执行 playbook 中的剩余 play |
只运行一次的任务
有时候,你可能不想让某个任务在所有服务器上都跑一遍,而是只执行一次,比如生成个全局配置文件、触发数据库迁移、发个通知啥的。这时候就可以用 run_once: true
,让 Ansible 只在一台机器上跑这个任务,而不是整个批次的每台都跑一遍。
比如,你有 100 台服务器要更新,每批 10 台在滚动更新,但你只想生成 一次 配置文件,而不是 10 次、20 次地重复搞,那就加上 run_once: true
,Ansible 就会乖乖只在第一批的某台机器上跑一次,剩下的服务器就直接用这个文件了。
简单说,run_once
适合那些 一次搞定,大家都能用 的任务,比如 创建配置、数据库变更、触发 CI/CD、发通知 这种。
1 |
|
hosts: all
:这个 Playbook 会在所有的主机上执行。run_once: true
:确保 “Gather host information” 这个任务只会在整个 Playbook 执行中运行一次。即使你有多个主机,这个任务也只会在第一个主机上执行一次,而不会在其他主机上重复执行。ansible.builtin.setup
:这是一个内建模块,用来收集目标主机的系统信息。ansible.builtin.debug
:这个任务会在每个主机上执行,显示一条调试信息,表示该主机是参与的主机之一。
结果:
在执行此 Playbook 时,setup
任务只会在一个主机上执行一次,其他主机会跳过这个任务。debug
任务则会在所有主机上运行。
如果你想在每个批次里只给所有主机执行一次任务,而你的 Playbook 有多个批次,那就用
run_once: true
。不过,如果你希望只在批次里的第一台主机上执行这个任务,可以加个条件when: inventory_hostname == ansible_play_hosts[0]
。这样,任务就只会在每批的第一台主机上跑,其他的主机就跳过了。
如果你把任务设置成每批次执行一次(用 run_once: true
),那么每个批次都会运行这项任务一次。也就是说,如果你把主机分成了多个批次,每个批次的第一台主机会执行这项任务,然后跳过其他的主机。所以,任务会在每个批次的第一台主机上跑一次,而不是对每个主机都执行一次。
假设你有10台主机,分成5个批次,如果你在任务中使用了 run_once: true
,这意味着任务只会在每个批次的第一台主机上执行一次,而其他主机都不会执行该任务。
所以,如果你将 10 台主机分成 5 个批次,每批次有 2 台主机,那么任务就会在每个批次的第一台主机上执行 5 次。这样就确保了任务仅在每批次的第一台主机上执行,而不是每台主机都执行一次。
举个例子,假设有 10 台主机,任务是“执行一些命令”,如果使用 run_once: true
,那么:
- 第1批次:任务会在第 1 台主机上执行(批次中的第一台主机)
- 第2批次:任务会在第 3 台主机上执行(批次中的第一台主机)
- 第3批次:任务会在第 5 台主机上执行
- 第4批次:任务会在第 7 台主机上执行
- 第5批次:任务会在第 9 台主机上执行