Перейти до змісту

Entrypoint

entrypoint.sh — shell скрипт що виконується при кожному docker compose up. Він запускає підготовчі кроки і потім uvicorn.


entrypoint.sh — повний код

#!/bin/sh
set -e   # зупинитись при будь-якій помилці (exit code ≠ 0)
# Без set -e: uvicorn стартує навіть якщо migrate провалилась → 500 на всіх запитах

echo "=== Running migrations ==="
python manage.py migrate --noinput
# --noinput: не питати підтвердження в інтерактивному режимі
# Застосовує всі нові міграції з попередньої версії коду

echo "=== Collecting static files ==="
python manage.py collectstatic --noinput
# Збирає static з усіх installed apps у STATIC_ROOT (/app/staticfiles)
# Потрібно при кожному deploy (якщо змінились CSS/JS)

echo "=== Seeding demo data (if enabled) ==="
if [ "${SEED_DEMO_DATA}" = "1" ]; then
    python manage.py seed_demo_data --force
    # --force: без підтвердження, навіть якщо БД не порожня
fi

echo "=== Starting uvicorn ==="
exec python -m uvicorn notes_project.asgi:application \
    --host 0.0.0.0 \
    --port 8001 \
    --reload
    # --reload: авто-перезапуск при зміні файлів (ТІЛЬКИ ДЛЯ DEV!)
    # У production: прибрати --reload + додати --workers 4

# exec: замінює shell процес uvicorn'ом
# Без exec: shell → uvicorn як child process
# SIGTERM від Docker → shell → shell може ігнорувати → uvicorn не отримує сигнал
# З exec: SIGTERM → uvicorn → graceful shutdown ✓

set -e — чому migrate має завершитись перед uvicorn

Ланцюг set -e у дії:

✅ Нормальний запуск:
  migrate OK → collectstatic OK → seed OK → uvicorn START

❌ Якщо migrate провалилась (наприклад, PostgreSQL ще не ready):
  migrate FAIL (exit 1) → set -e → shell EXIT
  entrypoint повертає exit 1
  → Docker: контейнер вийшов з помилкою → "Exited (1)"
  → nginx НЕ стартує (depends_on: service_healthy)
  → Зрозуміла помилка в логах замість таємничих 500

Без set -e:

migrate FAIL → shell продовжується
collectstatic → може теж провалитись (якщо БД потрібна для static discovery)
seed_demo_data → може провалитись
uvicorn START → стартує без міграцій
→ 500 IntegrityError або OperationalError на кожній сторінці
→ важко зрозуміти що пішло не так

--noinput прапор

python manage.py migrate --noinput
python manage.py collectstatic --noinput

--noinput (або --no-input) вимикає інтерактивні підтвердження. Без нього:

You have requested to collect static files at the destination location as
specified in your settings:

    /app/staticfiles

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel:

У Docker контейнері немає інтерактивного терміналу → команда зависає назавжди. --noinput автоматично відповідає yes.


collectstatic — коли і навіщо

python manage.py collectstatic --noinput

Ця команда збирає static files з усіх встановлених apps в один директорій (STATIC_ROOT = /app/staticfiles):

Джерела:
  notes_app/static/notes_app/css/style.css
  notes_app/static/notes_app/js/group_chat.js
  django/contrib/admin/static/admin/css/base.css
  crispy_forms/static/...

Результат: /app/staticfiles/
  notes_app/css/style.css
  notes_app/js/group_chat.js
  admin/css/base.css
  ...

collectstatic виконується при кожному docker compose up тому що: 1. При зміні CSS/JS у code → нові файли мають потрапити у volume 2. При першому запуску staticfiles volume порожній 3. Це ідемпотентна операція (завжди безпечно виконати)


exec — чому не просто uvicorn

Без exec:

# ❌ Без exec (погано):
python -m uvicorn notes_project.asgi:application ...
# Process tree:
#   PID 1: sh entrypoint.sh
#   PID 2: python uvicorn  ← SIGTERM не доходить до нього
#
# docker stop → SIGTERM → PID 1 (sh) → sh ігнорує
#             → 10 сек → SIGKILL → hard kill
# Активні WebSocket з'єднання обриваються без graceful shutdown

З exec:

# ✅ З exec (правильно):
exec python -m uvicorn notes_project.asgi:application ...
# Process tree:
#   PID 1: python uvicorn  ← SIGTERM приходить напряму
#
# docker stop → SIGTERM → PID 1 (uvicorn) → graceful shutdown
#             → допомагає завершити активні WebSocket з'єднання
#             → зупиняється коректно

exec замінює поточний shell процес процесом uvicorn. Після exec PID 1 — це uvicorn, а не shell. Docker завжди надсилає SIGTERM до PID 1 при docker stop, і тепер uvicorn правильно його отримує.


production vs dev entrypoint

Development (поточний):

exec python -m uvicorn notes_project.asgi:application \
    --host 0.0.0.0 \
    --port 8001 \
    --reload    # ← автоматичний перезапуск при зміні .py файлів

Production:

exec python -m uvicorn notes_project.asgi:application \
    --host 0.0.0.0 \
    --port 8001 \
    --workers 4        # ← 4 паралельних worker (2 × CPU cores)
    # Без --reload: статичний запуск, вища продуктивність

--reload у production — антипатерн: - Uvicorn стежить за файловою системою → зайве навантаження - При кожному записі temp файлу або логу → можливий перезапуск - Один worker process (не можна використовувати --workers з --reload)


У книзі


Офіційна документація