如何使用Ansible等待服务器重新启动?

47

我正在尝试使用以下命令重新启动服务器并等待:

- name: Restart server
  shell: reboot

- name: Wait for server to restart
  wait_for:
    port=22
    delay=1
    timeout=300

但我收到了这个错误:

TASK: [iptables | Wait for server to restart] ********************************* 
fatal: [example.com] => failed to transfer file to /root/.ansible/tmp/ansible-tmp-1401138291.69-222045017562709/wait_for:
sftp> put /tmp/tmpApPR8k /root/.ansible/tmp/ansible-tmp-1401138291.69-222045017562709/wait_for

Connected to example.com.
Connection closed

2
wait_for正在远程运行(在重新启动的服务器上)。您需要在本地计算机上运行它。 - tedder42
请注意,有一个适当的重新启动操作正在进行中: https://github.com/ansible/ansible/issues/16186 - bodo
11个回答

66

Ansible >= 2.7(于2018年10月发布)

使用内置的reboot模块:

- name: Wait for server to restart
  reboot:
    reboot_timeout: 3600

Ansible < 2.7

将重启作为一个任务

- name: restart server
  shell: 'sleep 1 && shutdown -r now "Reboot triggered by Ansible" && sleep 1'
  async: 1
  poll: 0
  become: true

这将作为异步任务运行shell命令,因此Ansible不会等待命令结束。通常async参数为任务提供最长时间,但由于将poll设置为0,如果命令已完成,Ansible将永远不会轮询-它将使此命令成为“fire and forget”。在shutdown之前和之后的休眠是为了防止在重启期间断开SSH连接,而Ansible仍然连接到您的远程主机。

等待作为任务

您可以直接使用:

- name: Wait for server to restart
  local_action:
    module: wait_for
      host={{ inventory_hostname }}
      port=22
      delay=10
    become: false

..但是你可能更喜欢使用{{ ansible_ssh_host }}变量作为主机名和/或{{ ansible_ssh_port }}作为SSH主机和端口,如果你使用如下条目:

hostname         ansible_ssh_host=some.other.name.com ansible_ssh_port=2222 

在您的清单 (Ansible hosts 文件) 中。

这将在运行 Ansible 的机器上运行 wait_for 任务。该任务将等待远程主机上的端口 22 开放,延迟 10 秒后开始等待。

重启并等待作为处理程序

但我建议将两者都用作处理程序,而不是任务。

这样做有两个主要原因:

  • 代码重用 - 您可以为许多任务使用处理程序。 示例:更改时区后触发服务器重新启动和更改内核之后

  • 仅触发一次 - 如果您将处理程序用于多个任务,并且其中超过1个任务会进行一些更改=>触发处理程序,则处理程序执行的操作将仅发生一次。 示例:如果您附加了httpd重启处理程序以进行httpd配置更改和SSL证书更新,则在配置和SSL证书同时更改的情况下,httpd仅重新启动一次。

在此处 了解有关处理程序的更多信息。

作为处理程序重新启动并等待重新启动:

  handlers:

    - name: Restart server
      command: 'sleep 1 && shutdown -r now "Reboot triggered by Ansible" && sleep 1'
      async: 1
      poll: 0
      ignore_errors: true
      become: true

    - name: Wait for server to restart
      local_action:
        module: wait_for
          host={{ inventory_hostname }}
          port=22
          delay=10
        become: false

..并且按照顺序在您的任务中使用它,就像这样,这里与重新启动服务器处理程序配对使用:

  tasks:
    - name: Set hostname
        hostname: name=somename
        notify:
          - Restart server
          - Wait for server to restart

请注意处理程序按照定义的顺序运行,而不是在notify中列出的顺序!

2
处理程序不一定按顺序执行。请查看:Handlers 或者我可能误解了? - Joze
1
感谢您指出这一点,@Joze。重要的是处理程序的定义顺序,而不是它们在“notify”中列出的顺序。我会在我的回答中澄清这一点。 - Greg Dubicki
1
你为什么认为作为处理程序更好? - Jonathan Hartley
1
我已在编辑后的答案中回复了你,@JonathanHartley。感谢你的提问! - Greg Dubicki

