feat: compose stack in devcontainer (db, langflow, lfx)
- docker-compose.yml with app/db/langflow/lfx services and healthchecks - Dockerfile.lfx (stateless flow runtime on :6004) - init-db.sql (langflow + test databases) - devcontainer.json switched to compose mode with runServices - setup.sh waits for compose services and requires .env
This commit is contained in:
parent
86fe4424e7
commit
2ba062c4c5
6 changed files with 154 additions and 9 deletions
19
.devcontainer/Dockerfile.lfx
Normal file
19
.devcontainer/Dockerfile.lfx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Stateless Langflow flow runtime (LFX). Serves versioned flow JSON files
|
||||||
|
# deployed by our backend via POST /flows/upload/ (persisted in /flows).
|
||||||
|
# LFX stays on the internal compose network; our FastAPI is the only
|
||||||
|
# public surface and proxies run/stream calls.
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
|
PIP_NO_CACHE_DIR=1
|
||||||
|
|
||||||
|
# lfx = flow runtime; requests = convenience for custom-component code.
|
||||||
|
RUN pip install "lfx==1.12.0" requests
|
||||||
|
|
||||||
|
RUN mkdir -p /flows
|
||||||
|
VOLUME /flows
|
||||||
|
EXPOSE 6004
|
||||||
|
|
||||||
|
# Serve the /flows dir (uploaded flows persist there via --flow-dir).
|
||||||
|
# Flags verified against `lfx serve --help` (lfx 1.12.0).
|
||||||
|
CMD ["lfx", "serve", "--host", "0.0.0.0", "--port", "6004", "--flow-dir", "/flows", "/flows"]
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "kirche-einfach",
|
"name": "kirche-einfach",
|
||||||
"build": {
|
"dockerComposeFile": "docker-compose.yml",
|
||||||
"dockerfile": "Dockerfile",
|
"service": "app",
|
||||||
"args": {
|
"runServices": ["db", "langflow", "lfx"],
|
||||||
"FORGEJO_USERNAME": "jonas"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mounts": [
|
"mounts": [
|
||||||
"source=${localEnv:USERPROFILE}/.ssh,target=/mnt/host-ssh,type=bind,readonly",
|
"source=${localEnv:USERPROFILE}/.ssh,target=/mnt/host-ssh,type=bind,readonly"
|
||||||
"source=kirche-einfach-venv,target=/workspaces/kirche-einfach/backend/.venv,type=volume",
|
|
||||||
"source=kirche-einfach-node-modules,target=/workspaces/kirche-einfach/frontend/node_modules,type=volume"
|
|
||||||
],
|
],
|
||||||
"remoteUser": "vscode",
|
"remoteUser": "vscode",
|
||||||
"containerEnv": {
|
"containerEnv": {
|
||||||
|
|
|
||||||
91
.devcontainer/docker-compose.yml
Normal file
91
.devcontainer/docker-compose.yml
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
# Compose stack for the kirche-einfach devcontainer.
|
||||||
|
# VS Code attaches to the `app` service (see devcontainer.json); all values
|
||||||
|
# come from the repo-root `.env` via `env_file` (no ${} interpolation needed).
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
FORGEJO_USERNAME: jonas
|
||||||
|
command: sleep infinity
|
||||||
|
volumes:
|
||||||
|
- ..:/workspaces/kirche-einfach:cached
|
||||||
|
- kirche-einfach-venv:/workspaces/kirche-einfach/backend/.venv
|
||||||
|
- kirche-einfach-node-modules:/workspaces/kirche-einfach/frontend/node_modules
|
||||||
|
env_file:
|
||||||
|
- ../.env
|
||||||
|
ports:
|
||||||
|
- "8000:8000" # FastAPI
|
||||||
|
- "3000:3000" # Vite
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:16-trixie
|
||||||
|
env_file:
|
||||||
|
- ../.env
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
- ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
langflow:
|
||||||
|
image: langflowai/langflow:1.12.0
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
env_file:
|
||||||
|
- ../.env
|
||||||
|
environment:
|
||||||
|
# Static values only; secrets/URLs come from ../.env via env_file
|
||||||
|
# (compose interpolates ${} from .devcontainer/.env, not the repo-root .env).
|
||||||
|
LANGFLOW_CONFIG_DIR: /app/langflow
|
||||||
|
LANGFLOW_HOST: 0.0.0.0
|
||||||
|
PORT: "7860"
|
||||||
|
LANGFLOW_AUTO_LOGIN: "false"
|
||||||
|
LANGFLOW_API_KEY_SOURCE: env
|
||||||
|
volumes:
|
||||||
|
- langflow-data:/app/langflow
|
||||||
|
ports:
|
||||||
|
- "7860:7860"
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD-SHELL",
|
||||||
|
"python -c \"import urllib.request; urllib.request.urlopen('http://localhost:7860/health', timeout=5)\"",
|
||||||
|
]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 20
|
||||||
|
start_period: 60s
|
||||||
|
|
||||||
|
lfx:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.lfx
|
||||||
|
volumes:
|
||||||
|
- lfx-data:/flows
|
||||||
|
ports:
|
||||||
|
- "6004:6004"
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
[
|
||||||
|
"CMD-SHELL",
|
||||||
|
"python -c \"import urllib.request; urllib.request.urlopen('http://localhost:6004/health', timeout=5)\"",
|
||||||
|
]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
kirche-einfach-venv:
|
||||||
|
kirche-einfach-node-modules:
|
||||||
|
postgres-data:
|
||||||
|
langflow-data:
|
||||||
|
lfx-data:
|
||||||
7
.devcontainer/init-db.sql
Normal file
7
.devcontainer/init-db.sql
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Runs once on first init of the postgres-data volume.
|
||||||
|
-- The main app database ($POSTGRES_DB) is created by the image itself.
|
||||||
|
-- Langflow (authoring UI) gets its own database on the same instance.
|
||||||
|
CREATE DATABASE langflow;
|
||||||
|
|
||||||
|
-- Isolated database for the pytest suite (migrated via Alembic in conftest).
|
||||||
|
CREATE DATABASE kirche_einfach_test;
|
||||||
|
|
@ -13,6 +13,38 @@ echo "--- Installing Node dependencies ---"
|
||||||
cd /workspaces/kirche-einfach/frontend
|
cd /workspaces/kirche-einfach/frontend
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
# Compose services (db, langflow, lfx) must be up; fail loudly otherwise.
|
||||||
|
echo "--- Waiting for compose services ---"
|
||||||
|
if [ ! -f /workspaces/kirche-einfach/.env ]; then
|
||||||
|
echo "ERROR: /workspaces/kirche-einfach/.env is missing. Copy .env.example to .env and fill in secrets." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
python3 - <<'EOF'
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
targets = [("db", 5432), ("langflow", 7860), ("lfx", 6004)]
|
||||||
|
deadline = time.time() + 180
|
||||||
|
pending = dict(targets)
|
||||||
|
while pending and time.time() < deadline:
|
||||||
|
for host, port in list(pending):
|
||||||
|
s = socket.socket()
|
||||||
|
s.settimeout(2)
|
||||||
|
try:
|
||||||
|
s.connect((host, port))
|
||||||
|
print(f"OK {host}:{port}", flush=True)
|
||||||
|
del pending[(host, port)]
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
s.close()
|
||||||
|
if pending:
|
||||||
|
time.sleep(2)
|
||||||
|
if pending:
|
||||||
|
sys.exit(f"ERROR: compose services not ready: {sorted(pending)}")
|
||||||
|
EOF
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Setup complete ==="
|
echo "=== Setup complete ==="
|
||||||
echo "Forgejo username: $FORGEJO_USERNAME"
|
echo "Forgejo username: $FORGEJO_USERNAME"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ LANGFLOW_SUPERUSER_PASSWORD=
|
||||||
LANGFLOW_SECRET_KEY=
|
LANGFLOW_SECRET_KEY=
|
||||||
# Single static key; validated against env (LANGFLOW_API_KEY_SOURCE=env), superuser scope.
|
# Single static key; validated against env (LANGFLOW_API_KEY_SOURCE=env), superuser scope.
|
||||||
LANGFLOW_API_KEY=sk-
|
LANGFLOW_API_KEY=sk-
|
||||||
|
LANGFLOW_DATABASE_URL=postgresql://kirche:<password>@db:5432/langflow
|
||||||
|
|
||||||
# --- lfx (flow runtime) ---
|
# --- lfx (flow runtime) ---
|
||||||
LFX_URL=http://lfx:6004
|
LFX_URL=http://lfx:6004
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue