Skip to content

Commit 29c8527

Browse files
authored
Merge pull request #220 from grillazz/217-simple-ha-behind-nginx
compose refactor and project dependencies bump
2 parents c16016c + 71cb80b commit 29c8527

File tree

7 files changed

+295
-223
lines changed

7 files changed

+295
-223
lines changed

Makefile

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,104 @@
1+
# Set the default goal to "help" so that running "make" without arguments will display the help message.
2+
.DEFAULT_GOAL := help
3+
4+
# ====================================================================================
5+
# HELP
6+
# ====================================================================================
7+
# This target uses a combination of egrep, sort, and awk to parse the Makefile itself
8+
# and generate a formatted help message. It looks for lines containing '##' and
9+
# uses the text that follows as the help description for the target.
110
.PHONY: help
211
help: ## Show this help
312
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
413

14+
# ====================================================================================
15+
# DOCKER COMPOSE MANAGEMENT
16+
# ====================================================================================
517
.PHONY: docker-build
6-
docker-build: ## Build project with compose
18+
docker-build: ## Build project Docker images using compose
719
docker compose build
820

921
.PHONY: docker-up
1022
docker-up: ## Run project with compose
1123
docker compose up --remove-orphans
1224

1325
.PHONY: docker-clean
14-
docker-clean: ## Clean Reset project containers and volumes with compose
26+
docker-clean: ## Clean and reset project containers and volumes
1527
docker compose down -v --remove-orphans | true
1628
docker compose rm -f | true
17-
docker volume rm fastapi_postgres_data | true
29+
docker volume rm panettone_postgres_data | true
1830

31+
# ====================================================================================
32+
# DATABASE MIGRATIONS
33+
# ====================================================================================
1934
.PHONY: docker-apply-db-migrations
20-
docker-apply-db-migrations: ## apply alembic migrations to database/schema
21-
docker compose run --rm app alembic upgrade head
35+
docker-apply-db-migrations: ## Apply alembic migrations to the database schema
36+
docker compose run --rm api1 alembic upgrade head
2237

2338
.PHONY: docker-create-db-migration
24-
docker-create-db-migration: ## Create new alembic database migration aka database revision. Example: make docker-create-db-migration msg="add users table"
25-
docker compose up -d db | true
26-
docker compose run --no-deps app alembic revision --autogenerate -m "$(msg)"
39+
docker-create-db-migration: ## Create a new alembic database migration. Example: make docker-create-db-migration msg="add users table"
40+
docker compose up -d postgres | true
41+
docker compose run --no-deps api1 alembic revision --autogenerate -m "$(msg)"
2742

43+
# ====================================================================================
44+
# TESTING
45+
# ====================================================================================
2846
.PHONY: docker-test
2947
docker-test: ## Run project tests
30-
docker compose -f compose.yml -f test-compose.yml run --rm app pytest tests --durations=0 -vv
48+
docker compose -f compose.yml -f test-compose.yml run --rm api1 pytest tests --durations=0 -vv
3149

3250
.PHONY: docker-test-snapshot
33-
docker-test-snapshot: ## Run project tests with inline snapshot
34-
docker compose -f compose.yml -f test-compose.yml run --rm app pytest tests --inline-snapshot=fix
51+
docker-test-snapshot: ## Run project tests and update snapshots
52+
docker compose -f compose.yml -f test-compose.yml run --rm api1 pytest tests --inline-snapshot=fix
3553

54+
# ====================================================================================
55+
# CODE QUALITY & LINTING
56+
# ====================================================================================
3657
.PHONY: safety
37-
safety: ## Check project and dependencies with safety https://github.com/pyupio/safety
38-
docker compose run --rm app safety check
58+
safety: ## Check for insecure dependencies
59+
docker compose run --rm api1 safety check
3960

4061
.PHONY: py-upgrade
41-
py-upgrade: ## Upgrade project py files with pyupgrade library for python version 3.10
62+
py-upgrade: ## Upgrade Python syntax to a newer version
4263
pyupgrade --py313-plus `find app -name "*.py"`
4364

4465
.PHONY: lint
45-
lint: ## Lint project code.
66+
lint: ## Lint and format project code
4667
uv run ruff check --fix .
4768

69+
# ====================================================================================
70+
# DOCKER IMAGE BUILDING
71+
# ====================================================================================
4872
.PHONY: slim-build
49-
slim-build: ## with power of docker-slim build smaller and safer images
50-
docker-slim build --compose-file docker-compose.yml --target-compose-svc app --dep-include-target-compose-svc-deps true --http-probe-exec app fastapi-sqlalchemy-asyncpg_app:latest
73+
slim-build: ## Build smaller and more secure Docker images with docker-slim
74+
docker-slim build --compose-file docker-compose.yml --target-compose-svc api1 --dep-include-target-compose-svc-deps true --http-probe-exec api1 fastapi-sqlalchemy-asyncpg_api1:latest
5175

