Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions deploy/docker/Dockerfile.backend
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# FinMind Backend - Production Dockerfile
FROM python:3.12-slim AS base
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc libpq-dev && rm -rf /var/lib/apt/lists/*

FROM base AS deps
COPY packages/backend/requirements*.txt ./
RUN pip install --no-cache-dir -r requirements.txt 2>/dev/null || pip install --no-cache-dir flask gunicorn psycopg2-binary redis prometheus-client

FROM base AS runtime
COPY --from=deps /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=deps /usr/local/bin /usr/local/bin
COPY packages/backend/ .

ENV PROMETHEUS_MULTIPROC_DIR=/tmp/prometheus_multiproc
RUN mkdir -p $PROMETHEUS_MULTIPROC_DIR

EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1

CMD ["sh", "-c", "python -m flask --app wsgi:app init-db && gunicorn --workers=2 --threads=4 --bind 0.0.0.0:8000 --access-logfile - --error-logfile - wsgi:app"]
14 changes: 14 additions & 0 deletions deploy/docker/Dockerfile.frontend
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# FinMind Frontend - Production Dockerfile (multi-stage)
FROM node:20-alpine AS build
WORKDIR /app
COPY app/package*.json ./
RUN npm ci --production=false
COPY app/ .
RUN npm run build

FROM nginx:1.27-alpine AS production
COPY --from=build /app/dist /usr/share/nginx/html
COPY deploy/docker/nginx-frontend.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD wget -q --spider http://localhost/ || exit 1
CMD ["nginx", "-g", "daemon off;"]
22 changes: 22 additions & 0 deletions deploy/docker/nginx-frontend.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
server {
listen 80;
root /usr/share/nginx/html;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}

location /api/ {
proxy_pass http://backend:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /health {
access_log off;
return 200 'OK';
}
}
81 changes: 81 additions & 0 deletions deploy/docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# FinMind Universal Deployment Guide

## Quick Start

### Docker Compose (Recommended for local/VPS)
```bash
cd deploy/docker
cp ../../.env.example ../../.env # Edit with your values
docker compose -f docker-compose.prod.yml up -d --build
```
- Frontend: http://localhost
- Backend: http://localhost:8000
- Grafana: http://localhost:3000

### Kubernetes (Production)
```bash
cd deploy/scripts
./deploy-kubernetes.sh
```

### Tilt (Local K8s Dev)
```bash
cd deploy/tilt
tilt up
```
- Backend: http://localhost:8000 (port-forwarded)
- Frontend: http://localhost:3080 (port-forwarded)

## Platform-Specific Deployments

| Platform | Script | Prerequisites |
|----------|--------|---------------|
| Railway | `deploy-railway.sh` | `railway` CLI |
| Fly.io | `deploy-flyio.sh` | `flyctl` CLI |
| Render | `deploy-render.sh` | Render account + GitHub repo |
| Heroku | `deploy-heroku.sh` | `heroku` CLI |
| AWS ECS | `deploy-aws-ecs.sh` | `aws` CLI + configured credentials |
| GCP Cloud Run | `deploy-gcp-cloudrun.sh` | `gcloud` CLI + project |
| Azure | `deploy-azure.sh` | `az` CLI + subscription |
| DigitalOcean | `deploy-digitalocean.sh` | `doctl` CLI |
| Netlify | `deploy-netlify.sh` | `netlify-cli` (frontend only) |
| Vercel | `deploy-vercel.sh` | `vercel` CLI (frontend only) |
| Kubernetes | `deploy-kubernetes.sh` | `kubectl` + `helm` |

## Kubernetes Architecture

```
┌─────────────────────────────────────────────┐
│ Ingress │
│ (TLS via cert-manager) │
├──────────────────┬──────────────────────────┤
│ Frontend │ Backend │
│ (Nginx/React) │ (Flask/Gunicorn) │
│ Replicas: 2+ │ Replicas: 2+ │
│ HPA enabled │ HPA enabled │
├──────────────────┴──────────────────────────┤
│ PostgreSQL │ Redis │
│ (Persistent) │ (In-memory) │
├─────────────────────────────────────────────┤
│ Prometheus │ Grafana │ ServiceMonitor │
└─────────────────────────────────────────────┘
```

## Helm Values Override Example
```bash
helm install finmind deploy/kubernetes/helm \
--set ingress.hosts[0].host=myfinmind.com \
--set autoscaling.maxReplicas=20 \
--set resources.backend.limits.memory=1Gi
```

## Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| POSTGRES_USER | Yes | finmind | Database username |
| POSTGRES_PASSWORD | Yes | - | Database password |
| POSTGRES_DB | Yes | finmind | Database name |
| SECRET_KEY | Yes | - | Flask secret key |
| REDIS_URL | No | redis://redis:6379 | Redis connection |
| DATABASE_URL | No | (auto) | PostgreSQL connection |
10 changes: 10 additions & 0 deletions deploy/kubernetes/helm/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v2
name: finmind
description: FinMind - Personal Finance Management Platform
type: application
version: 1.0.0
appVersion: "1.0.0"
keywords: [finance, budgeting, personal-finance]
maintainers:
- name: Padrao Bitcoin
email: standardbitcoin.io@gmail.com
63 changes: 63 additions & 0 deletions deploy/kubernetes/helm/templates/backend-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-backend
labels:
app.kubernetes.io/name: finmind-backend
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount.backend }}
selector:
matchLabels:
app.kubernetes.io/name: finmind-backend
template:
metadata:
labels:
app.kubernetes.io/name: finmind-backend
spec:
containers:
- name: backend
image: "{{ .Values.image.backend.repository }}:{{ .Values.image.backend.tag }}"
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-secrets
key: database-url
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-secrets
key: redis-url
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-secrets
key: secret-key
resources:
{{- toYaml .Values.resources.backend | nindent 12 }}
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-backend
spec:
selector:
app.kubernetes.io/name: finmind-backend
ports:
- port: 8000
targetPort: 8000
42 changes: 42 additions & 0 deletions deploy/kubernetes/helm/templates/frontend-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-frontend
spec:
replicas: {{ .Values.replicaCount.frontend }}
selector:
matchLabels:
app.kubernetes.io/name: finmind-frontend
template:
metadata:
labels:
app.kubernetes.io/name: finmind-frontend
spec:
containers:
- name: frontend
image: "{{ .Values.image.frontend.repository }}:{{ .Values.image.frontend.tag }}"
ports:
- containerPort: 80
resources:
{{- toYaml .Values.resources.frontend | nindent 12 }}
livenessProbe:
httpGet:
path: /health
port: 80
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-frontend
spec:
selector:
app.kubernetes.io/name: finmind-frontend
ports:
- port: 80
targetPort: 80
26 changes: 26 additions & 0 deletions deploy/kubernetes/helm/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ .Release.Name }}-backend-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ .Release.Name }}-backend
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
29 changes: 29 additions & 0 deletions deploy/kubernetes/helm/templates/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-ingress
annotations:
{{- toYaml .Values.ingress.annotations | nindent 4 }}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.ingress.tls }}
tls:
{{- toYaml .Values.ingress.tls | nindent 4 }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ $.Release.Name }}-{{ .service }}
port:
number: {{ if eq .service "backend" }}8000{{ else }}80{{ end }}
{{- end }}
{{- end }}
{{- end }}
9 changes: 9 additions & 0 deletions deploy/kubernetes/helm/templates/secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-secrets
type: Opaque
stringData:
database-url: {{ .Values.secrets.databaseUrl | default "postgresql://finmind:finmind@finmind-postgresql:5432/finmind" }}
redis-url: {{ .Values.secrets.redisUrl | default "redis://finmind-redis-master:6379" }}
secret-key: {{ .Values.secrets.secretKey | default (randAlphaNum 32) }}
14 changes: 14 additions & 0 deletions deploy/kubernetes/helm/templates/servicemonitor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{- if .Values.monitoring.serviceMonitor }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ .Release.Name }}-backend
spec:
selector:
matchLabels:
app.kubernetes.io/name: finmind-backend
endpoints:
- port: "8000"
path: /metrics
interval: 30s
{{- end }}
Loading