分批并委托给负载均衡器,做一次不停机发布
目标
学会编写这样的 playbook:把主机集合拆成批次,把正在部署的服务器从负载均衡器摘下再加回,并按批次判定状态,使事故不会蔓延到下一批。
为什么重要
在默认行为下,Ansible 会把一个 task 在所有主机上完成,然后进入下一个 task。如果有重启服务的 task,那一刻所有主机会一起下线。所以零停机部署不是靠工具,而是靠 playbook 的结构来实现的。拆分(serial)、摘除与加回(delegate_to)、每批判定(max_fail_percentage)。缺了其中任何一个就不是零停机——不摘除,请求就会进入正在部署的服务器;不判定,坏掉的版本就会蔓延到其余服务器。而且一旦设置了 serial,run_once 和 handler 的含义也会悄悄改变。不知道这一点就去用,“以为只跑一次的 task”就会按批次数重复运行。
步骤
- 在
/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(占位符为主机名)。 - 在
/root/ans/roll/p02.yml中把serial给成列表[1, 100%]来构成金丝雀批次,并像第 1 步那样把批次成员写到/root/ans/roll/out/02-<호스트>.txt(占位符为主机名)。 - 在
/root/ans/roll/p03.yml中设置serial: 2,用一个加了run_once: true和delegate_to: lb1的 task,向/root/ans/roll/out/03-runonce.txt留下batch <배치구성원> ran on <실행한호스트>(占位符依次为批次成员、执行所在的主机)格式的行。 - 把
/root/ans/roll/p04.yml设为serial: 1,把所有 task 都委托给 lb1,创建/root/ans/roll/out/lb/目录后,为每台主机分别留下内容为host=<호스트>的drained-<호스트>.txt和enabled-<호스트>.txt(占位符均为主机名)。 - 在
/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 主机一侧不应有这个值。 - 把
/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: <배치구성원>(占位符为批次成员)行。 - 把
/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。 - 在
/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。
参考
- web1、web2、web3 是用三个名称模拟本 Pod 内的一台 sshd(127.0.0.1:2222),lb1 是本地连接。是否真的到了不同的机器,在这个环境中无法证明,所以确认是靠委托留下的记录和运行日志中的
web1 -> lb1显示。 - 批次运行了几轮,按运行日志中
PLAY [...]标题的数量来数。 throttle和strategy: free只会表现在时间和并发上,而这个环境中的测量会有波动,所以从实验中去掉了。请参阅阅读课中“参考文档”一节。- 常见错误:只摘除而漏掉恢复。play 中途失败,最后一个被摘除的服务器就留在了负载均衡器之外。
- 常见错误:用手修改产出物文件。评分不只看文件,还会把 playbook 以检查模式重新运行一遍,看现在是否仍得到同样的结果。
- Strategies and more · Delegation and local actions · Error handling · Playbook keywords
两台两台地分批上线
在 /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 都必须是幂等的。