76+
# ====================================================================================
77+
# DATABASE SEEDING
78+
# ====================================================================================
5279
.PHONY: docker-feed-database
53-
docker-feed-database: ## create database objects and insert data
54-
docker compose exec db psql devdb devdb -f /home/gx/code/shakespeare_work.sql | true
55-
docker compose exec db psql devdb devdb -f /home/gx/code/shakespeare_chapter.sql | true
56-
docker compose exec db psql devdb devdb -f /home/gx/code/shakespeare_wordform.sql | true
57-
docker compose exec db psql devdb devdb -f /home/gx/code/shakespeare_character.sql | true
58-
docker compose exec db psql devdb devdb -f /home/gx/code/shakespeare_paragraph.sql | true
59-
docker compose exec db psql devdb devdb -f /home/gx/code/shakespeare_character_work.sql
80+
docker-feed-database: ## Create database objects and insert seed data
81+
docker compose exec postgres psql devdb devdb -f /home/gx/code/shakespeare_work.sql | true
82+
docker compose exec postgres psql devdb devdb -f /home/gx/code/shakespeare_chapter.sql | true
83+
docker compose exec postgres psql devdb devdb -f /home/gx/code/shakespeare_wordform.sql | true
84+
docker compose exec postgres psql devdb devdb -f /home/gx/code/shakespeare_character.sql | true
85+
docker compose exec postgres psql devdb devdb -f /home/gx/code/shakespeare_paragraph.sql | true
86+
docker compose exec postgres psql devdb devdb -f /home/gx/code/shakespeare_character_work.sql
6087

88+
# ====================================================================================
89+
# MODEL GENERATION
90+
# ====================================================================================
6191
.PHONY: model-generate
62-
model-generate: ## generate sqlalchemy models from database
92+
model-generate: ## Generate SQLAlchemy models from the database schema
6393
poetry run sqlacodegen --generator declarative postgresql://devdb:[email protected]/devdb --outfile models.py --schemas shakespeare --options nobidi
6494

95+
# ====================================================================================
96+
# ALTERNATIVE RUNTIMES
97+
# ====================================================================================
6598
.PHONY: docker-up-granian
66-
docker-up-granian: ## Run project with compose and granian
99+
docker-up-granian: ## Run project with compose and the Granian web server
67100
docker compose -f granian-compose.yml up --remove-orphans
68101

69102
.PHONY: docker-up-valkey
70-
docker-up-valkey: ## Run project with compose and valkey
103+
docker-up-valkey: ## Run project with compose and Valkey
71104
docker compose -f valkey-compose.yml up --remove-orphans

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<li><a href="#smtp-setup">Email Configuration</a></li>
3333
<li><a href="#uv-knowledge-and-inspirations">UV knowledge and inspirations</a></li>
3434
<li><a href="#large-language-model">Integration with local LLM</a></li>
35+
<li><a href="#ha-sample-with-nginx-as-load-balancer">High Availability sample with nginx as load balancer</a></li>
3536
</ul>
3637
</li>
3738
<li><a href="#acknowledgments">Acknowledgments</a></li>
@@ -184,6 +185,14 @@ ollama run llama3.2
184185

185186
<p align="right">(<a href="#readme-top">back to top</a>)</p>
186187

188+
### HA sample with nginx as load balancer
189+
Sample high availability setup with nginx as load balancer and 2 uvicorn instances running on different ports.
190+
```shell
191+
make docker-up-ha
192+
```
193+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
194+
195+
187196
### UV knowledge and inspirations
188197
- https://docs.astral.sh/uv/
189198
- https://hynek.me/articles/docker-uv/
@@ -217,6 +226,7 @@ I've included a few of my favorites to kick things off!
217226
<details>
218227
<summary>2025 (3 changes)</summary>
219228
<ul>
229+
<li>[SEP 2 2025] add sample high availability with nginx as load balancer</li>
220230
<li>[AUG 23 2025] intro exception handlers</li>
221231
<li>[JUL some sunny day 2025] add rotoger</li>
222232
<li>[MAY 3, 2025] add large language model integration :robot:</li>

app/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ async def lifespan(app: FastAPI):
3636
"Postgres pool created", idle_size=app.postgres_pool.get_idle_size()
3737
)
3838
yield
39+
except Exception as e:
40+
await logger.aerror("Error during app startup", error=repr(e))
41+
raise
3942
finally:
4043
await app.redis.close()
4144
await app.postgres_pool.close()

