-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
125 lines (121 loc) · 5.57 KB
/
Copy pathdocker-compose.yml
File metadata and controls
125 lines (121 loc) · 5.57 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
# docker-compose.yml — local docker stack for orrery (RFC-024).
#
# Lightweight by design:
# - `web` is nginx:alpine with the brotli dynamic module layered on
# (GH #273 / W3 — ops/docker/Dockerfile.web). No SvelteKit rebuild
# inside the container. Operator builds the bundle on the host with
# `npm run build` (same as for GH Pages); web container just serves
# it. Brotli + gzip pre-compressed siblings come from the vite
# build (vite-plugin-compression2); nginx serves them via
# brotli_static + gzip_static when Accept-Encoding allows.
# - `pipeline-runner` has its own Dockerfile (gdal-async + sharp +
# tsx) but is profile-gated — `docker compose up` does NOT build
# or start it. Its image only builds on first
# `docker compose run --rm pipeline-runner …`.
#
# Usage:
# npm run build — produce host build/
# docker compose up -d web — start nginx at :8080
# docker compose run --rm pipeline-runner scripts/<name>.ts [args]
# docker compose down — stop everything
#
# Live-data property: pipelines write to ./static/data on the host;
# web's bind-mount overlays that path over build/data, so nginx
# serves fresh JSON on next request without rebuild.
services:
web:
build:
context: .
dockerfile: ops/docker/Dockerfile.web
image: orrery-web:local
container_name: orrery-web
ports:
- '8080:80'
volumes:
# The built SPA. Operator must run `npm run build` on host
# before `docker compose up`. Read-only mount.
- ./build:/usr/share/nginx/html:ro
# Overlay live data on top of the build's seed copy. Pipelines
# write here; web serves on next request. PWA SW's NetworkFirst
# strategy (vite.config.ts:90) handles client-side freshness.
- ./static/data:/usr/share/nginx/html/data:ro
# nginx config (cache headers, SPA fallback, brotli + gzip).
# Alpine's nginx uses /etc/nginx/http.d/, not conf.d/.
- ./ops/docker/nginx.conf:/etc/nginx/http.d/default.conf:ro
restart: unless-stopped
healthcheck:
test: ['CMD-SHELL', 'wget -qO- http://127.0.0.1/ >/dev/null 2>&1 || exit 1']
interval: 30s
timeout: 3s
start_period: 5s
retries: 3
pipeline-runner:
build:
context: .
dockerfile: Dockerfile
image: orrery-pipeline:local
# `manual` profile means this service is NOT built or started by
# `docker compose up`. Pipelines run via
# docker compose run --rm pipeline-runner scripts/<name>.ts
profiles: ['manual']
volumes:
# Pipeline writes data here — same host directory the web bind-
# mount reads from, so refreshes are live.
- ./static/data:/repo/static/data
# Cache reused across pipeline invocations (HiRISE JP2s, GCAT
# TSV, LL2 responses). Local-only docker volume — survives
# `docker compose down`, cleared by `down -v` or
# `npm run docker:reset-data`.
- orrery-cache:/repo/.image-cache
# Source code bind-mount, read-only. Local-dev only — the future
# VPS image will bake source in (RFC-024 §10).
- .:/repo:ro
# Anonymous volume masks the host's darwin-arm64 node_modules so
# the image's linux-arm64 node_modules (with native bindings
# compiled for the container's platform) wins. Without this,
# esbuild / gdal-async / sharp would try to load the host's
# macOS binaries inside the linux container and crash on first
# tsx invocation.
- /repo/node_modules
environment:
- LL2_PATREON_KEY=${LL2_PATREON_KEY:-}
- NODE_OPTIONS=--max-old-space-size=4096
# ─── Observability (RFC-025 / ADR-068) ─────────────────────────────
# Grafana Cloud Agent — tails web + pipeline-runner container logs
# and ships them to Grafana Cloud Loki. Profile-gated to
# `observability`; `docker compose up -d web` neither builds nor
# starts it. Silent default: if any GRAFANA_CLOUD_* env var is
# empty, the agent runs but the Loki client has no remote target
# and emits zero outbound traffic.
#
# Bring up with:
# docker compose --profile observability up -d
grafana-agent:
image: grafana/agent:v0.43.4
container_name: orrery-grafana-agent
profiles: ['observability']
volumes:
# Three configs: real (shipping), silent (no shipping), and the
# entrypoint that picks between them based on env-var presence.
- ./ops/observability/grafana-agent.yaml:/etc/grafana-agent.yaml:ro
- ./ops/observability/grafana-agent.silent.yaml:/etc/grafana-agent.silent.yaml:ro
- ./ops/observability/agent-entrypoint.sh:/agent-entrypoint.sh:ro
# Read-only docker socket — agent uses docker_sd_configs to
# discover orrery-* containers and tail their stdout/stderr.
# Only mounted on the shipping path; silent config doesn't touch it.
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- GRAFANA_CLOUD_LOKI_URL=${GRAFANA_CLOUD_LOKI_URL:-}
- GRAFANA_CLOUD_LOKI_USER=${GRAFANA_CLOUD_LOKI_USER:-}
- GRAFANA_CLOUD_API_KEY=${GRAFANA_CLOUD_API_KEY:-}
- GRAFANA_AGENT_ENV=${GRAFANA_AGENT_ENV:-local-dev}
# Override the image's default entrypoint with our wrapper, which
# picks the right config based on env-var presence. Without this
# an empty GRAFANA_CLOUD_LOKI_URL crashes the agent in a restart
# loop on "client needs target URL".
entrypoint: ['/bin/sh', '/agent-entrypoint.sh']
command: []
restart: unless-stopped
volumes:
orrery-cache:
name: orrery-cache