如何用Ansible对比两台RedHat服务器的运行中服务差异?
问题
我需要通过Ansible对比两台RedHat服务器的运行中服务列表,仅输出server1独有的运行服务(例如:server1运行服务为a,b,c,d,server2为c,d,e,f时,仅输出a,b)。我编写了如下Playbook,但执行后却输出了server1的所有服务,怀疑difference过滤器未生效,请问该如何解决?
- hosts: server1:server2 gather_facts: true tasks: - name: Run command to get running services on server1 shell: systemctl list-units --type=service --state=running --no-pager | grep '\.service' | awk '{print $1}' register: result_server1 when: "'server1' in inventory_hostname" - name: Run command to get running services on server2 shell: systemctl list-units --type=service --state=running --no-pager | grep '\.service' | awk '{print $1}' register: result_server2 when: "'server2' in inventory_hostname" - name: Set facts for server1 services set_fact: server1_services: "{{ result_server1.stdout_lines }}" when: "'server1' in inventory_hostname" - name: Set facts for server2 services set_fact: server2_services: "{{ result_server2.stdout_lines }}" when: "'server2' in inventory_hostname" - name: Find the difference between services set_fact: service_difference: "{{ server1_services | difference(server2_services) }}" when: "'server1' in inventory_hostname" - name: Print the difference for server2 debug: msg: "Service difference for server1: {{ service_difference }}" when: "'server1' in inventory_hostname"
执行结果:
TASK [Print differences] ********************************************************************************************************************************************************************** task path: /home/jenkins/workspace/custom-playbooks/2getservicelist.yaml:49 ok: [server1] => { "msg": "Service difference for server1: [u'auditd.service', u'crond.service', u'dbus.service', u'getty@tty1.service', u'httpd.service', u'irqbalance.service', u'lvm2-lvmetad.service', u'NetworkManager.service', u'polkit.service', u'postfix.service', u'rhnsd.service', u'rhsmcertd.service', u'rsyslog.service', u'sshd.service', u'systemd-journald.service', u'systemd-logind.service', u'systemd-udevd.service', u'tuned.service', u'vgauthd.service', u'vmtoolsd.service']" skipping: [server2] => {} META: ran handlers META: ran handlers
问题分析与解决
核心问题
原Playbook的逻辑错误在于:Ansible的任务是在每个目标主机上独立执行的。当server1执行计算差异的任务时,server2_services变量根本不存在——因为设置该变量的任务仅在server2上运行,server1无法获取到这个值。此时difference过滤器相当于接收了一个未定义的参数,直接返回server1_services的完整列表,导致输出所有服务。
修正方案
方案一:将两台主机的服务列表汇聚到控制节点计算差异
这种方式把服务收集和差异计算分离,逻辑更清晰:
- hosts: server1:server2 gather_facts: true tasks: - name: 获取运行中服务列表 shell: systemctl list-units --type=service --state=running --no-pager --plain --no-legend | awk '{print $1}' register: running_services - name: 将服务列表同步到控制节点 set_fact: "{{ inventory_hostname }}_services": "{{ running_services.stdout_lines }}" delegate_to: localhost run_once: false - hosts: localhost tasks: - name: 计算server1独有的服务 set_fact: server1_unique_services: "{{ server1_services | difference(server2_services) }}" - name: 输出结果 debug: msg: "server1独有的运行中服务: {{ server1_unique_services }}"
方案二:在server1上直接获取server2的服务信息
通过delegate_to让server1的任务去server2上执行命令,直接拿到server2的服务列表:
- hosts: server1 gather_facts: true tasks: - name: 获取server1运行中服务 shell: systemctl list-units --type=service --state=running --no-pager --plain --no-legend | awk '{print $1}' register: server1_services_result - name: 获取server2运行中服务(委托执行) shell: systemctl list-units --type=service --state=running --no-pager --plain --no-legend | awk '{print $1}' register: server2_services_result delegate_to: server2 - name: 计算server1独有的服务 set_fact: server1_unique_services: "{{ server1_services_result.stdout_lines | difference(server2_services_result.stdout_lines) }}" - name: 输出结果 debug: msg: "server1独有的运行中服务: {{ server1_unique_services }}"
额外优化
原命令中的grep '\.service'可以去掉,通过systemctl的--plain --no-legend参数直接输出纯净的服务名列表,避免grep可能带来的匹配问题。
内容的提问来源于stack exchange,提问作者G.Baysec
相关产品推荐
相关产品推荐

