在 Ansible 中以分离模式运行远程脚本/应用程序

在 Ansible 中以分离模式运行远程脚本/应用程序

我在从 Ansible playbook 中“分离”运行远程应用程序启动脚本时遇到了麻烦。脚本可以运行,但我无法让它分离/保持分离状态。我可能做错了什么,但是什么呢?

这是我的复制器。

  1. Test.java 中的远程 Java 应用程序运行了 10 秒:

    class Test {
        public static void main(String[] args) {
            for (int I = 1; I <= 10; i++) {
                System.out.println("Hello world " + I);
    
                try { Thread.sleep(1000);
                } catch (Exception e) { System.out.println("Exception caught: " + e);
                }
    } } }
    

将其编译为 Test.class(javac Test.java),然后使用“java Test”运行该类即可按预期工作(给出 10 条输出消息然后退出)。

  1. 运行此应用程序的可执行 shell 脚本(如 chmod 755)如下所示:

    #!/bin/bash
    java Test &
    

手动运行它也完全没问题:Java 应用程序运行并在我的控制台中生成相同的标准输出,但是 shell 脚本已退出并且我重新控制我的 bash 会话。

  1. 现在通过另一台服务器的 ansible playbook 运行它。我尝试以不同的方式使用“命令”模块和“shell”模块,但无济于事……

    ---
    - hosts: vagrant1
      gather_facts: false
    
      tasks:
      - debug: msg="Running test app through Ansible shell module..."
    
      - name: Start application
        shell: "/tmp/test.sh"
        args:
          chdir: "/tmp"
          executable: "/bin/bash"
    
      - debug: msg="Running test app through Ansible command module..."
    
      - name: Start application
        command: "nohup /tmp/test.sh &"
        args:
          chdir: "/tmp"
    

所有这些都运行良好,即 shell 脚本运行,Java 应用程序运行并执行其任务(即运行 10 秒,生成输出并退出)。但是 ansible-playbook 会一直运行直到 Java 应用程序完成,然后返回 Java 应用程序生成的输出。为什么它不直接分离 shell 脚本并完成剧本?

ansible-playbook 输出是:

    monsterkill@monsterkill-ub-dt:~/playbooks$ ansible-playbook testrun.yaml -v

    PLAY [vagrant1] *************************************************************** 

    TASK: [debug msg="Running test app through Ansible shell module..."] ********** 
    ok: [vagrant1] => {
        "msg": "Running test app through Ansible shell module..."
    }

    TASK: [Start application] ***************************************************** 
    changed: [vagrant1] => {"changed": true, "cmd": " /tmp/test.sh ", "delta": "0:00:10.052927", "end": "2015-01-29 00:14:43.327418", "rc": 0, "start": "2015-01-29 00:14:33.274491", "stderr": "", "stdout": "Hello world 1\nHello world 2\nHello world 3\nHello world 4\nHello world 5\nHello world 6\nHello world 7\nHello world 8\nHello world 9\nHello world 10"}

    TASK: [debug msg="Running test app through Ansible command module..."] ******** 
    ok: [vagrant1] => {
        "msg": "Running test app through Ansible command module..."
    }

    TASK: [Start application] ***************************************************** 
    changed: [vagrant1] => {"changed": true, "cmd": ["nohup", "/tmp/test.sh", "&"], "delta": "0:00:10.045643", "end": "2015-01-29 00:14:53.557164", "rc": 0, "start": "2015-01-29 00:14:43.511521", "stderr": "nohup: ignoring input", "stdout": "Hello world 1\nHello world 2\nHello world 3\nHello world 4\nHello world 5\nHello world 6\nHello world 7\nHello world 8\nHello world 9\nHello world 10"}

    PLAY RECAP ******************************************************************** 
    vagrant1                   : ok=4    changed=2    unreachable=0    failed=0

答案1

只需使用异步操作poll: 0

- command: yourscript.sh
  async: 45
  poll: 0

需要这些选项来防止 ansible 使用以下命令终止该任务的子进程:os.killpg

然后在 yourscript.sh 中:

java Test &
disown

disown 从作业表中删除该进程,因此 SIGHUP 不会发送给该进程。nohup 执行相同的操作,但不是必需的。

编辑:请注意,disown 并非在所有 shell 中都可用(例如 dash)

答案2

谷歌服务器现在肯定出问题了,不过我找到了解决办法。当我意识到 Ansible 通过单独的 ssh 调用执行所有远程命令时,我通过从 ansible 服务器手动执行该操作进行了测试,发现这与 ssh 功能有关。

答案显然是,使用我调用的脚本来执行双背景技巧,结合 nohup 使其忽略挂断信号 (SIGHUP),并断开 stdout 和 stderr 流。因此,远程启动脚本不再执行以下操作:

java Test &

...但现在却:

( ( nohup java Test 1>/dev/null 2>&1 ) & )

这正是我想要的。 ansible-playbook 现在启动远程脚本,进而启动 java 应用程序,但随后将其双重背景化并 nohups 并断开输出流。

这个 stackoverflow 帖子帮助过我。

答案3

使用 start-stop-daemon 将脚本作为守护进程运行似乎是一个更优雅的解决方案。http://manpages.ubuntu.com/manpages/raring/man8/start-stop-daemon.8.html

- name: Start application
  shell: "start-stop-daemon --start --quiet --pidfile /var/run/test --exec /tmp/test.sh"
  args:
    executable: "/bin/bash"

相关内容