mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-15 13:21:11 +00:00
Merge pull request #5213 from Dokploy/fix/issue-5133-stack-monitoring-remote-node
fix: report remote-node containers correctly in stack monitoring
This commit is contained in:
+6
-1
@@ -1,5 +1,6 @@
|
||||
import { formatMb } from "@dokploy/server/monitoring/units";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { api } from "@/utils/api";
|
||||
@@ -167,6 +168,8 @@ export const ContainerFreeMonitoring = ({
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!appName) return;
|
||||
|
||||
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const wsUrl = `${protocol}//${window.location.host}/listen-docker-stats-monitoring?appName=${appName}&appType=${appType}`;
|
||||
const ws = new WebSocket(wsUrl);
|
||||
@@ -196,7 +199,9 @@ export const ContainerFreeMonitoring = ({
|
||||
};
|
||||
|
||||
ws.onclose = (e) => {
|
||||
console.log(e.reason);
|
||||
if (e.reason) {
|
||||
toast.error(e.reason);
|
||||
}
|
||||
};
|
||||
|
||||
return () => ws.close();
|
||||
|
||||
@@ -8,9 +8,57 @@ import {
|
||||
recordAdvancedStats,
|
||||
validateRequest,
|
||||
} from "@dokploy/server";
|
||||
import { quote } from "shell-quote";
|
||||
import { WebSocketServer } from "ws";
|
||||
import { canAccessDockerOverWss } from "./authorize";
|
||||
|
||||
type AppType = "application" | "stack" | "docker-compose";
|
||||
|
||||
// Swarm task names are "<service>.<slot>.<taskId>"; the manager's local
|
||||
// `docker ps` only sees containers scheduled on this host, so a task running
|
||||
// on another node looks identical to a stopped one. `docker service ps` is
|
||||
// swarm-aggregated and answers from the manager regardless of which node the
|
||||
// task landed on, so we use it here to tell the two cases apart.
|
||||
const findRemoteSwarmNode = async (
|
||||
appName: string,
|
||||
appType: AppType,
|
||||
): Promise<string | null> => {
|
||||
if (appType === "docker-compose") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// `docker service ps --format {{.Name}}` only prints "<service>.<slot>",
|
||||
// never the taskId, so we match on the task ID instead — the last segment
|
||||
// of the stack task name — rather than trying to reconstruct the full name.
|
||||
const [serviceName, taskId] =
|
||||
appType === "stack"
|
||||
? [appName.split(".").slice(0, -2).join("."), appName.split(".").pop()]
|
||||
: [appName, null];
|
||||
|
||||
if (!serviceName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout } = await execAsync(
|
||||
`docker service ps ${quote([serviceName])} --filter "desired-state=running" --no-trunc --format '{"ID":"{{.ID}}","Node":"{{.Node}}","CurrentState":"{{.CurrentState}}"}'`,
|
||||
);
|
||||
|
||||
for (const line of stdout.trim().split("\n")) {
|
||||
if (!line) continue;
|
||||
const task = JSON.parse(line);
|
||||
const isMatch = taskId ? task.ID === taskId : true;
|
||||
if (isMatch && task.CurrentState?.startsWith("Running")) {
|
||||
return task.Node;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Not a swarm service (or the swarm CLI isn't available) — fall back to "not running".
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const setupDockerStatsMonitoringSocketServer = (
|
||||
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
|
||||
) => {
|
||||
@@ -98,7 +146,13 @@ export const setupDockerStatsMonitoringSocketServer = (
|
||||
|
||||
const container = containers[0];
|
||||
if (!container || container?.State !== "running") {
|
||||
ws.close(4000, "Container not running");
|
||||
const remoteNode = await findRemoteSwarmNode(appName, appType);
|
||||
ws.close(
|
||||
4000,
|
||||
remoteNode
|
||||
? `Container running on remote node "${remoteNode}"`.slice(0, 123)
|
||||
: "Container not running",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const { stdout, stderr } = await execAsync(
|
||||
|
||||
Reference in New Issue
Block a user