-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
514 lines (487 loc) · 15.7 KB
/
docker-compose.yml
File metadata and controls
514 lines (487 loc) · 15.7 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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
services:
keycloak-postgres:
image: postgres:15-alpine
container_name: keycloak-db
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: ${KC_DB_USERNAME}
POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
ports:
- "5433:5432"
volumes:
- keycloak_pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${KC_DB_USERNAME} -d keycloak"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
keycloak-server:
image: quay.io/keycloak/keycloak:26.4
container_name: keycloak-server
environment:
# Keycloak Admin Setup
KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN_USER}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
# Keycloak Database Configuration
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloak-postgres:5432/keycloak
KC_DB_USERNAME: ${KC_DB_USERNAME}
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
ports:
- "8082:8080"
depends_on:
keycloak-postgres:
condition: service_healthy
volumes:
# Mount the realm file into the container's import directory
- ./docker/keycloak/realms/:/opt/keycloak/data/import/
command: [ "start-dev", "--import-realm" ]
restart: unless-stopped
product-mongodb:
image: mongo:latest
container_name: product-mongodb
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}
MONGO_INITDB_DATABASE: productdb
volumes:
- ./docker/mongodb/mongodb_data:/data/db # Persist data locally so you don't lose products on restart
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
cart-redis:
image: redis:alpine
container_name: cart-redis
ports:
- "6379:6379"
volumes:
- ./docker/redis/redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
restart: unless-stopped
# Optional: GUI for viewing Redis data
redis-insight:
image: redis/redisinsight:latest
container_name: redis-insight
ports:
- "5540:5540"
depends_on:
- cart-redis
volumes:
- ./docker/redis/redisinsight:/data
restart: unless-stopped
order-postgres:
image: postgres:15-alpine
container_name: order-postgres
environment:
POSTGRES_DB: orderdb
POSTGRES_USER: ${ORDER_DB_USER}
POSTGRES_PASSWORD: ${ORDER_DB_PASSWORD}
ports:
- "5432:5432"
volumes:
- ./docker/postgresql/order_pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${ORDER_DB_USER} -d orderdb"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Optional: pgAdmin for managing Order database
order-pgadmin:
image: dpage/pgadmin4
container_name: order-pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: ${ORDER_PGADMIN_USER}
PGADMIN_DEFAULT_PASSWORD: ${ORDER_PGADMIN_PASSWORD}
ports:
- "5050:80"
volumes:
- ./docker/pgadmin/servers.json:/pgadmin4/servers.json
depends_on:
- order-postgres
restart: unless-stopped
notification-rabbitmq:
image: rabbitmq:4.0-management
container_name: notification-rabbitmq
ports:
- "5672:5672" # Main Messaging Port
- "15672:15672" # Management Dashboard Port
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "check_running"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Email Testing Tool
notification-mailhog:
image: mailhog/mailhog:v1.0.1
container_name: notification-mailhog
ports:
- "1025:1025" # SMTP Port
- "8025:8025" # Web UI Port (View emails)
restart: unless-stopped
service-registry:
build: ./service-registry-discovery
container_name: service-registry
ports:
- "8761:8761"
healthcheck:
# Actuator returns a 200 OK only when the app is fully ready
test: ["CMD-SHELL", "wget -q --spider http://localhost:8761/actuator/health || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 60s
restart: unless-stopped
api-gateway:
build: ./api-gateway
container_name: api-gateway
ports:
- "8080:8080"
environment:
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://service-registry:8761/eureka
# Inside Docker, the Gateway must talk to 'keycloak-server' on port 8080
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak-server:8080/realms/ecommerce-realm
# Microservice Routing (Internal DNS)
SERVICES_PRODUCT_SERVICE_URI: http://product-service:8083/api/products
SERVICES_CART_SERVICE_URI: http://cart-service:8084/api/cart
SERVICES_ORDER_SERVICE_URI: http://order-service:8085/api/orders
SERVICES_NOTIFICATION_SERVICE_URI: http://notification-service:8086/api/notifications
SERVICES_AUTH_SERVICE_URI: http://auth-service:8081/api/auth
SERVICES_AUTH_SERVICE_USERS_URI: http://auth-service:8081/auth/users
# Cors Allowed Origins (for frontend to call the gateway)
SPRING_SECURITY_CORS_ALLOWED_ORIGINS: ${DOMAIN_NAME} # Production with custom domain
SPRING_DATA_REDIS_HOST: cart-redis
SPRING_DATA_REDIS_PORT: 6379
SPRING_DATA_REDIS_PASSWORD: ${REDIS_PASSWORD}
SPRING_PROFILES_ACTIVE: prod
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:8080/actuator/health || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 60s
depends_on:
service-registry:
condition: service_healthy
keycloak-server:
condition: service_started
cart-redis:
condition: service_healthy
restart: unless-stopped
auth-service:
build: ./auth-service
container_name: auth-service
ports:
- "8081:8081"
# env_file:
# Pass the Keycloak client secret from the host machine (.env file) to the container
# - ./docker/auth/.env
environment:
# Eureka: Maps to eureka.client.service-url.defaultZone
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://service-registry:8761/eureka
# Override Keycloak URIs to point to the keycloak container
KEYCLOAK_TOKEN_URI: http://keycloak-server:8080/realms/ecommerce-realm/protocol/openid-connect/token
KEYCLOAK_USERS_URI: http://keycloak-server:8080/admin/realms/ecommerce-realm/users
# Keycloak Client Secret (from .env file)
KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET}
depends_on:
- service-registry
- keycloak-server
restart: unless-stopped
product-service:
build: ./product-service
container_name: product-service
ports:
- "8083:8083"
environment:
# spring.data.mongodb.uri=mongodb://<user>:<password>@<host>:<port>/<database>
SPRING_DATA_MONGODB_URI: mongodb://${MONGO_ROOT_USER}:${MONGO_ROOT_PASSWORD}@product-mongodb:27017/productdb?authSource=admin
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://service-registry:8761/eureka
depends_on:
- service-registry
- product-mongodb
restart: unless-stopped
cart-service:
build: ./cart-service
container_name: cart-service
ports:
- "8084:8084"
environment:
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://service-registry:8761/eureka
# Override Redis Host (Maps to spring.data.redis.host)
SPRING_DATA_REDIS_HOST: cart-redis
# Override Redis Port (Maps to spring.data.redis.port)
SPRING_DATA_REDIS_PORT: 6379
# Override Redis Password (Maps to spring.data.redis.password)
SPRING_DATA_REDIS_PASSWORD: ${REDIS_PASSWORD}
# Override Product Service URI (Maps to services.product-service.uri)
SERVICES_PRODUCT_SERVICE_URI: http://product-service:8083/api/products
depends_on:
- service-registry
- product-service
- cart-redis
restart: unless-stopped
order-service:
build: ./order-service
container_name: order-service
ports:
- "8085:8085"
environment:
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://service-registry:8761/eureka
# Database: Maps to spring.datasource.url
SPRING_DATASOURCE_URL: jdbc:postgresql://order-postgres:5432/orderdb
SPRING_DATASOURCE_USERNAME: ${ORDER_DB_USER}
SPRING_DATASOURCE_PASSWORD: ${ORDER_DB_PASSWORD}
# RabbitMQ (Maps to spring.rabbitmq.host)
SPRING_RABBITMQ_HOST: notification-rabbitmq
SPRING_RABBITMQ_PORT: 5672
SPRING_RABBITMQ_USERNAME: ${RABBITMQ_USER}
SPRING_RABBITMQ_PASSWORD: ${RABBITMQ_PASSWORD}
# Internal Service Communication:
SERVICES_CART_SERVICE_URL: http://cart-service:8084
SERVICES_PRODUCT_SERVICE_URL: http://product-service:8083
# Resource Server JWT Issuer URI
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak-server:8080/realms/ecommerce-realm
depends_on:
service-registry:
condition: service_healthy
order-postgres:
condition: service_healthy
restart: unless-stopped
notification-service:
build: ./notification-service
container_name: notification-service
ports:
- "8086:8086"
environment:
EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: http://service-registry:8761/eureka
# RabbitMQ (Maps to spring.rabbitmq.host)
SPRING_RABBITMQ_HOST: notification-rabbitmq
SPRING_RABBITMQ_PORT: 5672
SPRING_RABBITMQ_USERNAME: ${RABBITMQ_USER}
SPRING_RABBITMQ_PASSWORD: ${RABBITMQ_PASSWORD}
# Mail (Maps to spring.mail.host)
SPRING_MAIL_HOST: ${MAIL_SMTP_HOST}
SPRING_MAIL_PORT: ${MAIL_SMTP_PORT}
depends_on:
service-registry:
condition: service_healthy
notification-rabbitmq:
condition: service_healthy
restart: unless-stopped
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
BUILD_CONFIG: production
API_URL: ${API_URL}
container_name: frontend
ports:
- "8087:8087"
depends_on:
service-registry:
condition: service_healthy
api-gateway:
condition: service_healthy
restart: unless-stopped
nginx-reverse-proxy:
image: nginx:latest
container_name: nginx-reverse-proxy
ports:
- "80:80"
# - "443:443"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
# Add volumes for SSL certificates (Certbot will manage this)
# - /etc/letsencrypt:/etc/letsencrypt:ro # Production with SSL
depends_on:
- api-gateway
- frontend
# ---------------------------------------------------------------------------
# Quality Dashboard — SonarQube Community Edition
# Runs only with: docker compose --profile quality up -d
# Never starts on local dev machines (insufficient RAM).
# Deploy on the production server that has >= 2 GB free RAM.
# ---------------------------------------------------------------------------
sonarqube-db:
image: postgres:17-alpine
container_name: sonarqube-db
profiles: ["quality"]
environment:
POSTGRES_DB: sonar
POSTGRES_USER: ${SONAR_DB_USER:-sonar}
POSTGRES_PASSWORD: ${SONAR_DB_PASSWORD:-sonar}
volumes:
- sonarqube_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${SONAR_DB_USER:-sonar} -d sonar"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
sonarqube:
image: sonarqube:community
container_name: sonarqube
profiles: ["quality"]
depends_on:
sonarqube-db:
condition: service_healthy
ports:
- "9000:9000"
environment:
SONAR_JDBC_URL: jdbc:postgresql://sonarqube-db:5432/sonar
SONAR_JDBC_USERNAME: ${SONAR_DB_USER:-sonar}
SONAR_JDBC_PASSWORD: ${SONAR_DB_PASSWORD:-sonar}
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions
deploy:
resources:
limits:
cpus: "2.0"
memory: 4G
reservations:
cpus: "0.5"
memory: 2G
restart: unless-stopped
# ---------------------------------------------------------------------------
# Observability Stack (LGTM: Loki + Grafana + Tempo + Micrometer/Prometheus)
# ---------------------------------------------------------------------------
prometheus:
image: prom/prometheus:v3.3.0
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=7d"
- "--web.enable-lifecycle"
- "--web.enable-remote-write-receiver"
deploy:
resources:
limits:
cpus: "1.0"
memory: 1G
reservations:
cpus: "0.05"
memory: 128M
restart: unless-stopped
loki:
image: grafana/loki:3.4.3
container_name: loki
ports:
- "3100:3100"
volumes:
- ./docker/loki/loki-config.yml:/etc/loki/config.yml:ro
- loki_data:/loki
command: -config.file=/etc/loki/config.yml
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.05"
memory: 64M
restart: unless-stopped
alloy:
image: grafana/alloy:v1.8.3
container_name: alloy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./docker/alloy/config.alloy:/etc/alloy/config.alloy:ro
command:
- run
- --server.http.listen-addr=0.0.0.0:12345
- /etc/alloy/config.alloy
depends_on:
- loki
deploy:
resources:
limits:
cpus: "0.3"
memory: 256M
reservations:
cpus: "0.05"
memory: 32M
restart: unless-stopped
tempo:
image: grafana/tempo:2.7.2
container_name: tempo
ports:
- "3200:3200" # Tempo HTTP API / query frontend
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
volumes:
- ./docker/tempo/tempo-config.yml:/etc/tempo.yml:ro
- tempo_data:/var/tempo
command: -config.file=/etc/tempo.yml
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.05"
memory: 64M
restart: unless-stopped
grafana:
image: grafana/grafana:11.6.14
container_name: grafana
ports:
- "3000:3000"
volumes:
- ./docker/grafana/grafana.ini:/etc/grafana/grafana.ini:ro
- ./docker/grafana/provisioning:/etc/grafana/provisioning:ro
- grafana_data:/var/lib/grafana
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
- GF_INSTALL_PLUGINS=grafana-lokiexplore-app,grafana-exploretraces-app
depends_on:
- prometheus
- loki
- tempo
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.05"
memory: 64M
restart: unless-stopped
# By removing specific network definitions, Docker Compose
# creates one default bridge network for all services.
volumes:
keycloak_pgdata:
prometheus_data:
loki_data:
tempo_data:
grafana_data:
sonarqube_db_data:
sonarqube_data:
sonarqube_logs:
sonarqube_extensions: