-
-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathdocker-compose.scale.yml
More file actions
180 lines (170 loc) · 5.24 KB
/
Copy pathdocker-compose.scale.yml
File metadata and controls
180 lines (170 loc) · 5.24 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
##
## docker-compose.scale.yml
##
## Production-ready Docker Compose for 1 M+ users.
##
## Usage:
## docker compose -f docker-compose.scale.yml up -d --scale app=4
##
## This file adds:
## • Nginx reverse-proxy with upstream load balancing (round-robin)
## • Horizontally scaled app containers (default: 3 replicas)
## • Redis 7 with AOF persistence, maxmemory policy, and connection limits
## • WebSocket server behind its own upstream group
## • Health checks everywhere
##
## For Kubernetes, convert with `kompose convert -f docker-compose.scale.yml`.
##
version: '3.9'
x-app-env: &app-env
NODE_ENV: production
PORT: "3000"
# Redis — point to the compose service
REDIS_URL: redis://redis:6379
KV_REST_API_URL: ${KV_REST_API_URL:-}
KV_REST_API_TOKEN: ${KV_REST_API_TOKEN:-}
UPSTASH_REDIS_REST_URL: ${UPSTASH_REDIS_REST_URL:-}
UPSTASH_REDIS_REST_TOKEN: ${UPSTASH_REDIS_REST_TOKEN:-}
# Sentry (optional)
SENTRY_DSN: ${SENTRY_DSN:-}
# x402 payment
X402_RECEIVE_ADDRESS: ${X402_RECEIVE_ADDRESS:-}
# Admin
ADMIN_TOKEN: ${ADMIN_TOKEN:-}
services:
# ─── Reverse Proxy / Load Balancer ────────────────────────────────────
nginx:
image: nginx:1.27-alpine
container_name: crypto-news-lb
ports:
- "80:80"
- "443:443"
volumes:
- ./infra/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./infra/nginx/certs:/etc/nginx/certs:ro # mount TLS certs if available
depends_on:
app:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost/api/health"]
interval: 15s
timeout: 5s
retries: 3
# ─── Next.js App (horizontally scaled) ────────────────────────────────
app:
build:
context: .
dockerfile: Dockerfile
environment:
<<: *app-env
deploy:
replicas: 3
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
depends_on:
redis:
condition: service_healthy
# ─── Redis (hot cache + rate limiting) ────────────────────────────────
redis:
image: redis:7-alpine
container_name: crypto-news-redis
expose:
- "6379"
# Do NOT expose port 6379 to host in production — only internal traffic.
# Uncomment the next line for local debugging only:
# ports: ["6379:6379"]
volumes:
- redis_data:/data
command: >
redis-server
--appendonly yes
--maxmemory 512mb
--maxmemory-policy allkeys-lru
--maxclients 10000
--tcp-keepalive 60
--timeout 300
--hz 10
--save 60 1000
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 768M
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
# ─── WebSocket Server (real-time updates) ─────────────────────────────
# ─── WebSocket Server (real-time updates — horizontally scaled) ──────
websocket:
build:
context: .
dockerfile: Dockerfile.ws
environment:
PORT: "8080"
REDIS_URL: redis://redis:6379
WS_MAX_CONNECTIONS: "25000"
WS_HEALTH_PORT: "8081"
expose:
- "8080"
- "8081"
restart: unless-stopped
deploy:
replicas: 4
resources:
limits:
cpus: '1.0'
memory: 512M
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
# ─── Prometheus (metrics collection) ──────────────────────────────────
prometheus:
image: prom/prometheus:v2.50.0
volumes:
- ./infra/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./infra/grafana/alerts:/etc/prometheus/alerts
ports:
- "9090:9090"
restart: unless-stopped
# ─── Grafana (dashboards & alerting) ──────────────────────────────────
grafana:
image: grafana/grafana:10.3.0
volumes:
- ./infra/grafana/dashboards:/var/lib/grafana/dashboards
- ./infra/grafana/provisioning:/etc/grafana/provisioning
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:?GRAFANA_ADMIN_PASSWORD must be set}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH=/var/lib/grafana/dashboards/api-overview.json
depends_on:
- prometheus
restart: unless-stopped
volumes:
redis_data:
driver: local
networks:
default:
name: crypto-news-scale