Despliegue de Kubernetes — paso a paso
Gráfico de Helm: deploy/helm/cmind. Despliegua Web, MCP, agentes de nodo de auto-registro, Postgres en clúster
opcional.
Validado de extremo a extremo en clúster local
kind: todos los pods llegan aReady, agente de nodo se auto-registra con nombre DNS sin cabeza por pod,/health+/versiondevuelven 200, agente escalado hacia abajo marcado como no alcanzable. El flujo abajo = lo que se probó.
0. Requisitos previos
- Clúster de Kubernetes (EKS/AKS/GKE administrado, o local
kind/k3d/minikube). kubectl(apuntado al contexto de destino) yhelm3.- Registro de contenedor que el clúster puede extraer (omitir para
kindlocal — en su lugar cargar imágenes).
1. Construir las tres imágenes
docker build -f Dockerfile.web -t <registry>/cmind-web:1.0.0 .
docker build -f Dockerfile.mcp -t <registry>/cmind-mcp:1.0.0 .
docker build -f Dockerfile.node-agent -t <registry>/cmind-node-agent:1.0.0 .
Impulsa (docker push <registry>/cmind-web:1.0.0, etc.), o para clúster kind local carga
directo:
kind create cluster --name cmind
for s in web mcp node-agent; do
docker tag <registry>/cmind-$s:1.0.0 local/cmind-$s:test
kind load docker-image local/cmind-$s:test --name cmind
done
2. Elige secretos
PG_PASSWORD=$(openssl rand -hex 16)
JOIN_TOKEN=$(openssl rand -hex 24) # >= 32 caracteres; secreto de clúster compartido para auto-descubrimiento de nodos
3. Instala el gráfico
Basado en registro (clúster administrado):
helm upgrade --install cmind deploy/helm/cmind \
--namespace cmind --create-namespace \
--set image.registry=<registry-host> --set image.repository=<org>/cmind --set image.tag=1.0.0 \
--set secrets.pgPassword="$PG_PASSWORD" \
--set secrets.ownerEmail=you@example.com \
--set secrets.ownerPassword='Change-Me-Str0ng!' \
--set secrets.discoveryJoinToken="$JOIN_TOKEN"
Local kind (imágenes cargadas, sin Postgres externo, agentes sin privilegios):
helm upgrade --install cmind deploy/helm/cmind \
--set image.registry=local --set image.repository=cmind --set image.tag=test --set image.pullPolicy=Never \
--set web.dockerSocket.enabled=false --set nodeAgent.privileged=false \
--set secrets.pgPassword="$PG_PASSWORD" --set secrets.discoveryJoinToken="$JOIN_TOKEN"
En
kind/containerd sin socket Docker del host, así queweb.dockerSocket.enabled=false(compilador en aplicación/LocalNode no disponible) ynodeAgent.privileged=false(agente aún se auto-registra; solo no puede ejecutar contenedores de cTrader sin DinD). Para ejecución de carga de trabajo real, ejecuta agentes en grupo de nodos dondenodeAgent.privileged=trueestá permitido.
¿Sin binario helm? Renderiza y aplica:
helm template cmind deploy/helm/cmind -f my-values.yaml | kubectl apply -f -
4. Espera a rollout
kubectl -n cmind get pods -w
kubectl -n cmind rollout status deploy/cmind-web
Espera: cmind-web, cmind-mcp, cmind-postgres (Deployments) y cmind-node-agent-0
(StatefulSet) todos Ready. La preparación web (/health) pasa solo una vez que la BD se migra (las migraciones
se ejecutan al iniciar).
5. Verifica auto-descubrimiento
# El agente de nodo debe aparecer en la BD con un nombre DNS sin cabeza por pod BaseUrl e IsReachable=true
PG=$(kubectl -n cmind get pod -l app.kubernetes.io/component=postgres -o jsonpath='{.items[0].metadata.name}')
kubectl -n cmind exec "$PG" -- psql -U postgres -d appdb -c \
'SELECT "Name","Kind","IsReachable","BaseUrl" FROM "Nodes";'
Ejemplo (verificado):
Name | Kind | IsReachable | BaseUrl
-------------------------+-------------+-------------+-------------------------------------------------
cmind-node-agent-0 | ActiveMixed | t | http://cmind-node-agent-0.cmind-node-agent...:8080
Escala capacidad agregando réplicas — cada nuevo pod se auto-registra dentro de un intervalo de latido:
kubectl -n cmind scale statefulset/cmind-node-agent --replicas=3
Reconciliación de obsolescencia (verificada): escala el agente hacia abajo, cambia a IsReachable=f después de
discovery.heartbeatTtl; escala hacia arriba, vuelve en línea.
6. Alcanza la interfaz de usuario
kubectl -n cmind port-forward svc/cmind-web 8080:8080
# http://localhost:8080 — inicia sesión con el propietario sembrado
Acceso externo: establecer web.ingress.enabled=true, web.ingress.host y TLS.
Por qué los agentes de nodo son un StatefulSet
El distribuidor de nodo principal envía trabajo a un agente específico por URL, por lo que cada agente necesita un nombre DNS estable
individualmente direccionable. El gráfico usa StatefulSet + servicio sin cabeza; cada pod
anuncia http://<pod>.<svc>.<ns>.svc.cluster.local:8080 y se auto-registra bajo el nombre del pod.
El mismo mecanismo de descubrimiento que usan los nodos CLI de cTrader desnudos —
ver ../operations/node-discovery.md.
Escala web (plano posterior de SignalR, S6)
Aplicación web = Blazor Server + SignalR (panel en vivo, centro de registros). Para ejecutar más de una réplica web,
establece la cadena de conexión signalr en el punto final de Redis — la aplicación se registra SignalR Redis
plano posterior (AddStackExchangeRedis) para que los mensajes del centro y la negociación de circuitos se distribuyan entre réplicas y una
reconexión que aterriza en un pod diferente permanece en vivo. Sin cadena de conexión signalr = única réplica
en memoria (sin cambios). Empareja con afinidad de sesión en entrada para circuitos Blazor Server más suave.
Escalado automático y resiliencia del agente de copia
Los agentes de copia alojan sockets comerciales de larga duración, por lo que se escalan en trabajo, no CPU. Con
copyAgent.keda.enabled=true el gráfico instala KEDA ScaledObject que consulta Postgres para
conteo de perfil de copia en ejecución y escala réplicas para que cada pod aloje aproximadamente copyAgent.keda.profilesPerPod
(predeterminado 25), entre minReplicas/maxReplicas. KEDA lee BD a través de TriggerAuthentication vinculada a
clave secreta copyAgent.keda.connectionSecretKey. Cuando copyAgent.replicas > 1 (o KEDA escala más de 1)
el gráfico también agrega topologySpreadConstraints (distribuir entre nodos) y PodDisruptionBudget
(minAvailable: 1); en escala hacia abajo / actualización gradual cada pod libera arrendamientos en SIGTERM