-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4fe5b35
commit f4da63d
Showing
3 changed files
with
171 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Remote Server for Laravel Pulse | ||
|
||
Add remote linux server to your server stats. This is meant for servers that do not run PHP, e.g. database or cache servers. Servers that run PHP should install their own instance of Laravel Pulse instead. | ||
|
||
## Installation | ||
|
||
Install the package using Composer: | ||
|
||
```shell | ||
composer require wrklst/pulse-remote-server | ||
``` | ||
|
||
## Register the recorder | ||
|
||
In your `pulse.php` configuration file, register the RemoteServerRecorder with the desired settings: | ||
|
||
```php | ||
return [ | ||
// ... | ||
|
||
'recorders' => [ | ||
\WrkLst\Pulse\RemoteServer\RemoteServerRecorder::class => [ | ||
'server_name' => "database-server-1", | ||
'server_ssh' => "ssh forge@1.2.3.4", | ||
'directories' => explode(':', env('PULSE_SERVER_DIRECTORIES', '/')), | ||
], | ||
] | ||
] | ||
``` | ||
|
||
Ensure you're running [the `pulse:check` command](https://laravel.com/docs/10.x/pulse#capturing-entries). | ||
|
||
|
||
And that's it! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "wrklst/pulse-remote-server", | ||
"description": "A Laravel Pulse Recorder for Remote Servers", | ||
"keywords": ["laravel", "remote", "server"], | ||
"type": "library", | ||
"license": "MIT", | ||
"authors": [ | ||
{ | ||
"name": "Tobias Vielmetter-Diekmann", | ||
"email": "tobias@wrklst.art" | ||
} | ||
], | ||
"require": { | ||
"php": "^8.1", | ||
"illuminate/support": "*", | ||
"laravel/pulse": "^1.0.0@beta" | ||
}, | ||
"autoload": { | ||
"psr-4": { | ||
"WrkLst\\Pulse\\RemoteServer\\": "src/" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<?php | ||
/* | ||
Requires SSH key authentication in place for authentication to remote server. | ||
Remote Server is assumed to be running Ubuntu Linux. | ||
This is usefull to record server performance for database/cache/queue etc only servers, that do not have pulse installed, which would also require nginx and php etc. | ||
Instead the performance measurement is taken from the app server via ssh remote commands. | ||
*/ | ||
|
||
namespace WrkLst\Pulse\RemoteServer\Recorders; | ||
|
||
use Illuminate\Config\Repository; | ||
use Illuminate\Support\Str; | ||
use Laravel\Pulse\Events\SharedBeat; | ||
use Laravel\Pulse\Pulse; | ||
use RuntimeException; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
class RemoteServers | ||
{ | ||
/** | ||
* The events to listen for. | ||
* | ||
* @var class-string | ||
*/ | ||
public string $listen = SharedBeat::class; | ||
|
||
/** | ||
* Create a new recorder instance. | ||
*/ | ||
public function __construct( | ||
protected Pulse $pulse, | ||
protected Repository $config | ||
) { | ||
// | ||
} | ||
|
||
/** | ||
* Record the system stats. | ||
*/ | ||
public function record(SharedBeat $event): void | ||
{ | ||
// run every 30 seconds, comapared to than every 15 seconds (what is the default for the Pulse Servers recorder) | ||
// this is to reduce the amount of ssh commands (4 per run) | ||
if ($event->time->second % 30 !== 0) { | ||
return; | ||
} | ||
|
||
$remote_ssh = $this->config->get('pulse.recorders.' . self::class . '.server_ssh'); | ||
$server = $this->config->get('pulse.recorders.' . self::class . '.server_name'); | ||
$slug = Str::slug($server); | ||
|
||
/* | ||
This needs to be optomized to reduce the amount of ssh commands fired. | ||
E.g. running all commands with one ssh call with piping a shell script into ssh. | ||
´cat server-stats.sh | ssh 1.2.3.4´ | ||
*/ | ||
|
||
$memoryTotal = match (PHP_OS_FAMILY) { | ||
'Darwin' => intval(`$remote_ssh 'cat /proc/meminfo' | grep MemTotal | grep -Eo '[0-9]+'` / 1024), | ||
'Linux' => intval(`$remote_ssh 'cat /proc/meminfo' | grep MemTotal | grep -E -o '[0-9]+'` / 1024), | ||
default => throw new RuntimeException('The pulse:check command does not currently support ' . PHP_OS_FAMILY), | ||
}; | ||
|
||
$memoryUsed = match (PHP_OS_FAMILY) { | ||
'Darwin' => $memoryTotal - intval(`$remote_ssh 'cat /proc/meminfo' | grep MemAvailable | grep -Eo '[0-9]+'` / 1024), // MB | ||
'Linux' => $memoryTotal - intval(`$remote_ssh 'cat /proc/meminfo' | grep MemAvailable | grep -E -o '[0-9]+'` / 1024), // MB | ||
default => throw new RuntimeException('The pulse:check command does not currently support ' . PHP_OS_FAMILY), | ||
}; | ||
|
||
$cpu = match (PHP_OS_FAMILY) { | ||
'Darwin' => (int) `$remote_ssh 'top -bn1' | grep -E '^(%Cpu|CPU)' | awk '{ print $2 + $4 }'`, | ||
'Linux' => (int) `$remote_ssh 'top -bn1' | grep -E '^(%Cpu|CPU)' | awk '{ print $2 + $4 }'`, | ||
default => throw new RuntimeException('The pulse:check command does not currently support ' . PHP_OS_FAMILY), | ||
}; | ||
|
||
$this->pulse->record('cpu', $slug, $cpu, $event->time)->avg()->onlyBuckets(); | ||
$this->pulse->record('memory', $slug, $memoryUsed, $event->time)->avg()->onlyBuckets(); | ||
$this->pulse->set('system', $slug, json_encode([ | ||
'name' => $server, | ||
'cpu' => $cpu, | ||
'memory_used' => $memoryUsed, | ||
'memory_total' => $memoryTotal, | ||
'storage' => collect($this->config->get('pulse.recorders.' . self::class . '.directories')) // @phpstan-ignore argument.templateType argument.templateType | ||
->map(function (string $directory) use ($remote_ssh) { | ||
$storage = match (PHP_OS_FAMILY) { | ||
'Darwin' => (`$remote_ssh 'df $directory'`), | ||
'Linux' => (`$remote_ssh 'df $directory'`), | ||
default => throw new RuntimeException('The pulse:check command does not currently support ' . PHP_OS_FAMILY), | ||
}; | ||
|
||
/* | ||
Filesystem 1K-blocks Used Available Use% Mounted on | ||
/dev/root 507930276 31400452 476513440 7% / | ||
*/ | ||
|
||
$storage = explode("\n", $storage); // break in lines | ||
$storage = preg_replace('/\s+/', ' ', $storage[1]); // replace multi space with single space | ||
$storage = explode(" ", $storage); // break into segments based on sigle space | ||
|
||
$storageTotal = $storage[2] + $storage[3]; // used and availble | ||
$storageUsed = $storage[2]; // used | ||
|
||
return [ | ||
'directory' => $directory, | ||
'total' => intval(round($storageTotal / 1024)), // MB | ||
'used' => intval(round($storageUsed / 1024)), // MB | ||
]; | ||
}) | ||
->all(), | ||
], flags: JSON_THROW_ON_ERROR), $event->time); | ||
} | ||
} |