37
你应该将等待任务更改为以local_action方式运行,并指定你正在等待的主机。例如:
- name: Wait for server to restart
  local_action:
    module: wait_for
      host=192.168.50.4
      port=22
      delay=1
      timeout=300

5
可能也需要使用 "sudo: false"。 - chachra
3
Ansible在这篇博客文章中也讨论了这个主题,很有帮助:https://support.ansible.com/hc/en-us/articles/201958037-Reboot-a-server-and-wait-for-it-to-come-back - chachra
2
@chachra在博客中提到了版本1.9.4的设置。那么对于非1.9.4的情况,是1.9.4之前还是之后的版本? - Franklin Yu
@chachra:您提供的URL从2018年1月起已不再有效。 - Greg Dubicki
该方法返回:致命错误:[rpi]:FAILED!=> {"changed": false, "module_stderr": "", "module_stdout": "", "msg": "MODULE FAILURE", "rc": -1} - Drew

10

我使用1.9.4版本后最可靠的是(此为更新版本,原始版本在底部):

- name: Example ansible play that requires reboot
  sudo: yes
  gather_facts: no
  hosts:
    - myhosts
  tasks:
    - name: example task that requires reboot
      yum: name=* state=latest
      notify: reboot sequence
  handlers:
    - name: reboot sequence
      changed_when: "true"
      debug: msg='trigger machine reboot sequence'
      notify:
        - get current time
        - reboot system
        - waiting for server to come back
        - verify a reboot was actually initiated
    - name: get current time
      command: /bin/date +%s
      register: before_reboot
      sudo: false
    - name: reboot system
      shell: sleep 2 && shutdown -r now "Ansible package updates triggered"
      async: 1
      poll: 0
      ignore_errors: true
    - name: waiting for server to come back
      local_action: wait_for host={{ inventory_hostname }} state=started delay=30 timeout=220
      sudo: false
    - name: verify a reboot was actually initiated
      # machine should have started after it has been rebooted
      shell: (( `date +%s` - `awk -F . '{print $1}' /proc/uptime` > {{ before_reboot.stdout }} ))
      sudo: false
请注意async选项。1.8和2.0版本可以使用0,但1.9版本需要1。上述代码还检查机器是否已经重新启动。这很有用,因为我曾经因为输错了字母导致重启失败,却没有任何提示。

最大的问题是等待机器启动。这个版本只是在那里等待330秒,从未尝试更早地访问主机。其他一些答案建议使用22号端口。如果以下两个条件都成立,那么这是好的:

  • 您可以直接访问这些机器
  • 22号端口开放后,您的机器可以立即访问

这些条件并不总是成立,所以我决定浪费5分钟的计算时间。我希望ansible能扩展wait_for模块来实际检查主机状态,以避免浪费时间。

顺便说一下,建议使用handlers的回答很好。我对handlers给出+1的评价(我已更新答案以使用handlers)。

以下是原始版本,它并不是很好也不是很可靠:

- name: Reboot
  sudo: yes
  gather_facts: no
  hosts:
    - OSEv3:children
  tasks:
    - name: get current uptime
      shell: cat /proc/uptime | awk -F . '{print $1}'
      register: uptime
      sudo: false
    - name: reboot system
      shell: sleep 2 && shutdown -r now "Ansible package updates triggered"
      async: 1
      poll: 0
      ignore_errors: true
    - name: waiting for server to come back
      local_action: wait_for host={{ inventory_hostname }} state=started delay=30 timeout=300
      sudo: false
    - name: verify a reboot was actually initiated
      # uptime after reboot should be smaller than before reboot
      shell: (( `cat /proc/uptime | awk -F . '{print $1}'` < {{ uptime.stdout }} ))
      sudo: false

8

2018更新

