From bb1d054192c9596af98e73b4c45fc77546a26ad9 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 1 Jul 2025 20:08:55 +0300 Subject: [PATCH] feat: Dockerize application for development This commit introduces a complete Docker and Docker Compose setup, enabling developers to build, run, and test the application in a consistent, isolated environment. It also prepares the application for containerized deployments. Changes include: - **Dockerfile**: A multi-stage build process is implemented. It separates build-time dependencies from the final runtime image, creating a smaller and more secure production artifact. `gunicorn` is used as the application server. - **docker-compose.yml**: Defines the `app` and `redis` services, linking them together. - **settings.py**: - Configured to connect to the Redis service for Django's caching backend. - Added settings for the Anti-fraud service URLs. - All new configurations (Redis URL, Anti-fraud URLs) are driven by environment variables for flexibility and security, with sensible defaults for local development. - **requirements/prod.txt**: Added `django-redis` and `requests`. - **.dockerignore**: Updated to exclude unnecessary files from the Docker build context, ensuring faster and smaller builds. --- .dockerignore | 38 +++++++++++++------- Dockerfile | 6 ++-- docker-compose.yml | 58 +++++++++++++++++++++++++++++++ promo_code/promo_code/settings.py | 15 ++++++++ requirements/prod.txt | 3 ++ 5 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore index 7b66fdf..080a908 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,17 +1,29 @@ -.dockerignore -Dockerfile -README.md +# Virtual Environments +venv/ +env/ +.venv/ +.env/ +# Python cache +__pycache__/ *.pyc -*.pyo -*.pyd -__pycache__ -env/ -.venv/ -.env -.env.example -.venv/ +# Tooling cache & reports +.ruff_cache/ +.coverage +.coverage.* + +# Version Control +.git/ +.gitignore + +# IDE and editor directories +.vscode/ +.idea/ + +# Requirements files not needed in production +requirements/dev.txt -.git -.gitignore \ No newline at end of file +# Log files +*.log +*.log.* diff --git a/Dockerfile b/Dockerfile index 7d9f9ea..d0232b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,10 +7,12 @@ ENV PYTHONBUFFERED=1 \ WORKDIR /usr/src/app -COPY . . +COPY requirements/prod.txt ./requirements.txt RUN pip install --upgrade pip && \ - pip install -r requirements/prod.txt + pip install --no-cache-dir -r requirements.txt + +COPY . . CMD ["sh", "-c", "cd /usr/src/app/promo_code && python manage.py migrate --noinput && gunicorn promo_code.wsgi:application --bind ${SERVER_ADDRESS}"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a2aeb4a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,58 @@ +services: + web: + build: . + container_name: promo_code + ports: + - 8000:8080 + volumes: + - .:/usr/src/app + depends_on: + db: + condition: service_healthy + restart: true + redis: + condition: service_started + antifraud: + condition: service_started + + env_file: + - .env + db: + image: postgres:17 + container_name: postgres_db + volumes: + - postgres_db:/var/lib/postgresql/data/ + environment: + POSTGRES_DB: ${POSTGRES_DATABASE} + POSTGRES_USER: ${POSTGRES_USERNAME} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USERNAME} -d ${POSTGRES_DATABASE}"] + interval: 10s + timeout: 10s + retries: 5 + start_period: 30s + env_file: + - .env + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + + + antifraud: + image: lodthe/prod-backend-antifraud:latest + environment: + SERVER_PORT: ${ANTIFRAUD_INTERNAL_PORT} + CACHE_DURATION_MS: ${ANTIFRAUD_CACHE_MS} + SLOWDOWN_AFTER: ${ANTIFRAUD_SLOWDOWN_AFTER} + BLOCKED_EMAILS: ${ANTIFRAUD_BLOCKED_EMAILS} + ports: + - "${ANTIFRAUD_EXTERNAL_PORT}:${ANTIFRAUD_INTERNAL_PORT}" + +volumes: + postgres_db: + redis_data: \ No newline at end of file diff --git a/promo_code/promo_code/settings.py b/promo_code/promo_code/settings.py index 9ca13f1..016a9d7 100644 --- a/promo_code/promo_code/settings.py +++ b/promo_code/promo_code/settings.py @@ -141,6 +141,21 @@ def load_bool(name, default): }, } +CACHES = { + 'default': { + 'BACKEND': 'django_redis.cache.RedisCache', + 'LOCATION': os.getenv('REDIS_URL', 'redis://127.0.0.1:6379/0'), + 'OPTIONS': { + 'CLIENT_CLASS': 'django_redis.client.DefaultClient', + }, + }, +} + +ANTIFRAUD_ADDRESS = f'{os.getenv("ANTIFRAUD_ADDRESS")}' +ANTIFRAUD_VALIDATE_URL = f'{ANTIFRAUD_ADDRESS}/api/validate' +ANTIFRAUD_UPDATE_USER_VERDICT_URL = ( + f'{ANTIFRAUD_ADDRESS}/internal/update_user_verdict' +) AUTH_PASSWORD_VALIDATORS = [ { diff --git a/requirements/prod.txt b/requirements/prod.txt index 1a04882..8755a4f 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -1,7 +1,10 @@ django==5.2 +django-redis==6.0.0 djangorestframework==3.15.2 djangorestframework-simplejwt==5.4.0 gunicorn==23.0.0 psycopg2-binary==2.9.10 pycountry==24.6.1 python-dotenv==1.0.1 +requests==2.32.4 +parameterized==0.9.0 \ No newline at end of file