106 lines
5.0 KiB
Markdown
106 lines
5.0 KiB
Markdown
# Homelab GitOps
|
|
|
|
Argo CD is the only component that deploys resources from this repository. Application repositories publish
|
|
immutable images and/or Helm charts, then commit the exact chart version selected by the corresponding Argo
|
|
`Application`. CI never receives Kubernetes credentials.
|
|
|
|
## Ownership boundary
|
|
|
|
- An application repository owns its image, reusable Kubernetes behavior, probes, services, ingress capability,
|
|
persistence capability, monitoring capability, tests, and Helm chart.
|
|
- This repository owns only Argo Applications, exact chart selections, cluster values such as domains, addresses,
|
|
storage classes and sizes, existing PVC names, resource sizing, and sealed credentials.
|
|
- A maintained chart records the upstream or custom application release in `appVersion`.
|
|
- One independently releasable workload has one chart and one Argo Application. The 13 Artcraft applications are
|
|
separate charts and Applications but all target the shared `artcraft` namespace.
|
|
- Upstream charts are referenced directly. GitOps-local dependency wrappers are forbidden.
|
|
- Secrets are either references to existing Secrets or `SealedSecret` ciphertext. Plaintext never belongs here.
|
|
|
|
See [docs/chart-ownership.md](docs/chart-ownership.md) for the complete chart versioning and promotion contract.
|
|
|
|
## Layout
|
|
|
|
- `bootstrap/` contains AppProjects and Applications.
|
|
- `root-application.yaml` is the app-of-apps entry point that continuously
|
|
reconciles `bootstrap/`; this is what makes CI commits to Application
|
|
`targetRevision` fields take effect without kubectl.
|
|
- `apps/<name>/values.yaml` contains only this cluster's values for that chart.
|
|
- `apps/<name>/resources/` is allowed only for genuinely cluster-owned resources, such as a MetalLB address pool or
|
|
an ImmichFrame `SealedSecret`.
|
|
- `scripts/update-chart-version.sh` is the common CI promotion interface.
|
|
|
|
## Version and promotion flow
|
|
|
|
Maintained chart repositories tag releases with `vMAJOR.MINOR.PATCH` (or `<chart>-vMAJOR.MINOR.PATCH` in the shared
|
|
chart repository). `git describe --tags --long` is canonical:
|
|
|
|
- tag `v1.0.0` packages chart `1.0.0`;
|
|
- the next commit packages `1.0.0-1-g41627ab`;
|
|
- the Git hash is retained to prevent two histories from producing the same immutable package version;
|
|
- dirty trees are rejected.
|
|
|
|
After publishing succeeds, CI checks out this repository with `GITOPS_TOKEN`, runs:
|
|
|
|
```sh
|
|
./scripts/update-chart-version.sh APPLICATION CHART_VERSION
|
|
```
|
|
|
|
and commits `bootstrap/APPLICATION.yaml`. Argo CD observes that Git commit, renders the published chart with the
|
|
cluster values, and reconciles Kubernetes.
|
|
|
|
Upstream chart upgrades have no repository under our control, so their exact `targetRevision` is updated here
|
|
manually or by a dependency updater. They do not need a fake promotion repository.
|
|
|
|
## Gitea credentials
|
|
|
|
Prefer owner- or organization-level Actions secrets so every maintained chart repository receives the same narrowly
|
|
scoped credentials:
|
|
|
|
- `PACKAGE_USERNAME` and `PACKAGE_TOKEN`: upload-only credentials for the `feedc0de` Helm package repository;
|
|
- `GITOPS_TOKEN`: a token with `write:repository` access to only `feedc0de/homelab-gitops`;
|
|
- `QUAY_USERNAME` and `QUAY_TOKEN`: required only by repositories that publish container images.
|
|
|
|
The workflow commits as the normal `feedc0de` Git identity. The token determines authorization; the commit name is
|
|
not a separate Gitea account. A dedicated promoter account is still preferable if a one-repository permission scope
|
|
is desired.
|
|
|
|
## Bootstrap and recovery
|
|
|
|
The expected kubectl context is `kubernetes-admin@kubernetes`.
|
|
|
|
```sh
|
|
./bootstrap/install-argocd.sh
|
|
./bootstrap/install.sh
|
|
```
|
|
|
|
Argo CD manages its own upstream chart after recovery. The repo-server is given cluster-local host aliases for
|
|
`code.brunner.ninja` and `brunner.ninja`, routed through the Traefik ClusterIP, because this cluster cannot hairpin to
|
|
the public WAN address. The canonical alias configuration is in `apps/argocd/values.yaml`.
|
|
|
|
Before adopting a stateful workload, record its PVC UID and volume, render and dry-run the new chart, then verify the
|
|
same values after synchronization:
|
|
|
|
```sh
|
|
kubectl get pvc -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,UID:.metadata.uid,VOLUME:.spec.volumeName'
|
|
kubectl --namespace argocd get applications \
|
|
-o custom-columns='NAME:.metadata.name,SYNC:.status.sync.status,HEALTH:.status.health.status'
|
|
```
|
|
|
|
Automatic sync, pruning, and self-healing mean direct live edits are temporary. Change Git and observe Argo return
|
|
the Application to `Synced` and `Healthy`.
|
|
|
|
## Sealed Secrets
|
|
|
|
Argo CD installs the official Sealed Secrets chart. Public Git stores only `SealedSecret` ciphertext; the controller's
|
|
private sealing key must be backed up to encrypted offline storage and never committed.
|
|
|
|
```sh
|
|
kubectl --namespace default create secret generic example \
|
|
--from-literal=token='replace-me' \
|
|
--dry-run=client --output=yaml \
|
|
| kubeseal \
|
|
--controller-name sealed-secrets-controller \
|
|
--controller-namespace sealed-secrets \
|
|
--format yaml > example-sealed-secret.yaml
|
|
```
|