从2.3版本开始,Ansible现在已经内置了wait_for_connection模块,可以用于此目的。

#
## Reboot
#

- name: (reboot) Reboot triggered
  command: /sbin/shutdown -r +1 "Ansible-triggered Reboot"
  async: 0
  poll: 0

- name: (reboot) Wait for server to restart
  wait_for_connection:
    delay: 75

使用“shutdown -r +1”命令可以防止返回代码1并导致ansible任务失败。由于shutdown作为异步任务运行,因此我们必须将“wait_for_connection”延迟至少60秒。75为我们提供了雪花情况的缓冲。

wait_for_connection - 等待远程系统可达/可用


6

我想评论Shahar的帖子,他正在使用一个硬编码的主机地址,最好是将其作为变量引用当前Ansible正在配置的主机{{inventory_hostname}},那么他的代码将会像这样:

- name: Wait for server to restart
  local_action:
    module: wait_for
     host={{ inventory_hostname }}
     port=22
     delay=1
     timeout=300

4

随着Ansible的新版本(例如,在我的情况下是1.9.1),将poll和async参数设置为0有时不足(可能取决于设置ansible的发行版是什么)。如https://github.com/ansible/ansible/issues/10616中所解释的那样,其中一种解决方法是:

- name: Reboot
  shell: sleep 2 && shutdown -r now "Ansible updates triggered"
  async: 1
  poll: 0
  ignore_errors: true

然后,按照本页面许多答案所解释的那样,等待重启完成。


2
请注意不要在此处使用 command 模块,而应使用 shell 模块。 - amichaud
3
为什么要使用 sleep 2 && 而不是直接使用 shutdown -r 2 - Franklin Yu
因为关机需要等待2分钟。 - Marcin Orlowski

4

通过反复试验和大量阅读,我最终使用 Ansible 2.0 版本找到了适合我的解决方案:

$ ansible --version
ansible 2.0.0 (devel 974b69d236) last updated 2015/09/01 13:37:26 (GMT -400)
  lib/ansible/modules/core: (detached HEAD bbcfb1092a) last updated 2015/09/01 13:37:29 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD b8803306d1) last updated 2015/09/01 13:37:29 (GMT -400)
  config file = /Users/sammingolelli/projects/git_repos/devops/ansible/playbooks/test-2/ansible.cfg
  configured module search path = None

我对禁用SELinux并在需要时重新启动节点的解决方案如下:

---
- name: disable SELinux
  selinux: state=disabled
  register: st

- name: reboot if SELinux changed
  shell: shutdown -r now "Ansible updates triggered"
  async: 0
  poll: 0
  ignore_errors: true
  when: st.changed

- name: waiting for server to reboot
  wait_for: host="{{ ansible_ssh_host | default(inventory_hostname) }}" port={{ ansible_ssh_port | default(22) }} search_regex=OpenSSH delay=30 timeout=120
  connection: local
  sudo: false
  when: st.changed

# vim:ft=ansible:

2

我没有看到很多关于这个的可见性,但最近的更改(https://github.com/ansible/ansible/pull/43857)添加了"ignore_unreachable"关键字。这使您可以像这样操作:

- name: restart server
  shell: reboot
  ignore_unreachable: true

- name: wait for server to come back
  wait_for_connection: 
      timeout: 120

- name: the next action
  ...

1
- wait_for:
    port: 22
    host: "{{ inventory_hostname }}"
  delegate_to: 127.0.0.1

1
根据 ansible-lint 的建议,这似乎是使用最新版本的推荐方式。 - l0b0

1
如果您尚未为远程服务器设置DNS,您可以传递IP地址而不是变量主机名:
- name: Restart server
  command: shutdown -r now

- name: Wait for server to restart successfully
  local_action:
    module: wait_for
      host={{ ansible_default_ipv4.address }}
      port=22
      delay=1
      timeout=120

这是我在ansible-swap playbook结尾添加的两个任务(用于在新的Digital Ocean droplets上安装4GB交换空间)。


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