使用 Handlers 管理服务

AnsibleBeginner
立即练习

介绍

Handlers(处理程序)是特殊的 Ansible 任务,仅在被发生变更的任务通知时才会运行。它们通常用于服务的重载和重启。

在本实验中,你将部署一个安全的演示用 systemd 单元文件,在单元文件发生变更时通知 handlers,并验证该服务是否处于活动且已启用状态。

创建服务自动化项目

在此步骤中,你将创建一个用于服务自动化的项目。由于该服务是 RHEL 10 虚拟机上的真实系统状态,本实验使用一个本地主机别名。

cd /home/labex/project
mkdir -p rhce-ansible/{inventory,files,playbooks,reports}
cd rhce-ansible
cat > ansible.cfg <<'EOF'
[defaults]
inventory = ./inventory/hosts
host_key_checking = False
retry_files_enabled = False
EOF
cat > inventory/hosts <<'EOF'
[service_hosts]
host ansible_connection=local
EOF

即使使用本地连接,Playbook 也会管理虚拟机上的真实 systemd 状态。这就是为什么本实验使用一个主机别名,并在稍后使用 become: true,而不是假装存在多个独立的服务主机。

创建演示用 systemd 单元文件

在此步骤中,你将创建 Ansible 将复制到 /etc/systemd/system 的源单元文件。该服务运行 /usr/bin/sleep infinity,因此它既安全又易于验证。 在运行此步骤的命令之前,请返回 Ansible 项目目录。即使 LabEx 分步运行,这也能保持命令的稳定性。

cd /home/labex/project/rhce-ansible
cat > files/rhce-handler-demo.service <<'EOF'
[Unit]
Description=RHCE handler demo service

[Service]
Type=simple
ExecStart=/usr/bin/sleep infinity
Restart=always
RestartSec=1

[Install]
WantedBy=multi-user.target
EOF

此单元文件特意设计为无害的:它启动一个长时间运行的 sleep 进程,并在其退出时重启它。[Install] 部分使得 Ansible 能够为 multi-user.target 启用该服务。

sed -n "1,12p" files/rhce-handler-demo.service

在继续之前,请检查 ExecStart 和 WantedBy 行。如果源单元文件有误,Ansible 将忠实地部署错误的定义。

编写带有 Handlers 的 Playbook

在此步骤中,你将编写一个复制单元文件并通知 handlers 的 Playbook。meta: flush_handlers 任务会在服务任务之前运行挂起的 handlers,以便 systemd 能够识别新的单元文件。 在运行此步骤的命令之前,请返回 Ansible 项目目录。

cd /home/labex/project/rhce-ansible
cat > playbooks/manage-service.yml <<'EOF'
---
- name: Deploy and manage the demo service
  hosts: service_hosts
  become: true
  gather_facts: false
  tasks:
    - name: Install demo systemd unit
      ansible.builtin.copy:
        src: ../files/rhce-handler-demo.service
        dest: /etc/systemd/system/rhce-handler-demo.service
        mode: '0644'
      notify:
        - Reload systemd
        - Restart demo service

    - name: Run handlers before enabling the service
      ansible.builtin.meta: flush_handlers

    - name: Ensure demo service is active and enabled
      ansible.builtin.systemd:
        name: rhce-handler-demo.service
        state: started
        enabled: true

  handlers:
    - name: Reload systemd
      ansible.builtin.systemd:
        daemon_reload: true

    - name: Restart demo service
      ansible.builtin.systemd:
        name: rhce-handler-demo.service
        state: restarted
EOF

copy 任务仅在单元文件发生变更时通知两个 handlers。meta: flush_handlers 会在 systemd 任务启动并启用服务之前运行所有挂起的 handlers,这一点很重要,因为 systemd 必须先重载单元文件才能识别新服务。

ansible-playbook playbooks/manage-service.yml --syntax-check

在 Playbook 写入 /etc/systemd/system 之前,语法检查应通过。它可以在不更改服务状态的情况下捕获任务列表和 handlers 部分中的缩进错误。

运行 Playbook 并检查服务

在此步骤中,你将运行服务 Playbook。首次运行应安装单元文件、重载 systemd、启动服务并将其设置为开机自启。 在运行此步骤的命令之前,请返回 Ansible 项目目录。

cd /home/labex/project/rhce-ansible
ansible-playbook playbooks/manage-service.yml | tee reports/service-first-run.txt

首次运行应复制单元文件、重载 systemd、通过 handler 重启演示服务,然后确保服务已启用。保存输出以便查看 handler 路径是否已执行。

systemctl is-active rhce-handler-demo.service
systemctl is-enabled rhce-handler-demo.service

is-active 检查当前引导的运行时状态,而 is-enabled 检查服务是否已链接到引导目标。RHCE 服务任务通常要求两者兼备。

修改单元文件并再次触发 Handler

在此步骤中,你将对源单元文件进行受控修改。当 copy 任务检测到变更时,它会再次通知 handlers。 在运行此步骤的命令之前,请返回 Ansible 项目目录。

cd /home/labex/project/rhce-ansible

lineinfile 模块也可以通过 ad hoc 命令编辑本地文件。这里它只修改源单元文件中的一行。

ansible localhost -c local -m ansible.builtin.lineinfile -a 'path=files/rhce-handler-demo.service regexp=^RestartSec= line=RestartSec=3'

这会更改源文件,而不是 /etc/systemd/system 下已安装的单元文件。下一次 Playbook 运行应注意到源文件变更,复制更新后的单元文件,并再次通知 handlers。

ansible-playbook playbooks/manage-service.yml | tee reports/service-second-run.txt
grep -F RestartSec /etc/systemd/system/rhce-handler-demo.service

已安装的单元文件现在应显示 RestartSec=3。服务应保持活动且已启用状态,因为 handlers 在文件变更后重载了 systemd 并重启了演示服务。

总结

你部署了一个安全的 systemd 单元文件,使用了用于 systemd 重载和服务重启的 handlers,验证了服务的活动和启用状态,并通过受控的单元文件变更触发了 handler。