Sync the self-hosting stack
This commit is contained in:
@@ -97,6 +97,90 @@ about the install screen applies unchanged — including that **whoever reaches
|
||||
instance first owns it**, with no deadline. Point a public hostname at this after you have
|
||||
installed, not before.
|
||||
|
||||
## GitOps: Argo CD, Flux, or anything that reconciles
|
||||
|
||||
`kustomization.yaml` is the entry point. Point a controller at this directory and it renders every
|
||||
manifest here except one — and `argocd-application.example.yaml` is a worked Application to copy.
|
||||
|
||||
```sh
|
||||
kubectl apply -k . # the same thing, by hand
|
||||
```
|
||||
|
||||
**The exception is `01-secret.example.yaml`, and it is left out on purpose.** It is a template full
|
||||
of placeholders. A controller applies what it is pointed at, continuously, without asking — so a
|
||||
Secret full of `REPLACE_ME` inside the source of truth is a controller that overwrites your real
|
||||
credentials with placeholders on its next reconcile and puts them back every time you fix them. The
|
||||
api cannot reach its database, the vault key is wrong, and nothing about the sync looks unhealthy
|
||||
while it happens. Write `jarvis-secrets` by hand, or through SealedSecrets, ExternalSecrets or
|
||||
Vault, whose ciphertext is safe to commit. `jarvis-generated` is minted in-cluster and never exists
|
||||
in git at all.
|
||||
|
||||
### The order is in the manifests, not in the filenames
|
||||
|
||||
A controller reads this directory as a set; `00-`, `01-` and `02-` mean nothing to it. The order
|
||||
that the `kubectl apply` sequence above keeps by hand is carried by
|
||||
`argocd.argoproj.io/sync-wave` annotations on the resources themselves, so that it lives beside the
|
||||
thing being ordered:
|
||||
|
||||
| wave | what | why it cannot be later |
|
||||
| --- | --- | --- |
|
||||
| `-3` | Namespace | it carries the Pod Security labels that decide whether any of these pods are admitted |
|
||||
| `-2` | ConfigMaps, the minter's RBAC, Redis | everything below reads the config; the api's readiness probe reaches Redis |
|
||||
| `-1` | `jarvis-mint-secrets` **(hook)** | the migration reads `DATABASE_URL` out of what it writes |
|
||||
| `0` | `jarvis-migrate` **(hook)** | an api pod against an unmigrated schema is the failure all of this prevents |
|
||||
| `1` | api and web Deployments, Services | the web pod's `/readyz` proxies the api's health endpoint, so it cannot be ready before the api is |
|
||||
| `2` | Ingress, PodDisruptionBudgets | an Ingress applied early is a route to a 503 |
|
||||
|
||||
Argo does not begin a wave until the previous one reports healthy, which is what the `kubectl wait`
|
||||
lines are doing by hand.
|
||||
|
||||
### Why the two Jobs are hooks
|
||||
|
||||
Without this, the directory works exactly once. **A Job's `spec.template` is immutable**, so the
|
||||
second release — the first one with a different image digest — is a patch the API server refuses
|
||||
with `field is immutable`, and the sync fails there and stays failed. `hook: Sync` with
|
||||
`hook-delete-policy: BeforeHookCreation` deletes the previous Job before creating the new one, which
|
||||
is the supported way to say "run it again, with new contents".
|
||||
|
||||
The Sync phase rather than PreSync, deliberately: a PreSync hook runs before any ordinary resource
|
||||
is applied, which on a first sync is before the namespace, the ConfigMap and the Secret these Jobs
|
||||
read exist. In the Sync phase they are ordered by the same waves as everything else.
|
||||
|
||||
Two consequences worth knowing:
|
||||
|
||||
- **The previous run's Job object, and its logs, are gone** by the time the next one starts. Ship
|
||||
the logs if you need them after the fact.
|
||||
- **Every sync runs the migration.** That is safe — the phase is idempotent and holds a Postgres
|
||||
advisory lock — and it is why the example Application leaves `automated` sync OFF. A commit that
|
||||
applies a schema change with nobody watching turns the one thing this directory works hardest to
|
||||
make visible, a migration that REFUSES because it would drop a column, into a notification.
|
||||
|
||||
### Pin the images in one place
|
||||
|
||||
`kustomization.yaml` carries an `images:` block, so the three digests are set once rather than in
|
||||
three files. Three, not two: `jarvis-agent-dist` is an initContainer in `04-api.yaml` and is easy to
|
||||
miss, and missing it renders cleanly while the api pod never starts.
|
||||
|
||||
A version bump is then those lines and nothing else.
|
||||
|
||||
### What most estates should actually do
|
||||
|
||||
Point the Application at **your** repository, not at this one, with this directory as a remote base:
|
||||
|
||||
```yaml
|
||||
# your-repo/jarvis/kustomization.yaml
|
||||
resources:
|
||||
- https://git.luxit.be/Luxit/jarvis-selfhost//kubernetes?ref=v1.2.3
|
||||
images:
|
||||
- name: git.luxit.be/luxit/jarvis-api
|
||||
digest: sha256:...
|
||||
patches:
|
||||
- path: replicas.yaml # your node count, your ingress host, your storage class
|
||||
```
|
||||
|
||||
Then the digests you run are in a commit you control, upgrading is a diff you can review and revert,
|
||||
and `?ref=` pins the base to a tag rather than a branch that moves under you. Pin it.
|
||||
|
||||
## The three things most likely to bite
|
||||
|
||||
**Never mint secrets per pod.** On Compose a one-shot generates the vault key and the JWT secrets
|
||||
|
||||
Reference in New Issue
Block a user