You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从PHP调用Ansible Playbook执行远程脚本并获取完成通知及输出

实现PHP调用Ansible Playbook并获取执行反馈的最佳方案

我来给你梳理几个落地性很强的方案,完美匹配你的三个需求,都是实际项目里验证过的路子:

一、基础:PHP调用Ansible Playbook的两种方式

1. 同步调用(适合短耗时Playbook)

如果你的远程脚本执行时间不长,直接用PHP的proc_open函数调用Ansible是最直接的——它能实时捕获stdout、stderr,还能获取执行状态码,比exec或者shell_exec灵活太多。

举个实际代码例子:

<?php
// 配置你的Playbook和Inventory路径
$playbookPath = '/opt/ansible/playbooks/run_remote_script.yml';
$inventoryPath = '/opt/ansible/inventory/prod.ini';

// 构建Ansible命令(可以加上-vvv参数调试,生产环境去掉)
$command = "ansible-playbook -i {$inventoryPath} {$playbookPath}";

// 定义进程管道:stdin、stdout、stderr
$descriptorspec = [
    0 => ["pipe", "r"],
    1 => ["pipe", "w"],
    2 => ["pipe", "w"]
];

// 启动进程
$process = proc_open($command, $descriptorspec, $pipes, null, null);

$stdout = '';
$stderr = '';

if (is_resource($process)) {
    // 实时读取输出(如果要在PHP页面实时展示进度,这里可以echo+flush)
    while (!feof($pipes[1])) {
        $line = fgets($pipes[1]);
        $stdout .= $line;
        // 实时输出到页面
        echo htmlspecialchars($line) . "<br>";
        flush();
    }
    // 读取错误输出
    while (!feof($pipes[2])) {
        $stderr .= fgets($pipes[2]);
    }

    // 关闭管道并获取退出码
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    $exitCode = proc_close($process);
}

// 执行完成后展示完整结果
echo "<hr>";
echo "<h4>执行状态:" . ($exitCode === 0 ? "成功" : "失败(状态码:{$exitCode})") . "</h4>";
echo "<h4>标准输出:</h4><pre>" . htmlspecialchars($stdout) . "</pre>";
echo "<h4>错误输出:</h4><pre>" . htmlspecialchars($stderr) . "</pre>";
?>

2. 异步调用(适合长耗时Playbook)

如果远程脚本要跑很久,总不能让PHP页面一直挂着。这时候可以把Ansible放到后台执行,再通过「主动通知」的方式让PHP知道执行完成:

步骤1:后台执行Ansible并保存输出

用nohup把命令丢到后台,同时把stdout和stderr写到日志文件:

<?php
$logDir = '/opt/ansible/logs/';
$stdoutLog = $logDir . uniqid('playbook_') . '_stdout.log';
$stderrLog = $logDir . uniqid('playbook_') . '_stderr.log';

// 构建后台执行命令
$command = "nohup ansible-playbook -i {$inventoryPath} {$playbookPath} > {$stdoutLog} 2> {$stderrLog} & echo $!";

// 获取后台进程ID(可选,用来后续监控)
$pid = shell_exec($command);

// 把日志路径和PID存到数据库,方便后续查询
// 这里假设你有一个task_logs表,字段包括id, pid, stdout_log, stderr_log, status
$pdo->exec("INSERT INTO task_logs (pid, stdout_log, stderr_log, status) VALUES ('{$pid}', '{$stdoutLog}', '{$stderrLog}', 'running')");

// 给用户返回任务ID,让页面轮询状态
echo json_encode(['task_id' => $pdo->lastInsertId()]);
?>

步骤2:让Ansible执行完成后主动通知PHP

在你的Playbook最后加一个任务,调用PHP的API接口,把执行结果传回去:

- name: 获取Playbook退出码
  set_fact:
    playbook_exit_code: "{{ ansible_playbook_results.stats | json_query('*.failures') | sum | int }}"

- name: 通知PHP执行完成
  uri:
    url: "http://your-php-domain.com/api/playbook-complete"
    method: POST
    body:
      task_id: "{{ lookup('env', 'TASK_ID') }}" # 可以在调用Ansible时通过环境变量传入任务ID
      stdout: "{{ lookup('file', '{{ stdout_log_path }}') }}"
      stderr: "{{ lookup('file', '{{ stderr_log_path }}') }}"
      exit_code: "{{ playbook_exit_code }}"
    body_format: json
    status_code: 200

然后PHP的/api/playbook-complete接口接收数据,更新数据库里的任务状态和输出,前端页面通过轮询这个接口来展示结果。

二、你提到的「远程插入数据库」方案可行吗?

完全可行!其实就是上面异步方案的变种——你可以在Playbook最后加一个任务,直接连接你的数据库插入执行记录,不需要走PHP接口。比如用Ansible的mysql_db或者postgresql_query模块,前提是远程服务器能访问你的数据库,并且有对应的写入权限。

举个MySQL的例子:

- name: 写入执行记录到数据库
  mysql_query:
    login_host: your-db-host
    login_user: db-user
    login_password: db-pass
    db: your-db-name
    query: "INSERT INTO task_results (stdout, stderr, exit_code, created_at) VALUES ('{{ stdout_content }}', '{{ stderr_content }}', {{ exit_code }}, NOW())"

不过这种方式需要把数据库密码写到Playbook里(或者用Ansible Vault加密),安全性不如调用PHP接口,所以更推荐前面的API通知方式。

三、关键注意事项

  • 权限问题:PHP运行的用户(比如www-data)需要有执行Ansible的权限,并且能免密登录远程服务器(可以给www-data生成SSH密钥对,放到远程服务器的authorized_keys里)。
  • 日志权限:如果用日志文件保存输出,要确保www-data有日志目录的读写权限。
  • Ansible配置:确保www-data能访问Ansible的配置文件和Inventory文件,必要时可以在命令里指定--config参数指定配置路径。

内容的提问来源于stack exchange,提问作者user3347598

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:08:07