Skip to content

Commit b296aa0

Browse files
committed
feat(swarm): create a stack for socrates
1 parent f348a49 commit b296aa0

File tree

4 files changed

+210
-2
lines changed

4 files changed

+210
-2
lines changed

docker/swarm/README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
> Labels are used for placement constraints in the docker stack templates.
3131
3232
Here are some example lables for the nodes. Adjust as needed
33-
3433
- Get the node ids using `docker node ls` on the manager node.
3534

3635
- On the manager node
@@ -63,10 +62,21 @@
6362
docker node update --label-add "api.variant=org" <node id>
6463
```
6564

65+
Socrates
66+
67+
```shell
68+
docker node update --label-add "socrates.enabled=true" <node id>
69+
```
70+
71+
```shell
72+
docker node update --label-add "socrates.variant=dev" <node id>
73+
docker node update --label-add "socrates.variant=org" <node id>
74+
```
75+
6676
4. Deploy [Portainer](stacks/portainer/README.md).
6777

6878
5. Complete the Portainer setup wizard & add the cluster to Portainer.
6979

7080
6. Add the container registry details to Portainer.
7181

72-
7. Deploy all the remaining stacks via Portainer. Note that you should not manage the portainer stack from within Portainer UI.
82+
7. Deploy all the remaining stacks via Portainer, including [Socrates](stacks/socrates/README.md). Note that you should not manage the portainer stack from within Portainer UI.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# -- Docker Configuration
2+
DOCKER_REGISTRY=
3+
DEPLOYMENT_ENV=
4+
DEPLOYMENT_VERSION=
5+
6+
# -- Runtime
7+
NODE_ENV=production
8+
SERVER_URL=
9+
# ALLOWED_ORIGINS=*
10+
# LOG_LEVEL=info
11+
12+
# -- Authentication
13+
API_KEY=
14+
DOCS_BASIC_AUTH_USER=
15+
DOCS_BASIC_AUTH_PASS=
16+
17+
# -- Groq AI
18+
GROQ_API_KEY=
19+
# GROQ_MODEL=openai/gpt-oss-20b
20+
# GROQ_MODEL_HTML=openai/gpt-oss-20b
21+
# GROQ_MODEL_CSS=openai/gpt-oss-20b
22+
# GROQ_MODEL_JAVASCRIPT=openai/gpt-oss-120b
23+
# GROQ_MODEL_PYTHON=openai/gpt-oss-120b
24+
# GROQ_TIMEOUT_MS=30000
25+
# GROQ_MAX_RETRIES=2
26+
# GROQ_BACKOFF_BASE_MS=500
27+
# GROQ_MAX_TOKENS=1024
28+
# GROQ_MAX_TOKENS_RETRY=2048
29+
30+
# -- Circuit Breaker
31+
# MODEL_CB_FAILURES=3
32+
# MODEL_CB_COOLDOWN_MS=30000
33+
34+
# -- Rate Limiting
35+
# PER_USER_LIMIT=10
36+
# GLOBAL_LIMIT=1000
37+
38+
# -- Resource Limits
39+
# CPU_LIMIT=1
40+
# MEMORY_LIMIT=1G
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Socrates Stack
2+
3+
Docker Swarm stack configuration for the Socrates AI hints API with Redis sidecar for caching and rate limiting.
4+
5+
## Overview
6+
7+
The Socrates stack provides the AI-powered hints API for freeCodeCamp coding challenges. It includes a Redis sidecar for caching and rate limiting. Services are deployed per environment with environment-specific configuration.
8+
9+
## Components
10+
11+
| Service | Purpose |
12+
| ------------ | ------------------------------------------ |
13+
| **socrates** | AI-powered hints API for coding challenges |
14+
| **redis** | Cache and rate limiting (sidecar) |
15+
16+
## Deployment
17+
18+
Name stacks per environment (e.g., `prd-socrates`, `stg-socrates`):
19+
20+
```bash
21+
# Set context and deploy
22+
docker context use <context_name>
23+
docker stack deploy -c stack-socrates.yml stg-socrates
24+
docker stack deploy -c stack-socrates.yml prd-socrates
25+
```
26+
27+
Then configure environment variables in Portainer UI for the deployed stack.
28+
29+
## Configuration
30+
31+
1. Copy `.env.sample` to `.env`
32+
2. Set required environment variables in Portainer UI
33+
3. Redeploy stack to apply changes
34+
35+
## Node Labels
36+
37+
Nodes running Socrates services require specific labels:
38+
39+
```shell
40+
docker node update --label-add "socrates.enabled=true" <node id>
41+
docker node update --label-add "socrates.variant=dev" <node id>
42+
# or
43+
docker node update --label-add "socrates.variant=org" <node id>
44+
```
45+
46+
## Notes
47+
48+
- Environment-specific naming allows managing multiple Socrates deployments
49+
- All configuration is managed through Portainer
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
x-deploy-config: &deploy-config
2+
deploy:
3+
replicas: 2
4+
placement:
5+
max_replicas_per_node: 1
6+
constraints:
7+
- node.labels.socrates.enabled == true
8+
- node.labels.socrates.variant == ${DEPLOYMENT_ENV}
9+
preferences:
10+
- spread: node.labels.socrates.variant == ${DEPLOYMENT_ENV}
11+
update_config:
12+
parallelism: 1
13+
delay: 10s
14+
failure_action: rollback
15+
resources:
16+
limits:
17+
cpus: ${CPU_LIMIT:-1}
18+
memory: ${MEMORY_LIMIT:-1G}
19+
reservations:
20+
memory: 512M
21+
restart_policy:
22+
condition: any
23+
labels:
24+
- org.freecodecamp.autoupdate=true
25+
26+
x-port-config: &port-config
27+
target: 3000
28+
protocol: tcp
29+
mode: host
30+
31+
services:
32+
svc-socrates:
33+
image: ${DOCKER_REGISTRY}/${DEPLOYMENT_ENV}/socrates:${DEPLOYMENT_VERSION}
34+
ports:
35+
- published: 4010
36+
<<: *port-config
37+
<<: *deploy-config
38+
healthcheck:
39+
test:
40+
[
41+
"CMD-SHELL",
42+
"wget --no-verbose --spider http://localhost:3000/health || exit 1",
43+
]
44+
interval: 30s
45+
timeout: 5s
46+
start_period: 15s
47+
retries: 3
48+
environment:
49+
# Runtime
50+
- NODE_ENV=${NODE_ENV:-production}
51+
- PORT=3000
52+
- SERVER_URL=${SERVER_URL}
53+
- ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-*}
54+
- LOG_LEVEL=${LOG_LEVEL:-info}
55+
# Redis (overlay network service discovery)
56+
- REDIS_URL=redis://svc-redis:6379
57+
# Authentication
58+
- API_KEY=${API_KEY}
59+
- DOCS_BASIC_AUTH_USER=${DOCS_BASIC_AUTH_USER}
60+
- DOCS_BASIC_AUTH_PASS=${DOCS_BASIC_AUTH_PASS}
61+
# Groq AI
62+
- GROQ_API_KEY=${GROQ_API_KEY}
63+
- GROQ_MODEL=${GROQ_MODEL:-openai/gpt-oss-20b}
64+
- GROQ_MODEL_HTML=${GROQ_MODEL_HTML:-openai/gpt-oss-20b}
65+
- GROQ_MODEL_CSS=${GROQ_MODEL_CSS:-openai/gpt-oss-20b}
66+
- GROQ_MODEL_JAVASCRIPT=${GROQ_MODEL_JAVASCRIPT:-openai/gpt-oss-120b}
67+
- GROQ_MODEL_PYTHON=${GROQ_MODEL_PYTHON:-openai/gpt-oss-120b}
68+
- GROQ_TIMEOUT_MS=${GROQ_TIMEOUT_MS:-30000}
69+
- GROQ_MAX_RETRIES=${GROQ_MAX_RETRIES:-2}
70+
- GROQ_BACKOFF_BASE_MS=${GROQ_BACKOFF_BASE_MS:-500}
71+
- GROQ_MAX_TOKENS=${GROQ_MAX_TOKENS:-1024}
72+
- GROQ_MAX_TOKENS_RETRY=${GROQ_MAX_TOKENS_RETRY:-2048}
73+
# Circuit Breaker
74+
- MODEL_CB_FAILURES=${MODEL_CB_FAILURES:-3}
75+
- MODEL_CB_COOLDOWN_MS=${MODEL_CB_COOLDOWN_MS:-30000}
76+
# Rate Limiting
77+
- PER_USER_LIMIT=${PER_USER_LIMIT:-10}
78+
- GLOBAL_LIMIT=${GLOBAL_LIMIT:-1000}
79+
80+
svc-redis:
81+
image: redis:7.4-alpine
82+
command: ["redis-server", "--appendonly", "yes"]
83+
volumes:
84+
- redis_data:/data
85+
healthcheck:
86+
test: ["CMD", "redis-cli", "ping"]
87+
interval: 10s
88+
timeout: 5s
89+
start_period: 5s
90+
retries: 3
91+
deploy:
92+
replicas: 1
93+
placement:
94+
constraints:
95+
- node.labels.socrates.enabled == true
96+
- node.labels.socrates.variant == ${DEPLOYMENT_ENV}
97+
update_config:
98+
parallelism: 1
99+
failure_action: rollback
100+
resources:
101+
limits:
102+
memory: 256M
103+
reservations:
104+
memory: 128M
105+
restart_policy:
106+
condition: any
107+
108+
volumes:
109+
redis_data:

0 commit comments

Comments
 (0)