TT Lab
开始
学习 学习路径 课程

Ansible 实战

分批并委托给负载均衡器,做一次不停机发布

在 TT Lab 中继续学习

目标

学会编写这样的 playbook:把主机集合拆成批次,把正在部署的服务器从负载均衡器摘下再加回,并按批次判定状态,使事故不会蔓延到下一批。

为什么重要

在默认行为下,Ansible 会把一个 task 在所有主机上完成,然后进入下一个 task。如果有重启服务的 task,那一刻所有主机会一起下线。所以零停机部署不是靠工具,而是靠 playbook 的结构来实现的。拆分(serial)、摘除与加回(delegate_to)、每批判定(max_fail_percentage)。缺了其中任何一个就不是零停机——不摘除,请求就会进入正在部署的服务器;不判定,坏掉的版本就会蔓延到其余服务器。而且一旦设置了 serial,run_once 和 handler 的含义也会悄悄改变。不知道这一点就去用,“以为只跑一次的 task”就会按批次数重复运行。

步骤

  1. 在 /root/ans/roll/inventory/hosts.ini 中写入 web 组(web1、web2、web3,ansible_host=127.0.0.1、ansible_port=2222)和 lb 组(lb1,ansible_connection=local)。在 /root/ans/roll/p01.yml 中设置 serial: 2,让每台主机把自己所在批次的成员用逗号连起来,写到 /root/ans/roll/out/01-<호스트>.txt(占位符为主机名)。
  2. 在 /root/ans/roll/p02.yml 中把 serial 给成列表 [1, 100%] 来构成金丝雀批次,并像第 1 步那样把批次成员写到 /root/ans/roll/out/02-<호스트>.txt(占位符为主机名)。
  3. 在 /root/ans/roll/p03.yml 中设置 serial: 2,用一个加了 run_once: true 和 delegate_to: lb1 的 task,向 /root/ans/roll/out/03-runonce.txt 留下 batch <배치구성원> ran on <실행한호스트>(占位符依次为批次成员、执行所在的主机)格式的行。
  4. 把 /root/ans/roll/p04.yml 设为 serial: 1,把所有 task 都委托给 lb1,创建 /root/ans/roll/out/lb/ 目录后,为每台主机分别留下内容为 host=<호스트> 的 drained-<호스트>.txt 和 enabled-<호스트>.txt(占位符均为主机名)。
  5. 在 /root/ans/roll/p05.yml 中,用同时给出 delegate_to: lb1 和 delegate_facts: true 的 set_fact,把 lb_pool_size 设为 play 的目标数,再通过 hostvars 读取该值,以 lb_pool_size=<값> on_web=<웹 호스트에서 본 값>(占位符依次为值、从 Web 主机一侧看到的值)格式留到 /root/ans/roll/out/05-lbfact.txt。Web 主机一侧不应有这个值。
  6. 把 /root/ans/roll/p06.yml 设为 serial: 2,让为每台主机放置 /root/ans/roll/out/06-<호스트>.conf(占位符为主机名)的 task 调用 handler。handler 用 run_once: true 和 delegate_to: lb1,向 /root/ans/roll/out/06-handlers.txt 留下 restarted: <배치구성원>(占位符为批次成员)行。
  7. 把 /root/ans/roll/p07.yml 设为 serial: 1、max_fail_percentage: 0,并在 play 的 vars 中把 healthy_hosts 设为 [web1, web3]。task 有两个:留下 /root/ans/roll/out/07-deployed-<호스트>.txt(占位符为主机名)的部署标记,以及检查是否在 healthy_hosts 之中的 assert 确认。把运行输出保存为 /root/ans/roll/out/07-run.txt。
  8. 在 /root/ans/roll/p08.yml 中放两个 play。第一个 play 以 serial: [1, 100%]、max_fail_percentage: 0 对 web 运行,把批次记录写到 /root/ans/roll/out/08-batches.txt,为每台主机在 /root/ans/roll/out/08/ 下留下 drained-、release-(内容 release=2.4.0)、enabled- 标记,并在其间用 assert 确认状态(三台都正常)。第二个 play 在 lb1 上汇总这些记录,把 batches、drained、enabled 写到 /root/ans/roll/out/rolling.json。最后把同一个 playbook 再运行一次,把输出保存到 /root/ans/roll/out/08-run2.txt,且必须是 changed=0。

参考

两台两台地分批上线

在 /root/ans/roll/inventory/hosts.ini 中写入 web 组(web1、web2、web3,ansible_host=127.0.0.1、ansible_port=2222)和 lb 组(lb1,ansible_connection=local)。在 /root/ans/roll/p01.yml 中设置 serial: 2,让每台主机把自己所在批次的成员用逗号连起来,写到 /root/ans/roll/out/01-<호스트>.txt(占位符为主机名)。

