Kubernetes Runtime Architecture
Overview
This document explains the current Kubernetes runtime architecture used by gobrave.
It is based on the runtime implementation in:
internal/container_runtime/kubernetes/runtime.gointernal/container_runtime/kubernetes/monitor_v2.go
The runtime supports two workload kinds:
deployment(long-running service)job(run-to-completion task)
Architecture Goals
- Keep runtime monitoring efficient at scale.
- Avoid duplicate monitor loops for the same runtime ID.
- Convert Kubernetes workload state into stable gobrave runtime events.
- Keep lifecycle behavior predictable for users (
start,stop,delete,logs,inspect).
High-Level Flow
flowchart TD
A[Create ContainerSpec] --> B[KubernetesRuntime.Create]
B --> C{WorkloadKind}
C -->|deployment| D[Create Deployment]
C -->|job| E[Create Job]
D --> F[Optional Service -svc]
D --> G[Return runtimeID]
E --> G
H[Start or Recovery] --> I[KubernetesRuntime.Monitor]
I --> J{MarkIfNotMonitoring}
J -->|already monitoring| K[Return idempotently]
J -->|new| L[Start shared informers once]
L --> M[Register subscription kind namespace name]
M --> N[Deployment or Job events]
N --> O[Emit RuntimeEvent]
O --> P[ContainerManager state transition]Runtime ID Model
Runtime IDs are encoded as:
<runtimeName>-<namespace>|<kind>|<name>
Examples:
k8s-default|deployment|web-apik3s-ai|job|batch-import-001
This format is required by runtime operations such as Start, Stop, Delete, Logs, and Inspect.
Resource Creation Model
Deployment Path
When WorkloadKind is deployment (or empty, default):
- Create Deployment with labels:
app=<workloadName>gobrave-workload=<workloadName>
- If
ExposeService=trueandExposedPort>0, create ClusterIP Service named<workloadName>-svc. - Return runtime ID.
Job Path
When WorkloadKind is job:
- Create Job with labels:
app=<workloadName>gobrave-workload=<workloadName>
- Return runtime ID.
Namespace Resolution
Namespace priority:
spec.RuntimeNamespace- runtime config namespace
default
Pod Spec Mapping (User-Facing Behavior)
From ContainerSpec to Kubernetes Pod spec:
Image,Entrypoint,Command,WorkDirmap directly.Envis sorted by key before injection (deterministic output).CPUandMemoryare mapped to container limits.Volumesare mapped ashostPathmounts.User(numeric uid oruid:gid) is mapped toRunAsUserwhen parsable.ExposedPortadds container port.- Scheduling constraints of type
nodeare mapped into required node affinity.
Restart policy:
- Deployment:
Always - Job:
Never
Monitor Architecture (Informer-Driven)
KubernetesRuntime uses monitor_v2 by default.
Key Design
- Shared informer startup happens once per runtime process.
- Monitor registration is idempotent via
MarkIfNotMonitoring(runtimeID). - Subscriptions are keyed by
kind|namespace|name. - A one-time snapshot check is executed right after subscription registration.
Informers Used
- Deployment informer: add/update/delete
- Job informer: add/update/delete
Why Snapshot Check Exists
Before waiting for informer updates, runtime performs a direct Get for the target workload.
This avoids missing terminal/start signals that may have happened just before registration.
Event Mapping
The runtime emits these events to ContainerManager:
Job
- Started:
Status.Active > 0orStatus.StartTime != nil->ContainerStarted - Succeeded:
Status.Succeeded > 0->ContainerExitedwith message0 - Failed:
Status.Failed > 0->ContainerFailedwith condition message or failed count - Deleted/NotFound: delete event or lookup not found ->
ContainerDeleted
Deployment
- Started:
Status.ReadyReplicas > 0->ContainerStarted - Failed: replica failure/progress deadline exceeded ->
ContainerFailed - Exited:
spec.replicas == 0andstatus.replicas == 0->ContainerExitedwith message0 - Deleted/NotFound: delete event or lookup not found ->
ContainerDeleted
After a terminal event is emitted, the subscription is removed and monitor membership is unmarked.
Lifecycle Semantics
Start
- Deployment: scale to 1, then monitor.
- Job: verify Job exists, then monitor.
Stop and Pause
- Deployment: scale to 0.
- Job: delete Job (foreground propagation).
Pauseis implemented asStop.
Resume
Resumeis implemented asStart.
Delete
- Deployment: delete Service
<name>-svc(ignore not found), then delete Deployment. - Job: delete Job (foreground propagation).
Logs and Inspect
Logs
Logs(runtimeID, tail):
- Resolve workload metadata from runtime ID.
- Find latest Pod by label
gobrave-workload=<name>. - Read pod logs (default tail: 200 lines).
Inspect
- Deployment:
IPAddressis returned as service DNS:<name>-svc.<namespace>.svc.cluster.localNodeNamecomes from latest Pod when available
- Job:
IPAddressis Pod IPNodeNameis Pod node
Limitations and Compatibility Notes
- Supported runtime names:
k8s,k3s. EnsureImageaccepts pull policyAlwaysandIfNotPresent.- Pull policy
Neveris rejected for preflight validation. Execis currently not implemented.- A deleted Job is not restartable as the same workload.
Operational Recommendations
- Always keep
gobrave-workloadlabel on managed workloads. - Use one runtime process with shared informers rather than per-workload clients.
- Treat monitor registration as safe to call repeatedly.
- Keep restart recovery enabled so non-terminal runtime IDs are reattached after process restarts.
Troubleshooting Checklist
- No lifecycle updates are arriving:
- Check runtime ID format and runtime prefix (
k8s-ork3s-). - Check informer startup/cache sync errors.
- Deployment never reports started:
- Check
ReadyReplicasand pod scheduling status. - Check deployment failure conditions (
ReplicaFailure,ProgressDeadlineExceeded).
- Job did not emit terminal event:
- Check
Succeeded/Failedstatus fields and Job conditions. - Check whether the Job was deleted before monitor registration.
Related Documents
- Runtime monitor recovery:
/docs/runtime-monitor-recovery - Container monitoring and queue status:
/docs/container-monitoring - Event subscribers:
/docs/event-bus-subscribers