321 lines
9.7 KiB
TypeScript
321 lines
9.7 KiB
TypeScript
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. trace-script module for scripts/src/platform-infra-observability.ts.
|
|
|
|
// Moved mechanically from scripts/src/platform-infra-observability.ts:1575-1813 for #903.
|
|
|
|
// SPEC: PJ2026-01060501 OTel追踪 draft-2026-06-19-p0.
|
|
// Responsibility: YAML-first platform-infra OpenTelemetry tracing control commands.
|
|
import { Buffer } from "node:buffer";
|
|
import { readFileSync } from "node:fs";
|
|
import type { UniDeskConfig } from "../config";
|
|
import { rootPath } from "../config";
|
|
import type { RenderedCliResult } from "../output";
|
|
import {
|
|
compactCapture,
|
|
createYamlFieldReader,
|
|
numberField,
|
|
parseJsonOutput,
|
|
redactSensitiveUnknown,
|
|
shQuote,
|
|
capture,
|
|
} from "../platform-infra-ops-library";
|
|
|
|
import type { ObservabilityConfig, ObservabilityTarget } from "./types";
|
|
import { tempoService } from "./search-script";
|
|
import { imageReference, indent } from "./summary";
|
|
|
|
export function asArray(value: unknown): unknown[] {
|
|
return Array.isArray(value) ? value : [];
|
|
}
|
|
|
|
export function limitArray(value: unknown, limit: number): unknown[] {
|
|
return asArray(value).slice(0, limit);
|
|
}
|
|
|
|
export function renderManifest(observability: ObservabilityConfig, target: ObservabilityTarget): string {
|
|
const collectorImage = imageReference(observability.images.collector);
|
|
const tempoImage = imageReference(observability.images.tempo);
|
|
return [
|
|
target.createNamespace ? namespaceManifest(target) : "",
|
|
allowAllNetworkPolicy(target),
|
|
collectorConfigMap(observability, target),
|
|
collectorDeployment(observability, target, collectorImage),
|
|
collectorService(observability, target),
|
|
tempoConfigMap(observability, target),
|
|
tempoDeployment(observability, target, tempoImage),
|
|
tempoService(observability, target),
|
|
].filter((item) => item.trim().length > 0).join("\n---\n");
|
|
}
|
|
|
|
export function namespaceManifest(target: ObservabilityTarget): string {
|
|
return `apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: ${target.namespace}
|
|
labels:
|
|
app.kubernetes.io/part-of: platform-infra
|
|
app.kubernetes.io/managed-by: unidesk
|
|
unidesk.ai/runtime-node: ${target.id}
|
|
`;
|
|
}
|
|
|
|
export function allowAllNetworkPolicy(target: ObservabilityTarget): string {
|
|
return `apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-all
|
|
namespace: ${target.namespace}
|
|
labels:
|
|
app.kubernetes.io/part-of: platform-infra
|
|
app.kubernetes.io/managed-by: unidesk
|
|
spec:
|
|
podSelector: {}
|
|
policyTypes:
|
|
- Ingress
|
|
- Egress
|
|
ingress:
|
|
- {}
|
|
egress:
|
|
- {}
|
|
`;
|
|
}
|
|
|
|
export function collectorConfigMap(observability: ObservabilityConfig, target: ObservabilityTarget): string {
|
|
const config = `receivers:
|
|
otlp:
|
|
protocols:
|
|
grpc:
|
|
endpoint: 0.0.0.0:${observability.collector.otlp.grpcPort}
|
|
http:
|
|
endpoint: 0.0.0.0:${observability.collector.otlp.httpPort}
|
|
processors:
|
|
batch: {}
|
|
exporters:
|
|
otlp/tempo:
|
|
endpoint: ${observability.traceBackend.serviceName}.${target.namespace}.svc.cluster.local:${observability.traceBackend.otlp.grpcPort}
|
|
tls:
|
|
insecure: true
|
|
extensions:
|
|
health_check:
|
|
endpoint: 0.0.0.0:${observability.collector.healthPort}
|
|
service:
|
|
extensions: [health_check]
|
|
pipelines:
|
|
traces:
|
|
receivers: [otlp]
|
|
processors: [batch]
|
|
exporters: [otlp/tempo]
|
|
`;
|
|
return `apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: ${observability.collector.configMapName}
|
|
namespace: ${target.namespace}
|
|
labels:
|
|
app.kubernetes.io/name: ${observability.collector.deploymentName}
|
|
app.kubernetes.io/component: tracing
|
|
app.kubernetes.io/part-of: platform-infra
|
|
app.kubernetes.io/managed-by: unidesk
|
|
annotations:
|
|
unidesk.ai/spec: "${observability.metadata.spec}"
|
|
data:
|
|
collector.yaml: |
|
|
${indent(config, 4)}
|
|
`;
|
|
}
|
|
|
|
export function collectorDeployment(observability: ObservabilityConfig, target: ObservabilityTarget, image: string): string {
|
|
return `apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: ${observability.collector.deploymentName}
|
|
namespace: ${target.namespace}
|
|
labels:
|
|
app.kubernetes.io/name: ${observability.collector.deploymentName}
|
|
app.kubernetes.io/component: tracing
|
|
app.kubernetes.io/part-of: platform-infra
|
|
app.kubernetes.io/managed-by: unidesk
|
|
spec:
|
|
replicas: ${observability.collector.replicas}
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxSurge: 0
|
|
maxUnavailable: 1
|
|
selector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: ${observability.collector.deploymentName}
|
|
app.kubernetes.io/component: tracing
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app.kubernetes.io/name: ${observability.collector.deploymentName}
|
|
app.kubernetes.io/component: tracing
|
|
app.kubernetes.io/part-of: platform-infra
|
|
annotations:
|
|
unidesk.ai/spec: "${observability.metadata.spec}"
|
|
spec:
|
|
containers:
|
|
- name: collector
|
|
image: ${image}
|
|
imagePullPolicy: ${observability.images.collector.pullPolicy}
|
|
args:
|
|
- --config=/etc/otelcol/collector.yaml
|
|
ports:
|
|
- name: otlp-grpc
|
|
containerPort: ${observability.collector.otlp.grpcPort}
|
|
- name: otlp-http
|
|
containerPort: ${observability.collector.otlp.httpPort}
|
|
- name: health
|
|
containerPort: ${observability.collector.healthPort}
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: health
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /etc/otelcol/collector.yaml
|
|
subPath: collector.yaml
|
|
readOnly: true
|
|
volumes:
|
|
- name: config
|
|
configMap:
|
|
name: ${observability.collector.configMapName}
|
|
`;
|
|
}
|
|
|
|
export function collectorService(observability: ObservabilityConfig, target: ObservabilityTarget): string {
|
|
return `apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: ${observability.collector.serviceName}
|
|
namespace: ${target.namespace}
|
|
labels:
|
|
app.kubernetes.io/name: ${observability.collector.deploymentName}
|
|
app.kubernetes.io/component: tracing
|
|
app.kubernetes.io/part-of: platform-infra
|
|
app.kubernetes.io/managed-by: unidesk
|
|
spec:
|
|
type: ClusterIP
|
|
selector:
|
|
app.kubernetes.io/name: ${observability.collector.deploymentName}
|
|
app.kubernetes.io/component: tracing
|
|
ports:
|
|
- name: otlp-grpc
|
|
port: ${observability.collector.otlp.grpcPort}
|
|
targetPort: otlp-grpc
|
|
- name: otlp-http
|
|
port: ${observability.collector.otlp.httpPort}
|
|
targetPort: otlp-http
|
|
- name: health
|
|
port: ${observability.collector.healthPort}
|
|
targetPort: health
|
|
`;
|
|
}
|
|
|
|
export function tempoConfigMap(observability: ObservabilityConfig, _target: ObservabilityTarget): string {
|
|
const config = `server:
|
|
http_listen_port: ${observability.traceBackend.httpPort}
|
|
distributor:
|
|
receivers:
|
|
otlp:
|
|
protocols:
|
|
grpc:
|
|
endpoint: 0.0.0.0:${observability.traceBackend.otlp.grpcPort}
|
|
http:
|
|
endpoint: 0.0.0.0:${observability.traceBackend.otlp.httpPort}
|
|
ingester:
|
|
trace_idle_period: 10s
|
|
max_block_duration: 5m
|
|
compactor:
|
|
compaction:
|
|
block_retention: ${observability.traceBackend.storage.retention}
|
|
storage:
|
|
trace:
|
|
backend: local
|
|
wal:
|
|
path: /var/tempo/wal
|
|
local:
|
|
path: /var/tempo/traces
|
|
`;
|
|
return `apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: ${observability.traceBackend.configMapName}
|
|
namespace: ${_target.namespace}
|
|
labels:
|
|
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
|
|
app.kubernetes.io/component: trace-backend
|
|
app.kubernetes.io/part-of: platform-infra
|
|
app.kubernetes.io/managed-by: unidesk
|
|
annotations:
|
|
unidesk.ai/spec: "${observability.metadata.spec}"
|
|
data:
|
|
tempo.yaml: |
|
|
${indent(config, 4)}
|
|
`;
|
|
}
|
|
|
|
export function tempoDeployment(observability: ObservabilityConfig, target: ObservabilityTarget, image: string): string {
|
|
return `apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: ${observability.traceBackend.deploymentName}
|
|
namespace: ${target.namespace}
|
|
labels:
|
|
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
|
|
app.kubernetes.io/component: trace-backend
|
|
app.kubernetes.io/part-of: platform-infra
|
|
app.kubernetes.io/managed-by: unidesk
|
|
spec:
|
|
replicas: ${observability.traceBackend.replicas}
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxSurge: 0
|
|
maxUnavailable: 1
|
|
selector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
|
|
app.kubernetes.io/component: trace-backend
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
|
|
app.kubernetes.io/component: trace-backend
|
|
app.kubernetes.io/part-of: platform-infra
|
|
annotations:
|
|
unidesk.ai/spec: "${observability.metadata.spec}"
|
|
spec:
|
|
containers:
|
|
- name: tempo
|
|
image: ${image}
|
|
imagePullPolicy: ${observability.images.tempo.pullPolicy}
|
|
args:
|
|
- -config.file=/etc/tempo/tempo.yaml
|
|
ports:
|
|
- name: http
|
|
containerPort: ${observability.traceBackend.httpPort}
|
|
- name: otlp-grpc
|
|
containerPort: ${observability.traceBackend.otlp.grpcPort}
|
|
- name: otlp-http
|
|
containerPort: ${observability.traceBackend.otlp.httpPort}
|
|
readinessProbe:
|
|
httpGet:
|
|
path: ${observability.probes.readinessPath}
|
|
port: http
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /etc/tempo/tempo.yaml
|
|
subPath: tempo.yaml
|
|
readOnly: true
|
|
- name: data
|
|
mountPath: /var/tempo
|
|
volumes:
|
|
- name: config
|
|
configMap:
|
|
name: ${observability.traceBackend.configMapName}
|
|
- name: data
|
|
emptyDir: {}
|
|
`;
|
|
}
|