compose.yml

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
services:
2-
app:
3-
container_name: fsap_app
4-
network_mode: host
2+
api1:
3+
container_name: panettone_api1
54
build: .
65
environment:
76
- PYTHONPATH=/panettone
@@ -12,7 +11,6 @@ services:
1211
uvicorn app.main:app
1312
--host 0.0.0.0 --port 8080
1413
--lifespan=on --use-colors --loop uvloop --http httptools
15-
--reload --log-level debug
1614
"
1715
volumes:
1816
- ./app:/panettone/app
@@ -22,17 +20,16 @@ services:
2220
ports:
2321
- "8080:8080"
2422
depends_on:
25-
- db
26-
- inmemory
23+
- postgres
24+
- redis
2725

28-
db:
29-
container_name: fsap_db
30-
network_mode: host
26+
postgres:
27+
container_name: panettone_postgres
3128
build:
3229
context: ./db
3330
dockerfile: Dockerfile
3431
volumes:
35-
- fastapi_postgres_data:/var/lib/postgresql/data
32+
- panettone_postgres_data:/var/lib/postgresql/data
3633
env_file:
3734
- .env
3835
ports:
@@ -50,15 +47,15 @@ services:
5047
timeout: 5s
5148
retries: 5
5249

53-
inmemory:
50+
redis:
5451
image: redis:latest
55-
network_mode: host
56-
container_name: fsap_inmemory
52+
container_name: panettone_redis
5753
ports:
5854
- "6379:6379"
5955
env_file:
6056
- .env
6157
entrypoint: redis-server --appendonly yes
6258

59+
6360
volumes:
64-
fastapi_postgres_data: {}
61+
panettone_postgres_data: {}

pyproject.toml

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
[project]
22
name = "fastapi-sqlalchemy-asyncpg"
3-
version = "0.20.0"
3+
version = "0.21.0"
44
description = "A modern FastAPI application with SQLAlchemy 2.0 and AsyncPG for high-performance async database operations. Features include JWT authentication with Redis token storage, password hashing, connection pooling, data processing with Polars, Rich logging, task scheduling with APScheduler, and Shakespeare datasets integration."
55
readme = "README.md"
66
requires-python = ">=3.13"
77
dependencies = [
8-
"fastapi[all]>=0.116.1",
8+
"fastapi[all]>=0.116.2",
99
"pydantic[email]>=2.12.0a1",
1010
"pydantic-settings>=2.10.1",
1111
"sqlalchemy>=2.0.43",
12-
"uvicorn[standard]>=0.35.0",
12+
"uvicorn[standard]>=0.36.0",
1313
"asyncpg>=0.30.0",
14-
"alembic>=1.16.4",
14+
"alembic>=1.16.5",
1515
"httpx>=0.28.1",
16-
"pytest>=8.4.1",
17-
"pytest-cov>=6.2.1",
16+
"pytest>=8.4.2",
17+
"pytest-cov>=7.0.0",
1818
"uvloop>=0.21.0",
1919
"httptools>=0.6.4",
2020
"rich>=14.1.0",
2121
"pyjwt>=2.10.1",
2222
"redis>=6.4.0",
2323
"bcrypt>=4.3.0",
24-
"polars>=1.32.3",
24+
"polars[pyarrow]>=1.33.1",
2525
"python-multipart>=0.0.20",
26-
"fastexcel>=0.14.0",
27-
"inline-snapshot>=0.27.2",
28-
"dirty-equals>=0.9.0",
26+
"fastexcel>=0.15.1",
27+
"inline-snapshot>=0.29.0",
28+
"dirty-equals>=0.10.0",
2929
"polyfactory>=2.22.2",
30-
"granian>=2.5.0",
30+
"granian>=2.5.4",
3131
"apscheduler[redis,sqlalchemy]>=4.0.0a6",
3232
"rotoger",
3333
]
3434

3535
[tool.uv]
3636
dev-dependencies = [
37-
"ruff>=0.12.10",
37+
"ruff>=0.13.1",
3838
"devtools[pygments]>=0.12.2",
3939
"pyupgrade>=3.20.0",
40-
"ipython>=9.4.0",
41-
"sqlacodegen<=3.1.0",
40+
"ipython>=9.5.0",
41+
"sqlacodegen<=3.1.1",
4242
"tryceratops>=2.4.1",
43-
"locust>=2.39.0"
43+
"locust>=2.40.5"
4444

4545
]
4646

test-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
services:
2-
app:
2+
api1:
33
environment:
44
- POSTGRES_DB=testdb
55

6-
db:
6+
postgres:
77
environment:
88
- POSTGRES_USER=${POSTGRES_USER}
99
- POSTGRES_DB=testdb

0 commit comments

Comments
 (0)