Deploy on Kubernetes
此内容尚不支持你的语言。
There is no shipped chart. The manifests below are the minimum that reflects how the server actually boots: a Job that provisions the database before the Deployment rolls, two probes answering two different questions, and configuration out of a Secret.
Secret
Section titled “Secret”apiVersion: v1kind: Secretmetadata: name: oauth-serverstringData: MONGODB_URI: mongodb+srv://user:password@cluster.example.mongodb.net ISSUER: https://auth.example.com DATABASE_NAME: OAuthFor PostgreSQL, the Secret holds POSTGRES_URL and ISSUER instead, since the database name is
part of that URL, and the Job’s command becomes db:setup:pg. Set one connection string, never
both. The server refuses to start when both are set instead of picking one, and that is the failure
mode you want: a server that silently chose the other database looks exactly like total data loss.
PostgreSQL covers it.
Only the connection string is a credential; keeping all of them together is simply one envFrom
instead of two. Each variable is described in the
environment variables reference. The published image tags are
listed on the GitHub releases page and
on the
container package page.
Provisioning Job
Section titled “Provisioning Job”apiVersion: batch/v1kind: Jobmetadata: name: oauth-server-setup-<version>spec: backoffLimit: 2 template: spec: restartPolicy: Never containers: - name: setup image: ghcr.io/redfox-soft/oauth-server-ts:<version> command: ['sh', '-c', 'bun run db:setup && bun run db:migrate'] envFrom: - secretRef: name: oauth-serverRun this to completion before rolling the Deployment, and gate the rollout on it: an
argocd.argoproj.io/hook: PreSync annotation, a Helm pre-install,pre-upgrade hook, or simply
kubectl wait --for=condition=complete job/oauth-server-setup-<version> in your pipeline.
The reason is the same one that puts release_command in the shipped fly.toml: without the
reserved admin project, bucket and client the seed creates, GET /admin/login redirects to /auth
with a client_id no client matches and the console is unreachable, while the first-run setup page
still answers. From outside, that is easy to mistake for a login bug when the seed is what is
missing. The Job also provisions the TTL and unique-e-mail indexes the collections would otherwise
be created without.
db:migrate follows it in the same Job because the server refuses to start against a database whose
migrations have not run: a release that declares one would otherwise roll out pods that cannot boot.
Both are safe to repeat; db:setup exits non-zero when a constraint it declares is not in force, so
a failed Job is a real signal and should stop the rollout. Name the Job after the release (as above)
so each upgrade runs its own, and take a backup before one whose release notes name a migration that
cannot be undone — bun run db:migrate --plan lists what would run and exits non-zero when work is
outstanding, so a pipeline can stop there for it.
Deployment and Service
Section titled “Deployment and Service”apiVersion: apps/v1kind: Deploymentmetadata: name: oauth-serverspec: replicas: 1 selector: matchLabels: app: oauth-server template: metadata: labels: app: oauth-server spec: containers: - name: oauth-server image: ghcr.io/redfox-soft/oauth-server-ts:<version> ports: - containerPort: 3000 envFrom: - secretRef: name: oauth-server readinessProbe: httpGet: path: /ready port: 3000 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /health port: 3000 periodSeconds: 30 resources: requests: cpu: 250m memory: 512Mi limits: memory: 1Gi---apiVersion: v1kind: Servicemetadata: name: oauth-serverspec: selector: app: oauth-server ports: - port: 80 targetPort: 3000The two probes answer two different questions and must not share a path. /health says the process
is alive: it touches nothing outside itself, is served unconditionally with no feature flag behind
it, and is the only route the rate limiter never counts — a refused liveness probe would restart a
healthy pod. /ready says the pod can actually serve, which means reaching the datastore; when the
database goes away it fails and the pod leaves the endpoints list, and when the database comes back
it passes again within about a second, with no restart in between.
Both mistakes are worth naming, because they fail in opposite directions. Pointing the readiness
probe at /health makes an outage invisible: the process is alive, so the probe passes, and the pod
keeps receiving requests it cannot serve. Pointing the liveness probe at /ready kills every
replica for the database’s outage, in a loop, and a restart fixes nothing.
Terminate TLS at the Ingress and set ISSUER to the public https URL; the
reverse proxy page applies to an Ingress controller exactly as it
does to nginx, including the rateLimit.trustedProxy decision.
Admitting only images we signed
Section titled “Admitting only images we signed”Every release is signed with cosign over its digest, keyless, and carries an SBOM and build
provenance. A cluster can therefore refuse to run anything at this image name that did not come
from this project’s release workflow. Whichever admission controller you use (sigstore
policy-controller, Kyverno’s
verifyImages, Connaisseur), a keyless policy needs the same two values, and they are what gives
such a policy any meaning:
| Field | Value |
|---|---|
| OIDC issuer | https://token.actions.githubusercontent.com |
| Subject (regexp) | ^https://github\.com/RedFox-Soft/OAuth-server\.ts/\.github/workflows/release\.yml@refs/tags/v |
A policy that checks for a signature without pinning that identity admits anything anyone has signed, which is every image on the internet. The same two values verify by hand before you roll: cosign verify and the attestations.
Scaling notes
Section titled “Scaling notes”- Sessions, tokens, codes and interactions live in the database and never in process memory, so more than one replica is fine as far as the protocol is concerned.
- Signing keys need no restart. Every issuer’s key set, the instance’s included, is read from the database and cached for thirty seconds per replica, so a generated, promoted or retired key reaches every replica within that time — which is why a new key cannot be promoted to signing until it has been published for a minute.
- Server settings are the exception, as the caution above says: a save applies to the replica that
served it, and the others take it on their next restart
(
kubectl rollout restart deployment/oauth-server).