Skip to content

Commit 9fe8068

Browse files
author
luisleo526
committed
Feat: Introduce multi-stage Docker build for optimized image size, update docker-compose for service dependencies and health checks, and add .dockerignore to exclude unnecessary files
1 parent 1dfcb45 commit 9fe8068

6 files changed

Lines changed: 177 additions & 62 deletions

File tree

.dockerignore

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Git files
2+
.git
3+
.gitignore
4+
.gitattributes
5+
6+
# Python cache files
7+
__pycache__
8+
*.py[cod]
9+
*$py.class
10+
*.so
11+
.Python
12+
*.egg-info/
13+
dist/
14+
build/
15+
*.egg
16+
.pytest_cache/
17+
.coverage
18+
htmlcov/
19+
.tox/
20+
.mypy_cache/
21+
.ruff_cache/
22+
23+
# Virtual environments
24+
venv/
25+
env/
26+
ENV/
27+
.venv/
28+
29+
# IDE files
30+
.idea/
31+
.vscode/
32+
*.swp
33+
*.swo
34+
*~
35+
.DS_Store
36+
37+
# Docker files (avoid recursive copies)
38+
Dockerfile*
39+
docker-compose*.yml
40+
docker-compose*.yaml
41+
.dockerignore
42+
43+
# Documentation
44+
*.md
45+
docs/
46+
README.md
47+
NGINX.md
48+
49+
# Environment files (use secrets/configs instead)
50+
.env
51+
.env.*
52+
example.env
53+
54+
# Volume directories (important to exclude)
55+
volume/
56+
./volume/
57+
58+
# Database files
59+
*.db
60+
*.sqlite
61+
*.sqlite3
62+
63+
# Logs
64+
*.log
65+
logs/
66+
67+
# Test files
68+
tests/
69+
test_*.py
70+
*_test.py
71+
72+
# CI/CD
73+
.github/
74+
.gitlab-ci.yml
75+
.travis.yml
76+
77+
# Scripts (unless needed in container)
78+
*.sh
79+
!minio-entrypoint.sh
80+
!init-minio.sh
81+
82+
# Backup files
83+
*.bak
84+
*.backup
85+
mysql-backup/
86+
87+
# Temporary files
88+
tmp/
89+
temp/
90+
*.tmp

.idea/.gitignore

Lines changed: 0 additions & 8 deletions
This file was deleted.

Dockerfile

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,64 @@
1-
FROM ubuntu:jammy-20240530
1+
# Multi-stage build for optimized image size
2+
# Stage 1: Builder stage
3+
FROM python:3.11-slim as builder
24

5+
# Set environment variables
6+
ENV PYTHONDONTWRITEBYTECODE=1 \
7+
PYTHONUNBUFFERED=1 \
8+
PIP_NO_CACHE_DIR=1 \
9+
PIP_DISABLE_PIP_VERSION_CHECK=1
310

4-
ENV PYTHONPATH="/run"
11+
WORKDIR /tmp
512

