fix(proxmox): Task-Status unter Last weiter abfragen, nesting an

Im Feldtest wurde ein LXC fertig angelegt, während ProxDeploy nach drei gescheiterten Statusabfragen aufgab (Verbindungsaufbau > 10 s bei 76 % I/O-Last). Statusabfragen werden jetzt bis zum Task-Zeitlimit wiederholt, der Verbindungsaufbau darf 30 s dauern. nesting ist für Container standardmäßig an wie in der Proxmox-Oberfläche – Proxmox warnt sonst bei systemd 249/252.

Refs #020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 15:46:15 +02:00
co-authored by Claude Opus 5.5
parent db57818eb3
commit bc13b94c7e
9 changed files with 63 additions and 14 deletions
+2 -2
View File
@@ -324,7 +324,7 @@ export function Assistent({
storage: vor.storage,
swapMb: "512",
unprivilegiert: true,
nesting: false,
nesting: true,
bridge: vor.bridge,
vlan: "",
modus: "dhcp",
@@ -817,7 +817,7 @@ export function Assistent({
hilfe={
werte.profile.includes("docker")
? "Das Profil docker schaltet es ohnehin ein."
: "Für Docker oder systemd-Dienste mit eigenem Namensraum."
: "Standard an, wie in Proxmox: neuere systemd-Versionen brauchen es im unprivilegierten Container."
}
checked={werte.nesting || werte.profile.includes("docker")}
disabled={werte.profile.includes("docker")}
+14 -5
View File
@@ -1,4 +1,4 @@
import { istTrockenUpid, pfad, type ProxmoxClient } from "./client";
import { istTrockenUpid, pfad, stoerung, type ProxmoxClient } from "./client";
import { ProxmoxFehler } from "./fehler";
/**
@@ -53,10 +53,19 @@ export async function aufgabeAbwarten(
};
for (;;) {
const status = await client.get<TaskStatus>(pfad`/nodes/${node}/tasks/${upid}/status`);
if (optionen.beiLog) {
const neu = await logLesen();
if (neu.length > 0) optionen.beiLog(neu);
// Nachsehen ist harmlos: scheitert eine Abfrage vorübergehend (ausgelasteter Node), wird bis
// zum Zeitlimit des Tasks weiter gefragt – der Task läuft auf dem Node ja weiter.
let status: TaskStatus;
try {
status = await client.get<TaskStatus>(pfad`/nodes/${node}/tasks/${upid}/status`);
if (optionen.beiLog) {
const neu = await logLesen();
if (neu.length > 0) optionen.beiLog(neu);
}
} catch (fehler) {
if (stoerung(fehler) === null || Date.now() - beginn > zeitlimit) throw fehler;
await warten(Math.max(intervall, 5000));
continue;
}
if (status.status === "stopped") {
if (status.exitstatus === "OK" || status.exitstatus?.startsWith("WARNINGS")) return;
+4 -1
View File
@@ -54,6 +54,9 @@ const VERSUCHE = 3;
export function stoerung(fehler: unknown): "nicht_angekommen" | "unklar" | null {
if (fehler instanceof ProxmoxFehler) {
const status = fehler.details.status;
// Schon übersetzte Netzwerkfehler (ohne HTTP-Status); TLS und Fingerprint sind nie vorübergehend.
if (status === undefined && (fehler.art === "zeitueberschreitung" || fehler.art === "verbindung"))
return "unklar";
if (status === 595 || status === 596) return "unklar";
if (
(status === 500 || status === 502 || status === 503) &&
@@ -122,7 +125,7 @@ export class ProxmoxClient {
);
}
this.basis = verbindung.url.replace(/\/+$/, "") + "/api2/json";
this.agent = new Agent({ connect: tlsConnector(verbindung), keepAliveTimeout: 2_000 });
this.agent = new Agent({ connect: tlsConnector(verbindung), keepAliveTimeout: 4_000 });
this.kopf = `PVEAPIToken=${verbindung.tokenId}=${verbindung.tokenGeheimnis}`;
}
+6 -1
View File
@@ -104,7 +104,12 @@ export function tlsConnector(einstellung: TlsEinstellung): buildConnector.connec
}
// Ohne Sitzungs-Cache: eine wiederaufgenommene TLS-Sitzung schickt kein Zertifikat mit,
// und der Pin ließe sich dann nicht prüfen.
const basis = buildConnector({ rejectUnauthorized: einstellung.tlsPruefen, maxCachedSessions: 0 });
// 30 s statt der 10 s von undici: ein ausgelasteter Node nimmt neue Verbindungen nur zögerlich an.
const basis = buildConnector({
rejectUnauthorized: einstellung.tlsPruefen,
maxCachedSessions: 0,
timeout: 30_000,
});
if (!pin) return basis;
return (optionen, rueckruf) => {
+4 -2
View File
@@ -98,8 +98,10 @@ export const AusrollAnfrage = z
swapMb: z.coerce.number().int().min(0).max(1_048_576).default(512),
unprivilegiert: z.boolean().default(true),
features: z
.object({ nesting: z.boolean().default(false), keyctl: z.boolean().default(false) })
.default({ nesting: false, keyctl: false }),
// nesting an wie in der Proxmox-Oberfläche: neuere systemd-Versionen brauchen es im
// unprivilegierten Container („Systemd 252 detected. You may need to enable nesting.“).
.object({ nesting: z.boolean().default(true), keyctl: z.boolean().default(false) })
.default({ nesting: true, keyctl: false }),
netz: Netz,
zugang: Zugang,
profile: z.array(z.string()).default(["base"]),