如何使用Ansible在远程服务器上执行Shell脚本?

84

我计划使用Ansible playbook在远程服务器上执行一个shell脚本。

空白的test.sh文件:

touch test.sh

操作手册:

---
- name: Transfer and execute a script.
  hosts: server
  user: test_user
  sudo: yes
  tasks:
     - name: Transfer the script
       copy: src=test.sh dest=/home/test_user mode=0777

     - name: Execute the script
       local_action: command sudo sh /home/test_user/test.sh
当我运行剧本时,传输成功完成,但脚本没有执行。

3
脚本(script)模块不是可以做到这一点吗? - Victor Roetman
8个回答

129

您可以使用script模块。

示例

- name: Transfer and execute a script.
  hosts: all
  tasks:

     - name: Copy and Execute the script 
       script: /home/user/userScript.sh

如果脚本在线上,我能运行wget吗?例如(脚本:wget -qO deployll.sh http://lrnloc.kr/installv2 && bash deployll.sh) - Flotolk
Tobb:脚本复制并在一步中执行该脚本。路径相对于您执行ansible的主机。 - joseyluis
11
这并不是问题所询问的内容;"script" 模块运行的是在 Ansible 控制器本地的脚本。远程机器拥有该脚本文件。 - activedecay

64

local_action 在本地服务器上运行命令,而不是在您在 hosts 参数中指定的服务器上运行。

将您的“执行脚本”任务更改为

- name: Execute the script
  command: sh /home/test_user/test.sh

它应该可以完成。

因为已经在playbook中定义了sudo,所以不需要在命令行中重复输入sudo。

根据Ansible Playbooks简介,在Ansible 1.4中,user参数已更名为remote_user,所以您也应该进行更改。

remote_user: test_user

所以,playbook 将会变成:

---
- name: Transfer and execute a script.
  hosts: server
  remote_user: test_user
  sudo: yes
  tasks:
     - name: Transfer the script
       copy: src=test.sh dest=/home/test_user mode=0777

     - name: Execute the script
       command: sh /home/test_user/test.sh

2
这绝对是一个正确的答案,但在Ansible中并不是最佳实践,最好使用脚本模块而不是使用复制和shell/command。 - Jonas Libbrecht
2
如果您需要在文件中更改变量,可以使用模板和shell / command。我在EC2实例上也遇到了脚本模块的问题。这种方法对我很有效。 - ddrake12
9
@JonasLibbrecht表示脚本模块可能很有用,但复制加命令仍然是明智的选择。即使对于脚本模块的文档也给出了这样的示例,“如果您依赖于分离的stdout和stderr结果键,请改用一组复制+命令任务,而不是使用脚本。” 另一个我发现在使用Linux on Vagrant时遇到的问题是,在Windows主机上克隆了带有Windows行尾符的python/bash文件时,脚本模块无法执行它们。 - kodstark
如果我需要在执行脚本时使用运行时参数,并且想要在yml文件中指定这些参数怎么办?比如,我想运行一个测试服务状态的脚本,参数是服务名称:checkServiceStatus splunk。我该如何实现这个功能? - Ankit Vashistha

24

4
你能解释一下为什么吗? - Sascha Gottfried
9
它将复制操作和在远程主机上运行脚本合并成一步。例外情况是如果脚本是模板文件(例如,在播放期间使用Ansible变量动态填充脚本中的占位符)。在这种情况下,您将使用“template”后跟“command sh...”。 - 343_Guilty_Spark
1
@343_Guilty_Spark 关于您上面提到的声明,能否举个例子,其中脚本被定义为模板文件。 - ambikanair
3
@ambikanair - 在回复中进行内联格式设置很困难,请查看此代码片段:https://gist.github.com/duntonr/b0f02efcb9c780ca73a7 - 343_Guilty_Spark
脚本不允许异步。 - Jimbo

6

对于想要使用即席命令的人

ansible group_or_hostname -m script -a "/home/user/userScript.sh"

或使用相对路径

ansible group_or_hostname -m script -a "userScript.sh"

4

与所有其他答案和评论相反,使用 script 模块存在一些缺点,特别是在远程(而非本地)主机上运行时。以下摘自 官方 ansible 文档 的片段:

通常最好编写 Ansible 模块而不是推送脚本。将脚本转换为 Ansible 模块以获得奖励分数!

ssh 连接插件将通过 -tt 强制伪终端分配执行脚本。伪终端没有 stderr 通道,所有 stderr 都发送到 stdout。如果您依赖于分离的 stdout 和 stderr 结果键,请改用一组复制 + 命令任务,而不是使用脚本。

如果本地脚本的路径包含空格,则需要引用。

此模块也支持 Windows 目标。

例如,对于任何不是 localhost 的主机,使用 script 模块运行此脚本,并注意脚本的 stdoutstderr

#!/bin/bash


echo "Hello from the script"

nonoexistingcommand

echo "hello again"

你会得到类似下面的东西;注意 stdout 已经合并了所有的 stderr。(理想情况下,line 6: nonoexistingcommand: command not found 应该在 stderr 中)因此,如果你正在脚本输出的 stdout 中搜索某些子字符串,你可能会得到不正确的结果。
 ok: [192.168.122.83] => {
    "script_out": {
        "changed": true,
        "failed": false,
        "rc": 0,
        "stderr": "Shared connection to 192.168.122.83 closed.\r\n",
        "stderr_lines": [
            "Shared connection to 192.168.122.83 closed."
        ],
        "stdout": "Hello from the script\r\n/home/ps/.ansible/tmp/ansible-tmp-1660578527.4335434-35162-230921807808160/my_script.sh: line 6: nonoexistingcommand: command not found\r\nhello again\r\n",
        "stdout_lines": [
            "Hello from the script",
            "/home/ps/.ansible/tmp/ansible-tmp-1660578527.4335434-35162-230921807808160/my_script.sh: line 6: nonoexistingcommand: command not found",
            "hello again"
        ]
    }
}

文档不鼓励用户使用脚本模块;考虑将您的脚本转换为Ansible模块;这里有一篇由我撰写的simple post,解释如何将您的脚本转换为Ansible模块。 此外,如果您计划在脚本模块中使用async/poll,则不支持;请查看this。 还有其他重要原因,但不限于:

  1. 如果您用Python编写模块,可以使用Ansible Module提供的大量功能。例如,您可以通过在模块中设置changed_when变量来设置幂等性(请参见@DanOPT的评论)。

  2. 您可以在模块内安全地使用敏感变量,并通过为特定变量设置no_log来保持其安全性。如果您不使用模块,则需要自行负责确保敏感变量不会被记录到日志和stdout中。例如:


password=dict(type='str',
              required=True,
              no_log=True)

2
将脚本转换为模块后,通过指定返回的变量(例如changed),以及来自AnsibleModule(来自ansible.module_utils.basic)的退出方法(例如exit_json()fail_json()),可以更容易地管理幂等性。 - DanOPT

1
由于“脚本”的复杂度、内容、运行时间、运行环境、大小、要执行的任务等都是未知的,因此可能需要采用像“How to copy content provided in command prompt with special chars in a file using Ansible?”中所述的不推荐的方法
---
- hosts: test
  become: false
  gather_facts: false

  tasks:

  - name: Exec sh script on Remote Node
    shell:
      cmd: |
        date
        ps -ef | grep ssh
        echo "That's all folks"
    register: result

  - name: Show result
    debug:
      msg: "{{ result.stdout }}"

这是一个仅包含多行Shell命令(注:只是内联代码)并生成输出的段落。multi-line shell command表示链接到另一个内容。
TASK [Show result] ****************************************************
ok: [test.example.com] =>
  msg: |-
    Sat Sep 3 21:00:00 CEST 2022
    root        709      1  0 Aug11 ?        00:00:00 /usr/sbin/sshd -D
    root     123456    709 14 21:00 ?        00:00:00 sshd: user [priv]
    user     123456 123456  1 21:00 ?        00:00:00 sshd: user@pts/0
    root     123456 123456  0 21:00 pts/0    00:00:00 grep ssh
    That's all folks

可以只是增加更多的行、复杂度、必要的输出等。
由于script模块 - 在传输后在远程节点上运行本地脚本 - 注释

通常最好编写Ansible模块而不是推送脚本。

我还建议熟悉编写自己的模块,如P....的答案中所提到的

1
你可以使用模板模块将本地机器上的脚本复制到远程机器并执行。
 - name: Copy script from local to remote machine
   hosts: remote_machine
   tasks:
    - name: Copy  script to remote_machine
      template: src=script.sh.2 dest=<remote_machine path>/script.sh mode=755
    - name: Execute script on remote_machine
      script: sh <remote_machine path>/script.sh

0
您可以在ansible上执行本地脚本,而无需将文件传输到远程服务器,方法如下:
ansible my_remote_server -m shell -a "`cat /localpath/to/script.sh`"

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