当前批次里有哪些成员,可以在运行时通过变量得知。把 ansible_play_batch 用逗号连起来写出来,文件里就会留下批次是怎样划分的。

先上一台金丝雀,其余一次性

在 /root/ans/roll/p02.yml 中把 serial 给成列表 [1, 100%] 来构成金丝雀批次,并像第 1 步那样把批次成员写到 /root/ans/roll/out/02-<호스트>.txt(占位符为主机名)。

列表的元素可以混用数字和比例。把最后一个元素设为 100%,意思就是“剩下的全部”。

run_once 是每批一次

在 /root/ans/roll/p03.yml 中设置 serial: 2,用一个加了 run_once: true 和 delegate_to: lb1 的 task,向 /root/ans/roll/out/03-runonce.txt 留下 batch <배치구성원> ran on <실행한호스트>(占位符依次为批次成员、执行所在的主机)格式的行。

同一行运行多次,文件也不应该增加。请使用按行“没有才加入”的模块。数一数出现了几行,run_once 的范围就显露出来了。

从负载均衡器摘除再加回

把 /root/ans/roll/p04.yml 设为 serial: 1,把所有 task 都委托给 lb1,创建 /root/ans/roll/out/lb/ 目录后,为每台主机分别留下内容为 host=<호스트> 的 drained-<호스트>.txt 和 enabled-<호스트>.txt(占位符均为主机名)。

在被委托的 task 里,目标仍然是原来的主机。把那个名称原样写进文件名和内容,“摘掉了谁”就会留在负载均衡器一侧的记录里。这一步应当按每台主机而不是每批发生。

把 fact 写到谁的名下

在 /root/ans/roll/p05.yml 中,用同时给出 delegate_to: lb1 和 delegate_facts: true 的 set_fact,把 lb_pool_size 设为 play 的目标数,再通过 hostvars 读取该值,以 lb_pool_size=<값> on_web=<웹 호스트에서 본 값>(占位符依次为值、从 Web 主机一侧看到的值)格式留到 /root/ans/roll/out/05-lbfact.txt。Web 主机一侧不应有这个值。

play 的目标数在运行时可以通过变量得知。从 Web 主机一侧读取同名变量时可能没有,所以请给出默认值,让它打印出 없음(韩文,意为“无”)。

handler 在批次结束而不是 play 结束时运行

把 /root/ans/roll/p06.yml 设为 serial: 2,让为每台主机放置 /root/ans/roll/out/06-<호스트>.conf(占位符为主机名)的 task 调用 handler。handler 用 run_once: true 和 delegate_to: lb1,向 /root/ans/roll/out/06-handlers.txt 留下 restarted: <배치구성원>(占位符为批次成员)行。

handler 通过名称串联。数一数留下了几行,就能知道 handler 是什么时候运行的——如果是在 play 结束时一次,就应该是一行。

一台垮了就在那里停下

把 /root/ans/roll/p07.yml 设为 serial: 1、max_fail_percentage: 0,并在 play 的 vars 中把 healthy_hosts 设为 [web1, web3]。task 有两个:留下 /root/ans/roll/out/07-deployed-<호스트>.txt(占位符为主机名)的部署标记,以及检查是否在 healthy_hosts 之中的 assert 确认。把运行输出保存为 /root/ans/roll/out/07-run.txt。

这个 playbook 是故意失败的。把输出存到文件时,不要让失败把脚本中断。第三台主机的部署标记是否生成,就是这一步的答案。

走完一圈并留下摘要

在 /root/ans/roll/p08.yml 中放两个 play。第一个 play 以 serial: [1, 100%]、max_fail_percentage: 0 对 web 运行,把批次记录写到 /root/ans/roll/out/08-batches.txt,为每台主机在 /root/ans/roll/out/08/ 下留下 drained-、release-(内容 release=2.4.0)、enabled- 标记,并在其间用 assert 确认状态(三台都正常)。第二个 play 在 lb1 上汇总这些记录,把 batches、drained、enabled 写到 /root/ans/roll/out/rolling.json。最后把同一个 playbook 再运行一次,把输出保存到 /root/ans/roll/out/08-run2.txt,且必须是 changed=0。

摘除与加回委托给负载均衡器,部署在目标主机上做。用查找文件的模块来数标记,用读取文件的模块来读取批次记录,第二个 play 会变短。第二次运行要安静,所有 task 都必须是幂等的。