From 4ee27bdcd5f06921fe474683fef631b634ccc307 Mon Sep 17 00:00:00 2001 From: artem Date: Mon, 20 Apr 2026 17:20:51 +0300 Subject: [PATCH 1/4] Solution --- .dockerignore | 8 +++++ Dockerfile | 22 +++++++++++++ cinema/management/__init__.py | 0 cinema/management/commands/__init__.py | 0 cinema/management/commands/wait_for_db.py | 33 ++++++++++++++++++++ cinema_service/settings.py | 27 ++++++++++------ docker-compose.yml | 38 +++++++++++++++++++++++ requirements.txt | 1 + 8 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 cinema/management/__init__.py create mode 100644 cinema/management/commands/__init__.py create mode 100644 cinema/management/commands/wait_for_db.py create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..10720bbe --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.git +__pycache__ +*.pyc +venv +.env +db.sqlite3 +static +media diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..503f9b9c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.12-slim +LABEL maintainer="your_email@example.com" + +ENV PYTHONUNBUFFERED 1 +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + libpq-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +RUN mkdir -p /vol/web/media /vol/web/static && \ + adduser --disabled-password --no-create-home django-user && \ + chown -R django-user:django-user /app /vol && \ + chmod -R 755 /vol + +USER django-user diff --git a/cinema/management/__init__.py b/cinema/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cinema/management/commands/__init__.py b/cinema/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cinema/management/commands/wait_for_db.py b/cinema/management/commands/wait_for_db.py new file mode 100644 index 00000000..f76dc8a8 --- /dev/null +++ b/cinema/management/commands/wait_for_db.py @@ -0,0 +1,33 @@ +import time +from django.db import connections +from django.db.utils import OperationalError +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + def handle(self, *args, **options): + self.stdout.write("Waiting for database...") + db_conn = None + attempts = 0 + max_attempts = 20 + + while attempts < max_attempts: + try: + db_conn = connections["default"] + db_conn.cursor() + self.stdout.write(self.style.SUCCESS( + f"Database available after {attempts} attempts!" + )) + return + except OperationalError: + attempts += 1 + self.stdout.write( + f"Database unavailable" + f" (attempt {attempts}/{max_attempts}), waiting 1s..." + ) + time.sleep(1) + + self.stdout.write(self.style.ERROR( + "Database unavailable after maximum attempts. Exiting." + )) + exit(1) diff --git a/cinema_service/settings.py b/cinema_service/settings.py index 90dde772..a0f3e19a 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -1,7 +1,7 @@ """ Django settings for cinema_service project. -Generated by 'django-admin startproject' using Django 4.0.4. +Generated by "django-admin startproject" using Django 4.0.4. For more information on this file, see https://docs.djangoproject.com/en/4.0/topics/settings/ @@ -9,10 +9,11 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.0/ref/settings/ """ +import os from datetime import timedelta from pathlib import Path -# Build paths inside the project like this: BASE_DIR / 'subdir'. +# Build paths inside the project like this: BASE_DIR / "subdir". BASE_DIR = Path(__file__).resolve().parent.parent @@ -24,10 +25,10 @@ "django-insecure-6vubhk2$++agnctay_4pxy_8cq)mosmn(*-#2b^v4cgsh-^!i3" ) -# SECURITY WARNING: don't run with debug turned on in production! +# SECURITY WARNING: don"t run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "127.0.0.1,localhost").split(",") INTERNAL_IPS = [ "127.0.0.1", @@ -86,12 +87,22 @@ DATABASES = { "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", + "ENGINE": "django.db.backends.postgresql", + "NAME": os.getenv("DB_NAME", "cinema"), + "USER": os.getenv("DB_USER", "postgres"), + "PASSWORD": os.getenv("DB_PASS", "postgres"), + "HOST": os.getenv("DB_HOST", "db"), + "PORT": os.getenv("DB_PORT", "5432"), } } +STATIC_URL = "/static/" +STATIC_ROOT = "/vol/web/static" +MEDIA_URL = "/media/" +MEDIA_ROOT = "/vol/web/media" + + # Password validation # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators @@ -131,10 +142,6 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/ -STATIC_URL = "static/" - -MEDIA_URL = "/media/" -MEDIA_ROOT = BASE_DIR / "media" # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..47d3de56 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.8' + +services: + db: + image: postgres:15-alpine + volumes: + - postgres_data:/var/lib/postgresql/data + environment: + - POSTGRES_DB=cinema + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + + app: + build: . + ports: + - "8000:8000" + volumes: + - .:/app + - media_data:/vol/web/media + - static_data:/vol/web/static + environment: + - DB_HOST=db + - DB_NAME=cinema + - DB_USER=postgres + - DB_PASS=postgres + - ALLOWED_HOSTS=127.0.0.1,localhost + command: > + sh -c "python manage.py wait_for_db && + python manage.py migrate && + python manage.py collectstatic --no-input && + python manage.py runserver 0.0.0.0:8000" + depends_on: + - db + +volumes: + postgres_data: + media_data: + static_data: diff --git a/requirements.txt b/requirements.txt index 2dc12c67..2536f121 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ djangorestframework djangorestframework-simplejwt drf-spectacular Pillow +psycopg2-binary>=2.9.9 From 9f455681efc1fea168e139fcf0252dc13b7949a1 Mon Sep 17 00:00:00 2001 From: artem Date: Mon, 20 Apr 2026 17:35:32 +0300 Subject: [PATCH 2/4] Solution --- Dockerfile | 7 +++++-- cinema/management/commands/wait_for_db.py | 14 +++++--------- cinema_service/settings.py | 2 +- docker-compose.yml | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 503f9b9c..1d18ed8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,11 @@ LABEL maintainer="your_email@example.com" ENV PYTHONUNBUFFERED 1 WORKDIR /app -RUN apt-get update && apt-get install -y \ +RUN apt-get update && apt-get install -y --no-install-recommends \ libpq-dev \ + gcc \ + libc6-dev \ + bash \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -15,7 +18,7 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN mkdir -p /vol/web/media /vol/web/static && \ - adduser --disabled-password --no-create-home django-user && \ + adduser --disabled-password --no-create-home --shell /bin/bash django-user && \ chown -R django-user:django-user /app /vol && \ chmod -R 755 /vol diff --git a/cinema/management/commands/wait_for_db.py b/cinema/management/commands/wait_for_db.py index f76dc8a8..56bc6e35 100644 --- a/cinema/management/commands/wait_for_db.py +++ b/cinema/management/commands/wait_for_db.py @@ -1,13 +1,12 @@ import time from django.db import connections from django.db.utils import OperationalError -from django.core.management.base import BaseCommand +from django.core.management.base import BaseCommand, CommandError class Command(BaseCommand): def handle(self, *args, **options): self.stdout.write("Waiting for database...") - db_conn = None attempts = 0 max_attempts = 20 @@ -16,18 +15,15 @@ def handle(self, *args, **options): db_conn = connections["default"] db_conn.cursor() self.stdout.write(self.style.SUCCESS( - f"Database available after {attempts} attempts!" + f"Database available after {attempts + 1} attempt(s)!" )) return except OperationalError: attempts += 1 self.stdout.write( - f"Database unavailable" - f" (attempt {attempts}/{max_attempts}), waiting 1s..." + f"Database unavailable " + f"(attempt {attempts}/{max_attempts}), waiting 1s..." ) time.sleep(1) - self.stdout.write(self.style.ERROR( - "Database unavailable after maximum attempts. Exiting." - )) - exit(1) + raise CommandError("Database unavailable after maximum attempts.") diff --git a/cinema_service/settings.py b/cinema_service/settings.py index a0f3e19a..b4f495b8 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -26,7 +26,7 @@ ) # SECURITY WARNING: don"t run with debug turned on in production! -DEBUG = True +DEBUG = os.getenv("DEBUG", "False") == "True" ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "127.0.0.1,localhost").split(",") diff --git a/docker-compose.yml b/docker-compose.yml index 47d3de56..4afce16a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,6 @@ services: ports: - "8000:8000" volumes: - - .:/app - media_data:/vol/web/media - static_data:/vol/web/static environment: @@ -24,6 +23,7 @@ services: - DB_USER=postgres - DB_PASS=postgres - ALLOWED_HOSTS=127.0.0.1,localhost + - DEBUG=False command: > sh -c "python manage.py wait_for_db && python manage.py migrate && From c0f9f58c893dea2b5ac33546f1e22cfc9d1f4004 Mon Sep 17 00:00:00 2001 From: artem Date: Mon, 20 Apr 2026 17:41:36 +0300 Subject: [PATCH 3/4] Solution --- Dockerfile | 12 ++++++------ cinema_service/settings.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1d18ed8e..759813a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,14 +12,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +RUN adduser --disabled-password --no-create-home --shell /bin/bash django-user && \ + mkdir -p /vol/web/media /vol/web/static && \ + chown -R django-user:django-user /vol && \ + chmod -R 755 /vol + COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -COPY . . - -RUN mkdir -p /vol/web/media /vol/web/static && \ - adduser --disabled-password --no-create-home --shell /bin/bash django-user && \ - chown -R django-user:django-user /app /vol && \ - chmod -R 755 /vol +COPY --chown=django-user:django-user . . USER django-user diff --git a/cinema_service/settings.py b/cinema_service/settings.py index b4f495b8..4facf146 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -97,9 +97,9 @@ } -STATIC_URL = "/static/" +STATIC_URL = "static/" STATIC_ROOT = "/vol/web/static" -MEDIA_URL = "/media/" +MEDIA_URL = "media/" MEDIA_ROOT = "/vol/web/media" From 81cfa49fe426e72e06ea2bd9423331431df02936 Mon Sep 17 00:00:00 2001 From: artem Date: Mon, 20 Apr 2026 18:03:55 +0300 Subject: [PATCH 4/4] Solution --- Dockerfile | 20 +++++++++++++++----- cinema_service/settings.py | 6 +++--- docker-compose.yml | 7 +------ scripts/__init__.py | 0 scripts/entrypoint.sh | 9 +++++++++ 5 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 scripts/__init__.py create mode 100644 scripts/entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 759813a3..ad85b3e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,18 @@ WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ libpq-dev \ + bash \ + gosu \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . + +RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ libc6-dev \ - bash \ + && pip install --no-cache-dir -r requirements.txt \ + && apt-get purge -y --auto-remove gcc libc6-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -17,9 +26,10 @@ RUN adduser --disabled-password --no-create-home --shell /bin/bash django-user & chown -R django-user:django-user /vol && \ chmod -R 755 /vol -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt +COPY . . +RUN chown -R django-user:django-user /app -COPY --chown=django-user:django-user . . +COPY ./scripts/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh -USER django-user +ENTRYPOINT ["/entrypoint.sh"] diff --git a/cinema_service/settings.py b/cinema_service/settings.py index 4facf146..94574abc 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -97,12 +97,12 @@ } -STATIC_URL = "static/" +STATIC_URL = "/static/" +MEDIA_URL = "/media/" + STATIC_ROOT = "/vol/web/static" -MEDIA_URL = "media/" MEDIA_ROOT = "/vol/web/media" - # Password validation # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators diff --git a/docker-compose.yml b/docker-compose.yml index 4afce16a..edb81e5c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.8' +version: "3.8" services: db: @@ -24,11 +24,6 @@ services: - DB_PASS=postgres - ALLOWED_HOSTS=127.0.0.1,localhost - DEBUG=False - command: > - sh -c "python manage.py wait_for_db && - python manage.py migrate && - python manage.py collectstatic --no-input && - python manage.py runserver 0.0.0.0:8000" depends_on: - db diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh new file mode 100644 index 00000000..9c234249 --- /dev/null +++ b/scripts/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +chown -R django-user:django-user /vol/web + +exec gosu django-user python manage.py wait_for_db && \ + python manage.py migrate && \ + python manage.py collectstatic --no-input && \ + python manage.py runserver 0.0.0.0:8000