mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-13 01:14:21 -04:00
- Servers with caddy-docker-proxy older than 2.9 show a warning on the Proxy page and the traffic analytics settings, with the fix. - Caddy 2.9+ images get the basic_auth label; older ones keep the deprecated basicauth, which Caddy 2.7 still needs. - Caddy mounts StartSentinel::trafficLogDirectory() as /traffic, so Caddy and Sentinel use the same access log, also in development. - The development data volume is configurable (DEV_COOLIFY_DATA_VOLUME, same default as before) and shared by Traefik, Caddy, and Sentinel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
145 lines
6.1 KiB
PHP
145 lines
6.1 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Server;
|
|
|
|
use App\Events\SentinelRestarted;
|
|
use App\Models\Server;
|
|
use Lorisleiva\Actions\Concerns\AsAction;
|
|
|
|
class StartSentinel
|
|
{
|
|
use AsAction;
|
|
|
|
public static function trafficLogDirectory(Server $server): string
|
|
{
|
|
return isDev()
|
|
? devCoolifyDataPath().'/proxy'
|
|
: rtrim($server->proxyPath(), '/');
|
|
}
|
|
|
|
/**
|
|
* Sentinel reads the proxy access log only when analytics is on and a Traefik or Caddy proxy writes it.
|
|
* After a switch to a proxy without analytics support, Sentinel must not keep the old log mount.
|
|
*/
|
|
public static function collectsTraffic(Server $server): bool
|
|
{
|
|
return $server->isTrafficAnalyticsEnabled() && $server->hasTrafficAnalyticsProxy();
|
|
}
|
|
|
|
public static function sentinelTrafficEnvironment(Server $server): array
|
|
{
|
|
if (! self::collectsTraffic($server)) {
|
|
return [];
|
|
}
|
|
|
|
$logPath = self::trafficLogDirectory($server).'/access.log';
|
|
$settings = $server->settings;
|
|
$env = [
|
|
'TRAFFIC_ENABLED' => 'true',
|
|
'TRAFFIC_PROXY_TYPE' => 'auto',
|
|
'TRAFFIC_ACCESS_LOG_PATH' => $logPath,
|
|
'TRAFFIC_TOPN' => (string) ($settings->traffic_topn ?: 50),
|
|
'TRAFFIC_SAMPLE_THRESHOLD' => (string) ($settings->traffic_sample_threshold ?? 0),
|
|
'TRAFFIC_RETENTION_1H_DAYS' => (string) ($settings->traffic_retention_1h_days ?: 30),
|
|
'TRAFFIC_RETENTION_1D_DAYS' => (string) ($settings->traffic_retention_1d_days ?: 395),
|
|
'GEOIP_ENABLED' => $settings->is_geoip_enabled ? 'true' : 'false',
|
|
'GEOIP_REFRESH_DAYS' => (string) ($settings->geoip_refresh_days ?: 30),
|
|
];
|
|
$license = data_get($settings, 'geoip_maxmind_license_key');
|
|
if ($settings->is_geoip_enabled && filled($license)) {
|
|
$env['GEOIP_MAXMIND_LICENSE_KEY'] = $license;
|
|
}
|
|
|
|
return $env;
|
|
}
|
|
|
|
/**
|
|
* Sentinel opens the access log once at startup and never retries, so the file must exist
|
|
* before the container starts. `touch` keeps an existing log intact.
|
|
*
|
|
* @return array<int, string>
|
|
*/
|
|
public static function trafficLogPreparationCommands(Server $server): array
|
|
{
|
|
if (! $server->isTrafficAnalyticsEnabled() || ! $server->supportsTrafficAnalytics()) {
|
|
return [];
|
|
}
|
|
|
|
$directory = self::trafficLogDirectory($server);
|
|
|
|
return [
|
|
'mkdir -p '.escapeshellarg($directory),
|
|
'touch '.escapeshellarg($directory.'/access.log'),
|
|
];
|
|
}
|
|
|
|
public function handle(Server $server, bool $restart = false, ?string $latestVersion = null, ?string $customImage = null)
|
|
{
|
|
if ($server->isSwarm() || $server->isBuildServer()) {
|
|
return;
|
|
}
|
|
if ($restart) {
|
|
StopSentinel::run($server);
|
|
}
|
|
$version = $latestVersion ?? get_latest_sentinel_version();
|
|
$metricsHistory = data_get($server, 'settings.sentinel_metrics_history_days');
|
|
$refreshRate = data_get($server, 'settings.sentinel_metrics_refresh_rate_seconds');
|
|
$pushInterval = data_get($server, 'settings.sentinel_push_interval_seconds');
|
|
$token = $server->settings->ensureValidSentinelToken();
|
|
$endpoint = $server->settings->ensureSentinelUrl();
|
|
$debug = data_get($server, 'settings.is_sentinel_debug_enabled');
|
|
$mountDir = '/data/coolify/sentinel';
|
|
$image = coolifyRegistryUrl().'/coollabsio/sentinel:'.$version;
|
|
$environments = [
|
|
'TOKEN' => $token,
|
|
'DEBUG' => $debug ? 'true' : 'false',
|
|
'PUSH_ENDPOINT' => $endpoint,
|
|
'PUSH_INTERVAL_SECONDS' => $pushInterval,
|
|
'COLLECTOR_ENABLED' => $server->isMetricsEnabled() ? 'true' : 'false',
|
|
'COLLECTOR_REFRESH_RATE_SECONDS' => $refreshRate,
|
|
'COLLECTOR_RETENTION_PERIOD_DAYS' => $metricsHistory,
|
|
];
|
|
$environments = array_merge($environments, self::sentinelTrafficEnvironment($server));
|
|
$labels = [
|
|
'coolify.managed' => 'true',
|
|
];
|
|
if (isDev()) {
|
|
// data_set($environments, 'DEBUG', 'true');
|
|
if ($customImage && ! empty($customImage)) {
|
|
$image = $customImage;
|
|
}
|
|
$mountDir = devCoolifyDataPath().'/sentinel';
|
|
}
|
|
$dockerEnvironments = implode(' ', array_map(fn ($key, $value) => '-e '.escapeshellarg("$key=$value"), array_keys($environments), $environments));
|
|
$dockerLabels = implode(' ', array_map(fn ($key, $value) => "$key=$value", array_keys($labels), $labels));
|
|
$trafficLogDirectory = self::trafficLogDirectory($server);
|
|
$trafficMount = self::collectsTraffic($server)
|
|
? '-v '.escapeshellarg("{$trafficLogDirectory}:{$trafficLogDirectory}:ro").' '
|
|
: '';
|
|
$network = $server->isLocalhost() ? ' --network coolify' : '';
|
|
$dockerCommand = "docker run -d$network $dockerEnvironments --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v $mountDir:/app/db {$trafficMount}--pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-start-period 120s --health-interval 10s --health-retries 3 --add-host=host.docker.internal:host-gateway --label $dockerLabels $image";
|
|
|
|
$server->sentinelHeartbeat(isReset: true);
|
|
$server->forceFill(['sentinel_waiting_since' => now()])->save();
|
|
|
|
instant_remote_process([
|
|
'docker rm -f coolify-sentinel || true',
|
|
"mkdir -p $mountDir",
|
|
...self::trafficLogPreparationCommands($server),
|
|
$dockerCommand,
|
|
"chown -R 9999:root $mountDir",
|
|
"chmod -R 700 $mountDir",
|
|
], $server);
|
|
|
|
$server->settings->is_sentinel_enabled = true;
|
|
$server->settings->save();
|
|
$server->refresh();
|
|
if ($server->sentinel_waiting_since !== null) {
|
|
$server->forceFill(['sentinel_waiting_since' => now()])->save();
|
|
}
|
|
|
|
// Dispatch event to notify UI components
|
|
SentinelRestarted::dispatch($server, $version);
|
|
}
|
|
}
|