Skip to content

Commit 78f6ecb

Browse files
committed
feat(lab-05): PostgreSQL integration -- PG multi-DB, Redis cache, Keycloak, Traefik LB, Prometheus
1 parent f567916 commit 78f6ecb

4 files changed

Lines changed: 299 additions & 86 deletions

File tree

.github/workflows/ci.yml

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ jobs:
3434
echo "Validating: docker/docker-compose.sso.yml"
3535
docker compose -f docker/docker-compose.sso.yml config -q
3636
echo "OK: docker/docker-compose.sso.yml"
37-
for f in docker/docker-compose.integration.yml \
38-
docker/docker-compose.integration.yml docker/docker-compose.production.yml; do
37+
echo "Validating: docker/docker-compose.integration.yml"
38+
docker compose -f docker/docker-compose.integration.yml config -q
39+
echo "OK: docker/docker-compose.integration.yml"
40+
for f in docker/docker-compose.production.yml; do
3941
echo "Checking scaffold: $f"
4042
docker compose -f "$f" config --no-interpolate -q 2>&1 && echo "OK: $f" \
4143
|| echo "WARN: $f has placeholder variables (scaffold — not yet built out)"
@@ -226,4 +228,38 @@ jobs:
226228

227229
- name: Cleanup
228230
if: always()
229-
run: docker compose -f docker/docker-compose.sso.yml down -v
231+
run: docker compose -f docker/docker-compose.sso.yml down -v
232+
233+
lab-05-smoke:
234+
name: Lab 05 -- PG multi-DB + Keycloak + Redis + Traefik integration
235+
runs-on: ubuntu-latest
236+
needs: validate
237+
continue-on-error: true
238+
steps:
239+
- uses: actions/checkout@v4
240+
241+
- name: Install tools
242+
run: sudo apt-get install -y postgresql-client redis-tools curl netcat-openbsd
243+
244+
- name: Start integration stack
245+
run: docker compose -f docker/docker-compose.integration.yml up -d
246+
247+
- name: Wait for Keycloak
248+
run: timeout 200 bash -c 'until curl -sf http://localhost:8080/health/ready | grep -q UP; do sleep 5; done'
249+
250+
- name: Wait for PostgreSQL
251+
run: timeout 60 bash -c 'until pg_isready -h localhost -p 5432 -U postgres; do sleep 3; done'
252+
253+
- name: Run Lab 03-05 test script
254+
env:
255+
PG_PASS: "Lab05Password!"
256+
KC_PASS: "Lab05Password!"
257+
run: bash tests/labs/test-lab-03-05.sh
258+
259+
- name: Collect logs on failure
260+
if: failure()
261+
run: docker compose -f docker/docker-compose.integration.yml logs
262+
263+
- name: Cleanup
264+
if: always()
265+
run: docker compose -f docker/docker-compose.integration.yml down -v
Lines changed: 124 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,131 @@
1-
# Lab 05 — Advanced Integration: postgresql with full IT-Stack ecosystem
2-
---
31
services:
4-
postgresql:
5-
image: postgres:16
6-
container_name: it-stack-postgresql
7-
restart: unless-stopped
2+
3+
# ── PostgreSQL: serves as DB for Keycloak AND app ─────────────────
4+
postgres:
5+
image: postgres:16-alpine
6+
environment:
7+
POSTGRES_PASSWORD: Lab05Password!
8+
command: >
9+
postgres
10+
-c shared_buffers=256MB
11+
-c max_connections=200
12+
-c log_connections=on
13+
ports:
14+
- "5432:5432"
15+
networks:
16+
- int-net
17+
volumes:
18+
- pg-int:/var/lib/postgresql/data
19+
- ./integration/pg-init.sh:/docker-entrypoint-initdb.d/pg-init.sh:ro
20+
healthcheck:
21+
test: ["CMD-SHELL", "pg_isready -U postgres"]
22+
interval: 5s
23+
timeout: 3s
24+
retries: 20
25+
26+
# ── Redis: session cache shared by app nodes ──────────────────────
27+
redis:
28+
image: redis:7.2-alpine
29+
command: redis-server --requirepass Lab05Password! --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
830
ports:
9-
- "5432:$firstPort"
31+
- "6379:6379"
32+
networks:
33+
- int-net
34+
volumes:
35+
- redis-int:/data
36+
healthcheck:
37+
test: ["CMD", "redis-cli", "-a", "Lab05Password!", "--no-auth-warning", "PING"]
38+
interval: 5s
39+
timeout: 3s
40+
retries: 15
41+
42+
# ── Keycloak: uses PostgreSQL as its database ─────────────────────
43+
keycloak:
44+
image: quay.io/keycloak/keycloak:24.0
45+
command: start-dev
46+
depends_on:
47+
postgres:
48+
condition: service_healthy
1049
environment:
11-
- IT_STACK_ENV=lab-05-integration
12-
- KEYCLOAK_URL=
13-
- DB_HOST=
14-
- REDIS_HOST=
15-
- SMTP_HOST=
16-
- GRAYLOG_HOST=
17-
extra_hosts:
18-
- "lab-id1:10.0.50.11"
19-
- "lab-db1:10.0.50.12"
20-
- "lab-proxy1:10.0.50.15"
50+
KC_BOOTSTRAP_ADMIN_USERNAME: admin
51+
KC_BOOTSTRAP_ADMIN_PASSWORD: Lab05Password!
52+
KC_DB: postgres
53+
KC_DB_URL: "jdbc:postgresql://postgres:5432/keycloak"
54+
KC_DB_USERNAME: keycloak
55+
KC_DB_PASSWORD: Lab05Password!
56+
KC_HTTP_PORT: "8080"
57+
KC_HOSTNAME_STRICT: "false"
58+
KC_PROXY: edge
2159
networks:
22-
- it-stack-net
60+
- int-net
61+
healthcheck:
62+
test: ["CMD-SHELL", "curl -sf http://localhost:8080/health/ready || exit 1"]
63+
interval: 10s
64+
timeout: 5s
65+
retries: 30
66+
start_period: 60s
67+
labels:
68+
- "traefik.enable=true"
69+
- "traefik.http.routers.keycloak.rule=PathPrefix(`/auth`) || PathPrefix(`/realms`) || PathPrefix(`/admin`)"
70+
- "traefik.http.routers.keycloak.entrypoints=web"
71+
- "traefik.http.services.keycloak.loadbalancer.server.port=8080"
72+
73+
# ── App node 1 (whoami — simulates app backed by PG + Redis) ──────
74+
app-1:
75+
image: traefik/whoami:latest
76+
environment:
77+
WHOAMI_NAME: "app-node-1"
78+
networks:
79+
- int-net
80+
labels:
81+
- "traefik.enable=true"
82+
- "traefik.http.routers.app.rule=PathPrefix(`/app`)"
83+
- "traefik.http.routers.app.entrypoints=web"
84+
- "traefik.http.services.app.loadbalancer.server.port=80"
85+
86+
# ── App node 2 (second replica — load balanced by Traefik) ────────
87+
app-2:
88+
image: traefik/whoami:latest
89+
environment:
90+
WHOAMI_NAME: "app-node-2"
91+
networks:
92+
- int-net
93+
labels:
94+
- "traefik.enable=true"
95+
- "traefik.http.routers.app.rule=PathPrefix(`/app`)"
96+
- "traefik.http.routers.app.entrypoints=web"
97+
- "traefik.http.services.app.loadbalancer.server.port=80"
98+
99+
# ── Traefik: routes to Keycloak + app ─────────────────────────────
100+
traefik:
101+
image: traefik:v3.0
102+
command:
103+
- --api.dashboard=true
104+
- --api.insecure=true
105+
- --entrypoints.web.address=:80
106+
- --entrypoints.metrics.address=:8082
107+
- --metrics.prometheus=true
108+
- --metrics.prometheus.entrypoint=metrics
109+
- --providers.docker=true
110+
- --providers.docker.exposedbydefault=false
111+
- --accesslog=true
112+
- --accesslog.format=json
113+
ports:
114+
- "80:80"
115+
- "8080:8080"
116+
- "8082:8082"
117+
volumes:
118+
- /var/run/docker.sock:/var/run/docker.sock:ro
119+
networks:
120+
- int-net
121+
depends_on:
122+
keycloak:
123+
condition: service_healthy
23124

24125
networks:
25-
it-stack-net:
126+
int-net:
26127
driver: bridge
128+
129+
volumes:
130+
pg-int:
131+
redis-int:

docker/integration/pg-init.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
set -e
3+
# Create Keycloak database + user
4+
psql -v ON_ERROR_STOP=1 --username=postgres <<-EOSQL
5+
CREATE USER keycloak WITH PASSWORD 'Lab05Password!';
6+
CREATE DATABASE keycloak OWNER keycloak;
7+
GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;
8+
9+
CREATE USER labapp WITH PASSWORD 'Lab05Password!';
10+
CREATE DATABASE labapp OWNER labapp;
11+
GRANT ALL PRIVILEGES ON DATABASE labapp TO labapp;
12+
EOSQL

0 commit comments

Comments
 (0)