Adds complete k8s/ manifest tree: Namespace, VaultAuth + VaultStaticSecret CRDs (VSO secret sync from Vault KV v2), API and UI Deployments and Services, nginx Ingress with cert-manager TLS, MinIO StatefulSet with PVC and init Job, and Alembic init container on the API Deployment for automatic schema migrations. Includes .yamllint.yml config and validate-k8s Makefile target. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
93 lines
2.9 KiB
Markdown
93 lines
2.9 KiB
Markdown
# Quickstart: Kubernetes Production Deployment
|
|
|
|
## Before You Apply
|
|
|
|
1. Store API secrets in Vault at `reactbin/api/config` (KV v2):
|
|
```
|
|
DATABASE_URL = postgresql+asyncpg://reactbin:<pw>@<host>:5432/reactbin
|
|
JWT_SECRET_KEY = <long-random-string>
|
|
OWNER_USERNAME = <your-username>
|
|
OWNER_PASSWORD = <your-password>
|
|
S3_ENDPOINT_URL = http://minio.reactbin.svc.cluster.local:9000
|
|
S3_BUCKET_NAME = reactbin
|
|
S3_ACCESS_KEY_ID = <same as MINIO_ROOT_USER>
|
|
S3_SECRET_ACCESS_KEY = <same as MINIO_ROOT_PASSWORD>
|
|
API_BASE_URL = https://<your-domain>
|
|
API_DOCS_ENABLED = false
|
|
```
|
|
|
|
2. Store MinIO credentials in Vault at `reactbin/minio/credentials` (KV v2):
|
|
```
|
|
MINIO_ROOT_USER = <choose a strong username>
|
|
MINIO_ROOT_PASSWORD = <choose a strong password>
|
|
```
|
|
|
|
3. Create a Vault Kubernetes auth role bound to the `default` service account in the `reactbin` namespace with read access to both paths above.
|
|
|
|
4. Confirm DNS resolves to the cluster ingress IP and the `letsencrypt-prod` ClusterIssuer exists.
|
|
|
|
## Deploy
|
|
|
|
```bash
|
|
# Substitute the real image tags
|
|
sed -i 's|reactbin-api:latest|reactbin-api:v1.0.0|g' k8s/api/deployment.yaml
|
|
sed -i 's|reactbin-ui:latest|reactbin-ui:v1.0.0|g' k8s/ui/deployment.yaml
|
|
|
|
# Apply everything
|
|
kubectl apply -f k8s/
|
|
```
|
|
|
|
## Verify
|
|
|
|
```bash
|
|
# Watch pods come up (init container runs first on the API pod)
|
|
kubectl get pods -n reactbin -w
|
|
|
|
# API health
|
|
curl -sf https://<your-domain>/api/v1/health && echo "API OK"
|
|
|
|
# UI reachable
|
|
curl -sf -o /dev/null -w "%{http_code}\n" https://<your-domain>/
|
|
|
|
# Docs correctly gated
|
|
curl -o /dev/null -w "%{http_code}\n" https://<your-domain>/docs # → 404
|
|
curl -o /dev/null -w "%{http_code}\n" https://<your-domain>/redoc # → 404
|
|
|
|
# Check migration init container ran
|
|
kubectl logs -n reactbin -l app=api -c alembic-migrate
|
|
```
|
|
|
|
## Scenario: Migration fails on deploy
|
|
|
|
```bash
|
|
# Pod will be stuck in Init state
|
|
kubectl get pods -n reactbin
|
|
# NAME READY STATUS RESTARTS
|
|
# api-xxx-yyy 0/1 Init:CrashLoopBackOff 2
|
|
|
|
# See why
|
|
kubectl logs -n reactbin <pod-name> -c alembic-migrate
|
|
|
|
# Fix the issue (e.g. correct DATABASE_URL in Vault, wait for VSO to resync)
|
|
# Then delete the pod to force a fresh rollout
|
|
kubectl rollout restart deployment/api -n reactbin
|
|
```
|
|
|
|
## Scenario: Update to a new image version
|
|
|
|
```bash
|
|
kubectl set image deployment/api api=reactbin-api:v1.1.0 -n reactbin
|
|
kubectl set image deployment/ui ui=reactbin-ui:v1.1.0 -n reactbin
|
|
# Kubernetes rolls out new pods; init container runs migrations before traffic switches
|
|
```
|
|
|
|
## Scenario: Restore after MinIO pod restart
|
|
|
|
MinIO uses a PersistentVolumeClaim. Pod restarts do not affect stored data. Verify:
|
|
|
|
```bash
|
|
kubectl delete pod -n reactbin minio-0
|
|
kubectl get pods -n reactbin -w # minio-0 restarts, PVC reattaches
|
|
# Previously uploaded images should still be accessible via the API
|
|
```
|