{"id":51573100,"url":"https://github.com/czhao-dev/control-plane","last_synced_at":"2026-07-10T21:30:31.841Z","repository":{"id":366753088,"uuid":"1277697307","full_name":"czhao-dev/control-plane","owner":"czhao-dev","description":"A declarative infrastructure control plane: Deployment scheduling, Node registration and heartbeating, desired-state reconciliation, failure recovery, label-based Service discovery, and health-aware proxy routing — with Prometheus/Grafana observability.","archived":false,"fork":false,"pushed_at":"2026-07-08T13:09:44.000Z","size":13182,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-08T14:17:22.718Z","etag":null,"topics":["container-orchestration","control-plane","distributed-systems","docker","docker-compose","fault-tolerance","golang","grafana","health-check","infrastructure","load-balancer","microservices","node-agent","orchestration","prometheus","reconciler","rest-api","reverse-proxy","scheduler","service-discovery"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/czhao-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-23T05:45:49.000Z","updated_at":"2026-07-08T13:10:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/czhao-dev/control-plane","commit_stats":null,"previous_names":["czhao-dev/distributed-job-platform","czhao-dev/backend-infra-platform","czhao-dev/load-balanced-orchestrator","czhao-dev/control-plane"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/czhao-dev/control-plane","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/czhao-dev%2Fcontrol-plane","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/czhao-dev%2Fcontrol-plane/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/czhao-dev%2Fcontrol-plane/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/czhao-dev%2Fcontrol-plane/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/czhao-dev","download_url":"https://codeload.github.com/czhao-dev/control-plane/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/czhao-dev%2Fcontrol-plane/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35344523,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-10T02:00:06.465Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["container-orchestration","control-plane","distributed-systems","docker","docker-compose","fault-tolerance","golang","grafana","health-check","infrastructure","load-balancer","microservices","node-agent","orchestration","prometheus","reconciler","rest-api","reverse-proxy","scheduler","service-discovery"],"created_at":"2026-07-10T21:30:31.341Z","updated_at":"2026-07-10T21:30:31.836Z","avatar_url":"https://github.com/czhao-dev.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Distributed Container Orchestration Platform\n\nA declarative infrastructure control plane that accepts Deployment specs, schedules Pods onto registered Nodes, tracks Node health via heartbeats, reconciles desired vs. actual state, recovers from Node failures, and drives a health-aware reverse proxy — with Prometheus/Grafana observability across the full stack.\n\n```\n                        infractl / curl\n                              │\n                              ▼\n                     ┌─────────────────┐\n                     │  control-plane  │  deployments, pods, nodes, services\n                     │      :7070      │  scheduler + controller loops\n                     └────────┬────────┘\n              ┌───────────────┼───────────────┐\n              ▼               ▼               ▼\n         worker-1         worker-2         worker-3   (register, heartbeat,\n        (:9100 metrics)  (:9101 metrics)  (:9102 metrics)  poll, execute)\n              ▲               ▲               ▲\n              └───────────────┼───────────────┘\n                     ┌─────────────────┐\n       client  ───▶  │      proxy      │  fetches healthy nodes from the\n                     │      :8080      │  control plane, routes/fails over\n                     └─────────────────┘\n                              │\n                     ┌────────┴────────┐\n                     ▼                 ▼\n               Prometheus :9090   Grafana :3000\n```\n\nFour binaries make up the system: `control-plane` (the API server plus its scheduler and controller loops), `worker` (a node agent that registers, heartbeats, and executes pods), `proxy` (a health-aware reverse proxy that discovers backends dynamically from the control plane), and `infractl` (the CLI client). See [`docs/`](docs/) for the design of each component.\n\n## Repository Layout\n\n```text\n.\n├── go.mod                   # single Go module for the whole system\n├── docker-compose.yml       # control-plane + 3 workers + proxy + Prometheus/Grafana\n├── Dockerfile                # multi-stage build producing all 4 binaries\n├── cmd/\n│   ├── control-plane/        # API server entrypoint\n│   ├── worker/                # node agent entrypoint\n│   ├── proxy/                  # reverse proxy entrypoint\n│   └── infractl/                # CLI entrypoint\n├── pkg/\n│   ├── apis/                  # domain types: Deployment, Pod, Node, Service\n│   ├── api/                    # REST server: routes, handlers, middleware\n│   ├── scheduler/                # assigns PENDING pods to HEALTHY nodes\n│   ├── controller/                # reconciliation + node failure detection\n│   ├── agent/                      # node agent: register/heartbeat/poll/execute\n│   │   └── metrics/\n│   ├── proxy/                        # reverse proxy: balancing, health checks, discovery\n│   │   └── balancer/ backend/ health/ admin/ config/ metrics/\n│   ├── registry/                      # state store: BoltDB or in-memory\n│   ├── metrics/                        # control-plane Prometheus metrics\n│   ├── logging/                         # shared structured logger\n│   └── config/                          # control-plane env config\n├── deployments/              # Prometheus config, proxy config, Grafana dashboard/provisioning\n├── docs/                      # architecture, scheduler, controller, node-model, proxy design docs\n├── examples/                   # example Deployment/Service manifests\n├── scripts/                     # demo and benchmark scripts\n└── tests/integration/            # black-box integration suite\n```\n\n## Quickstart\n\n```bash\n./scripts/run-local-cluster.sh\n```\n\nBuilds and starts the control plane, 3 node agents, the proxy, Prometheus, and Grafana.\n\n- **Control plane API**: http://localhost:7070\n- **Proxy**: http://localhost:8080 (backend status: `/admin/backends`)\n- **Prometheus**: http://localhost:9090\n- **Grafana** (admin/admin): http://localhost:3000\n\nThen walk through the demo scripts in [`scripts/`](scripts/):\n\n```bash\n./scripts/submit-demo-jobs.sh      # submit a 20-pod deployment, watch it schedule across nodes\n./scripts/demo-worker-failure.sh   # kill a node mid-pod, watch the controller detect and reschedule\n./scripts/demo-proxy-failover.sh   # kill a node, watch the proxy route around it and recover\n./scripts/benchmark-scheduler.sh   # throughput: deployments submitted to all pods scheduled\n```\n\nOr drive it directly with `infractl` (`go run ./cmd/infractl ...`; `INFRACTL_SERVER` defaults to `http://localhost:7070`):\n\n```bash\ninfractl deployment submit examples/batch-job.yaml\ninfractl deployment status \u003cdeployment-id\u003e\ninfractl node list\ninfractl cluster status\n```\n\n**Known limitation:** a node that crashes and never comes back stays `UNHEALTHY` in the control plane's store forever — the heartbeat timeout only ever flips `HEALTHY → UNHEALTHY`; there's no automatic garbage-collection of permanently-dead nodes. Its pods are correctly rescheduled onto the remaining healthy nodes, so this is a bookkeeping gap, not a scheduling one. A `drain`-then-timeout path does fully remove a node (see [`docs/controller.md`](docs/controller.md)).\n\n## Component overview\n\n| Component | Package/binary |\n|---|---|\n| API Server | `cmd/control-plane` + `pkg/api` |\n| Persistent state store | `pkg/registry` (BoltDB or in-memory) |\n| Scheduler | `pkg/scheduler` |\n| Controller | `pkg/controller` |\n| Node agent | `cmd/worker` + `pkg/agent` |\n| Proxy | `cmd/proxy` + `pkg/proxy` |\n| CLI client | `cmd/infractl` |\n\n## Domain model\n\nA `Deployment` is a declarative template (image or command, replicas, retry policy, resources, namespace, labels). The controller creates `Pod`s from it — one execution unit per replica — and reconciles their count against `Replicas` every tick. Pods are scheduled onto `Node`s (registered, heartbeating agent processes) by the scheduler, which picks the least-loaded healthy node with enough capacity. A `Service` describes path-based routing and a label `Selector` that resolves to a set of `Node` backends, which the proxy discovers dynamically. All four resource types share the same pattern: a `Status` field, an `allowedTransitions` map, and a pure `Transition` validator.\n\n## Labels and namespaces\n\n`Deployment`, `Pod`, and `Service` are namespaced (`Namespace` field, defaults to `default`); `Node`s are cluster-scoped (no `Namespace` field — a node can run pods from any namespace). Pods inherit their Deployment's `Namespace` and `Labels` at creation time. List endpoints accept label-selector query params (`?label=key=value`), matched via exact-match `Service.Selector`-style matching.\n\n## Persistent state\n\n`CTRLPLANE_DB_PATH` toggles the state store: unset uses an in-memory store (data lost on restart), set to a file path uses BoltDB (persists across restarts, single-node, no replication — see [`docs/architecture.md`](docs/architecture.md) for the tradeoff).\n\n## Local development\n\n```bash\ngo build ./...\ngo vet ./...\ngo test ./... -race\n```\n\nAll four binaries live in one Go module (`go.mod` at the repo root), so these commands work directly — no per-component build steps needed.\n\n## Documented simplifications\n\nThese are intentional design decisions for the scope of this project, not bugs:\n\n| Production orchestrators | This project |\n|---|---|\n| Distributed consensus store (e.g. etcd with Raft, gRPC) | BoltDB: embedded single-file ACID store, no network |\n| Watch/informer event streams from the state store | Node agent polls on a timer (500 ms); controller ticks on a timer (2 s) |\n| Services select Pods by label via overlay networking | Services select Nodes by label (Pods have no network address; nodes are the routable entities) |\n| Full container runtime (containerd, runc, CNI networking) | Docker SDK — pods run as Docker containers when `image:` is set, or OS subprocesses otherwise |\n| RBAC, admission webhooks, CRDs | Not implemented |\n| Metrics-server-backed autoscaling on real CPU/memory utilization | `pkg/autoscaler` — cluster-wide PENDING-pod-count proxy signal instead (see [docs/autoscaling.md](docs/autoscaling.md)) |\n| Dedicated load-balancing layer as a separate, mature project | `pkg/proxy` — a from-scratch reverse proxy, not a replacement for NGINX/HAProxy/Envoy/Traefik |\n\n## Docs\n\n- [`docs/architecture.md`](docs/architecture.md) — component mapping, topology, responsibilities\n- [`docs/scheduler.md`](docs/scheduler.md) — scheduling policy and capacity bookkeeping\n- [`docs/controller.md`](docs/controller.md) — reconciliation passes and failure recovery\n- [`docs/autoscaling.md`](docs/autoscaling.md) — replica autoscaling policy, signal, and observability\n- [`docs/node-model.md`](docs/node-model.md) — node agent lifecycle, execution, graceful shutdown\n- [`docs/proxy.md`](docs/proxy.md) — proxy design, load-balancing strategies, health checking\n- [`docs/security-hardening.md`](docs/security-hardening.md) — before/after hardening checklist with real Trivy findings\n\n## References\n\n- [Go Workspaces](https://go.dev/doc/workspaces) — a mechanism this project previously used to tie multiple modules together; the system is now a single module\n- [Prometheus Go client](https://github.com/prometheus/client_golang) — instrumentation library used by the control plane and node agent\n- [Prometheus](https://prometheus.io/docs/introduction/overview/) — metrics collection and alerting\n- [Grafana](https://grafana.com/docs/grafana/latest/) — metrics visualization and dashboards\n- [Docker Compose](https://docs.docker.com/compose/) — local multi-service orchestration for the demo stack\n- Controller / reconcile-loop pattern — the desired-vs-actual-state design that inspired `pkg/controller`\n- [go.etcd.io/bbolt](https://pkg.go.dev/go.etcd.io/bbolt) — embedded single-file ACID key-value store used as the persistent state backend\n- [gopkg.in/yaml.v3](https://pkg.go.dev/gopkg.in/yaml.v3) — YAML parsing used for declarative deployment spec files and proxy config\n- [testify](https://github.com/stretchr/testify) — assertion library used across the test suite\n- [Docker Go SDK](https://pkg.go.dev/github.com/docker/docker/client) — official Docker client used by the node agent to pull images and manage container lifecycle when `image:` is set in a Deployment spec\n\n## License\n\nThis project is licensed under the MIT License. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fczhao-dev%2Fcontrol-plane","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fczhao-dev%2Fcontrol-plane","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fczhao-dev%2Fcontrol-plane/lists"}