GitOps deployment
- Git is the source of truth: Helm charts, GitOps manifests, Terraform
- Argo CD watches live cluster state continuously, drift reverted in ~1.4s
- Argo CD polls Git independently on a ~3 min default cadence
AI Infrastructure / Platform Engineering
A local ML platform reference implementation on Kubernetes: an inference service, its full MLflow/PostgreSQL/MinIO lifecycle, GitOps, autoscaling, security hardening and observability, validated with real failure drills instead of descriptions.
Every headline number here comes from running the cluster and breaking it on purpose, not from documenting intent: GitOps reconciliation speed, autoscaling under load, pod recovery time, a NetworkPolicy deny rule, and eight injected faults were all measured against a live kind cluster.
AWS is designed as code — Terraform, a cost model, a migration doc — but is explicitly marked not-yet-applied, so local evidence is never mistaken for a cloud deployment claim.
An inference service, its full ML lifecycle, and the GitOps, autoscaling, security and observability layers around it, all running inside one Kubernetes cluster.
Argo CD watches live cluster state continuously and also polls Git independently. The two paths have very different latency, which is why a Git commit lands slower than a manual edit gets reverted.
make local-up builds a kind cluster, bootstraps GitOps, runs the ML lifecycle and stands up observability from a clean checkout, in roughly 15 minutes.
Applications platform-local and inference-local are reconciled from helm/, gitops/ and infra/terraform/ — no manual kubectl apply.
Argo CD watches live cluster state continuously and self-heals in ~1.4s; it polls Git on its own ~3 min cadence. A manual edit is reverted almost instantly; a Git commit lands on the next poll.
The inference Service is the only path in. NetworkPolicy denies inference → PostgreSQL directly; the allowed path is inference → MLflow → PostgreSQL / MinIO.
k6 load tests, HPA scale events, pod deletion, Argo drift, and 8 injected faults are run against the live cluster and timed.
An 11-check acceptance suite runs against the live cluster; M11 proved the whole sequence again from a destroyed cluster, images and build cache.
Argo CD’s live-state watch self-heals drift in about 1.4 seconds; its independent Git poll defaults to roughly every 3 minutes.
A default-deny NetworkPolicy plus an explicit allow-list governs which pods may reach which services. The inference → PostgreSQL direct path was actually attempted against the running cluster and confirmed denied, not inferred from the policy file.
| Allowed | Denied and verified |
|---|---|
|
|
Pod Security Standards: restricted is enforced cluster-wide. On its first run the security drill rejected its own probe pods under that same policy, which was initially misread as a NetworkPolicy denial rather than a Pod Security Standards rejection.
Git is the only source of truth; Argo CD applies it into the cluster. Client traffic reaches only the inference Service, governed by an HPA and a PodDisruptionBudget. A default-deny NetworkPolicy allows inference → MLflow → PostgreSQL/MinIO while denying inference from reaching PostgreSQL directly. Prometheus feeds Grafana and a promtool-tested Alertmanager.
Git is the source of truth; Argo CD applies it, watching live state continuously while polling Git on its own slower cadence.
Git poll ~3 min Argo CD. Argo CD watch + self-heal ~1.4s kind cluster. kind cluster applies Two independent loops.
The Service is the only entry point; HPA and a PodDisruptionBudget govern how the Deployment absorbs load and voluntary disruption.
Client GET/POST inference Service. inference Service routes inference Deployment.
NetworkPolicy default-deny allows inference → MLflow → PostgreSQL/MinIO, and denies inference from reaching PostgreSQL directly.
inference allowed MLflow. MLflow allowed PostgreSQL (StatefulSet + PVC). MLflow allowed MinIO (StatefulSet + PVC). PostgreSQL and MinIO then connects to inference → PostgreSQL directly.
Metrics feed both a dashboard and an alerting path with rules validated by promtool before deployment.
inference /metrics scrapes Prometheus. Prometheus feeds Grafana (dashboards). Prometheus feeds Alertmanager (static thresholds).
Architecture notes
Model loading ran on the request path at startup, so a slow artifact store took /health down with it. Fixed by moving model loading to a background thread so liveness and readiness stop being hostage to one dependency's latency.
kubectl rollout status reported success while an external probe measured 1 failure in 90 during a rolling update. The Kubernetes-level signal and the client-observed signal were both correct and still disagreed. Fixed with a preStop hook that drains in-flight connections before the pod terminates.
A labelled Prometheus counter emits no series until its first increment, so an empty dashboard panel could mean either. Fixed twice with or vector(0), because the first fix did not cover every label combination.
Pod Security Standards: restricted rejected the drill's own probe pods, and every admission rejection was misread as a NetworkPolicy DENY during the first run. Fixed by making the probe pods themselves PSS-compliant, then rerunning the drill against the real boundary.
The 297 MiB MLflow image carried 7 unpatched CRITICAL CVEs; every fix landed only in a build with a 1.46 GiB floor. The tradeoff was made explicitly and the larger image was kept, rather than shipping the smaller image with known criticals.
Not simulated: each scenario below was actually triggered against the live cluster, with detection and recovery observed and timed.
| Scenario | Observed | Recovery |
|---|---|---|
| Pod crash | ReplicaSet notices; the surviving replica keeps serving | replacement ready in 12–15s |
| Invalid model artifact | readiness probe returns 503; pod held out of Service endpoints | Git revert |
| Artifact store outage | 100% of requests still return 200 while the pod is not ready | background recheck, 0 restarts |
| Config drift | Argo CD marks the app OutOfSync the moment it diverges | self-heal in ~1.4s |
| Bad rollout | maxUnavailable: 0 keeps old replicas serving | Git revert, bad ReplicaSet pruned |
| Node drain with a stateful pod on it | surviving inference pod absorbs traffic | PostgreSQL / MinIO reschedule automatically |
Load, autoscaling, recovery, reproducibility, failure engineering and the security boundary describe different parts of the system. They stay separate instead of being compressed into one score.
| Evidence slice | Current result | What it means |
|---|---|---|
| Load test | 645,809 requests · 0% errors | k6 against the live cluster; saturated throughput 2,935 req/s at a saturated /predict p95 of 32.9 ms. |
| Autoscaling | 2 → 6 replicas in 71s | HPA scale-up under load; scale-down back to 2 takes ~230s, stepped rather than immediate. |
| Recovery timing | 12–15s pod · ~1.4s drift | Pod deleted to replacement serving is 12–15s. Argo CD detects and reconciles config drift in ~1.4s, independent of its ~3 min Git poll. |
| Reproducibility | 11/11 acceptance · 901s | Cluster, images and build cache destroyed first, then make local-up → make local-test from the repo alone, proven in M11. |
| Failure engineering | 8 faults injected, not simulated | Run against the live cluster with measured detection and recovery, not described in documentation. |
| Security boundary | NetworkPolicy deny verified | inference → PostgreSQL directly is denied and confirmed by test, not assumed from the policy YAML; Pod Security Standards: restricted is enforced. |
This is a validated local Kubernetes reference implementation, not a production certification and not a cloud deployment. AWS is designed as code and has not been applied.