高级 Ansible 技巧:滚动更新、任务委托与集群任务控制
1. 异步任务与滚动更新控制
1.1 异步任务执行
在一些操作中,我们会遇到长时间运行的任务。例如下面的代码展示了一个异步任务的检查过程:
TASK [Check on the asynchronous task] *******************************
***********
FAILED - RETRYING: Check on the asynchronous task (30 retries left).
FAILED - RETRYING: Check on the asynchronous task (30 retries left).
FAILED - RETRYING: Check on the asynchronous task (29 retries left).
FAILED - RETRYING: Check on the asynchronous task (29 retries left).
FAILED - RETRYING: Check on the asynchronous task (28 retries left).
FAILED - RETRYING: Check on the asynchronous task (28 retries left).
FAILED - RETRYING: Check on the asynchronous task (27 retries left).
FAILED - RETRYING: Check on the asynchronous task (27 retries left).
changed: [frt01.example.com]
changed: [frt02.example.com]
PLAY RECAP **********************************************************
***********
frt01.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0
rescued=0 ignored=0
frt02.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0
rescued=0 ignored=0
从这段代码可以看出,长时间运行的任务会持续执行,后续任务会不断轮询其状态,直到满足我们设定的条件。在这个例子中,任务最终成功完成,整个操作结果也是成功的。异步操作在大型下载、软件包更新等可能需要长时间运行的任务中特别有用,在复杂的基础设施中开发剧本时,它们会非常有帮助。
1.2 滚动更新的执行控制
默认情况下,Ansible 会同时在多个主机上并行执行任务,以加快大型清单中自动化任务的执行速度。这个设置由 Ansible 配置文件中的
forks
参数定义,默认值为 5,即默认情况下,Ansible 会尝试同时在 5 个主机上运行自动化任务。
但在负载均衡的环境中,这种方式并不理想,尤其是在需要避免服务中断的情况下。例如,假设有 5 台前端服务器,如果同时对它们进行更新,最终用户可能会遇到服务中断。因此,考虑在不同时间更新所有服务器是很重要的。
下面通过一个实际例子来演示如何使用
serial
关键字控制滚动更新:
1.
创建简单剧本
:创建一个简单的剧本,在清单中的两个主机上运行两个命令。这里以
date
命令为例,运行时可以使用
-v
增加详细信息输出。
---
- name: Simple serial demonstration play
hosts: frontends
gather_facts: false
tasks:
- name: First task
command: date
- name: Second task
command: date
运行这个剧本:
$ ansible-playbook -i hosts serial.yml
输出结果显示,由于主机数量少于默认的
forks
值 5,所有操作会同时在每个主机上执行,这可能会导致用户服务中断。
PLAY [Simple serial demonstration play] ************************
****************
TASK [First task] **********************************************
****************
changed: [frt02.example.com]
changed: [frt01.example.com]
TASK [Second task] *********************************************
****************
changed: [frt01.example.com]
changed: [frt02.example.com]
PLAY RECAP *****************************************************
****************
frt01.example.com : ok=2 changed=2 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt02.example.com : ok=2 changed=2 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
-
修改剧本使用
serial关键字 :修改剧本定义,添加serial: 1,表示一次只在 1 个主机上完成操作,然后再处理下一个主机。
---
- name: Simple serial demonstration play
hosts: frontends
serial: 1
gather_facts: false
tasks:
- name: First task
command: date
- name: Second task
command: date
再次运行剧本:
$ ansible-playbook -i hosts serial.yml
输出结果如下:
PLAY [Simple serial demonstration play] ************************
****************
TASK [First task] **********************************************
****************
changed: [frt01.example.com]
TASK [Second task] *********************************************
****************
changed: [frt01.example.com]
PLAY [Simple serial demonstration play] ************************
****************
TASK [First task] **********************************************
****************
changed: [frt02.example.com]
TASK [Second task] *********************************************
****************
changed: [frt02.example.com]
PLAY RECAP *****************************************************
****************
frt01.example.com : ok=2 changed=2 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt02.example.com : ok=2 changed=2 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
可以看到,操作按顺序依次在每个主机上执行,避免了同时更新所有主机导致的服务中断。
1.3
serial
关键字的其他用法
serial
指令不仅可以使用整数,还可以使用百分比。当指定百分比时,Ansible 会按照该百分比的主机数量同时运行剧本。例如,清单中有 4 个主机,指定
serial: 25%
,Ansible 会一次只在 1 个主机上运行剧本;如果有 8 个主机,则会一次在 2 个主机上运行。
还可以将列表传递给
serial
指令,例如:
serial:
- 1
- 3
- 5
这表示最初在 1 个主机上运行剧本,然后在接下来的 3 个主机上运行,最后每次以 5 个主机为一批运行,直到完成清单中所有主机的操作。也可以指定百分比列表来代替主机数量的整数。通过这种方式,可以构建一个强大的剧本,在不影响最终用户服务的情况下执行滚动更新。
1.4 最大失败百分比的配置
在默认操作模式下,只要清单中有主机且没有记录到失败,Ansible 会继续在一批服务器上执行剧本(批次大小由前面讨论的
serial
指令确定)。但在高可用性或负载均衡的环境中,这种方式并不理想。如果剧本中有错误,或者推出的代码有问题,我们不希望 Ansible 继续将其部署到集群中的所有服务器,导致所有节点升级失败而引发服务中断。在这种环境中,尽早失败并保留集群中至少一些主机不受影响,直到有人介入解决问题会更好。
下面通过一个实际例子来演示如何配置最大失败百分比:
1.
定义清单和剧本
:考虑一个包含 10 个主机的清单:
[frontends]
frt[01:10].example.com
创建一个简单的剧本,在剧本定义中设置批次大小为 5,最大失败百分比为 50%:
---
- name: A simple play to demonstrate use of max_fail_percentage
hosts: frontends
gather_facts: no
serial: 5
max_fail_percentage: 50
这里定义了 10 个主机,会以 5 个主机为一批进行处理。如果一批中超过 50% 的主机失败,剧本将中止,处理也会停止。需要注意的是,失败主机的百分比必须超过
max_fail_percentage
的值,等于时剧本会继续执行。
2.
定义任务
:
tasks:
- name: A task that will sometimes fail
debug:
msg: This might fail
failed_when: inventory_hostname in ansible_play_batch[0:3]
- name: A task that will succeed
debug:
msg: Success!
第一个任务包含一个特殊的子句
failed_when
,用于故意模拟失败。如果该任务在批次中的前 3 个主机上运行,无论结果如何,都会故意使该任务失败;否则,任务将正常运行。第二个任务总是会成功,只有在剧本允许继续执行时才会运行。
3.
运行剧本
:
$ ansible-playbook -i morehosts maxfail.yml
运行结果如下:
PLAY [A simple play to demonstrate use of max_fail_percentage]
*****************
TASK [A task that will sometimes fail] *************************
****************
fatal: [frt01.example.com]: FAILED! => {
"msg": "This might fail"
}
fatal: [frt02.example.com]: FAILED! => {
"msg": "This might fail"
}
fatal: [frt03.example.com]: FAILED! => {
"msg": "This might fail"
}
ok: [frt04.example.com] => {
"msg": "This might fail"
}
ok: [frt05.example.com] => {
"msg": "This might fail"
}
NO MORE HOSTS LEFT *********************************************
****************
NO MORE HOSTS LEFT *********************************************
****************
PLAY RECAP *****************************************************
****************
frt01.example.com : ok=0 changed=0 unreachable=0 failed=1
skipped=0 rescued=0 ignored=0
frt02.example.com : ok=0 changed=0 unreachable=0 failed=1
skipped=0 rescued=0 ignored=0
frt03.example.com : ok=0 changed=0 unreachable=0 failed=1
skipped=0 rescued=0 ignored=0
frt04.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt05.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
可以看到,我们故意让第一批 5 个主机中的 3 个失败,超过了设定的最大失败百分比阈值,剧本立即中止,第二批 5 个主机也不会被处理。通过谨慎使用批次和最大失败百分比,可以在集群中安全地运行自动化任务,而不用担心在出现问题时破坏整个集群。
1.5 任务执行委托的设置
在之前运行的每个剧本中,我们都假设所有任务会依次在清单中的每个主机上执行。但有时可能需要在不同的主机上运行一两个任务。例如,在自动化集群升级时,我们希望自动化整个过程,包括依次将每个主机从负载均衡器中移除,任务完成后再将其恢复。虽然我们仍然希望在整个清单上运行剧本,但不希望从这些主机上运行负载均衡器命令。
下面通过一个实际例子来演示如何设置任务执行委托:
1.
定义清单
:使用之前的两个主机清单:
[frontends]
frt01.example.com
frt02.example.com
-
创建脚本
:在剧本所在的同一目录下创建两个简单的 shell 脚本,用于模拟添加和移除主机到负载均衡器的操作:
-
remove_from_loadbalancer.sh:
-
#!/bin/sh
echo Removing $1 from load balancer...
- `add_to_loadbalancer.sh`:
#!/bin/sh
echo Adding $1 to load balancer...
- 创建剧本 :
---
- name: Play to demonstrate task delegation
hosts: frontends
tasks:
- name: Remove host from the load balancer
command: ./remove_from_loadbalancer.sh {{ inventory_hostname }}
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
- name: Deploy code to host
debug:
msg: Deployment code would go here....
- name: Add host back to the load balancer
command: ./add_to_loadbalancer.sh {{ inventory_hostname }}
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
在这个剧本中,第一个任务使用
delegate_to: localhost
指令,告诉 Ansible 即使清单中不包含
localhost
,也要在
localhost
上运行该操作。因为我们没有将脚本复制到远程主机,所以如果尝试从远程主机运行脚本,它将无法执行。第二个任务没有
delegate_to
指令,会在清单中的远程主机上运行。第三个任务同样使用
delegate_to: localhost
,将主机添加回负载均衡器。
4.
运行剧本
:
$ ansible-playbook -i hosts delegate.yml
运行结果如下:
PLAY [Play to demonstrate task delegation] *********************
****************
TASK [Gathering Facts] *****************************************
****************
ok: [frt01.example.com]
ok: [frt02.example.com]
TASK [Remove host from the load balancer] **********************
****************
changed: [frt02.example.com -> localhost]
changed: [frt01.example.com -> localhost]
TASK [Deploy code to host] *************************************
****************
ok: [frt01.example.com] => {
"msg": "Deployment code would go here...."
}
ok: [frt02.example.com] => {
"msg": "Deployment code would go here...."
}
TASK [Add host back to the load balancer] **********************
****************
changed: [frt01.example.com -> localhost]
changed: [frt02.example.com -> localhost]
PLAY RECAP *****************************************************
****************
frt01.example.com : ok=4 changed=2 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt02.example.com : ok=4 changed=2 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
可以看到,虽然 Ansible 在清单中的主机上运行,但与负载均衡器相关的脚本实际上是在
localhost
上运行的,而升级任务直接在远程主机上执行。
除了将任务委托给
localhost
,还可以将任务委托给其他非清单主机。例如,可以使用
local_action
这种简写符号来指定任务,它相当于
delegate_to: localhost
。下面是一个使用
local_action
的例子:
---
- name: Second task delegation example
hosts: frontends
tasks:
- name: Perform an rsync from localhost to inventory hosts
local_action: command rsync -a /tmp/ {{ inventory_hostname }}:/tmp/target/
这相当于:
tasks:
- name: Perform an rsync from localhost to inventory hosts
command: rsync -a /tmp/ {{ inventory_hostname }}:/tmp/target/
delegate_to: localhost
运行这个剧本:
$ ansible-playbook -i hosts delegate2.yml
可以看到
local_action
会在运行 Ansible 的机器上运行模块,从而可以高效地将整个目录树复制到清单中的远程服务器。
1.6 运行一次选项的使用
在处理集群时,有时会遇到只需要为整个集群执行一次的任务。例如,升级集群数据库的架构或发出命令重新配置 Pacemaker 集群,通常会在一个节点上发出命令,然后由集群管理软件自动将其传播到所有其他节点。可以通过创建只包含一个主机的特殊清单,或者编写引用清单中一个主机的特殊剧本来解决这个问题,但这种方式效率低下,会使代码变得碎片化。
可以使用
run_once
指令来解决这个问题。对于任何希望在清单上只运行一次的任务,可以在任务定义中添加
run_once: true
。例如,使用之前定义的包含 10 个主机的清单,创建一个简单的剧本:
---
- name: Play to demonstrate the run_once directive
hosts: frontends
tasks:
- name: Upgrade database schema
debug:
msg: Upgrading database schema...
run_once: true
运行这个剧本:
$ ansible-playbook -i morehosts runonce.yml
运行结果如下:
PLAY [Play to demonstrate the run_once directive]
******************************
TASK [Gathering Facts] *****************************************
****************
ok: [frt02.example.com]
ok: [frt05.example.com]
ok: [frt03.example.com]
ok: [frt01.example.com]
ok: [frt04.example.com]
ok: [frt06.example.com]
ok: [frt08.example.com]
ok: [frt09.example.com]
ok: [frt07.example.com]
ok: [frt10.example.com]
TASK [Upgrade database schema] *********************************
****************
ok: [frt01.example.com] => {
"msg": "Upgrading database schema..."
}
PLAY RECAP *****************************************************
****************
frt01.example.com : ok=2 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt02.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt03.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt04.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt05.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt06.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt07.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt08.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt09.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
frt10.example.com : ok=1 changed=0 unreachable=0 failed=0
skipped=0 rescued=0 ignored=0
可以看到,虽然剧本在所有 10 个主机上运行并收集了所有主机的事实,但 Ansible 只在一个主机上运行了升级任务。
需要注意的是,
run_once
选项是按服务器批次应用的。如果在剧本定义中添加
serial: 5
(在包含 10 个服务器的清单上以 5 个服务器为一批运行剧本),架构升级任务会运行两次,即每个批次运行一次,而不是整个清单只运行一次。在集群环境中使用这个指令时,要注意这个细微差别。
总结
通过本文的介绍,我们了解了 Ansible 中一些高级技巧,包括异步任务、滚动更新控制、最大失败百分比配置、任务执行委托和运行一次选项的使用。这些技巧可以帮助我们在复杂的基础设施中更高效、更安全地运行自动化任务,避免服务中断,提高系统的可用性和稳定性。在实际应用中,可以根据具体需求灵活运用这些技巧,构建强大的自动化剧本。
流程图
graph LR
A[开始] --> B[异步任务执行]
B --> C[滚动更新控制]
C --> D[最大失败百分比配置]
D --> E[任务执行委托]
E --> F[运行一次选项使用]
F --> G[结束]
表格
| 技巧 | 描述 | 示例 |
|---|---|---|
| 异步任务 | 长时间运行的任务持续执行,后续任务轮询其状态 | 大型下载、软件包更新 |
| 滚动更新控制 |
使用
serial
关键字控制同时更新的主机数量
|
serial: 1
或
serial: 25%
|
| 最大失败百分比配置 | 配置 Ansible 在一批主机中允许的最大失败百分比 |
max_fail_percentage: 50
|
| 任务执行委托 | 将任务委托给其他主机执行 |
delegate_to: localhost
或
local_action
|
| 运行一次选项 | 确保任务在整个集群中只运行一次 |
run_once: true
|
2. 高级技巧的实际应用案例分析
2.1 滚动更新在大型电商系统中的应用
在大型电商系统中,前端服务器通常处于负载均衡环境下。假设该电商系统有 20 台前端服务器,为了进行系统升级且不影响用户购物体验,我们可以运用滚动更新技巧。
2.1.1 剧本编写
---
- name: E - commerce front - end rolling update
hosts: frontends
serial: 20%
gather_facts: false
tasks:
- name: Stop front - end service
command: systemctl stop frontend.service
- name: Upgrade front - end software
command: apt - get update && apt - get upgrade - y
- name: Start front - end service
command: systemctl start frontend.service
2.1.2 执行步骤
-
定义好包含 20 台前端服务器的清单文件
hosts。 - 运行剧本:
$ ansible - playbook - i hosts e_commerce_rolling_update.yml
2.1.3 效果分析
由于设置了
serial: 20%
,Ansible 会每次更新 4 台服务器。这样在更新过程中,始终有部分服务器正常提供服务,用户基本不会感受到服务中断,保证了电商系统的高可用性。
2.2 任务委托在分布式存储系统中的应用
在分布式存储系统中,需要对存储节点进行数据同步操作,同时要通过管理节点来控制存储节点的挂载和卸载。
2.2.1 环境准备
假设存储节点清单如下:
[storagenodes]
storage01.example.com
storage02.example.com
storage03.example.com
在管理节点上创建两个脚本:
-
unmount_storage.sh
:
#!/bin/sh
echo Unmounting $1 from storage system...
umount $1
-
mount_storage.sh:
#!/bin/sh
echo Mounting $1 to storage system...
mount $1
2.2.2 剧本编写
---
- name: Distributed storage data synchronization
hosts: storagenodes
tasks:
- name: Unmount storage node
command: ./unmount_storage.sh {{ inventory_hostname }}
args:
chdir: "{{ playbook_dir }}"
delegate_to: managementnode.example.com
- name: Synchronize data
command: rsync - a /data/source/ {{ inventory_hostname }}:/data/target/
- name: Mount storage node
command: ./mount_storage.sh {{ inventory_hostname }}
args:
chdir: "{{ playbook_dir }}"
delegate_to: managementnode.example.com
2.2.3 执行步骤
- 将上述脚本和剧本放在同一目录下。
- 运行剧本:
$ ansible - playbook - i hosts distributed_storage_sync.yml
2.2.4 效果分析
通过任务委托,将存储节点的挂载和卸载操作委托给管理节点执行,避免了在存储节点上直接执行这些操作可能带来的权限问题和数据不一致问题,保证了数据同步的顺利进行。
2.3 运行一次选项在集群数据库升级中的应用
在一个包含 15 台服务器的集群数据库中,需要对数据库的架构进行升级。
2.3.1 剧本编写
---
- name: Cluster database schema upgrade
hosts: databaseservers
serial: 5
tasks:
- name: Upgrade database schema
command: psql - U postgres - d mydatabase - f /path/to/schema_upgrade.sql
run_once: true
- name: Restart database service
command: systemctl restart postgresql.service
2.3.2 执行步骤
-
定义好包含 15 台数据库服务器的清单文件
hosts。 - 运行剧本:
$ ansible - playbook - i hosts cluster_database_upgrade.yml
2.3.3 效果分析
由于设置了
run_once: true
,数据库架构升级操作只会在一个节点上执行一次,然后通过集群的同步机制将升级后的架构传播到其他节点。同时,通过
serial: 5
控制每次处理 5 台服务器,避免了同时重启所有服务器导致的服务中断。
3. 高级技巧的组合使用
3.1 滚动更新与最大失败百分比的组合
在一个包含 30 台服务器的集群中进行软件升级,我们可以同时使用滚动更新和最大失败百分比来保证升级的安全性。
3.1.1 剧本编写
---
- name: Combined rolling update and max fail percentage
hosts: servers
serial: 10
max_fail_percentage: 30
gather_facts: false
tasks:
- name: Upgrade software
command: yum update - y
3.1.2 执行步骤
-
定义好包含 30 台服务器的清单文件
hosts。 - 运行剧本:
$ ansible - playbook - i hosts combined_rolling_max_fail.yml
3.1.3 效果分析
每次以 10 台服务器为一批进行升级,如果一批中失败的服务器数量超过 3 台(30%),Ansible 会立即中止升级操作,避免了将有问题的升级扩展到整个集群。
3.2 任务委托与运行一次选项的组合
在一个多节点的大数据处理集群中,需要在一个特定节点上执行数据初始化操作,然后将结果同步到其他节点。
3.2.1 剧本编写
---
- name: Combined task delegation and run once
hosts: datanodes
tasks:
- name: Initialize data on a specific node
command: python /path/to/init_data.py
delegate_to: masternode.example.com
run_once: true
- name: Synchronize data to other nodes
command: scp masternode.example.com:/data/result/ {{ inventory_hostname }}:/data/target/
3.2.2 执行步骤
-
定义好包含多个数据节点的清单文件
hosts。 - 运行剧本:
$ ansible - playbook - i hosts combined_delegate_run_once.yml
3.2.3 效果分析
通过
delegate_to: masternode.example.com
将数据初始化任务委托给特定节点,并且使用
run_once: true
确保该任务只执行一次。然后将初始化结果同步到其他节点,提高了数据处理的效率和准确性。
4. 总结与展望
4.1 总结
本文详细介绍了 Ansible 中的多种高级技巧,包括异步任务、滚动更新控制、最大失败百分比配置、任务执行委托和运行一次选项的使用,并通过实际应用案例和组合使用的示例展示了这些技巧的强大功能。这些技巧可以帮助我们在复杂的基础设施中更高效、更安全地运行自动化任务,避免服务中断,提高系统的可用性和稳定性。
4.2 展望
随着信息技术的不断发展,基础设施的复杂性和规模将不断增加。Ansible 的这些高级技巧也将不断发展和完善。未来,可能会有更多与云原生、容器化技术相结合的高级功能出现,帮助我们更好地管理和自动化大规模的分布式系统。同时,在安全性方面,也可能会有更多的改进,确保自动化任务在执行过程中不会泄露敏感信息,进一步提高系统的安全性。
流程图
graph LR
A[实际应用场景] --> B[滚动更新应用]
A --> C[任务委托应用]
A --> D[运行一次选项应用]
B --> E[滚动更新与最大失败百分比组合]
C --> F[任务委托与运行一次选项组合]
D --> F
E --> G[结束应用]
F --> G
表格
| 组合技巧 | 应用场景 | 优势 |
|---|---|---|
| 滚动更新与最大失败百分比 | 大规模集群软件升级 | 避免有问题的升级扩展到整个集群,保证升级安全 |
| 任务委托与运行一次选项 | 多节点大数据处理集群 | 提高数据处理效率和准确性,避免重复操作 |
超级会员免费看
98

被折叠的 条评论
为什么被折叠?



