Files

4.6 KiB

local-k8s — GitOps for the local GreenKube Minikube cluster

This repository is the single source of truth for the workloads running in the local Minikube cluster. Declarative state is reconciled by ArgoCD (app-of-apps): the root Application watches clusters/minikube/apps/ and creates one child Application per component.

bootstrap/bootstrap.sh          # day-0: cluster + ingress + cert-manager + ArgoCD + Gitea
        │
        ▼
clusters/minikube/root-app.yaml # applied by the bootstrap, points at the in-cluster Gitea repo
        │
        ▼
clusters/minikube/apps/*.yaml   # child Applications (nginx, OpenCost, Prometheus, Grafana, VPA, demo)

Everything under clusters/ is reconciled continuously. Editing a manifest in this repository (directly or through a GreenKube pull request) is the only way to change the cluster.

Why Gitea?

ArgoCD needs a Git server it can reach; the GreenKube PR bot needs a Git API it can open pull requests against. Running Gitea inside the cluster keeps the loop self-contained and fully testable offline:

GreenKube UI ──► PR bot ──► Gitea PR ──► merge ──► ArgoCD sync ──► cluster
      ▲                                                              │
      └────────────── apply detection & verification ◄──────────────┘

On a real installation, point the Applications and the greenkube.cloud/git-repo annotations at your GitHub/GitLab repository instead.

Bootstrap

Prerequisites: Docker, Minikube, kubectl and Helm.

./bootstrap/bootstrap.sh

The script is idempotent and performs the day-0 steps that cannot be GitOps-managed (a Git server and ArgoCD must exist before they can be self-managed):

  1. starts Minikube if needed;
  2. installs ingress-nginx and cert-manager (and the letsencrypt-prod issuer);
  3. installs ArgoCD (pinned version);
  4. installs Gitea and creates the greenkube user + API token;
  5. pushes this repository and the GreenKube source repository (dev branch) to Gitea;
  6. creates the greenkube-gitops namespace secret (GIT_TOKEN, Postgres credentials);
  7. applies the AppProject and the root Application.

After that, ArgoCD owns the rest.

Access

kubectl port-forward svc/argocd-server -n argocd 8080:443
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d

Gitea is reachable from the host after kubectl port-forward -n gitea svc/gitea-http 3000:3000.

Managed applications

Application Source Namespace Notes
nginx ingress-nginx chart + local ingress manifests ingress-nginx Adopts the controller and manages platform routes; ingress resources use Prune=false
opencost OpenCost chart opencost Uses the in-cluster Prometheus service
prometheus kube-prometheus manifests monitoring Owns the monitoring stack except Grafana resources
grafana kube-prometheus manifests monitoring Owns only the grafana-* manifests
gitea Gitea chart gitea Adopts the bootstrap release and serves gitea.greenkube.cloud
vpa Fairwinds chart vpa Vertical Pod Autoscaler in recommendation mode (updater disabled)
demo-app this repo demo Annotated workload used to exercise rightsizing and the PR bot

ArgoCD adopts the existing ingress-nginx and Gitea releases and manages OpenCost, kube-prometheus, Grafana and VPA. ArgoCD itself and cert-manager remain bootstrap components. The prometheus and grafana Applications use the same pinned kube-prometheus source revision but select disjoint manifest sets.

GreenKube source annotations

The PR bot finds the manifest of a workload through annotations:

Annotation Required Meaning
greenkube.cloud/git-repo yes Git remote URL (Gitea by default)
greenkube.cloud/git-path no Manifest path; discovery scans the repo when omitted
greenkube.cloud/git-branch no Base branch override (defaults to main)

Each managed workload must carry the annotation of the repository that contains its own manifest — that is what makes the pull request possible.

Repository layout

bootstrap/bootstrap.sh              Day-0 installer (idempotent)
clusters/minikube/
├── project.yaml                    ArgoCD AppProject
├── root-app.yaml                   App-of-apps root Application
├── apps/                           One ArgoCD Application per component
├── values/                         Helm values per component
└── manifests/                      Raw manifests (demo workloads)