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
# Armature Microservices Architecture
# ====================================
# Multi-service setup demonstrating service mesh patterns
#
# Services:
# - API Gateway (Kong or custom)
# - User Service
# - Product Service
# - Order Service
# - Notification Service
# - Shared infrastructure
#
# Usage:
# docker compose up -d
# Gateway: http://localhost:8000
# Kong Admin: http://localhost:8001
#
# NOTE — demo simplification:
# User/Product/Order/Notification "services" below are illustrative only.
# docker/microservices/Dockerfile.service does not currently branch on the
# SERVICE_NAME build arg — it always builds and runs examples/rest_api.rs.
# So all four containers currently run byte-identical code; the distinct
# service blocks exist to demonstrate the deployment topology (Kong
# routing, per-service scaling/resource limits, service discovery via DNS
# names, dependency wiring) rather than genuinely different service
# implementations. SERVICE_NAME is kept (it's still useful for container
# naming/labels) but env vars that examples/rest_api.rs never reads
# (ELASTICSEARCH_URL, SMTP_HOST/PORT, RABBITMQ_URL) have been removed
# rather than left in as unused config. Wiring real per-service binaries
# is a follow-up.
version: "3.8"
x-service-defaults:
restart: unless-stopped
networks:
- mesh
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
x-armature-env:
RUST_LOG: info,armature=debug
OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317
REDIS_URL: redis://redis:6379
services:
# ============================================
# API Gateway (Kong)
# ============================================
kong:
image: kong:3.5
<<: *service-defaults
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /kong/kong.yml
KONG_PROXY_LISTEN: 0.0.0.0:8000
KONG_ADMIN_LISTEN: 0.0.0.0:8001
KONG_LOG_LEVEL: info
ports:
- "8000:8000" # Proxy
- "8001:8001" # Admin API
volumes:
- ./kong/kong.yml:/kong/kong.yml:ro
healthcheck:
test:
interval: 10s
timeout: 5s
retries: 5
# ============================================
# User Service
# ============================================
user-service:
# See the NOTE at the top of this file re: SERVICE_NAME/unused env vars.
build:
context: ../..
dockerfile: docker/microservices/Dockerfile.service
args:
SERVICE_NAME: user_service
<<: *service-defaults
environment:
<<: *armature-env
SERVICE_NAME: user-service
PORT: 3000
DATABASE_URL: postgres://armature:armature@postgres:5432/users
JWT_SECRET: ${JWT_SECRET:-user-service-secret}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
deploy:
replicas: 2
resources:
limits:
cpus: "0.5"
memory: 256M
# ============================================
# Product Service
# ============================================
product-service:
# See the NOTE at the top of this file re: SERVICE_NAME/unused env vars.
build:
context: ../..
dockerfile: docker/microservices/Dockerfile.service
args:
SERVICE_NAME: product_service
<<: *service-defaults
environment:
<<: *armature-env
SERVICE_NAME: product-service
PORT: 3000
DATABASE_URL: postgres://armature:armature@postgres:5432/products
depends_on:
postgres:
condition: service_healthy
elasticsearch:
condition: service_healthy
deploy:
replicas: 2
resources:
limits:
cpus: "0.5"
memory: 256M
# ============================================
# Order Service
# ============================================
order-service:
# See the NOTE at the top of this file re: SERVICE_NAME/unused env vars.
build:
context: ../..
dockerfile: docker/microservices/Dockerfile.service
args:
SERVICE_NAME: order_service
<<: *service-defaults
environment:
<<: *armature-env
SERVICE_NAME: order-service
PORT: 3000
DATABASE_URL: postgres://armature:armature@postgres:5432/orders
USER_SERVICE_URL: http://user-service:3000
PRODUCT_SERVICE_URL: http://product-service:3000
depends_on:
postgres:
condition: service_healthy
rabbitmq:
condition: service_healthy
deploy:
replicas: 2
resources:
limits:
cpus: "0.5"
memory: 256M
# ============================================
# Notification Service
# ============================================
notification-service:
# See the NOTE at the top of this file re: SERVICE_NAME/unused env vars.
build:
context: ../..
dockerfile: docker/microservices/Dockerfile.service
args:
SERVICE_NAME: notification_service
<<: *service-defaults
environment:
<<: *armature-env
SERVICE_NAME: notification-service
PORT: 3000
depends_on:
rabbitmq:
condition: service_healthy
deploy:
replicas: 1
resources:
limits:
cpus: "0.25"
memory: 128M
# ============================================
# Background Worker Service
# ============================================
worker-service:
build:
context: ../..
dockerfile: docker/microservices/Dockerfile.worker
<<: *service-defaults
environment:
<<: *armature-env
SERVICE_NAME: worker-service
RABBITMQ_URL: amqp://armature:armature@rabbitmq:5672
DATABASE_URL: postgres://armature:armature@postgres:5432/workers
depends_on:
rabbitmq:
condition: service_healthy
postgres:
condition: service_healthy
deploy:
replicas: 3
resources:
limits:
cpus: "0.5"
memory: 256M
# ============================================
# PostgreSQL
# ============================================
postgres:
image: postgres:16-alpine
<<: *service-defaults
environment:
POSTGRES_USER: armature
POSTGRES_PASSWORD: armature
POSTGRES_MULTIPLE_DATABASES: users,products,orders,workers
volumes:
- postgres_data:/var/lib/postgresql/data
- ./postgres/create-databases.sh:/docker-entrypoint-initdb.d/create-databases.sh:ro
ports:
- "5432:5432"
healthcheck:
test:
interval: 10s
timeout: 5s
retries: 5
# ============================================
# Redis
# ============================================
redis:
image: redis:7-alpine
<<: *service-defaults
command: redis-server --appendonly yes
volumes:
- redis_data:/data
ports:
- "6379:6379"
healthcheck:
test:
interval: 10s
timeout: 5s
retries: 5
# ============================================
# RabbitMQ
# ============================================
rabbitmq:
image: rabbitmq:3.12-management-alpine
<<: *service-defaults
environment:
RABBITMQ_DEFAULT_USER: armature
RABBITMQ_DEFAULT_PASS: armature
volumes:
- rabbitmq_data:/var/lib/rabbitmq
ports:
- "5672:5672"
- "15672:15672"
healthcheck:
test:
interval: 30s
timeout: 10s
retries: 5
# ============================================
# Elasticsearch
# ============================================
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.3
<<: *service-defaults
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms256m -Xmx256m"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
healthcheck:
test:
interval: 30s
timeout: 10s
retries: 5
# ============================================
# Jaeger (Tracing)
# ============================================
jaeger:
image: jaegertracing/all-in-one:1.52
<<: *service-defaults
environment:
COLLECTOR_OTLP_ENABLED: "true"
ports:
- "16686:16686" # UI
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
# ============================================
# Mailhog (Email Testing)
# ============================================
mailhog:
image: mailhog/mailhog:latest
<<: *service-defaults
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
networks:
mesh:
driver: bridge
volumes:
postgres_data:
redis_data:
rabbitmq_data:
elasticsearch_data: