-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
141 lines (134 loc) · 5.94 KB
/
Copy pathdocker-compose.yml
File metadata and controls
141 lines (134 loc) · 5.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Local development dependencies.
#
# make up # Postgres + Elasticsearch (fast)
# make up-all # also the embedding service
# make web # the frontend behind nginx, as deployed
#
# Every credential is required from the environment with `:?`, so a missing value
# fails immediately with a named variable instead of starting a service on a
# well-known default password (SEC-005).
name: tenantchat
services:
postgres:
image: postgres:16.11-alpine3.23@sha256:4327b9fd295502f326f44153a1045a7170ddbfffed1c3829798328556cfd09e2
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB in .env}
POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER in .env}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
# Deterministic collation keeps index ordering identical across machines.
POSTGRES_INITDB_ARGS: "--locale=C --encoding=UTF8"
ports:
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
# Naming the database and user avoids a false ready signal during the
# brief window when initdb has a server up but the app database absent.
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 12
start_period: 10s
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3@sha256:01c1732062b4a846c5ca687b0094b89bad0bfed00c6d71626db32fb8f3131a78
restart: unless-stopped
environment:
discovery.type: single-node
ELASTIC_PASSWORD: ${ES_PASSWORD:?set ES_PASSWORD in .env}
# Authentication on, transport encryption off. The retrieval client then
# exercises the same credential path locally that it uses in Kubernetes;
# disabling security outright would leave that code untested until deploy.
xpack.security.enabled: "true"
xpack.security.http.ssl.enabled: "false"
xpack.license.self_generated.type: basic
ES_JAVA_OPTS: "-Xms1g -Xmx1g"
ports:
- "${ELASTICSEARCH_PORT:-9200}:9200"
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
healthcheck:
test:
- CMD-SHELL
- >-
curl -fsS -u "elastic:$${ELASTIC_PASSWORD}"
"http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=5s"
interval: 10s
timeout: 10s
retries: 18
start_period: 30s
ulimits:
memlock: { soft: -1, hard: -1 }
# Optional: `make up-all`. Excluded from the default set because the first run
# downloads model weights and the container is memory-hungry on a laptop.
embedding:
profiles: ["embedding"]
build:
context: .
dockerfile: services/embedding/Dockerfile
restart: unless-stopped
environment:
EMBEDDING_MODEL: ${EMBEDDING_MODEL:-Qwen/Qwen3-Embedding-0.6B}
EMBEDDING_DEVICE: ${EMBEDDING_DEVICE:-cpu}
EMBEDDING_BATCH_SIZE: ${EMBEDDING_BATCH_SIZE:-16}
INGESTION_TO_EMBEDDING_TOKEN: ${INGESTION_TO_EMBEDDING_TOKEN:?set INGESTION_TO_EMBEDDING_TOKEN in .env}
CHAT_TO_EMBEDDING_TOKEN: ${CHAT_TO_EMBEDDING_TOKEN:-}
ports:
- "${EMBEDDING_PORT:-8001}:8001"
volumes:
# Cache weights across container rebuilds. The path must track HF_HOME in
# services/embedding/Dockerfile: the image runs as uid 10001, which cannot
# write /root, so a mount there silently re-downloads the model every run.
- huggingface-cache:/home/tenantchat/.cache/huggingface
healthcheck:
test: ["CMD-SHELL", "python -c \"import urllib.request as r; r.urlopen('http://localhost:8001/health')\""]
interval: 15s
timeout: 10s
retries: 20
start_period: 120s
# Optional: `make web`. The same image Kubernetes serves the site from, so a
# broken proxy rule, cache header, or CSP shows up locally rather than after a
# deploy. Hot-reload development is `make dev` instead; this is the deployed
# shape, and its assets are baked into the image at build time.
web:
profiles: ["web"]
build:
context: .
dockerfile: frontend/Dockerfile
restart: unless-stopped
environment:
# The API runs on the host, not in compose: `make api` serves services/api
# on 127.0.0.1:8080. The API image serves visitor and admin routes on the
# same port, so both origins point at the same place here; Kubernetes
# routes them through the gateway's public and admin listeners.
CHAT_BACKEND_ORIGIN: ${CHAT_BACKEND_ORIGIN:-http://host.docker.internal:8080}
CHAT_ADMIN_ORIGIN: ${CHAT_ADMIN_ORIGIN:-http://host.docker.internal:8080}
# Local compose has no oauth2-proxy sidecar. The browser-based admin
# console therefore needs a local auth proxy; CHAT_API_DEV_AUTH alone
# does not supply operator identity headers. The visitor surface does not
# use this origin. nginx -t requires it to be a valid URL.
AUTH_PROXY_ORIGIN: ${AUTH_PROXY_ORIGIN:-http://host.docker.internal:8080}
ADMIN_GATEWAY_TOKEN: ${ADMIN_GATEWAY_TOKEN:?set ADMIN_GATEWAY_TOKEN in .env}
WIDGET_ALLOWED_ORIGINS: ${WIDGET_ALLOWED_ORIGINS:-}
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "${WEB_PORT:-8080}:8080"
healthcheck:
# 127.0.0.1, not localhost: the listener is IPv4 and localhost resolves to
# ::1 first inside the container.
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:8080/healthz >/dev/null"]
interval: 10s
timeout: 5s
retries: 6
start_period: 5s
# Kibana is deliberately absent. Kibana 8 refuses to start when configured with
# the `elastic` superuser, so a local instance needs the `kibana_system` account
# provisioned first (see k8s/kibana-setup-job.yaml). Until RAG-004 makes index
# inspection a routine task, curl against Elasticsearch is enough:
#
# curl -u "elastic:$ES_PASSWORD" localhost:9200/tenant-knowledge-chunks/_search
volumes:
postgres-data:
elasticsearch-data:
huggingface-cache: