docker-compose.yml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. # docker-compose.yml — single-host M0 stack.
  2. # Run: docker compose up -d
  3. # Then: see M0_VERIFICATION.md for the loadgen smoke test.
  4. #
  5. # Port conventions (project rule):
  6. # - app HTTP services: 8800–8899 (ingestd/routerd/deliverd-fcm/deliverd-telegram/telegramd/admind/loadgen/faketgmd)
  7. # - canonical ports stay (5432 postgres, 4222 nats, 6379 redis,
  8. # 1883 mqtt, 9090 prometheus, 3000 grafana, etc.)
  9. services:
  10. # ── Data tier ────────────────────────────────────────────────────
  11. postgres:
  12. image: timescale/timescaledb:latest-pg16
  13. environment:
  14. POSTGRES_USER: ba
  15. POSTGRES_PASSWORD: ba
  16. POSTGRES_DB: ba
  17. # No host port mapping: a host-local postgres is already on
  18. # :5432. The app services reach this one via the docker network
  19. # DNS name 'postgres'. Use `docker compose exec postgres psql ...`
  20. # to talk to it from the host.
  21. expose: ["5432"]
  22. volumes:
  23. - pgdata:/var/lib/postgresql/data
  24. healthcheck:
  25. test: ["CMD-SHELL", "pg_isready -U ba"]
  26. interval: 5s
  27. timeout: 3s
  28. retries: 10
  29. redis:
  30. image: redis:7-alpine
  31. # No host port mapping: a host-local redis is on :6379. App
  32. # services reach this one via docker DNS 'redis'.
  33. expose: ["6379"]
  34. healthcheck:
  35. test: ["CMD", "redis-cli", "ping"]
  36. interval: 5s
  37. timeout: 3s
  38. retries: 10
  39. nats:
  40. image: nats:2.10-alpine
  41. # -js : JetStream enabled
  42. # -m 8222 : HTTP monitoring port
  43. # -c /etc/nats/nats.conf : load the JetStream config from
  44. # a file. 2.10 does not expose
  45. # max_file_store / max_mem_store as
  46. # CLI flags; they have to come from
  47. # a .conf. The config also sets
  48. # store_dir. See deploy/nats/nats.conf
  49. # and M11_NATS_INVESTIGATION.md.
  50. command: ["-js", "-m", "8222", "-c", "/etc/nats/nats.conf"]
  51. ports: ["4222:4222", "8222:8222"] # 8222 is the monitoring HTTP
  52. volumes:
  53. - natsdata:/data
  54. - ./deploy/nats/nats.conf:/etc/nats/nats.conf:ro
  55. healthcheck:
  56. test: ["CMD", "wget", "-qO-", "http://localhost:8222/healthz"]
  57. interval: 5s
  58. timeout: 3s
  59. retries: 20
  60. emqx:
  61. image: emqx/emqx:5.10.4
  62. ports: ["1883:1883", "18083:18083"] # MQTT + admin UI
  63. volumes:
  64. # M4: per-company ACL + auth bootstrap. acl.conf is read on
  65. # SIGHUP; the auth CSV is read on first boot. M11 promotes
  66. # this to a Postgres-backed authentication chain.
  67. - ./deploy/emqx/acl.conf:/opt/emqx/etc/acl.conf:ro
  68. - ./deploy/emqx/auth-built-in-db-bootstrap.csv:/opt/emqx/etc/auth-built-in-db-bootstrap.csv:ro
  69. # EMQX 5.x prefers env-var config over emqx.conf. The HOCON
  70. # path is emqx.conf → base.hocon → cluster.hocon → env vars
  71. # (highest precedence). The double-underscore separator in
  72. # env-var names maps to nested HOCON keys.
  73. environment:
  74. # Built-in-db authentication (one chain, password_based, plain)
  75. EMQX_AUTHENTICATION__1__BACKEND: "built_in_database"
  76. EMQX_AUTHENTICATION__1__MECHANISM: "password_based"
  77. EMQX_AUTHENTICATION__1__USER_ID_TYPE: "username"
  78. EMQX_AUTHENTICATION__1__PASSWORD_HASH_ALGORITHM__NAME: "plain"
  79. EMQX_AUTHENTICATION__1__PASSWORD_HASH_ALGORITHM__SALT_POSITION: "disable"
  80. # File-based authorization, default-deny
  81. EMQX_AUTHORIZATION__NO_MATCH: "deny"
  82. EMQX_AUTHORIZATION__DENY_ACTION: "disconnect"
  83. EMQX_AUTHORIZATION__SOURCES__1__TYPE: "file"
  84. EMQX_AUTHORIZATION__SOURCES__1__ENABLE: "true"
  85. EMQX_AUTHORIZATION__SOURCES__1__PATH: "/opt/emqx/etc/acl.conf"
  86. healthcheck:
  87. # The original `echo > /dev/tcp/...` ran under sh on
  88. # Debian-based EMQX and reported unhealthy even when the
  89. # broker was fine. Switch to `bash -c` and a TCP probe.
  90. test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/1883"]
  91. interval: 10s
  92. timeout: 5s
  93. retries: 20
  94. clickhouse:
  95. image: clickhouse/clickhouse-server:24-alpine
  96. ports: ["8123:8123", "9000:9000"]
  97. volumes:
  98. - chdata:/var/lib/clickhouse
  99. ulimits:
  100. nofile: { soft: 262144, hard: 262144 }
  101. # ── App tier ─────────────────────────────────────────────────────
  102. ingestd:
  103. build: .
  104. command: ["/app/ingestd"]
  105. environment:
  106. BA_ENV: dev
  107. BA_HTTP_ADDR: ":8800"
  108. BA_NATS_URL: nats://nats:4222
  109. BA_REDIS_URL: redis://redis:6379/0
  110. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  111. BA_INGESTD_SOURCES: "acme-001:acme-001-prom:s3cret-acme-001,acme-002:acme-002-prom:s3cret-acme-002,globex-002:grafana:s3cret-globex"
  112. BA_INGESTD_RATE_LIMIT_PER_SOURCE: "20000"
  113. BA_INGESTD_RATE_LIMIT_PER_COMPANY: "10000"
  114. # M4: MQTT subscriber. ingestd subscribes to ba/+/+/incoming
  115. # with the dedicated `ingestd` user. The auth file lists
  116. # this user (password "ingestd-broker-only") so EMQX's
  117. # built-in-db authenticates the connection.
  118. BA_INGESTD_MQTT_BROKER: "tcp://emqx:1883"
  119. BA_INGESTD_MQTT_USERNAME: "ingestd"
  120. BA_INGESTD_MQTT_PASSWORD: "ingestd-broker-only"
  121. BA_INGESTD_MQTT_SUBSCRIBE: "ba/+/+/incoming"
  122. # M5: WebSocket ingest + live tail. TAIL_TOKEN gates the
  123. # /v1/tail/ws endpoint; a static token is fine for the
  124. # dev path; M11 swaps for JWT.
  125. BA_INGESTD_TAIL_TOKEN: "tail-dev-token-please-change-in-prod"
  126. BA_INGESTD_MAX_CONCURRENT_PER_IP: "32"
  127. BA_INGESTD_DEDUPE_TTL_SECONDS: "300"
  128. # M9 layer 6: circuit breaker (trips after 5 failures in 10s, 30s open)
  129. BA_INGESTD_CB_FAILURE_THRESHOLD: "5"
  130. BA_INGESTD_CB_FAILURE_WINDOW_SECS: "10"
  131. BA_INGESTD_CB_OPEN_DURATION_SECS: "30"
  132. BA_INGESTD_CB_MAX_HALF_OPEN: "1"
  133. # M9 layer 7: quarantine (bans source at 100 hits/5min for 10min)
  134. BA_INGESTD_QUARANTINE_HITS_THRESHOLD: "100"
  135. BA_INGESTD_QUARANTINE_WINDOW_SECONDS: "300"
  136. BA_INGESTD_QUARANTINE_DURATION_SECONDS: "600"
  137. # M13a W4: JWT gate for the /v1/admin/ingest route.
  138. # Shares the secret with authd. Set to empty string to
  139. # disable the admin route (back to M11 behavior).
  140. BA_INGESTD_AUTHD_JWT_SECRET: "${BA_AUTHD_JWT_SECRET:-}"
  141. BA_AUTHD_ISSUER: "broad-announce"
  142. ports: ["8800:8800"]
  143. depends_on:
  144. nats: { condition: service_healthy }
  145. redis: { condition: service_healthy }
  146. postgres: { condition: service_healthy }
  147. emqx: { condition: service_healthy }
  148. routerd:
  149. build: .
  150. command: ["/app/routerd"]
  151. environment:
  152. BA_ENV: dev
  153. BA_HTTP_ADDR: ":8801"
  154. BA_NATS_URL: nats://nats:4222
  155. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  156. # M6.5: router-level dedupe collapse window. A burst of
  157. # identical alerts is held for up to this many ms, then
  158. # a single delivery is fanned out with the final
  159. # dedupe_count. A continuous stream re-flushes every
  160. # DedupeFlushMs.
  161. BA_ROUTERD_DEDUPE_FLUSH_MS: "2000"
  162. # M13a W5: JWT gate on /v1/admin/dedupe/{state,flush}.
  163. # Shares the same secret as authd so its tokens verify.
  164. BA_AUTHD_JWT_SECRET: "${BA_AUTHD_JWT_SECRET:-}"
  165. BA_AUTHD_ISSUER: "broad-announce"
  166. ports: ["8801:8801"]
  167. depends_on:
  168. nats: { condition: service_healthy }
  169. postgres: { condition: service_healthy }
  170. deliverd-fcm:
  171. build: .
  172. command: ["/app/deliverd-fcm"]
  173. environment:
  174. BA_ENV: dev
  175. BA_HTTP_ADDR: ":8802"
  176. BA_NATS_URL: nats://nats:4222
  177. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  178. BA_FAKECMD_URL: "http://fakefcmd:8820"
  179. # M8: in-process retry. Defaults match
  180. # internal/retry.Default() — 10 attempts, base
  181. # 100ms, cap 2s, total budget 30s. Tuned to
  182. # terminate in ~12s for a fully failing target
  183. # so a single misbehaving source can't tie up a
  184. # consumer.
  185. BA_DELIVERD_MAX_ATTEMPTS: "10"
  186. BA_DELIVERD_RETRY_BASE_MS: "100"
  187. BA_DELIVERD_RETRY_MAX_MS: "2000"
  188. BA_DELIVERD_RETRY_BUDGET_MS: "30000"
  189. # M13a W5: JWT gate on /v1/admin/dlq (channel=fcm).
  190. BA_AUTHD_JWT_SECRET: "${BA_AUTHD_JWT_SECRET:-}"
  191. BA_AUTHD_ISSUER: "broad-announce"
  192. ports: ["8802:8802"]
  193. depends_on:
  194. nats: { condition: service_healthy }
  195. postgres: { condition: service_healthy }
  196. fakefcmd: { condition: service_started }
  197. deliverd-telegram:
  198. build: .
  199. command: ["/app/deliverd-telegram"]
  200. environment:
  201. BA_ENV: dev
  202. BA_HTTP_ADDR: ":8821"
  203. BA_NATS_URL: nats://nats:4222
  204. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  205. BA_TELEGRAM_BOT_TOKEN: "fake-tg-bot-token-acme-001"
  206. BA_TELEGRAM_FAKE_URL: "http://faketgmd:8830"
  207. # M8: same retry knobs as deliverd-fcm.
  208. BA_DELIVERD_MAX_ATTEMPTS: "10"
  209. BA_DELIVERD_RETRY_BASE_MS: "100"
  210. BA_DELIVERD_RETRY_MAX_MS: "2000"
  211. BA_DELIVERD_RETRY_BUDGET_MS: "30000"
  212. # M13a W5: JWT gate on /v1/admin/dlq (channel=telegram).
  213. BA_AUTHD_JWT_SECRET: "${BA_AUTHD_JWT_SECRET:-}"
  214. BA_AUTHD_ISSUER: "broad-announce"
  215. ports: ["8821:8821"]
  216. depends_on:
  217. nats: { condition: service_healthy }
  218. postgres: { condition: service_healthy }
  219. faketgmd: { condition: service_started }
  220. telegramd:
  221. build: .
  222. command: ["/app/telegramd"]
  223. environment:
  224. BA_ENV: dev
  225. BA_HTTP_ADDR: ":8822"
  226. BA_NATS_URL: nats://nats:4222
  227. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  228. BA_TELEGRAM_FAKE_URL: "http://faketgmd:8830"
  229. ports: ["8822:8822"]
  230. depends_on:
  231. nats: { condition: service_healthy }
  232. postgres: { condition: service_healthy }
  233. faketgmd: { condition: service_started }
  234. faketgmd:
  235. build: .
  236. command: ["/app/faketgmd", "--addr", ":8830", "--timeout", "5"]
  237. ports: ["8830:8830"]
  238. fakefcmd:
  239. build: .
  240. command: ["/app/fakefcmd", "--addr", ":8820"]
  241. ports: ["8820:8820"]
  242. healthcheck:
  243. test: ["CMD", "wget", "-qO-", "http://localhost:8820/health"]
  244. interval: 5s
  245. timeout: 3s
  246. retries: 10
  247. seed:
  248. build: .
  249. command: ["/app/seed"]
  250. environment:
  251. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  252. BA_MIGRATIONS_DIR: /migrations
  253. volumes:
  254. - ./migrations:/migrations:ro
  255. depends_on:
  256. postgres: { condition: service_healthy }
  257. admind:
  258. build: .
  259. command: ["/app/admind"]
  260. environment:
  261. BA_ENV: dev
  262. BA_HTTP_ADDR: ":8803"
  263. BA_NATS_URL: nats://nats:4222
  264. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  265. # M8: admind connects to NATS to publish DLQ
  266. # replays back onto the deliveries.<chan>.<co>
  267. # subject. The HTTPAddr serves /v1/ping, /v1/dlq*,
  268. # /dlq, /health, /metrics.
  269. # M13a W4: enable the JWT gate by sharing the authd secret.
  270. BA_AUTHD_JWT_SECRET: "${BA_AUTHD_JWT_SECRET:?BA_AUTHD_JWT_SECRET must be set (see scripts/generate-jwt-secret.sh)}"
  271. BA_AUTHD_ISSUER: "broad-announce"
  272. ports: ["8803:8803"]
  273. depends_on:
  274. nats: { condition: service_healthy }
  275. postgres: { condition: service_healthy }
  276. authd: { condition: service_healthy }
  277. # M13a: multi-tenant auth IdP. Issues JWTs and stores
  278. # refresh tokens in Postgres. Other services share
  279. # BA_AUTHD_JWT_SECRET with this one to verify tokens.
  280. authd:
  281. build: .
  282. command: ["/app/authd"]
  283. environment:
  284. BA_ENV: dev
  285. BA_AUTHD_HTTP_ADDR: ":8804"
  286. BA_AUTHD_ISSUER: "broad-announce"
  287. BA_AUTHD_JWT_SECRET: "${BA_AUTHD_JWT_SECRET:?BA_AUTHD_JWT_SECRET must be set (see scripts/generate-jwt-secret.sh)}"
  288. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  289. # M13a W1: dev-only flag that lets authd generate a
  290. # secret on first run if BA_AUTHD_JWT_SECRET is unset.
  291. # In production this MUST be unset; the secret comes
  292. # from a sealed-secret or KMS.
  293. BA_AUTHD_ALLOW_GENERATED_SECRET: "1"
  294. BA_AUTHD_SECRET_FILE: /var/run/broad-announce/authd.jwt
  295. ports: ["8804:8804"]
  296. volumes:
  297. - authd-data:/var/run/broad-announce
  298. depends_on:
  299. postgres: { condition: service_healthy }
  300. archiverd:
  301. build: .
  302. command: ["/app/archiverd"]
  303. environment:
  304. BA_ENV: dev
  305. BA_HTTP_ADDR: ":8805"
  306. BA_POSTGRES_DSN: postgres://ba:ba@postgres:5432/ba?sslmode=disable
  307. # M7: archiver cadence + retention cutoff. The
  308. # Timescale retention policy does the same at 7d;
  309. # the archiver just runs ahead so ClickHouse has
  310. # the data before TS drops it.
  311. BA_ARCHIVERD_RUN_EVERY_SECONDS: "3600"
  312. BA_ARCHIVERD_OLDER_THAN_HOURS: "168"
  313. BA_ARCHIVERD_BATCH_SIZE: "10000"
  314. BA_ARCHIVERD_CLICKHOUSE_URL: "http://clickhouse:8123"
  315. # M13a W5: JWT gate on /v1/admin/archiver/run.
  316. BA_AUTHD_JWT_SECRET: "${BA_AUTHD_JWT_SECRET:-}"
  317. BA_AUTHD_ISSUER: "broad-announce"
  318. ports: ["8805:8805"]
  319. depends_on:
  320. postgres: { condition: service_healthy }
  321. clickhouse: { condition: service_started }
  322. # ── Observability ────────────────────────────────────────────────
  323. prometheus:
  324. image: prom/prometheus:latest
  325. command:
  326. - --config.file=/etc/prometheus/prometheus.yml
  327. volumes:
  328. - ./deploy/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
  329. # F2: alert rules (NATS resource limits, ingestd publish-path health).
  330. - ./deploy/prometheus/rules:/etc/prometheus/rules:ro
  331. ports: ["9090:9090"]
  332. depends_on: [ingestd, routerd, deliverd-fcm, deliverd-telegram, telegramd, admind]
  333. grafana:
  334. image: grafana/grafana:latest
  335. # :3000 is held by gogs (git3) on this host. Map to :3001
  336. # on the host, keep :3000 internal. M0 ports convention
  337. # says canonical ports stay; the host-port override is a
  338. # one-line exception, fully isolated to grafana.
  339. ports: ["3001:3000"]
  340. environment:
  341. GF_SECURITY_ADMIN_USER: admin
  342. GF_SECURITY_ADMIN_PASSWORD: admin
  343. GF_SECURITY_DISABLE_LOGIN_FORM: "false"
  344. # M9: provisioning paths for dashboards + datasources.
  345. GF_PATHS_PROVISIONING: /etc/grafana/provisioning
  346. GF_DASHBOARDS_ENABLED: "true"
  347. volumes:
  348. - ./deploy/grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro
  349. - ./deploy/grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards:ro
  350. - ./deploy/grafana/provisioning/dashboards/broad-announce-overview.json:/var/lib/grafana/dashboards/broad-announce-overview.json:ro
  351. depends_on: [prometheus]
  352. # ── Loadgen (one-shot smoke profile) ────────────────────
  353. loadgen-http:
  354. build: .
  355. command:
  356. - /app/loadgen-http
  357. - --target=http://ingestd:8800
  358. - --api-key=acme-001:prom-prod:s3cret-acme
  359. - --mode=normal
  360. - --rate=50
  361. - --duration=30s
  362. - --metrics=:8891
  363. - --instance=loadgen-http
  364. - --cluster-id=smoke
  365. profiles: ["loadgen"]
  366. depends_on:
  367. ingestd: { condition: service_started }
  368. # M10: 3-instance cluster hitting 5k/s. Each instance does ~1.7k/s.
  369. # Per-company cap is 10k/s, so 3 × 1.7k = 5.1k is safe.
  370. # Use `docker compose --profile loadgen-m10 up -d` to bring up all 3.
  371. loadgen-http-1:
  372. build: .
  373. command:
  374. - /app/loadgen-http
  375. - --target=http://ingestd:8800
  376. - --api-key=acme-001:prom-prod:s3cret-acme-001
  377. - --mode=normal
  378. - --rate=1700
  379. - --duration=10m
  380. - --ramp-up=30s
  381. - --metrics=:8891
  382. - --instance=loadgen-http-1
  383. - --cluster-id=m10
  384. profiles: ["loadgen-m10"]
  385. depends_on:
  386. ingestd: { condition: service_started }
  387. loadgen-http-2:
  388. build: .
  389. command:
  390. - /app/loadgen-http
  391. - --target=http://ingestd:8800
  392. - --api-key=acme-002:prom-prod:s3cret-acme-002
  393. - --mode=normal
  394. - --rate=1700
  395. - --duration=10m
  396. - --ramp-up=30s
  397. - --metrics=:8891
  398. - --instance=loadgen-http-2
  399. - --cluster-id=m10
  400. profiles: ["loadgen-m10"]
  401. depends_on:
  402. ingestd: { condition: service_started }
  403. loadgen-http-3:
  404. build: .
  405. command:
  406. - /app/loadgen-http
  407. - --target=http://ingestd:8800
  408. - --api-key=acme-003:prom-prod:s3cret-acme-003
  409. - --mode=normal
  410. - --rate=1700
  411. - --duration=10m
  412. - --ramp-up=30s
  413. - --metrics=:8891
  414. - --instance=loadgen-http-3
  415. - --cluster-id=m10
  416. profiles: ["loadgen-m10"]
  417. depends_on:
  418. ingestd: { condition: service_started }
  419. # M10 W4: Rogue loadgen — same company+source as loadgen-http-1 but
  420. # firing at 10× the per-source cap (100/s). Simulates a compromised source
  421. # at 1000/s. Per-source limiter drops 900/s (429); other companies stay clean.
  422. # NOT started by default with --profile loadgen-m10; spawned by step3_runaway_test().
  423. loadgen-http-4:
  424. build: .
  425. command:
  426. - /app/loadgen-http
  427. - --target=http://ingestd:8800
  428. - --api-key=acme-001:prom-prod:s3cret-acme-001
  429. - --mode=normal
  430. - --rate=1000
  431. - --duration=10m
  432. - --ramp-up=10s
  433. - --metrics=:8891
  434. - --instance=loadgen-http-4
  435. - --cluster-id=m10
  436. profiles: ["loadgen-m10"]
  437. depends_on:
  438. ingestd: { condition: service_started }
  439. # M11 W4: gRPC loadgen — 2-instance cluster at 10k/s total.
  440. # Each instance opens 8 parallel streams (workers) at 5k/s each.
  441. # Use: docker compose --profile loadgen-grpc up -d
  442. loadgen-grpc-1:
  443. build: .
  444. command:
  445. - /app/loadgen-grpc
  446. - --target=ingestd:9090
  447. - --api-key=acme-001:acme-001-prom:s3cret-acme-001
  448. - --rate=8000
  449. - --workers=16
  450. - --dedupe-pct=0
  451. - --duration=15m
  452. - --metrics=:8892
  453. - --instance=loadgen-grpc-1
  454. - --cluster-id=m11
  455. profiles: ["loadgen-grpc"]
  456. depends_on:
  457. ingestd: { condition: service_started }
  458. loadgen-grpc-2:
  459. build: .
  460. command:
  461. - /app/loadgen-grpc
  462. - --target=ingestd:9090
  463. - --api-key=acme-002:acme-002-prom:s3cret-acme-002
  464. - --rate=8000
  465. - --workers=16
  466. - --dedupe-pct=0
  467. - --duration=15m
  468. - --metrics=:8892
  469. - --instance=loadgen-grpc-2
  470. - --cluster-id=m11
  471. profiles: ["loadgen-grpc"]
  472. depends_on:
  473. ingestd: { condition: service_started }
  474. # M10-bench: delivery tier stubbed with deliverd-bench (no-op).
  475. # No FCM, no Telegram, no Postgres writes — just consume and ACK.
  476. # Allows broker+router ceiling testing at 50k/s without burning FCM credits.
  477. deliverd-bench:
  478. build: .
  479. command: ["/app/deliverd-bench"]
  480. environment:
  481. BA_ENV: dev
  482. BA_NATS_URL: nats://nats:4222
  483. profiles: ["bench"]
  484. depends_on:
  485. nats: { condition: service_healthy }
  486. # 10-instance loadgen cluster for 50k/s bench (5 × 5k instances × 2 = 50k).
  487. # Split across 2 host machines in production; on docker-compose single-host
  488. # we run 10 instances at 5k/s each for a total of 50k/s.
  489. loadgen-bench-1:
  490. build: .
  491. command:
  492. - /app/loadgen-http
  493. - --target=http://ingestd:8800
  494. - --api-key=acme-bench:prom-bench:s3cret-bench
  495. - --mode=normal
  496. - --rate=5000
  497. - --duration=5m
  498. - --ramp-up=15s
  499. - --metrics=:8891
  500. - --instance=loadgen-bench-1
  501. - --cluster-id=m10-bench
  502. profiles: ["bench"]
  503. depends_on:
  504. ingestd: { condition: service_started }
  505. loadgen-bench-2:
  506. build: .
  507. command:
  508. - /app/loadgen-http
  509. - --target=http://ingestd:8800
  510. - --api-key=acme-bench:prom-bench:s3cret-bench
  511. - --mode=normal
  512. - --rate=5000
  513. - --duration=5m
  514. - --ramp-up=15s
  515. - --metrics=:8891
  516. - --instance=loadgen-bench-2
  517. - --cluster-id=m10-bench
  518. profiles: ["bench"]
  519. depends_on:
  520. ingestd: { condition: service_started }
  521. volumes:
  522. pgdata: {}
  523. natsdata: {}
  524. chdata: {}
  525. authd-data: {}