使用Ansible检查服务是否存在

64

我有一个用于部署Java应用程序作为init.d守护程序的Ansible playbook。

作为Ansible和Linux的初学者,我在主机状态基础上条件性地执行任务遇到了问题。

具体而言,我有一些已经存在并运行服务的主机,我想在执行其他任何操作之前停止它。然后可能会有新主机,它们还没有该服务。因此,我不能简单地使用service: name = {{service_name}} state = stopped,因为这将在新主机上失败。

我该如何实现这一点?以下是我目前拥有的内容:

  - name: Check if Service Exists
    shell: "if chkconfig --list | grep -q my_service;   then echo true;   else echo false; fi;"
    register: service_exists

# This should only execute on hosts where the service is present
  - name: Stop Service
    service: name={{service_name}} state=stopped
    when: service_exists
    register: service_stopped

# This too
  - name: Remove Old App Folder
    command: rm -rf {{app_target_folder}}
    when: service_exists

# This should be executed on all hosts, but only after the service has stopped, if it was present
  - name: Unpack App Archive
    unarchive: src=../target/{{app_tar_name}} dest=/opt
9个回答

72
请参见 Ansible 2.5 中新添加的服务事实模块 service_facts 模块。
- name: Populate service facts
  service_facts:
- debug:
    msg: Docker installed!
  when: "'docker' in services"

1
这看起来很有前途,但其输出与预期的服务名称似乎不一致,至少对于检查 telnet.socket 服务而言是如此,它在 ansible_facts.services 中显示为 telnet@0.service - StockB
服务信息是在hostvars[$host]['services']下收集的。 - HermanTheGermanHesse
6
当使用when: "'docker' in services"时会失败,因为收集到的事实是使用完整名称(docker.service)而不是docker进行索引。可能与平台有关,但至少在CentOS上,这些事实只使用完整名称。 - jso
6
在相关模块页面上有一个重要的注释,关于如何访问服务名称,这值得注意。记住这一点,我在Ubuntu上成功使用了以下语句:when: ansible_facts.services ['service-name.service'] is defined - Ian Gibbs
1
似乎 service_facts 有点“慢”,我更喜欢检查服务文件是否存在。 - Manoochehr Dadashi
似乎这在本地主机上被跳过了,导致我需要在我的情况下这样做(我可以忽略本地主机,但想要捕获错误): when: "ansible_facts.services is defined and 'docker' in ansible_facts.services" - dom

49

当然,我也可以检查/etc/init.d中是否存在包装脚本。所以这就是我最终得出的:

  - name: Check if Service Exists
    stat: path=/etc/init.d/{{service_name}}
    register: service_status

  - name: Stop Service
    service: name={{service_name}} state=stopped
    when: service_status.stat.exists
    register: service_stopped

6
我之前避免使用ignore_errors的原因是,我担心因为打错字等原因导致出现假阴性结果。此外,我正在努力说服团队投资自动化配置管理,不想让他们看到的第一件事情看起来像某种黑客攻击。 :) - EagleBeak
1
Ansible在v2.0版本中的service模块中添加了一个must_exist标志,这将消除第一个检查所需的需要:http://docs.ansible.com/service_module.html - ryantuck
10
@RyanTuck,您提供的链接页面上没有列出“must_exist”参数。目前2.0版本已正式发布。 - aef
1
这个方法在我的Mint 18.2上只部分有效。我的一些systemd服务有init脚本,但不是全部,要注意并检查是否使用此方法。 - ddrake12
1
根据@darkwing和@abzarak的评论:与其检查init脚本包装器,不如直接检查“/etc/systemd/system/{{service_name}}.service”。许多较新的系统和软件包发布都放弃了SysVinit包装器脚本。 - ncoghlan
显示剩余4条评论

10

我修改了Florian的答案,只使用service命令的返回代码(在Mint 18.2上有效)

- name: Check if Logstash service exist
  shell: service logstash status 
  register: logstash_status
  failed_when: not(logstash_status.rc == 3 or logstash_status.rc == 0)

- name: Check if Logstash service exist
  service:
    name: logstash
    state: stopped 
  when: logstash_status.rc == 0

9

如果“service”模块能够处理“未识别服务”错误,那就太好了。

这是我的解决方法,使用service命令而非检查init脚本:

- name: check for apache
  shell: "service apache2 status"
  register: _svc_apache
  failed_when: >
    _svc_apache.rc != 0 and ("unrecognized service" not in _svc_apache.stderr)

- name: disable apache
  service: name=apache2 state=stopped enabled=no
  when: "_svc_apache.rc == 0"
  • 检查 "service status" 命令的退出代码,并在输出包含 "unrecognized service" 时接受退出代码为0。
  • 如果退出代码为0,则表示该服务已安装(已停止或正在运行)。

4

基于 @Maciej 对 RedHat 8 的回答,并结合相关评论,以下是我如何停止已安装的 Celery:

- name: Populate service facts
  service_facts:
- debug:
    msg: httpd installed!
  when: ansible_facts.services['httpd.service'] is defined

- name: Stop celery.service
  service:
    name: celery.service
    state: stopped
    enabled: true
  when: ansible_facts.services['celery.service'] is defined

您可以删除调试语句--它只是用来确认ansible_facts是否正常工作。


3

另一种systemd的方法(来自Jakuje的回答):

- name: Check if cups-browsed service exists
  command: systemctl cat cups-browsed
  check_mode: no
  register: cups_browsed_exists
  changed_when: False
  failed_when: cups_browsed_exists.rc not in [0, 1]

- name: Stop cups-browsed service
  systemd:
    name: cups-browsed
    state: stopped 
  when: cups_browsed_exists.rc == 0

2

我们只使用了service模块,这种方法对我们很有效:

- name: Disable *service_name*
  service:
    name: *service_name*
    enabled: no
    state: stopped
  register: service_command_output
  failed_when: >
    service_command_output|failed
    and 'unrecognized service' not in service_command_output.msg
    and 'Could not find the requested service' not in service_command_output.msg

-1

自 Ansible 2.5 版本开始,您可以使用 service_facts 模块。但是需要注意的是输出结果是服务的真实名称,例如 docker.servicesomthing@.service 等。因此,您有不同的选项可供选择:

- name: Populate service facts
  service_facts:
- debug:
    msg: Docker installed!
  when: "'docker.service' in services"

或者你可以搜索以服务名称开头的字符串;这样更可靠,因为不同发行版的服务名称是不同的:

- name: Populate service facts
  service_facts:
- debug:
    msg: Docker installed!
  when: "services.keys()|list|select('search', '^docker')|length >0"

-1

我的一些建议。与上述方法相同,但针对Kubernetes。

检查kublete服务是否正在运行

- name: "Obtain state of kublet service"
  command: systemctl status kubelet.service
  register: kubelet_status
  failed_when: kubelet_status.rc > 3

如果kublet服务未运行,则显示调试消息

- debug:
    msg: "{{ kubelet_status.stdout }}"
  when: "'running' not in kubelet_status.stdout"

1
不幸的是,如果服务不存在(这就是 OP 所要求的),这种方法将不起作用。使用您的代码仍会出现“找不到 Unit kubelet.service”错误。 - jso
@jso 你是对的。这个解决方案假设服务已经在主机上安装,正如问题所述“我有一些已经存在并运行服务的主机”,但如果服务不存在,则会失败。因此,我支持你的评论 :) - Lukasz Dynowski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接