6-
# Set working directory for the application
7-
WORKDIR /run
13+
# Install build dependencies
14+
RUN apt-get update && apt-get install -y --no-install-recommends \
15+
gcc \
16+
g++ \
17+
python3-dev \
18+
libpq-dev \
19+
&& rm -rf /var/lib/apt/lists/*
820

9-
# Update and install required packages, clean up lists to reduce image size
10-
RUN apt-get update -qq && apt-get upgrade -y -qq && \
11-
apt-get install -y -qq --no-install-recommends \
12-
python3.11 \
13-
python3.11-dev \
14-
python3.11-distutils \
15-
python3-pip \
16-
git \
17-
build-essential && \
18-
rm -rf /var/lib/apt/lists/*
21+
# Copy requirements and install Python dependencies
22+
COPY requirements.txt .
23+
RUN pip install --user --no-warn-script-location -r requirements.txt
1924

20-
# Upgrade pip
21-
RUN python3.11 -m pip install --no-cache-dir --upgrade pip
25+
# Stage 2: Runtime stage
26+
FROM python:3.11-slim
2227

23-
# Copy requirements.txt and install Python dependencies
24-
COPY ./requirements.txt /requirements.txt
25-
RUN python3.11 -m pip install --no-cache-dir --upgrade -r /requirements.txt
28+
# Set environment variables
29+
ENV PYTHONDONTWRITEBYTECODE=1 \
30+
PYTHONUNBUFFERED=1 \
31+
PYTHONPATH="/app" \
32+
PATH="/home/appuser/.local/bin:${PATH}"
2633

27-
# Copy the application code to the working directory
28-
#COPY . /app
34+
# Install runtime dependencies only
35+
RUN apt-get update && apt-get install -y --no-install-recommends \
36+
curl \
37+
&& rm -rf /var/lib/apt/lists/* \
38+
&& apt-get clean
2939

30-
# Command to run the application
31-
CMD ["python3.11", "-m", "fastapi", "dev", "src/server.py", "--host", "0.0.0.0", "--port", "8000"]
40+
# Create non-root user for security
41+
RUN useradd -m -u 1000 appuser && \
42+
mkdir -p /app && \
43+
chown -R appuser:appuser /app
44+
45+
WORKDIR /app
46+
47+
# Copy Python packages from builder
48+
COPY --from=builder --chown=appuser:appuser /root/.local /home/appuser/.local
49+
50+
# Copy application code
51+
COPY --chown=appuser:appuser . /app
52+
53+
# Switch to non-root user
54+
USER appuser
55+
56+
# Health check
57+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
58+
CMD curl -f http://localhost:8000/health || exit 1
59+
60+
# Expose the application port
61+
EXPOSE 8000
62+
63+
# Default to production mode, but can be overridden in docker-compose
64+
CMD ["uvicorn", "src.server:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

docker-compose.yaml

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ services:
1111
env_file:
1212
- example.env
1313
volumes:
14-
- ./src:/run/src
15-
- ./alembic:/run/alembic
16-
- ./alembic.ini:/run/alembic.ini
17-
- ./run_alembic.sh:/run/run_alembic.sh
14+
- ./src:/app/src
15+
- ./alembic:/app/alembic
16+
- ./alembic.ini:/app/alembic.ini
17+
- ./run_alembic.sh:/app/run_alembic.sh
1818
environment:
19-
- PYTHONPATH=/run
19+
- PYTHONPATH=/app
2020
- DEV=true
21-
command: "python3.11 -m fastapi run src/server.py --host 0.0.0.0 --port 8000 --reload"
21+
depends_on:
22+
- redis
23+
- mysql
24+
- minio
2225
networks:
2326
- shared_network
2427

@@ -30,6 +33,11 @@ services:
3033
- shared_network
3134
volumes:
3235
- ./volume/redis_data:/data
36+
healthcheck:
37+
test: ["CMD", "redis-cli", "ping"]
38+
interval: 10s
39+
timeout: 5s
40+
retries: 5
3341

3442
mysql:
3543
container_name: template-mysql
@@ -45,35 +53,20 @@ services:
4553
command: --default-authentication-plugin=mysql_native_password
4654
networks:
4755
- shared_network
48-
49-
mysql-backup:
50-
container_name: template-mysql-backup
51-
build:
52-
context: ./mysql-backup
53-
dockerfile: Dockerfile
54-
restart: always
55-
depends_on:
56-
- mysql
57-
environment:
58-
MYSQL_HOST: mysql
59-
MYSQL_PORT: 3306
60-
MYSQL_USER: root
61-
MYSQL_PASSWORD: root
62-
MYSQL_DATABASE: template_db
63-
BACKUP_SCHEDULE: "0 2 * * *" # Daily at 2 AM
64-
BACKUP_RETENTION_DAYS: 7 # Keep backups for 7 days
65-
BACKUP_ON_STARTUP: "true" # Create backup when container starts
66-
volumes:
67-
- ./volume/mysql_backups:/backups
68-
networks:
69-
- shared_network
56+
healthcheck:
57+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
58+
interval: 10s
59+
timeout: 5s
60+
retries: 5
7061

7162
adminer:
7263
container_name: template-adminer
7364
image: adminer
7465
restart: always
7566
ports:
7667
- "8001:8080"
68+
depends_on:
69+
- mysql
7770
networks:
7871
- shared_network
7972

@@ -94,6 +87,11 @@ services:
9487
- shared_network
9588
entrypoint: /bin/sh
9689
command: ["/minio-entrypoint.sh"]
90+
healthcheck:
91+
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
92+
interval: 30s
93+
timeout: 10s
94+
retries: 3
9795

9896
networks:
9997
shared_network:

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ email-validator
1111
python-ulid[pydantic]
1212
redis[hiredis]
1313
boto3
14-
alembic
14+
alembic
15+
pwdlib[argon2]

src/utils/credentials.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from datetime import datetime, timedelta, timezone
22

33
from jose import jwt
4-
from passlib.context import CryptContext
4+
# from passlib.context import CryptContext
5+
from pwdlib import PasswordHash
56

67
from src.utils.handler import handle_jwt_error
78

8-
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
9+
pwd_context = PasswordHash.recommended() #CryptContext(schemes=["bcrypt"], deprecated="auto")
910

1011
SECRET_KEY = "c1b3e4b3-4b3c-4b3e-8b3c-4b3e4b3c4b3e"
1112
ALGORITHM = "HS256"

0 commit comments

Comments
 (0)