feat(v3s): add D518 code queue standby pod
This commit is contained in:
@@ -17,18 +17,18 @@ services:
|
||||
HOST: "0.0.0.0"
|
||||
PORT: "4266"
|
||||
LOG_FILE: "/var/log/unidesk/v3sctl-adapter.jsonl"
|
||||
V3SCTL_CLUSTER_ID: "${V3SCTL_CLUSTER_ID:-D601}"
|
||||
V3SCTL_CLUSTER_ID: "${V3SCTL_CLUSTER_ID:-unidesk-v8s}"
|
||||
V3SCTL_NODE_ID: "${V3SCTL_NODE_ID:-D601}"
|
||||
V3SCTL_KUBECTL_ENABLED: "${V3SCTL_KUBECTL_ENABLED:-false}"
|
||||
V3SCTL_KUBE_API_PROXY_ENABLED: "${V3SCTL_KUBE_API_PROXY_ENABLED:-true}"
|
||||
V3SCTL_KUBECONFIG_PATH: "/var/lib/unidesk/v3s/kubeconfig"
|
||||
V3SCTL_KUBECONFIG_PATH: "/var/lib/unidesk/v8s/kubeconfig"
|
||||
V3SCTL_KUBE_API_CONNECT_HOST: "${V3SCTL_KUBE_API_CONNECT_HOST:-host.docker.internal}"
|
||||
V3SCTL_MANIFEST_PATHS: "${V3SCTL_MANIFEST_PATHS:-v3s/code-queue.v3s.json}"
|
||||
V3SCTL_SERVICES_JSON: "${V3SCTL_SERVICES_JSON:-[]}"
|
||||
UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-512MiB}"
|
||||
volumes:
|
||||
- ${V3SCTL_ADAPTER_LOG_DIR:-../../../../.state/v3sctl-adapter/logs}:/var/log/unidesk
|
||||
- ${V3SCTL_KUBECONFIG_HOST_PATH:-../../../../.state/v3s/kubeconfig}:/var/lib/unidesk/v3s/kubeconfig:ro
|
||||
- ${V3SCTL_KUBECONFIG_HOST_PATH:-../../../../.state/v8s/kubeconfig}:/var/lib/unidesk/v8s/kubeconfig:ro
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
networks:
|
||||
|
||||
@@ -8,6 +8,7 @@ type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string
|
||||
type JsonRecord = Record<string, JsonValue>;
|
||||
|
||||
type InstanceRole = "primary" | "standby" | "worker";
|
||||
type EndpointHealthMode = "service-proxy" | "pod-ready";
|
||||
|
||||
interface ManagedEndpoint {
|
||||
id: string;
|
||||
@@ -15,6 +16,7 @@ interface ManagedEndpoint {
|
||||
role: InstanceRole;
|
||||
baseUrl: string;
|
||||
healthPath: string;
|
||||
healthMode: EndpointHealthMode;
|
||||
}
|
||||
|
||||
interface ManagedService {
|
||||
@@ -143,6 +145,11 @@ function normalizeRole(value: string): InstanceRole {
|
||||
return "worker";
|
||||
}
|
||||
|
||||
function normalizeHealthMode(value: string): EndpointHealthMode {
|
||||
if (value === "service-proxy" || value === "pod-ready") return value;
|
||||
return "service-proxy";
|
||||
}
|
||||
|
||||
function parseEndpoint(value: unknown, index: number, ownerPath = "endpoint"): ManagedEndpoint {
|
||||
const path = `${ownerPath}[${index}]`;
|
||||
const item = asRecord(value, path);
|
||||
@@ -154,6 +161,7 @@ function parseEndpoint(value: unknown, index: number, ownerPath = "endpoint"): M
|
||||
role: normalizeRole(optionalStringField(item, "role", id === "D601" ? "primary" : "standby")),
|
||||
baseUrl: stringField(item, "baseUrl", path).replace(/\/+$/u, ""),
|
||||
healthPath: optionalStringField(item, "healthPath", "/health"),
|
||||
healthMode: normalizeHealthMode(optionalStringField(item, "healthMode", "service-proxy")),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -244,12 +252,12 @@ function readConfig(): RuntimeConfig {
|
||||
port: envNumber("PORT", 4266),
|
||||
logFile: envString("LOG_FILE", "/var/log/unidesk/v3sctl-adapter.jsonl"),
|
||||
manifestPaths: paths,
|
||||
clusterId: envString("V3SCTL_CLUSTER_ID", "D601"),
|
||||
clusterId: envString("V3SCTL_CLUSTER_ID", "unidesk-v8s"),
|
||||
nodeId: envString("V3SCTL_NODE_ID", "D601"),
|
||||
kubectlEnabled: envBool("V3SCTL_KUBECTL_ENABLED", false),
|
||||
kubectlContext: envString("V3SCTL_KUBECTL_CONTEXT", ""),
|
||||
kubeApiProxyEnabled: envBool("V3SCTL_KUBE_API_PROXY_ENABLED", true),
|
||||
kubeconfigPath: envString("V3SCTL_KUBECONFIG_PATH", "/var/lib/unidesk/v3s/kubeconfig"),
|
||||
kubeconfigPath: envString("V3SCTL_KUBECONFIG_PATH", "/var/lib/unidesk/v8s/kubeconfig"),
|
||||
kubeApiConnectHost: envString("V3SCTL_KUBE_API_CONNECT_HOST", "host.docker.internal"),
|
||||
requestTimeoutMs: Math.max(1000, Math.min(120_000, envNumber("V3SCTL_REQUEST_TIMEOUT_MS", 30_000))),
|
||||
healthTimeoutMs: Math.max(500, Math.min(30_000, envNumber("V3SCTL_HEALTH_TIMEOUT_MS", 2500))),
|
||||
@@ -385,6 +393,23 @@ function serviceProxyApiPath(service: ManagedService, targetPath: string): strin
|
||||
return `/api/v1/namespaces/${encodeURIComponent(service.namespace)}/services/${encodeURIComponent(`${serviceName}:${servicePort}`)}/proxy${safeTargetPath}`;
|
||||
}
|
||||
|
||||
function endpointProxyApiPath(service: ManagedService, endpoint: ManagedEndpoint, targetPath: string): string {
|
||||
const { namespace, serviceRef } = kubernetesEndpointServiceRef(service, endpoint);
|
||||
const safeTargetPath = targetPath.startsWith("/") ? targetPath : `/${targetPath}`;
|
||||
return `/api/v1/namespaces/${encodeURIComponent(namespace)}/services/${encodeURIComponent(serviceRef)}/proxy${safeTargetPath}`;
|
||||
}
|
||||
|
||||
function kubernetesEndpointServiceRef(service: ManagedService, endpoint: ManagedEndpoint): { namespace: string; serviceRef: string } {
|
||||
const base = new URL(endpoint.baseUrl);
|
||||
if (base.protocol !== "kubernetes:") throw new Error(`endpoint ${endpoint.id} must use kubernetes:// baseUrl`);
|
||||
const namespace = base.hostname || service.namespace;
|
||||
const parts = base.pathname.split("/").filter(Boolean);
|
||||
if (parts.length !== 2 || parts[0] !== "services" || parts[1].length === 0) {
|
||||
throw new Error(`endpoint ${endpoint.id} baseUrl must be kubernetes://<namespace>/services/<service>:<port>`);
|
||||
}
|
||||
return { namespace, serviceRef: parts[1] };
|
||||
}
|
||||
|
||||
function kubeProxyCurlArgs(client: KubeApiClient, method: string, url: URL, headers: Headers, hasBody: boolean, timeoutMs: number): string[] {
|
||||
const args = [
|
||||
"-sS",
|
||||
@@ -431,11 +456,32 @@ async function kubeApiServiceProxyResponse(
|
||||
targetPath: string,
|
||||
query: string,
|
||||
timeoutMs: number,
|
||||
): Promise<Response> {
|
||||
return kubeApiProxyResponse(service, req, serviceProxyApiPath(service, targetPath), query, timeoutMs);
|
||||
}
|
||||
|
||||
async function kubeApiEndpointProxyResponse(
|
||||
service: ManagedService,
|
||||
endpoint: ManagedEndpoint,
|
||||
req: Request,
|
||||
targetPath: string,
|
||||
query: string,
|
||||
timeoutMs: number,
|
||||
): Promise<Response> {
|
||||
return kubeApiProxyResponse(service, req, endpointProxyApiPath(service, endpoint, targetPath), query, timeoutMs);
|
||||
}
|
||||
|
||||
async function kubeApiProxyResponse(
|
||||
service: ManagedService,
|
||||
req: Request,
|
||||
apiPath: string,
|
||||
query: string,
|
||||
timeoutMs: number,
|
||||
): Promise<Response> {
|
||||
if (kubeClient === null) {
|
||||
return jsonResponse({ ok: false, error: "kubernetes api proxy is not configured", serviceId: service.id, kubeconfigPath: config.kubeconfigPath, noFallback: true }, 502);
|
||||
}
|
||||
const upstreamUrl = new URL(serviceProxyApiPath(service, targetPath), kubeClient.serverUrl);
|
||||
const upstreamUrl = new URL(apiPath, kubeClient.serverUrl);
|
||||
upstreamUrl.search = query;
|
||||
const headers = forwardHeaders(req);
|
||||
const bodyText = req.method === "GET" || req.method === "HEAD" ? "" : await req.text();
|
||||
@@ -455,7 +501,7 @@ async function kubeApiServiceProxyResponse(
|
||||
proc.exited,
|
||||
]);
|
||||
if (exitCode !== 0) {
|
||||
log("error", "kube_api_proxy_failed", { serviceId: service.id, targetPath, exitCode, stderr: stderr.slice(0, 2000), noFallback: true });
|
||||
log("error", "kube_api_proxy_failed", { serviceId: service.id, apiPath, exitCode, stderr: stderr.slice(0, 2000), noFallback: true });
|
||||
return jsonResponse({ ok: false, error: "kubernetes api service proxy failed", serviceId: service.id, detail: stderr.slice(0, 4000), noFallback: true }, 502);
|
||||
}
|
||||
const parsed = parseCurlHeaderBody(Buffer.from(stdout));
|
||||
@@ -522,14 +568,28 @@ async function probeEndpoint(endpoint: ManagedEndpoint): Promise<JsonRecord> {
|
||||
|
||||
async function probeKubernetesServiceActive(service: ManagedService): Promise<JsonRecord> {
|
||||
const endpoint = activeEndpoint(service);
|
||||
return probeKubernetesEndpoint(service, endpoint, true);
|
||||
}
|
||||
|
||||
async function probeKubernetesEndpoint(service: ManagedService, endpoint: ManagedEndpoint, active = false): Promise<JsonRecord> {
|
||||
if (!active && endpoint.healthMode === "pod-ready") return await probeKubernetesPodReady(service, endpoint);
|
||||
const checkedAt = new Date().toISOString();
|
||||
const response = await kubeApiServiceProxyResponse(
|
||||
service,
|
||||
new Request("http://v3sctl-adapter.local/health", { method: "GET", headers: { accept: "application/json" } }),
|
||||
endpoint.healthPath,
|
||||
"",
|
||||
config.healthTimeoutMs,
|
||||
);
|
||||
const response = active
|
||||
? await kubeApiServiceProxyResponse(
|
||||
service,
|
||||
new Request("http://v3sctl-adapter.local/health", { method: "GET", headers: { accept: "application/json" } }),
|
||||
endpoint.healthPath,
|
||||
"",
|
||||
config.healthTimeoutMs,
|
||||
)
|
||||
: await kubeApiEndpointProxyResponse(
|
||||
service,
|
||||
endpoint,
|
||||
new Request("http://v3sctl-adapter.local/health", { method: "GET", headers: { accept: "application/json" } }),
|
||||
endpoint.healthPath,
|
||||
"",
|
||||
config.healthTimeoutMs,
|
||||
);
|
||||
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
||||
const bodyText = await response.text();
|
||||
let body: JsonValue = bodyText.slice(0, 2000);
|
||||
@@ -544,6 +604,7 @@ async function probeKubernetesServiceActive(service: ManagedService): Promise<Js
|
||||
role: endpoint.role,
|
||||
baseUrl: endpoint.baseUrl,
|
||||
healthPath: endpoint.healthPath,
|
||||
healthMode: endpoint.healthMode,
|
||||
proxyMode: "kubernetes-api-service-proxy",
|
||||
route: service.route,
|
||||
healthy: response.ok,
|
||||
@@ -555,9 +616,79 @@ async function probeKubernetesServiceActive(service: ManagedService): Promise<Js
|
||||
};
|
||||
}
|
||||
|
||||
function jsonAtPath(value: unknown, path: string): unknown {
|
||||
return path.split(".").reduce((current, key) => {
|
||||
if (typeof current !== "object" || current === null) return undefined;
|
||||
return (current as Record<string, unknown>)[key];
|
||||
}, value);
|
||||
}
|
||||
|
||||
function podReady(item: unknown): boolean {
|
||||
const conditions = jsonAtPath(item, "status.conditions");
|
||||
return Array.isArray(conditions) && conditions.some((condition) => {
|
||||
const record = typeof condition === "object" && condition !== null ? condition as Record<string, unknown> : {};
|
||||
return record.type === "Ready" && record.status === "True";
|
||||
});
|
||||
}
|
||||
|
||||
function podSummary(item: unknown): JsonRecord {
|
||||
const metadata = typeof jsonAtPath(item, "metadata") === "object" && jsonAtPath(item, "metadata") !== null ? jsonAtPath(item, "metadata") as Record<string, unknown> : {};
|
||||
return {
|
||||
name: typeof metadata.name === "string" ? metadata.name : "",
|
||||
nodeName: typeof jsonAtPath(item, "spec.nodeName") === "string" ? jsonAtPath(item, "spec.nodeName") as string : "",
|
||||
phase: typeof jsonAtPath(item, "status.phase") === "string" ? jsonAtPath(item, "status.phase") as string : "",
|
||||
podIP: typeof jsonAtPath(item, "status.podIP") === "string" ? jsonAtPath(item, "status.podIP") as string : "",
|
||||
ready: podReady(item),
|
||||
};
|
||||
}
|
||||
|
||||
async function probeKubernetesPodReady(service: ManagedService, endpoint: ManagedEndpoint): Promise<JsonRecord> {
|
||||
const checkedAt = new Date().toISOString();
|
||||
const { namespace } = kubernetesEndpointServiceRef(service, endpoint);
|
||||
const labelSelector = new URLSearchParams({
|
||||
labelSelector: `app.kubernetes.io/name=${service.id},unidesk.ai/instance-id=${endpoint.id}`,
|
||||
}).toString();
|
||||
const response = await kubeApiProxyResponse(
|
||||
service,
|
||||
new Request("http://v3sctl-adapter.local/api/pods", { method: "GET", headers: { accept: "application/json" } }),
|
||||
`/api/v1/namespaces/${encodeURIComponent(namespace)}/pods`,
|
||||
`?${labelSelector}`,
|
||||
config.healthTimeoutMs,
|
||||
);
|
||||
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
||||
const bodyText = await response.text();
|
||||
let body: JsonValue = bodyText.slice(0, 2000);
|
||||
let pods: JsonRecord[] = [];
|
||||
try {
|
||||
const parsed = JSON.parse(bodyText) as JsonRecord;
|
||||
const items = Array.isArray(parsed.items) ? parsed.items : [];
|
||||
pods = items.map(podSummary);
|
||||
body = { itemCount: items.length, pods };
|
||||
} catch {
|
||||
// Keep the raw text preview below.
|
||||
}
|
||||
const healthy = response.ok && pods.some((pod) => pod.ready === true);
|
||||
return {
|
||||
id: endpoint.id,
|
||||
nodeId: endpoint.nodeId,
|
||||
role: endpoint.role,
|
||||
baseUrl: endpoint.baseUrl,
|
||||
healthPath: endpoint.healthPath,
|
||||
healthMode: endpoint.healthMode,
|
||||
proxyMode: "kubernetes-api-pod-readiness",
|
||||
route: service.route,
|
||||
healthy,
|
||||
status: healthy ? "healthy" : "unhealthy",
|
||||
upstreamStatus: response.status,
|
||||
contentType,
|
||||
checkedAt,
|
||||
body,
|
||||
};
|
||||
}
|
||||
|
||||
async function serviceStatus(service: ManagedService): Promise<JsonRecord> {
|
||||
const instances = isKubernetesServiceRoute(service)
|
||||
? [await probeKubernetesServiceActive(service)]
|
||||
? await Promise.all(service.endpoints.map((endpoint) => endpoint.id === service.activeInstanceId ? probeKubernetesServiceActive(service) : probeKubernetesEndpoint(service, endpoint)))
|
||||
: [{
|
||||
id: service.activeInstanceId,
|
||||
nodeId: activeEndpoint(service).nodeId,
|
||||
@@ -576,7 +707,7 @@ async function serviceStatus(service: ManagedService): Promise<JsonRecord> {
|
||||
const activeHealthy = active?.healthy === true;
|
||||
const allInstancesHealthy = instances.every((item) => item.healthy === true);
|
||||
const expectedNodeIds = service.expectedNodeIds;
|
||||
const presentNodeIds = Array.from(new Set(instances.map((item) => String(item.nodeId))));
|
||||
const presentNodeIds = Array.from(new Set(instances.filter((item) => item.healthy === true).map((item) => String(item.nodeId))));
|
||||
const missingNodeIds = expectedNodeIds.filter((nodeId) => !presentNodeIds.includes(nodeId));
|
||||
const topologyComplete = missingNodeIds.length === 0;
|
||||
const requiredTopologyHealthy = !service.requireAllInstancesHealthy || (topologyComplete && allInstancesHealthy);
|
||||
|
||||
@@ -4,7 +4,43 @@ metadata:
|
||||
name: unidesk
|
||||
labels:
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/v3s-cluster: unidesk-v3s
|
||||
unidesk.ai/v3s-cluster: unidesk-v8s
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: d601-provider-egress-proxy
|
||||
namespace: unidesk
|
||||
labels:
|
||||
app.kubernetes.io/name: provider-egress-proxy
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/provider-id: D601
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 18789
|
||||
targetPort: 18789
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: d601-provider-egress-proxy
|
||||
namespace: unidesk
|
||||
labels:
|
||||
kubernetes.io/service-name: d601-provider-egress-proxy
|
||||
app.kubernetes.io/name: provider-egress-proxy
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/provider-id: D601
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 18789
|
||||
endpoints:
|
||||
- addresses:
|
||||
- "172.25.0.3"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -31,6 +67,8 @@ spec:
|
||||
unidesk.ai/instance-id: D601
|
||||
unidesk.ai/node-id: D601
|
||||
spec:
|
||||
nodeSelector:
|
||||
unidesk.ai/node-id: D601
|
||||
terminationGracePeriodSeconds: 30
|
||||
containers:
|
||||
- name: code-queue
|
||||
@@ -99,25 +137,25 @@ spec:
|
||||
- name: CODE_QUEUE_EGRESS_PROXY_ENABLED
|
||||
value: "true"
|
||||
- name: CODE_QUEUE_EGRESS_PROXY_URL
|
||||
value: "http://host.docker.internal:18789"
|
||||
value: "http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789"
|
||||
- name: CODE_QUEUE_EGRESS_PROXY_NO_PROXY
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,unidesk-provider-gateway-D601,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,d601-provider-egress-proxy,d601-provider-egress-proxy.unidesk,d601-provider-egress-proxy.unidesk.svc,d601-provider-egress-proxy.unidesk.svc.cluster.local,172.25.0.3,unidesk-provider-gateway-D601,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
- name: HTTP_PROXY
|
||||
value: "http://host.docker.internal:18789"
|
||||
value: "http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789"
|
||||
- name: HTTPS_PROXY
|
||||
value: "http://host.docker.internal:18789"
|
||||
value: "http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789"
|
||||
- name: ALL_PROXY
|
||||
value: "http://host.docker.internal:18789"
|
||||
value: "http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789"
|
||||
- name: http_proxy
|
||||
value: "http://host.docker.internal:18789"
|
||||
value: "http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789"
|
||||
- name: https_proxy
|
||||
value: "http://host.docker.internal:18789"
|
||||
value: "http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789"
|
||||
- name: all_proxy
|
||||
value: "http://host.docker.internal:18789"
|
||||
value: "http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789"
|
||||
- name: NO_PROXY
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,unidesk-provider-gateway-D601,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,d601-provider-egress-proxy,d601-provider-egress-proxy.unidesk,d601-provider-egress-proxy.unidesk.svc,d601-provider-egress-proxy.unidesk.svc.cluster.local,172.25.0.3,unidesk-provider-gateway-D601,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
- name: no_proxy
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,unidesk-provider-gateway-D601,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,d601-provider-egress-proxy,d601-provider-egress-proxy.unidesk,d601-provider-egress-proxy.unidesk.svc,d601-provider-egress-proxy.unidesk.svc.cluster.local,172.25.0.3,unidesk-provider-gateway-D601,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
- name: OA_EVENT_FLOW_BASE_URL
|
||||
value: "http://74.48.78.17:4255"
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED
|
||||
@@ -226,3 +264,228 @@ spec:
|
||||
- name: http
|
||||
port: 4222
|
||||
targetPort: http
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: code-queue-d518
|
||||
namespace: unidesk
|
||||
labels:
|
||||
app.kubernetes.io/name: code-queue
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/deployment-mode: v3sctl-managed
|
||||
unidesk.ai/instance-id: D518
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: code-queue
|
||||
unidesk.ai/instance-id: D518
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: code-queue
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/deployment-mode: v3sctl-managed
|
||||
unidesk.ai/instance-id: D518
|
||||
unidesk.ai/node-id: D518
|
||||
spec:
|
||||
nodeSelector:
|
||||
unidesk.ai/node-id: D518
|
||||
terminationGracePeriodSeconds: 30
|
||||
containers:
|
||||
- name: code-queue
|
||||
image: unidesk-code-queue:d601
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 4222
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: code-queue-env
|
||||
optional: true
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4222"
|
||||
- name: CODE_QUEUE_INSTANCE_ID
|
||||
value: "D518"
|
||||
- name: CODE_QUEUE_SCHEDULER_ENABLED
|
||||
value: "false"
|
||||
- name: CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED
|
||||
value: "false"
|
||||
- name: CODE_QUEUE_DATA_DIR
|
||||
value: "/var/lib/unidesk/code-queue"
|
||||
- name: CODE_QUEUE_WORKDIR
|
||||
value: "/workspace"
|
||||
- name: CODE_QUEUE_CODEX_HOME
|
||||
value: "/var/lib/unidesk/code-queue/codex-home"
|
||||
- name: CODE_QUEUE_OPENCODE_XDG_DIR
|
||||
value: "/var/lib/unidesk/code-queue/opencode-xdg"
|
||||
- name: CODE_QUEUE_SOURCE_CODEX_CONFIG
|
||||
value: "/root/.codex/config.toml"
|
||||
- name: CODE_QUEUE_DEFAULT_MODEL
|
||||
value: "gpt-5.5"
|
||||
- name: CODE_QUEUE_MODELS
|
||||
value: "gpt-5.5,gpt-5.4-mini,gpt-5.4,minimax-m2.7"
|
||||
- name: CODE_QUEUE_MODEL_REASONING_EFFORTS
|
||||
value: "gpt-5.5=xhigh"
|
||||
- name: CODE_QUEUE_SANDBOX
|
||||
value: "danger-full-access"
|
||||
- name: CODE_QUEUE_APPROVAL_POLICY
|
||||
value: "never"
|
||||
- name: CODE_QUEUE_MAX_ACTIVE_QUEUES
|
||||
value: "0"
|
||||
- name: CODE_QUEUE_DATABASE_POOL_MAX
|
||||
value: "2"
|
||||
- name: NODE_OPTIONS
|
||||
value: "--max-old-space-size=1024"
|
||||
- name: CODE_QUEUE_IN_MEMORY_OUTPUT_RECORDS
|
||||
value: "10"
|
||||
- name: CODE_QUEUE_IN_MEMORY_EVENT_RECORDS
|
||||
value: "10"
|
||||
- name: CODE_QUEUE_MAIN_PROVIDER_ID
|
||||
value: "D518"
|
||||
- name: CODE_QUEUE_REMOTE_WORKDIR
|
||||
value: "/home/ubuntu"
|
||||
- name: CODE_QUEUE_EXECUTION_PROVIDER_IDS
|
||||
value: "D518"
|
||||
- name: CODE_QUEUE_DEV_CONTAINER_MASTER_HOST
|
||||
value: "74.48.78.17"
|
||||
- name: CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID
|
||||
value: "D518"
|
||||
- name: CODE_QUEUE_DEV_CONTAINER_WORKDIR
|
||||
value: "/home/ubuntu"
|
||||
- name: CODE_QUEUE_EGRESS_PROXY_ENABLED
|
||||
value: "false"
|
||||
- name: CODE_QUEUE_EGRESS_PROXY_URL
|
||||
value: ""
|
||||
- name: CODE_QUEUE_EGRESS_PROXY_NO_PROXY
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
- name: HTTP_PROXY
|
||||
value: ""
|
||||
- name: HTTPS_PROXY
|
||||
value: ""
|
||||
- name: ALL_PROXY
|
||||
value: ""
|
||||
- name: http_proxy
|
||||
value: ""
|
||||
- name: https_proxy
|
||||
value: ""
|
||||
- name: all_proxy
|
||||
value: ""
|
||||
- name: NO_PROXY
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
- name: no_proxy
|
||||
value: "localhost,127.0.0.1,::1,host.docker.internal,74.48.78.17,backend-core,oa-event-flow,database"
|
||||
- name: OA_EVENT_FLOW_BASE_URL
|
||||
value: "http://74.48.78.17:4255"
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED
|
||||
value: "false"
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL
|
||||
value: ""
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE
|
||||
value: "private"
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID
|
||||
value: "645275593"
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS
|
||||
value: "12000"
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS
|
||||
value: "15000"
|
||||
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS
|
||||
value: "3"
|
||||
- name: LOG_FILE
|
||||
value: "/var/log/unidesk/code-queue-d518.jsonl"
|
||||
- name: UNIDESK_LOG_RETENTION_BYTES
|
||||
value: "1GiB"
|
||||
volumeMounts:
|
||||
- name: docker-sock
|
||||
mountPath: /var/run/docker.sock
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
- name: workspace
|
||||
mountPath: /root/unidesk
|
||||
- name: codex-config
|
||||
mountPath: /root/.codex/config.toml
|
||||
readOnly: true
|
||||
- name: ssh-dir
|
||||
mountPath: /root/.ssh
|
||||
readOnly: true
|
||||
- name: logs
|
||||
mountPath: /var/log/unidesk
|
||||
- name: state
|
||||
mountPath: /var/lib/unidesk/code-queue
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 20
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 60
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
memory: 4Gi
|
||||
volumes:
|
||||
- name: docker-sock
|
||||
hostPath:
|
||||
path: /var/run/docker.sock
|
||||
type: Socket
|
||||
- name: workspace
|
||||
hostPath:
|
||||
path: /home/ubuntu/cq-deploy
|
||||
type: Directory
|
||||
- name: codex-config
|
||||
hostPath:
|
||||
path: /home/ubuntu/.codex/config.toml
|
||||
type: File
|
||||
- name: ssh-dir
|
||||
hostPath:
|
||||
path: /home/ubuntu/.ssh
|
||||
type: Directory
|
||||
- name: logs
|
||||
hostPath:
|
||||
path: /home/ubuntu/cq-deploy/.state/code-queue/logs
|
||||
type: DirectoryOrCreate
|
||||
- name: state
|
||||
hostPath:
|
||||
path: /home/ubuntu/cq-deploy/.state/code-queue
|
||||
type: DirectoryOrCreate
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: code-queue-d518
|
||||
namespace: unidesk
|
||||
labels:
|
||||
app.kubernetes.io/name: code-queue
|
||||
app.kubernetes.io/part-of: unidesk
|
||||
unidesk.ai/deployment-mode: v3sctl-managed
|
||||
unidesk.ai/instance-id: D518
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: code-queue
|
||||
unidesk.ai/instance-id: D518
|
||||
ports:
|
||||
- name: http
|
||||
port: 4222
|
||||
targetPort: http
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
"adapterServiceId": "v3sctl-adapter",
|
||||
"controlPlane": {
|
||||
"type": "kubernetes",
|
||||
"cluster": "unidesk-v3s",
|
||||
"context": "kind-unidesk-v3s"
|
||||
"cluster": "unidesk-v8s",
|
||||
"context": "unidesk-v8s"
|
||||
},
|
||||
"route": {
|
||||
"kind": "kubernetes-service",
|
||||
@@ -29,7 +29,16 @@
|
||||
"nodeId": "D601",
|
||||
"role": "primary",
|
||||
"baseUrl": "kubernetes://unidesk/services/code-queue:4222",
|
||||
"healthPath": "/health"
|
||||
"healthPath": "/health",
|
||||
"healthMode": "service-proxy"
|
||||
},
|
||||
{
|
||||
"id": "D518",
|
||||
"nodeId": "D518",
|
||||
"role": "standby",
|
||||
"baseUrl": "kubernetes://unidesk/services/code-queue-d518:4222",
|
||||
"healthPath": "/health",
|
||||
"healthMode": "pod-ready"
|
||||
}
|
||||
],
|
||||
"requireAllInstancesHealthy": false
|
||||
|
||||
Reference in New Issue
Block a user