6.8 KiB
Deploy Helm Chart
Monolithic Helm chart for all backend services.
Services
| Service | Port(s) | Replicas | HPA | Purpose |
|---|---|---|---|---|
app |
3000 (HTTP) | 2 | 2–10 | Main API server |
gitserver |
8021 (HTTP), 2222 (SSH) | 1 | 1–5 | Git HTTP + SSH server |
email_worker |
8084 (HTTP) | 1 | disabled | Email queue consumer (single instance only) |
git_hook |
8083 (HTTP) | 1 | 1–5 | Git hook worker pool |
metrics_aggregator |
9090 (HTTP) | 1 | 1–5 | Prometheus scrape + Loki push |
static_server |
8081 (HTTP) | 1 | 1–5 | Static file server (avatars, blobs, media) |
Prerequisites
The following resources must exist in the cluster before installing the Helm chart. They are not managed by Helm — install, upgrade, and uninstall of the chart will not touch them.
1. Namespace
kubectl create namespace app
2. PVC (aliyun-nfs-app, 200Ti, ReadWriteMany)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-data
namespace: app
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Ti
storageClassName: aliyun-nfs-app
EOF
The chart references this PVC by hardcoded name
shared-data. This name is immutable — it cannot be changed via Helm values.
3. ConfigMap
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: app-env
namespace: app
data:
APP_REPOS_ROOT: "/data/repos"
APP_AVATAR_PATH: "/data/avatars"
STORAGE_PATH: "/data/files"
STATIC_ROOT: "/data"
APP_LOG_LEVEL: "info"
APP_COOKIE_SECURE: "false"
APP_DOMAIN_URL: "https://your-domain.com"
APP_DATABASE_URL: "postgres://user:pass@postgres:5432/app"
APP_REDIS_URL: "redis://redis:6379"
APP_AI_BASIC_URL: "https://api.openai.com/v1"
APP_AI_API_KEY: "sk-..."
APP_SMTP_PASSWORD: "..."
APP_SESSION_SECRET: "min-32-byte-random-string..."
APP_SSH_SERVER_PRIVATE_KEY: "<hex-encoded-private-key>"
EOF
| Variable | Default / Example | Required |
|---|---|---|
APP_REPOS_ROOT |
/data/repos |
Yes |
APP_AVATAR_PATH |
/data/avatars |
Yes |
STORAGE_PATH |
/data/files |
Yes |
STATIC_ROOT |
/data |
Yes |
APP_LOG_LEVEL |
info |
No |
APP_COOKIE_SECURE |
false |
No |
APP_DOMAIN_URL |
https://your-domain.com |
Yes |
APP_DATABASE_URL |
postgres://... |
Yes |
APP_REDIS_URL |
redis://... |
Yes |
APP_AI_BASIC_URL |
https://api.openai.com/v1 |
Yes |
APP_AI_API_KEY |
sk-... |
Yes |
APP_SMTP_PASSWORD |
... |
Yes |
APP_SESSION_SECRET |
min 32 bytes | Yes |
APP_SSH_SERVER_PRIVATE_KEY |
hex-encoded PEM | Yes |
APP_SSH_PORT |
2222 |
Yes (k8s) |
SSH host key:
APP_SSH_SERVER_PRIVATE_KEYmust be the hex-encoded Ed25519 private key PEM bytes.ssh-keygen -t ed25519 -f /tmp/ssh_host_key -N "" hexdump -v -e '/1 "%02x"' < /tmp/ssh_host_keySession secret: generate 48 random bytes:
openssl rand -base64 48Override the ConfigMap name with
--set configMapName=your-cm-name.
4. Verify prerequisites
kubectl get namespace app
kubectl get pvc -n app shared-data
kubectl get configmap -n app app-env
Quick Start
helm template deploy ./deploy --namespace app --set imageRegistry=ghcr.io/your-org
helm lint ./deploy
# Install
helm upgrade --install deploy ./deploy \
--namespace app \
--set imageRegistry=ghcr.io/your-org \
--set imageTag=v0.2.9
Storage
All services share a single PVC (shared-data) via subPath mounts:
| SubPath | Mount | Used By |
|---|---|---|
repos |
/data/repos |
app, gitserver, git-hook |
avatars |
/data/avatars |
app |
files |
/data/files |
app |
static |
/data |
static-server |
Pods run as UID/GID 1000 and set fsGroup: 1000 so Git processes can create temporary object
directories under bare repositories. If an existing PVC was previously written by another UID,
fix ownership once from a maintenance pod:
chown -R 1000:1000 /data/repos
chmod -R u+rwX,g+rwX /data/repos
Autoscaling
All services except email_worker have HPA enabled by default. The email worker is fixed at 1 replica and must not be
scaled.
To adjust HPA bounds per service:
--set services.app.autoscaling.maxReplicas=20
--set services.app.autoscaling.targetCPUUtilization=70
To disable HPA for a service:
--set services.git_hook.autoscaling.enabled=false
Ingress
helm upgrade --install deploy ./deploy \
--namespace app \
--set ingress.enabled=true \
--set ingress.className=nginx \
--set ingress.hosts[0].host=your-domain.com
Dependencies
All services require these to be reachable from the cluster:
- PostgreSQL (via
APP_DATABASE_URL) - Redis (via
APP_REDIS_URL) - Git binary (included in all Docker images)
- OpenAI-compatible API (via
APP_AI_BASIC_URL+APP_AI_API_KEY) - Qdrant vector DB (via
APP_QDRANT_URL) - SMTP server (via
APP_SMTP_*) - Embedding model (via
APP_EMBED_MODEL_*)
Optional dependencies with graceful degradation:
| Dependency | Variable | Fallback |
|---|---|---|
| NATS JetStream | NATS_URL + NATS_TOKEN |
Redis queue |
| Loki | LOKI_URL |
Logs discarded |
| OTEL Collector | OTEL_EXPORTER_OTLP_ENDPOINT |
Tracing disabled |
Production Example
helm upgrade --install deploy ./deploy \
--namespace app \
--set imageRegistry=ghcr.io/your-org \
--set imageTag=v0.2.9 \
--set services.app.replicas=3 \
--set services.app.autoscaling.maxReplicas=20 \
--set ingress.enabled=true \
--set ingress.className=nginx \
--set ingress.hosts[0].host=your-domain.com \
--set configMapName=app-env