fix(platform-infra): fast-path converged k3s apply

This commit is contained in:
pikastech
2026-07-20 10:38:24 +02:00
parent a593188d78
commit 4d6210fef2
3 changed files with 95 additions and 3 deletions
+6
View File
@@ -29,6 +29,12 @@
`platform-infra k3s-cluster plan|apply|status|smoke|registry-smoke
--target d601``apply` 先收敛 host proxy,再比较 server 声明摘要;
内容一致时不得重装或重启。
- `apply` 必须先用一次目标侧短查询比较:
- server 与 host proxy service 状态;
- 独立集群声明摘要;
- Node、系统 Deployment 和业务 Pod 状态。
- 全部一致时直接返回 `mode=already-converged``mutation=false`
不分发 proxy/k3s 工件,也不进入长控制通道。
- `status``smoke` 必须证明:
- 只有一个 Ready 节点;
- server active 且 agent inactive
+4
View File
@@ -153,8 +153,10 @@ export interface HostProxyGuestBundle {
prepareScript: string;
applyScript: string;
statusScript: string;
serviceName: string;
sourceFingerprint: string;
materialFingerprint: string;
desiredFingerprint: string;
valuesPrinted: false;
}
@@ -203,8 +205,10 @@ export function prepareHostProxyGuestBundle(targetId: string): HostProxyGuestBun
prepareScript: remotePrepareScript(target.source.client),
applyScript: remoteApplyScript(target, material.clientConfigJson),
statusScript: remoteStatusScript(target),
serviceName: target.source.client.serviceName,
sourceFingerprint: target.source.fingerprint,
materialFingerprint: material.fingerprint,
desiredFingerprint: fingerprint({ target, materialFingerprint: material.fingerprint }),
valuesPrinted: false,
};
}
+85 -3
View File
@@ -149,9 +149,29 @@ function registryCredentialInit(target: K3sClusterTarget, confirm: boolean): Rec
function apply(target: K3sClusterTarget): Record<string, unknown> {
const secret = readRegistrySecret(target);
const proxyBundle = prepareHostProxyGuestBundle(target.node.hostProxyTargetId);
const desired = standaloneDesired(target, secret, proxyBundle.desiredFingerprint);
const before = runNode(
target,
standaloneConvergenceScript(target, desired.desiredHash, proxyBundle.serviceName),
false,
60_000,
);
const beforeData = parseFirstJson(before.stdout);
if (before.exitCode === 0 && beforeData.ok === true) {
return {
ok: true,
mutation: false,
mode: "already-converged",
targetId: target.id,
topology: "standalone-single-node",
status: beforeData,
result: { convergence: compact(before) },
valuesPrinted: false,
};
}
const proxy = runNodeHostProxy(target, proxyBundle, 240_000);
requireSuccess(proxy, "standalone-host-proxy-apply");
const server = runNode(target, standaloneApplyScript(target, secret, proxyBundle.materialFingerprint), true, 300_000);
const server = runNode(target, standaloneApplyScript(target, secret, desired), true, 300_000);
requireSuccess(server, "standalone-k3s-apply");
const converged = waitForStatus(target, 180_000);
requireSuccess(converged, "standalone-k3s-status");
@@ -163,7 +183,12 @@ function apply(target: K3sClusterTarget): Record<string, unknown> {
targetId: target.id,
topology: "standalone-single-node",
status: data,
result: { hostProxy: compact(proxy), server: compact(server), status: compact(converged) },
result: {
convergenceBefore: compact(before),
hostProxy: compact(proxy),
server: compact(server),
status: compact(converged),
},
valuesPrinted: false,
};
}
@@ -231,7 +256,11 @@ function registrySmoke(target: K3sClusterTarget, confirm: boolean): Record<strin
};
}
function standaloneApplyScript(target: K3sClusterTarget, secret: RegistrySecret, proxyFingerprint: string): string {
function standaloneDesired(
target: K3sClusterTarget,
secret: RegistrySecret,
proxyFingerprint: string,
): { exec: string; desiredHash: string } {
const disableArgs = target.k3s.disableComponents.flatMap((component) => ["--disable", component]);
const labels = Object.entries(target.node.labels).flatMap(([key, value]) => ["--node-label", `${key}=${value}`]);
const exec = [
@@ -251,6 +280,15 @@ function standaloneApplyScript(target: K3sClusterTarget, secret: RegistrySecret,
registryPassword: secret.password,
proxyFingerprint,
}));
return { exec, desiredHash };
}
function standaloneApplyScript(
target: K3sClusterTarget,
secret: RegistrySecret,
desired: { exec: string; desiredHash: string },
): string {
const { exec, desiredHash } = desired;
return `set -eu
desired_hash=${shQuote(desiredHash)}
desired_hash_file=/etc/unidesk/k3s-standalone-desired.sha256
@@ -290,6 +328,50 @@ systemctl start --no-block k3s
`;
}
function standaloneConvergenceScript(
target: K3sClusterTarget,
desiredHash: string,
proxyServiceName: string,
): string {
return `set -eu
active=false; systemctl is-active --quiet k3s && active=true
agent_active=false; systemctl is-active --quiet k3s-agent && agent_active=true
proxy_active=false; systemctl is-active --quiet ${shellToken(proxyServiceName)} && proxy_active=true
desired_current=false; [ "$(cat /etc/unidesk/k3s-standalone-desired.sha256 2>/dev/null || true)" = ${shQuote(desiredHash)} ] && desired_current=true
ready="$(k3s kubectl get node ${shQuote(target.node.nodeName)} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || true)"
node_count="$(k3s kubectl get nodes --no-headers 2>/dev/null | wc -l | tr -d ' ')"
business_pods="$(k3s kubectl get pods -A --no-headers 2>/dev/null | awk '$1 != "kube-system" {count++} END {print count+0}')"
deployments="$(k3s kubectl get deployments -A --no-headers 2>/dev/null | awk '$1 == "kube-system" {count++} END {print count+0}')"
ready_deployments="$(k3s kubectl get deployments -A --no-headers 2>/dev/null | awk '$1 == "kube-system" {split($3, ready, "/"); if (ready[1] == ready[2] && ready[2] != "0" && $5 == ready[2]) count++} END {print count+0}')"
python3 - "$active" "$agent_active" "$proxy_active" "$desired_current" "$ready" "$node_count" "$business_pods" "$deployments" "$ready_deployments" <<'PY'
import json, sys
active, agent, proxy, desired, ready, nodes, business, deployments, ready_deployments = sys.argv[1:]
ok = (
active == "true"
and agent == "false"
and proxy == "true"
and desired == "true"
and ready == "True"
and nodes == "1"
and business == "0"
and deployments == ready_deployments
)
print(json.dumps({
"ok": ok,
"serverActive": active == "true",
"oldAgentActive": agent == "true",
"hostProxyActive": proxy == "true",
"desiredHashCurrent": desired == "true",
"nodeReady": ready == "True",
"nodeCount": int(nodes or 0),
"businessPodCount": int(business or 0),
"systemDeploymentCount": int(deployments or 0),
"systemReadyDeploymentCount": int(ready_deployments or 0),
}, separators=(",", ":")))
PY
`;
}
function standaloneStatusScript(target: K3sClusterTarget): string {
return `set -eu
active=false; systemctl is-active --quiet k3s && active=true