- Definition of extra eveai_ops service to run (db) jobs
- Definition of manifests for all jobs - Definition of manifests for all eveai services
This commit is contained in:
@@ -24,6 +24,7 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
COPY ../common /app/common
|
COPY ../common /app/common
|
||||||
COPY ../config /app/config
|
COPY ../config /app/config
|
||||||
COPY ../scripts /app/scripts
|
COPY ../scripts /app/scripts
|
||||||
|
COPY ../patched_packages /app/patched_packages
|
||||||
|
|
||||||
RUN chown -R appuser:appuser /app && chmod +x /app/scripts/start.sh
|
RUN chown -R appuser:appuser /app && chmod +x /app/scripts/start.sh
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,51 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- eveai-dev-network
|
- eveai-dev-network
|
||||||
|
|
||||||
|
eveai_ops:
|
||||||
|
image: ${REGISTRY_PREFIX:-}josakola/eveai_ops:latest
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: ./docker/eveai_ops/Dockerfile
|
||||||
|
ports:
|
||||||
|
- 3002:8080 # Dev app volgens port schema
|
||||||
|
expose:
|
||||||
|
- 8000
|
||||||
|
environment:
|
||||||
|
<<: *common-variables
|
||||||
|
COMPONENT_NAME: eveai_ops
|
||||||
|
ROLE: web
|
||||||
|
PORT: 8080
|
||||||
|
WORKERS: 1 # Dev: lagere concurrency
|
||||||
|
WORKER_CLASS: gevent
|
||||||
|
WORKER_CONN: 100
|
||||||
|
LOGLEVEL: debug # Lowercase voor gunicorn
|
||||||
|
MAX_REQUESTS: 1000
|
||||||
|
MAX_REQUESTS_JITTER: 100
|
||||||
|
volumes:
|
||||||
|
- ../eveai_ops:/app/eveai_ops
|
||||||
|
- ../common:/app/common
|
||||||
|
- ../content:/app/content
|
||||||
|
- ../config:/app/config
|
||||||
|
- ../migrations:/app/migrations
|
||||||
|
- ../scripts:/app/scripts
|
||||||
|
- ../patched_packages:/app/patched_packages
|
||||||
|
- ./eveai_logs:/app/logs
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
minio:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "-f", "http://localhost:8080/healthz/ready" ]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 60s
|
||||||
|
networks:
|
||||||
|
- eveai-dev-network
|
||||||
|
|
||||||
eveai_app:
|
eveai_app:
|
||||||
image: ${REGISTRY_PREFIX:-}josakola/eveai_app:latest
|
image: ${REGISTRY_PREFIX:-}josakola/eveai_app:latest
|
||||||
build:
|
build:
|
||||||
|
|||||||
4
docker/eveai_ops/Dockerfile
Normal file
4
docker/eveai_ops/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
FROM registry.ask-eve-ai-local.com/josakola/eveai-base:latest
|
||||||
|
# Copy the source code into the container.
|
||||||
|
COPY eveai_ops /app/eveai_ops
|
||||||
|
COPY migrations /app/migrations
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM registry.ask-eve-ai-local.com/josakola/eveai-base:latest
|
pcupFROM registry.ask-eve-ai-local.com/josakola/eveai-base:latest
|
||||||
|
|
||||||
# Service-specific packages (ffmpeg only needed for this service)
|
# Service-specific packages (ffmpeg only needed for this service)
|
||||||
USER root
|
USER root
|
||||||
|
|||||||
@@ -462,6 +462,38 @@ kubectl -n tools port-forward svc/pgadmin-pgadmin4 8080:80
|
|||||||
# Login with PGADMIN_DEFAULT_EMAIL / PGADMIN_DEFAULT_PASSWORD (from eveai-pgadmin-admin)
|
# Login with PGADMIN_DEFAULT_EMAIL / PGADMIN_DEFAULT_PASSWORD (from eveai-pgadmin-admin)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Phase 8: RedisInsight Tool Deployment
|
||||||
|
|
||||||
|
|
||||||
|
### Phase 9: Ops Jobs Invocation (if required)
|
||||||
|
|
||||||
|
Run the DB ops scripts manually in order. Each manifest uses generateName; use kubectl create.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/00-env-check-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=env-check --timeout=600s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/02-db-bootstrap-ext-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-bootstrap-ext --timeout=1800s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/03-db-migrate-public-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-migrate-public --timeout=1800s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/04-db-migrate-tenant-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-migrate-tenant --timeout=3600s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/05-seed-or-init-data-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-seed-or-init --timeout=1800s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/06-verify-minimal-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-verify-minimal --timeout=900s
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Phase 10: Application Services Deployment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Verification and Testing
|
## Verification and Testing
|
||||||
|
|
||||||
|
|||||||
133
documentation/Production Setup/phase-8-application-services.md
Normal file
133
documentation/Production Setup/phase-8-application-services.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Phase 8: Application Services (Staging)
|
||||||
|
|
||||||
|
This guide describes how to deploy EveAI application services to the Scaleway Kubernetes cluster, building on Phases 1–7 in cluster-install.md.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
- Ingress-NGINX running with external IP
|
||||||
|
- cert-manager installed and Certificate evie-staging-tls is READY (via HTTP ACME first, then HTTPS-only)
|
||||||
|
- External Secrets Operator installed; Kubernetes Secret eveai-secrets exists in namespace eveai-staging
|
||||||
|
- Verification service deployed and reachable via /verify
|
||||||
|
- Optional: Monitoring stack running, Pushgateway deployed or reachable; PUSH_GATEWAY_HOST/PORT available to apps (via eveai-secrets)
|
||||||
|
|
||||||
|
## What we deploy (structure)
|
||||||
|
- Frontend (web) services
|
||||||
|
- eveai-app → exposed at /admin
|
||||||
|
- eveai-api → exposed at /api
|
||||||
|
- eveai-chat-client → exposed at /client
|
||||||
|
- Backend worker services (internal)
|
||||||
|
- eveai-workers (queue: embeddings)
|
||||||
|
- eveai-chat-workers (queue: llm_interactions)
|
||||||
|
- eveai-entitlements (queue: entitlements)
|
||||||
|
- Ops Jobs (manual DB ops)
|
||||||
|
- 00-env-check
|
||||||
|
- 02-db-bootstrap-ext
|
||||||
|
- 03-db-migrate-public
|
||||||
|
- 04-db-migrate-tenant
|
||||||
|
- 05-seed-or-init-data
|
||||||
|
- 06-verify-minimal
|
||||||
|
|
||||||
|
Manifests are under:
|
||||||
|
- scaleway/manifests/base/applications/frontend/
|
||||||
|
- scaleway/manifests/base/applications/backend/
|
||||||
|
- scaleway/manifests/base/applications/ops/jobs/
|
||||||
|
- Aggregate kustomization: scaleway/manifests/base/applications/kustomization.yaml
|
||||||
|
|
||||||
|
## Step 1: Validate secrets
|
||||||
|
```bash
|
||||||
|
kubectl get secret eveai-secrets -n eveai-staging
|
||||||
|
kubectl get secret eveai-secrets -n eveai-staging -o jsonpath='{.data}' | jq 'keys'
|
||||||
|
```
|
||||||
|
Confirm presence of DB_*, REDIS_*, OPENAI_API_KEY, MISTRAL_API_KEY, JWT_SECRET_KEY, API_ENCRYPTION_KEY, MINIO_*, PUSH_GATEWAY_HOST, PUSH_GATEWAY_PORT.
|
||||||
|
|
||||||
|
## Step 2: Deploy Ops Jobs (manual pre-deploy)
|
||||||
|
Run the DB ops scripts manually in order. Each manifest uses generateName; use kubectl create.
|
||||||
|
```bash
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/00-env-check-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=env-check --timeout=600s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/02-db-bootstrap-ext-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-bootstrap-ext --timeout=1800s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/03-db-migrate-public-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-migrate-public --timeout=1800s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/04-db-migrate-tenant-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-migrate-tenant --timeout=3600s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/05-seed-or-init-data-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-seed-or-init --timeout=1800s
|
||||||
|
|
||||||
|
kubectl create -f scaleway/manifests/base/applications/ops/jobs/06-verify-minimal-job.yaml
|
||||||
|
kubectl wait --for=condition=complete job -n eveai-staging -l job-type=db-verify-minimal --timeout=900s
|
||||||
|
```
|
||||||
|
View logs:
|
||||||
|
```bash
|
||||||
|
kubectl -n eveai-staging get jobs
|
||||||
|
kubectl -n eveai-staging logs job/<created-job-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3: Deploy backend workers
|
||||||
|
```bash
|
||||||
|
kubectl apply -k scaleway/manifests/base/applications/backend/
|
||||||
|
|
||||||
|
kubectl -n eveai-staging get deploy | egrep 'eveai-(workers|chat-workers|entitlements)'
|
||||||
|
# Optional: quick logs
|
||||||
|
kubectl -n eveai-staging logs deploy/eveai-workers --tail=100 || true
|
||||||
|
kubectl -n eveai-staging logs deploy/eveai-chat-workers --tail=100 || true
|
||||||
|
kubectl -n eveai-staging logs deploy/eveai-entitlements --tail=100 || true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Deploy frontend services
|
||||||
|
```bash
|
||||||
|
kubectl apply -k scaleway/manifests/base/applications/frontend/
|
||||||
|
|
||||||
|
kubectl -n eveai-staging get deploy,svc | egrep 'eveai-(app|api|chat-client)'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Verify Ingress routes
|
||||||
|
The HTTPS ingress has paths enabled for /admin, /api, /client. Verify:
|
||||||
|
```bash
|
||||||
|
kubectl -n eveai-staging describe ingress eveai-staging-ingress
|
||||||
|
|
||||||
|
curl -k https://evie-staging.askeveai.com/verify/health
|
||||||
|
curl -k https://evie-staging.askeveai.com/admin/healthz/ready
|
||||||
|
curl -k https://evie-staging.askeveai.com/api/healthz/ready
|
||||||
|
curl -k https://evie-staging.askeveai.com/client/healthz/ready
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources and probes (staging defaults)
|
||||||
|
- Web (app, api, chat-client):
|
||||||
|
- requests: 150m CPU, 256Mi RAM; limits: 500m CPU, 512Mi RAM; replicas: 1
|
||||||
|
- readiness/liveness: GET /healthz/ready
|
||||||
|
- Workers:
|
||||||
|
- eveai-workers: req 200m/512Mi, lim 1CPU/1Gi
|
||||||
|
- eveai-chat-workers: req 500m/1Gi, lim 2CPU/3Gi
|
||||||
|
- eveai-entitlements: req 100m/256Mi, lim 500m/512Mi
|
||||||
|
|
||||||
|
## Pushgateway usage
|
||||||
|
- Ensure PUSH_GATEWAY_HOST and PUSH_GATEWAY_PORT are provided (e.g., pushgateway.monitoring.svc.cluster.local:9091), typically via eveai-secrets or a ConfigMap.
|
||||||
|
- Apps will continue to push business metrics; Prometheus scrapes the Pushgateway.
|
||||||
|
|
||||||
|
## Bunny.net WAF (TODO)
|
||||||
|
- Configure Pull Zone for evie-staging.askeveai.com
|
||||||
|
- Set Origin to the LoadBalancer IP with HTTPS and Host header evie-staging.askeveai.com
|
||||||
|
- Define rate limits primarily on /api, looser on /client; enable bot filtering
|
||||||
|
- Only switch DNS (CNAME) to Bunny after TLS issuance completed directly against LoadBalancer
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
```bash
|
||||||
|
kubectl get all -n eveai-staging
|
||||||
|
kubectl get events -n eveai-staging --sort-by=.lastTimestamp
|
||||||
|
kubectl describe ingress eveai-staging-ingress -n eveai-staging
|
||||||
|
kubectl logs -n eveai-staging deploy/eveai-api --tail=200
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rollback / Cleanup
|
||||||
|
```bash
|
||||||
|
# Remove frontend/backend (keeps verification and other base resources)
|
||||||
|
kubectl delete -k scaleway/manifests/base/applications/frontend/
|
||||||
|
kubectl delete -k scaleway/manifests/base/applications/backend/
|
||||||
|
|
||||||
|
# Jobs are kept for history due to ttlSecondsAfterFinished; to delete immediately:
|
||||||
|
kubectl -n eveai-staging delete jobs --all
|
||||||
|
```
|
||||||
86
eveai_ops/__init__.py
Normal file
86
eveai_ops/__init__.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from flask import Flask
|
||||||
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
import logging.config
|
||||||
|
|
||||||
|
from common.extensions import db, migrate
|
||||||
|
from config.logging_config import configure_logging
|
||||||
|
from config.config import get_config
|
||||||
|
|
||||||
|
|
||||||
|
def create_app(config_file=None):
|
||||||
|
app = Flask(__name__, static_url_path='/static')
|
||||||
|
|
||||||
|
# Ensure all necessary headers are handled
|
||||||
|
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
|
||||||
|
|
||||||
|
environment = os.getenv('FLASK_ENV', 'development')
|
||||||
|
|
||||||
|
match environment:
|
||||||
|
case 'development':
|
||||||
|
app.config.from_object(get_config('dev'))
|
||||||
|
case 'staging':
|
||||||
|
app.config.from_object(get_config('staging'))
|
||||||
|
case 'production':
|
||||||
|
app.config.from_object(get_config('prod'))
|
||||||
|
case _:
|
||||||
|
app.config.from_object(get_config('dev'))
|
||||||
|
|
||||||
|
app.config['SESSION_KEY_PREFIX'] = 'eveai_ops_'
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.makedirs(app.instance_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Configureer logging op basis van de omgeving (K8s of traditioneel)
|
||||||
|
try:
|
||||||
|
configure_logging()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
# Test dat logging werkt
|
||||||
|
logger.debug("Logging test in eveai_ops")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Critical Error Initialising Error: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
logger.info("eveai_ops starting up")
|
||||||
|
|
||||||
|
# Register extensions
|
||||||
|
|
||||||
|
register_extensions(app)
|
||||||
|
|
||||||
|
# Register Blueprints
|
||||||
|
register_blueprints(app)
|
||||||
|
|
||||||
|
# Register Cache Handlers
|
||||||
|
register_cache_handlers(app)
|
||||||
|
|
||||||
|
# Debugging settings
|
||||||
|
if app.config['DEBUG'] is True:
|
||||||
|
app.logger.setLevel(logging.DEBUG)
|
||||||
|
security_logger = logging.getLogger('flask_security')
|
||||||
|
security_logger.setLevel(logging.DEBUG)
|
||||||
|
sqlalchemy_logger = logging.getLogger('sqlalchemy.engine')
|
||||||
|
sqlalchemy_logger.setLevel(logging.DEBUG)
|
||||||
|
# log_request_middleware(app) # Add this when debugging nginx or another proxy
|
||||||
|
|
||||||
|
app.logger.info(f"EveAI Ops Server Started Successfully (PID: {os.getpid()})")
|
||||||
|
app.logger.info("-------------------------------------------------------------------------------------------------")
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
def register_extensions(app):
|
||||||
|
db.init_app(app)
|
||||||
|
migrate.init_app(app, db)
|
||||||
|
|
||||||
|
|
||||||
|
def register_blueprints(app):
|
||||||
|
# minimal health blueprint
|
||||||
|
from .healthz import healthz_bp
|
||||||
|
app.register_blueprint(healthz_bp)
|
||||||
|
|
||||||
|
|
||||||
|
def register_cache_handlers(app):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: eveai-chat-workers
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-chat-workers
|
||||||
|
component: backend
|
||||||
|
role: worker
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: eveai-chat-workers
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai-chat-workers
|
||||||
|
component: backend
|
||||||
|
role: worker
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: eveai-chat-workers
|
||||||
|
image: josakola/eveai_chat_workers:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
env:
|
||||||
|
- name: COMPONENT_NAME
|
||||||
|
value: "eveai_chat_workers"
|
||||||
|
- name: ROLE
|
||||||
|
value: "worker"
|
||||||
|
- name: CELERY_CONCURRENCY
|
||||||
|
value: "1"
|
||||||
|
- name: CELERY_LOGLEVEL
|
||||||
|
value: "DEBUG"
|
||||||
|
- name: CELERY_MAX_TASKS_PER_CHILD
|
||||||
|
value: "1000"
|
||||||
|
- name: CELERY_PREFETCH
|
||||||
|
value: "1"
|
||||||
|
- name: CELERY_QUEUE_NAME
|
||||||
|
value: "llm_interactions"
|
||||||
|
- name: PUSH_GATEWAY_HOST
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_HOST
|
||||||
|
optional: true
|
||||||
|
- name: PUSH_GATEWAY_PORT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_PORT
|
||||||
|
optional: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "1Gi"
|
||||||
|
limits:
|
||||||
|
cpu: "2"
|
||||||
|
memory: "3Gi"
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: eveai-entitlements
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-entitlements
|
||||||
|
component: backend
|
||||||
|
role: worker
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: eveai-entitlements
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai-entitlements
|
||||||
|
component: backend
|
||||||
|
role: worker
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: eveai-entitlements
|
||||||
|
image: josakola/eveai_entitlements:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
env:
|
||||||
|
- name: COMPONENT_NAME
|
||||||
|
value: "eveai_entitlements"
|
||||||
|
- name: ROLE
|
||||||
|
value: "worker"
|
||||||
|
- name: CELERY_CONCURRENCY
|
||||||
|
value: "1"
|
||||||
|
- name: CELERY_LOGLEVEL
|
||||||
|
value: "DEBUG"
|
||||||
|
- name: CELERY_MAX_TASKS_PER_CHILD
|
||||||
|
value: "1000"
|
||||||
|
- name: CELERY_PREFETCH
|
||||||
|
value: "1"
|
||||||
|
- name: CELERY_QUEUE_NAME
|
||||||
|
value: "entitlements"
|
||||||
|
- name: PUSH_GATEWAY_HOST
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_HOST
|
||||||
|
optional: true
|
||||||
|
- name: PUSH_GATEWAY_PORT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_PORT
|
||||||
|
optional: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "100m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: eveai-workers
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-workers
|
||||||
|
component: backend
|
||||||
|
role: worker
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: eveai-workers
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai-workers
|
||||||
|
component: backend
|
||||||
|
role: worker
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: eveai-workers
|
||||||
|
image: josakola/eveai_workers:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
env:
|
||||||
|
- name: COMPONENT_NAME
|
||||||
|
value: "eveai_workers"
|
||||||
|
- name: ROLE
|
||||||
|
value: "worker"
|
||||||
|
- name: CELERY_CONCURRENCY
|
||||||
|
value: "1"
|
||||||
|
- name: CELERY_LOGLEVEL
|
||||||
|
value: "DEBUG"
|
||||||
|
- name: CELERY_MAX_TASKS_PER_CHILD
|
||||||
|
value: "1000"
|
||||||
|
- name: CELERY_PREFETCH
|
||||||
|
value: "1"
|
||||||
|
- name: CELERY_QUEUE_NAME
|
||||||
|
value: "embeddings"
|
||||||
|
- name: PUSH_GATEWAY_HOST
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_HOST
|
||||||
|
optional: true
|
||||||
|
- name: PUSH_GATEWAY_PORT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_PORT
|
||||||
|
optional: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "200m"
|
||||||
|
memory: "512Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: "1Gi"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namespace: eveai-staging
|
||||||
|
resources:
|
||||||
|
- eveai-workers/deployment.yaml
|
||||||
|
- eveai-chat-workers/deployment.yaml
|
||||||
|
- eveai-entitlements/deployment.yaml
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: eveai-api
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-api
|
||||||
|
component: frontend
|
||||||
|
role: web
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: eveai-api
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai-api
|
||||||
|
component: frontend
|
||||||
|
role: web
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: eveai-api
|
||||||
|
image: josakola/eveai_api:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
env:
|
||||||
|
- name: COMPONENT_NAME
|
||||||
|
value: "eveai_api"
|
||||||
|
- name: ROLE
|
||||||
|
value: "web"
|
||||||
|
- name: PORT
|
||||||
|
value: "8080"
|
||||||
|
- name: PUSH_GATEWAY_HOST
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_HOST
|
||||||
|
optional: true
|
||||||
|
- name: PUSH_GATEWAY_PORT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_PORT
|
||||||
|
optional: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "150m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz/ready
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz/ready
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 20
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: eveai-api-service
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-api
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: eveai-api
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: eveai-app
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-app
|
||||||
|
component: frontend
|
||||||
|
role: web
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: eveai-app
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai-app
|
||||||
|
component: frontend
|
||||||
|
role: web
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: eveai-app
|
||||||
|
image: josakola/eveai_app:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
env:
|
||||||
|
- name: COMPONENT_NAME
|
||||||
|
value: "eveai_app"
|
||||||
|
- name: ROLE
|
||||||
|
value: "web"
|
||||||
|
- name: PORT
|
||||||
|
value: "8080"
|
||||||
|
- name: PUSH_GATEWAY_HOST
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_HOST
|
||||||
|
optional: true
|
||||||
|
- name: PUSH_GATEWAY_PORT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_PORT
|
||||||
|
optional: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "150m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz/ready
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz/ready
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 20
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: eveai-app-service
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-app
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: eveai-app
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: eveai-chat-client
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-chat-client
|
||||||
|
component: frontend
|
||||||
|
role: web
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: eveai-chat-client
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai-chat-client
|
||||||
|
component: frontend
|
||||||
|
role: web
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: eveai-chat-client
|
||||||
|
image: josakola/eveai_chat_client:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
env:
|
||||||
|
- name: COMPONENT_NAME
|
||||||
|
value: "eveai_chat_client"
|
||||||
|
- name: ROLE
|
||||||
|
value: "web"
|
||||||
|
- name: PORT
|
||||||
|
value: "8080"
|
||||||
|
- name: PUSH_GATEWAY_HOST
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_HOST
|
||||||
|
optional: true
|
||||||
|
- name: PUSH_GATEWAY_PORT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
key: PUSH_GATEWAY_PORT
|
||||||
|
optional: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "150m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz/ready
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz/ready
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 20
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: eveai-chat-client-service
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai-chat-client
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: eveai-chat-client
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
type: ClusterIP
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namespace: eveai-staging
|
||||||
|
resources:
|
||||||
|
- eveai-app/deployment.yaml
|
||||||
|
- eveai-api/deployment.yaml
|
||||||
|
- eveai-chat-client/deployment.yaml
|
||||||
8
scaleway/manifests/base/applications/kustomization.yaml
Normal file
8
scaleway/manifests/base/applications/kustomization.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namespace: eveai-staging
|
||||||
|
resources:
|
||||||
|
- verification/
|
||||||
|
- frontend/
|
||||||
|
- backend/
|
||||||
|
- ops/jobs/
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
generateName: dbops-env-check-
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: env-check
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 1800
|
||||||
|
backoffLimit: 2
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: env-check
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: dbops
|
||||||
|
image: josakola/eveai_ops:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
command: ["/bin/bash","-lc","/app/scripts/dbops/00-env-check.sh"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "100m"
|
||||||
|
memory: "128Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
generateName: dbops-bootstrap-ext-
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-bootstrap-ext
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 1800
|
||||||
|
backoffLimit: 2
|
||||||
|
activeDeadlineSeconds: 1800
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-bootstrap-ext
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: dbops
|
||||||
|
image: josakola/eveai_ops:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
command: ["/bin/bash","-lc","/app/scripts/dbops/02-db-bootstrap-ext.sh"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "100m"
|
||||||
|
memory: "128Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
generateName: dbops-migrate-public-
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-migrate-public
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 1800
|
||||||
|
backoffLimit: 2
|
||||||
|
activeDeadlineSeconds: 1800
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-migrate-public
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: dbops
|
||||||
|
image: josakola/eveai_ops:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
command: ["/bin/bash","-lc","/app/scripts/dbops/03-db-migrate-public.sh"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "100m"
|
||||||
|
memory: "128Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
generateName: dbops-migrate-tenant-
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-migrate-tenant
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 1800
|
||||||
|
backoffLimit: 2
|
||||||
|
activeDeadlineSeconds: 3600
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-migrate-tenant
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: dbops
|
||||||
|
image: josakola/eveai_ops:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
command: ["/bin/bash","-lc","/app/scripts/dbops/04-db-migrate-tenant.sh"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "200m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: "1Gi"
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
generateName: dbops-seed-or-init-
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-seed-or-init
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 1800
|
||||||
|
backoffLimit: 2
|
||||||
|
activeDeadlineSeconds: 1800
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-seed-or-init
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: dbops
|
||||||
|
image: josakola/eveai_ops:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
command: ["/bin/bash","-lc","/app/scripts/dbops/05-seed-or-init-data.sh"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "100m"
|
||||||
|
memory: "128Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
generateName: dbops-verify-minimal-
|
||||||
|
namespace: eveai-staging
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-verify-minimal
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 1800
|
||||||
|
backoffLimit: 2
|
||||||
|
activeDeadlineSeconds: 900
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: eveai
|
||||||
|
component: ops
|
||||||
|
job-type: db-verify-minimal
|
||||||
|
spec:
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: dbops
|
||||||
|
image: josakola/eveai_ops:latest
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: eveai-secrets
|
||||||
|
command: ["/bin/bash","-lc","/app/scripts/dbops/06-verify-minimal.sh"]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "100m"
|
||||||
|
memory: "128Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namespace: eveai-staging
|
||||||
|
resources:
|
||||||
|
- 00-env-check-job.yaml
|
||||||
|
- 02-db-bootstrap-ext-job.yaml
|
||||||
|
- 03-db-migrate-public-job.yaml
|
||||||
|
- 04-db-migrate-tenant-job.yaml
|
||||||
|
- 05-seed-or-init-data-job.yaml
|
||||||
|
- 06-verify-minimal-job.yaml
|
||||||
@@ -33,33 +33,30 @@ spec:
|
|||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
|
|
||||||
# Future services (ready for deployment)
|
# Application services
|
||||||
# Admin service
|
- path: /admin
|
||||||
# - path: /admin
|
pathType: Prefix
|
||||||
# pathType: Prefix
|
backend:
|
||||||
# backend:
|
service:
|
||||||
# service:
|
name: eveai-app-service
|
||||||
# name: eveai-app-service
|
port:
|
||||||
# port:
|
number: 80
|
||||||
# number: 80
|
|
||||||
|
|
||||||
# API service
|
- path: /api
|
||||||
# - path: /api
|
pathType: Prefix
|
||||||
# pathType: Prefix
|
backend:
|
||||||
# backend:
|
service:
|
||||||
# service:
|
name: eveai-api-service
|
||||||
# name: eveai-api-service
|
port:
|
||||||
# port:
|
number: 80
|
||||||
# number: 80
|
|
||||||
|
|
||||||
# Client/Frontend service
|
- path: /client
|
||||||
# - path: /client
|
pathType: Prefix
|
||||||
# pathType: Prefix
|
backend:
|
||||||
# backend:
|
service:
|
||||||
# service:
|
name: eveai-chat-client-service
|
||||||
# name: eveai-chat-client-service
|
port:
|
||||||
# port:
|
number: 80
|
||||||
# number: 80
|
|
||||||
|
|
||||||
# Monitoring (when deployed)
|
# Monitoring (when deployed)
|
||||||
# - path: /monitoring
|
# - path: /monitoring
|
||||||
|
|||||||
40
scripts/dbops/00-env-check.sh
Executable file
40
scripts/dbops/00-env-check.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Required env vars
|
||||||
|
REQUIRED_VARS=(DB_HOST DB_PORT DB_NAME DB_USER DB_PASS)
|
||||||
|
|
||||||
|
MISSING=()
|
||||||
|
for v in "${REQUIRED_VARS[@]}"; do
|
||||||
|
if [[ -z "${!v-}" ]]; then MISSING+=("$v"); fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
export PROJECT_DIR="${PROJECT_DIR:-/app}"
|
||||||
|
export FLASK_APP="${FLASK_APP:-${PROJECT_DIR}/scripts/run.py}"
|
||||||
|
export COMPONENT_NAME="${COMPONENT_NAME:-eveai_app}"
|
||||||
|
export PYTHONPATH="${PYTHONPATH:-${PROJECT_DIR}:${PYTHONPATH-}}"
|
||||||
|
|
||||||
|
if ((${#MISSING[@]})); then
|
||||||
|
fail "Missing required env vars: ${MISSING[*]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tools check
|
||||||
|
need() { command -v "$1" >/dev/null 2>&1 || fail "Required tool not found in PATH: $1"; }
|
||||||
|
need psql
|
||||||
|
need pg_isready
|
||||||
|
need flask
|
||||||
|
|
||||||
|
log "Environment OK"
|
||||||
|
log "DB_HOST=$DB_HOST DB_PORT=$DB_PORT DB_NAME=$DB_NAME"
|
||||||
|
log "PROJECT_DIR=$PROJECT_DIR"
|
||||||
|
log "FLASK_APP=$FLASK_APP COMPONENT_NAME=$COMPONENT_NAME"
|
||||||
|
|
||||||
|
# Versions (do not leak secrets)
|
||||||
|
psql --version || true
|
||||||
|
flask --version || true
|
||||||
|
|
||||||
|
log "00-env-check completed successfully."
|
||||||
28
scripts/dbops/01-wait-for-db.sh
Executable file
28
scripts/dbops/01-wait-for-db.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
: "${DB_HOST:?DB_HOST required}"
|
||||||
|
: "${DB_PORT:?DB_PORT required}"
|
||||||
|
: "${DB_NAME:?DB_NAME required}"
|
||||||
|
|
||||||
|
INTERVAL=${WAIT_FOR_DB_INTERVAL:-2}
|
||||||
|
TIMEOUT=${WAIT_FOR_DB_TIMEOUT:-30}
|
||||||
|
|
||||||
|
need() { command -v "$1" >/dev/null 2>&1 || fail "Required tool not found in PATH: $1"; }
|
||||||
|
need pg_isready
|
||||||
|
|
||||||
|
log "Waiting for database to be ready (timeout=${TIMEOUT}s, interval=${INTERVAL}s)"
|
||||||
|
|
||||||
|
end=$((SECONDS + TIMEOUT))
|
||||||
|
while (( SECONDS < end )); do
|
||||||
|
if pg_isready -h "$DB_HOST" -p "$DB_PORT" -d "$DB_NAME" >/dev/null 2>&1; then
|
||||||
|
log "Postgres is ready."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep "$INTERVAL"
|
||||||
|
done
|
||||||
|
|
||||||
|
fail "Database not ready within ${TIMEOUT}s."
|
||||||
24
scripts/dbops/02-db-bootstrap-ext.sh
Executable file
24
scripts/dbops/02-db-bootstrap-ext.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
: "${DB_HOST:?DB_HOST required}"
|
||||||
|
: "${DB_PORT:?DB_PORT required}"
|
||||||
|
: "${DB_NAME:?DB_NAME required}"
|
||||||
|
: "${DB_USER:?DB_USER required}"
|
||||||
|
: "${DB_PASS:?DB_PASS required}"
|
||||||
|
|
||||||
|
export PGPASSWORD="$DB_PASS"
|
||||||
|
|
||||||
|
log "Ensuring pgvector extension exists in database '$DB_NAME'"
|
||||||
|
|
||||||
|
# Verify DB exists and is reachable
|
||||||
|
if ! psql -U "$DB_USER" -h "$DB_HOST" -p "$DB_PORT" -d "$DB_NAME" -tAc "SELECT 1" >/dev/null 2>&1; then
|
||||||
|
fail "Unable to connect to database '$DB_NAME'. Ensure it exists and credentials are valid."
|
||||||
|
fi
|
||||||
|
|
||||||
|
psql -U "$DB_USER" -h "$DB_HOST" -p "$DB_PORT" -d "$DB_NAME" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS vector;"
|
||||||
|
|
||||||
|
log "pgvector extension ensured (CREATE EXTENSION IF NOT EXISTS vector)."
|
||||||
20
scripts/dbops/03-db-migrate-public.sh
Executable file
20
scripts/dbops/03-db-migrate-public.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Env requirements
|
||||||
|
REQUIRED_VARS=(DB_HOST DB_PORT DB_NAME DB_USER DB_PASS)
|
||||||
|
for v in "${REQUIRED_VARS[@]}"; do : "${!v:?$v required}"; done
|
||||||
|
|
||||||
|
export PROJECT_DIR="${PROJECT_DIR:-/app}"
|
||||||
|
export FLASK_APP="${FLASK_APP:-${PROJECT_DIR}/scripts/run.py}"
|
||||||
|
export COMPONENT_NAME="${COMPONENT_NAME:-eveai_app}"
|
||||||
|
export PYTHONPATH="${PYTHONPATH:-${PROJECT_DIR}:${PYTHONPATH-}}"
|
||||||
|
|
||||||
|
export PGPASSWORD="$DB_PASS"
|
||||||
|
|
||||||
|
log "Applying Alembic migrations to the public schema..."
|
||||||
|
flask db upgrade -d "${PROJECT_DIR}/migrations/public"
|
||||||
|
log "Finished applying migrations to the public schema."
|
||||||
20
scripts/dbops/04-db-migrate-tenant.sh
Executable file
20
scripts/dbops/04-db-migrate-tenant.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Env requirements
|
||||||
|
REQUIRED_VARS=(DB_HOST DB_PORT DB_NAME DB_USER DB_PASS)
|
||||||
|
for v in "${REQUIRED_VARS[@]}"; do : "${!v:?$v required}"; done
|
||||||
|
|
||||||
|
export PROJECT_DIR="${PROJECT_DIR:-/app}"
|
||||||
|
export FLASK_APP="${FLASK_APP:-${PROJECT_DIR}/scripts/run.py}"
|
||||||
|
export COMPONENT_NAME="${COMPONENT_NAME:-eveai_app}"
|
||||||
|
export PYTHONPATH="${PYTHONPATH:-${PROJECT_DIR}:${PYTHONPATH-}}"
|
||||||
|
|
||||||
|
export PGPASSWORD="$DB_PASS"
|
||||||
|
|
||||||
|
log "Applying Alembic migrations to the tenant schemas (single run handling all tenants)..."
|
||||||
|
flask db upgrade -d "${PROJECT_DIR}/migrations/tenant"
|
||||||
|
log "Finished applying migrations to the tenant schemas."
|
||||||
18
scripts/dbops/05-seed-or-init-data.sh
Executable file
18
scripts/dbops/05-seed-or-init-data.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
export PROJECT_DIR="${PROJECT_DIR:-/app}"
|
||||||
|
SCRIPT_PATH="${PROJECT_DIR}/scripts/initialize_data.py"
|
||||||
|
|
||||||
|
[[ -f "$SCRIPT_PATH" ]] || fail "Seed/init script not found: $SCRIPT_PATH"
|
||||||
|
|
||||||
|
export FLASK_APP="${FLASK_APP:-${PROJECT_DIR}/scripts/run.py}"
|
||||||
|
export COMPONENT_NAME="${COMPONENT_NAME:-eveai_app}"
|
||||||
|
export PYTHONPATH="${PYTHONPATH:-${PROJECT_DIR}:${PYTHONPATH-}}"
|
||||||
|
|
||||||
|
log "Running initialize_data.py (idempotent one-off per environment)..."
|
||||||
|
python "$SCRIPT_PATH"
|
||||||
|
log "initialize_data.py completed."
|
||||||
26
scripts/dbops/06-verify-minimal.sh
Executable file
26
scripts/dbops/06-verify-minimal.sh
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
: "${DB_HOST:?DB_HOST required}"
|
||||||
|
: "${DB_PORT:?DB_PORT required}"
|
||||||
|
: "${DB_NAME:?DB_NAME required}"
|
||||||
|
: "${DB_USER:?DB_USER required}"
|
||||||
|
: "${DB_PASS:?DB_PASS required}"
|
||||||
|
|
||||||
|
export PGPASSWORD="$DB_PASS"
|
||||||
|
|
||||||
|
log "Verifying DB connectivity and pgvector extension in '$DB_NAME'"
|
||||||
|
|
||||||
|
# Connectivity check
|
||||||
|
psql -U "$DB_USER" -h "$DB_HOST" -p "$DB_PORT" -d "$DB_NAME" -tAc "SELECT 1" >/dev/null 2>&1 || fail "Cannot connect to database '$DB_NAME'"
|
||||||
|
|
||||||
|
# pgvector check
|
||||||
|
HAS_VECTOR=$(psql -U "$DB_USER" -h "$DB_HOST" -p "$DB_PORT" -d "$DB_NAME" -tAc "SELECT 1 FROM pg_extension WHERE extname='vector'" | tr -d '[:space:]')
|
||||||
|
if [[ "$HAS_VECTOR" != "1" ]]; then
|
||||||
|
fail "pgvector extension not found in '$DB_NAME'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Verification OK: DB reachable and pgvector extension present."
|
||||||
25
scripts/dbops/90-initial-cluster-db-create.sh
Executable file
25
scripts/dbops/90-initial-cluster-db-create.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
: "${DB_HOST:?DB_HOST required}"
|
||||||
|
: "${DB_PORT:?DB_PORT required}"
|
||||||
|
: "${DB_NAME:?DB_NAME required}"
|
||||||
|
: "${DB_USER:?DB_USER required}"
|
||||||
|
: "${DB_PASS:?DB_PASS required}"
|
||||||
|
|
||||||
|
export PGPASSWORD="$DB_PASS"
|
||||||
|
|
||||||
|
log "Checking if database '$DB_NAME' exists..."
|
||||||
|
EXISTS=$(psql -U "$DB_USER" -h "$DB_HOST" -p "$DB_PORT" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" | tr -d '[:space:]') || true
|
||||||
|
|
||||||
|
if [[ "$EXISTS" == "1" ]]; then
|
||||||
|
log "Database '$DB_NAME' already exists. Nothing to do."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Creating database '$DB_NAME'..."
|
||||||
|
psql -U "$DB_USER" -h "$DB_HOST" -p "$DB_PORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE \"$DB_NAME\";"
|
||||||
|
log "Database '$DB_NAME' created successfully."
|
||||||
4
scripts/dbops/99-cleanup.sh
Executable file
4
scripts/dbops/99-cleanup.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Cleanup complete (no-op)."
|
||||||
140
scripts/dbops/README.md
Executable file
140
scripts/dbops/README.md
Executable file
@@ -0,0 +1,140 @@
|
|||||||
|
# DB Operations (Manual)
|
||||||
|
|
||||||
|
Scripts to run database initialization and migrations manually during your release process. Default wait timeout is 30s with 2s interval.
|
||||||
|
|
||||||
|
Required env (typical):
|
||||||
|
- DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS
|
||||||
|
- PROJECT_DIR (defaults /app)
|
||||||
|
- FLASK_APP (defaults to ${PROJECT_DIR}/scripts/run.py)
|
||||||
|
- COMPONENT_NAME (defaults to eveai_app)
|
||||||
|
- PYTHONPATH (defaults to ${PROJECT_DIR})
|
||||||
|
|
||||||
|
Order (standard: DB already exists):
|
||||||
|
1. 00-env-check.sh
|
||||||
|
2. 01-wait-for-db.sh (WAIT_FOR_DB_TIMEOUT=30 WAIT_FOR_DB_INTERVAL=2)
|
||||||
|
3. 02-db-bootstrap-ext.sh (creates pgvector extension if missing)
|
||||||
|
4. 03-db-migrate-public.sh
|
||||||
|
5. 04-db-migrate-tenant.sh (single run for all tenants)
|
||||||
|
6. 06-verify-minimal.sh
|
||||||
|
|
||||||
|
Optional, one-off for a new environment where DB does not exist yet:
|
||||||
|
- 90-initial-cluster-db-create.sh (run before step 3)
|
||||||
|
|
||||||
|
Optional seed/init data (one-off per env, idempotent):
|
||||||
|
- 05-seed-or-init-data.sh
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Scripts are idempotent where applicable.
|
||||||
|
- Do not echo secrets. Password is read from DB_PASS via PGPASSWORD env.
|
||||||
|
- Ensure you run these inside the app image/container or any environment with psql, pg_isready, flask and the project code present.
|
||||||
|
|
||||||
|
|
||||||
|
## Podman quickstart (manual testing)
|
||||||
|
|
||||||
|
Below are two simple ways to run these scripts in a Podman-based setup. Choose the approach that best matches your workflow.
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- A reachable PostgreSQL 16 instance and credentials (DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS)
|
||||||
|
- Your application code available inside the container at /app (default PROJECT_DIR)
|
||||||
|
- Tools available in the container: psql, pg_isready, flask, python
|
||||||
|
- docker/Dockerfile.base already includes psql; use it to build a tools image if needed
|
||||||
|
|
||||||
|
Environment variables (typical):
|
||||||
|
- DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS
|
||||||
|
- PROJECT_DIR=/app (default)
|
||||||
|
- FLASK_APP=/app/scripts/run.py (default)
|
||||||
|
- COMPONENT_NAME=eveai_app (default)
|
||||||
|
- PYTHONPATH="/app" (default)
|
||||||
|
|
||||||
|
Order to run (standard):
|
||||||
|
1) scripts/dbops/00-env-check.sh
|
||||||
|
2) scripts/dbops/01-wait-for-db.sh (WAIT_FOR_DB_TIMEOUT=30 WAIT_FOR_DB_INTERVAL=2)
|
||||||
|
3) scripts/dbops/02-db-bootstrap-ext.sh
|
||||||
|
4) scripts/dbops/03-db-migrate-public.sh
|
||||||
|
5) scripts/dbops/04-db-migrate-tenant.sh
|
||||||
|
6) scripts/dbops/06-verify-minimal.sh
|
||||||
|
|
||||||
|
Optional (one-time for a new environment where the DB does not yet exist):
|
||||||
|
- scripts/dbops/90-initial-cluster-db-create.sh (run before step 3)
|
||||||
|
|
||||||
|
Optional seed/init (one-time per environment):
|
||||||
|
- scripts/dbops/05-seed-or-init-data.sh
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Approach A — Exec into an existing app container
|
||||||
|
1. Start your stack (e.g., via Podman Compose) so the app container and DB are running.
|
||||||
|
2. Find the app container name:
|
||||||
|
podman ps
|
||||||
|
3. Exec into the container shell:
|
||||||
|
podman exec -it <app-container-name> bash
|
||||||
|
4. Export DB variables inside the container (adjust values):
|
||||||
|
export DB_HOST=127.0.0.1
|
||||||
|
export DB_PORT=5432
|
||||||
|
export DB_NAME=eveai
|
||||||
|
export DB_USER=postgres
|
||||||
|
export DB_PASS=yourpassword
|
||||||
|
# Optional (defaults exist)
|
||||||
|
export PROJECT_DIR=/app
|
||||||
|
export FLASK_APP=/app/scripts/run.py
|
||||||
|
export COMPONENT_NAME=eveai_app
|
||||||
|
export PYTHONPATH="/app"
|
||||||
|
5. Run the scripts in order, for example:
|
||||||
|
scripts/dbops/00-env-check.sh && \
|
||||||
|
scripts/dbops/01-wait-for-db.sh && \
|
||||||
|
scripts/dbops/02-db-bootstrap-ext.sh && \
|
||||||
|
scripts/dbops/03-db-migrate-public.sh && \
|
||||||
|
scripts/dbops/04-db-migrate-tenant.sh && \
|
||||||
|
scripts/dbops/06-verify-minimal.sh
|
||||||
|
6. Optionally seed/init once per environment:
|
||||||
|
scripts/dbops/05-seed-or-init-data.sh
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- If your DB runs in a separate container, DB_HOST should be that container's network hostname (often the service name from compose). If using host networking, 127.0.0.1 may work.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Approach B — Use a one-off tools container based on Dockerfile.base
|
||||||
|
This runs the scripts without depending on a running app container. It mounts the repo at /app and uses the base image with psql installed.
|
||||||
|
|
||||||
|
1. Build the tools image (from repo root):
|
||||||
|
podman build -f docker/Dockerfile.base -t eveai/tools:local .
|
||||||
|
|
||||||
|
2. Run a one-off container with your env vars and the repo mounted:
|
||||||
|
podman run --rm -it \
|
||||||
|
--name eveai-dbops \
|
||||||
|
--network host \
|
||||||
|
-e DB_HOST=127.0.0.1 \
|
||||||
|
-e DB_PORT=5432 \
|
||||||
|
-e DB_NAME=eveai \
|
||||||
|
-e DB_USER=postgres \
|
||||||
|
-e DB_PASS=yourpassword \
|
||||||
|
-e PROJECT_DIR=/app \
|
||||||
|
-e FLASK_APP=/app/scripts/run.py \
|
||||||
|
-e COMPONENT_NAME=eveai_app \
|
||||||
|
-e PYTHONPATH="/app" \
|
||||||
|
-e WAIT_FOR_DB_TIMEOUT=30 \
|
||||||
|
-e WAIT_FOR_DB_INTERVAL=2 \
|
||||||
|
-v "$(pwd)":/app \
|
||||||
|
-w /app \
|
||||||
|
eveai/tools:local \
|
||||||
|
bash -lc 'scripts/dbops/00-env-check.sh && scripts/dbops/01-wait-for-db.sh && scripts/dbops/02-db-bootstrap-ext.sh && scripts/dbops/03-db-migrate-public.sh && scripts/dbops/04-db-migrate-tenant.sh && scripts/dbops/06-verify-minimal.sh'
|
||||||
|
|
||||||
|
3. Optional seed/init once per environment:
|
||||||
|
podman run --rm -it --network host \
|
||||||
|
-e DB_HOST -e DB_PORT -e DB_NAME -e DB_USER -e DB_PASS \
|
||||||
|
-e PROJECT_DIR -e FLASK_APP -e COMPONENT_NAME -e PYTHONPATH \
|
||||||
|
-v "$(pwd)":/app -w /app \
|
||||||
|
eveai/tools:local \
|
||||||
|
bash -lc 'scripts/dbops/05-seed-or-init-data.sh'
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- --network host simplifies connectivity on Linux. If you cannot use it, use a user-defined Podman network and set DB_HOST to the DB container hostname.
|
||||||
|
- Do not echo secrets; scripts read DB_PASS via the PGPASSWORD env variable automatically.
|
||||||
|
- If flask is missing at runtime, ensure your tools image has Python deps installed or run inside your actual app image/container that already has them.
|
||||||
|
|
||||||
|
Troubleshooting
|
||||||
|
- Connection refused: verify DB_HOST/PORT and network mode; try --network host.
|
||||||
|
- Authentication failed: check DB_USER/DB_PASS and that the user has rights on DB_NAME.
|
||||||
|
- pgvector missing even after step 3: ensure your DB user has rights to CREATE EXTENSION in DB_NAME.
|
||||||
|
- Flask command not found: run within the app image or install project dependencies in the tools image before running.
|
||||||
Reference in New Issue
Block a user