Cronjob в контейнере Docker использует устаревшие настройки в Django, несмотря на переменные окружения в Docker Compose

Я столкнулся с постоянной проблемой в приложении Django, использующем Django REST Framework. В приложении есть несколько моделей, включая одну с названием Project с временной меткой created_at. В файле manage.py есть команда управления, которая архивирует проект, если пользователь не предпринял никаких действий в течение 72 часов после его создания. В производстве эта команда выполняется cronjob, а вся система работает в контейнере Docker.

Первоначальная настройка

В прошлом я разделил внутреннюю среду на два Docker-контейнера:

Один контейнер запускал приложение Django и обслуживал API. Другой контейнер был предназначен исключительно для запуска cronjob, который выполнял эти команды управления.

Причина объединения контейнеров

При такой установке возникала та же проблема с устаревшими настройками, а управление обоими контейнерами по отдельности приводило к значительным накладным расходам, поскольку мне нужно было часто очищать кэши обоих контейнеров и восстанавливать их. Для упрощения я объединил API и cronjob в один контейнер и использовал Supervisor для управления cronjob в этом контейнере. Обратите внимание, что Supervisor работает правильно, поскольку сам cronjob запускается по расписанию (журналы подтверждают это), но это всего лишь проблема переменной окружения в cronjob.

Проблема

В cronjob используются устаревшие настройки - в частности, старый DB_HOST, который указывает на тестовую базу данных вместо правильного URL базы данных, определенного в переменных окружения, установленных Docker Compose. Однако само приложение Django (при обычном обращении) корректно подключается к производственной базе данных, как указано в этих переменных окружения.

Однако, когда я вручную запускаю команду archive_old_projects, используя

docker exec -it backend python3 manage.py archive_expired_new_project

он работает нормально и использует правильную переменную DB_HOST. И только когда выполняется cronjob, он возвращается к старой, недоступной конфигурации базы данных. Это вынуждает меня часто чистить Docker, удалять кэшированные данные и пересобирать образ для восстановления функциональности, что является непрактичным решением для постоянного обслуживания.

Вопрос

<

Шаги, которые я пробовал

Docker Cleanup: Пересобрали образ Docker, очистили кэшированные слои и убедились, что переменные окружения Docker Compose актуальны. Это временно устранило проблему, но она возникла вновь.

Проверка конфигурации: Убедились, что DB_HOST и другие критические настройки правильно загружены из окружения в settings.py.

<

Соответствующая настройка

Dockerfile

FROM python:3.10.12-slim
WORKDIR /app

COPY requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app/

RUN apt-get update && \
    apt-get install -y cron && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY crontab /etc/cron.d/cron_jobs
RUN chmod 0644 /etc/cron.d/cron_jobs
RUN crontab /etc/cron.d/cron_jobs

RUN pip install supervisor

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

EXPOSE 8000

CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

crontab

*/5 * * * * /usr/local/bin/python3 /app/manage.py archive_expired_new_project >> /app/logs/cron.log 2>&1
*/5 * * * * /usr/local/bin/python3 /app/manage.py archive_expired_to_contact_project >> /app/logs/cron.log 2>&1

supervisord.conf

[supervisord]
nodaemon=true

[program:gunicorn]
command=gunicorn --bind 0.0.0.0:8000 backend.wsgi:application
autostart=true
autorestart=true
stderr_logfile=/dev/stderr
stdout_logfile=/dev/stdout

[program:cron]
command=cron -f
autostart=true
autorestart=true
stderr_logfile=/dev/stderr
stdout_logfile=/dev/stdout

docker-compose.yml

name: my-app

services:
  backend:
    image: docker-user/repo-name:1.0.2
    container_name: backend
    expose:
      - "8000"
    networks:
      - app-network
    volumes:
      - ./logs:/app/logs
      - ./media:/app/media
    environment:
      SECRET_KEY: "*****"
      ...
      DB_HOST: "DATABASE_URL"
      DB_DATABASE: "db_name"
      DB_PORT: "5432"
      DB_USER: "db_user"
      DB_PASSWORD: "*****"
      ...

  frontend:
     ...

  nginx:
     ...

settings.py

...

DATABASES = {
  'default': {
    'ENGINE': 'django.db.backends.postgresql',
    'NAME': getenv('DB_DATABASE'),
    'USER': getenv('DB_USER'),
    'PASSWORD': getenv('DB_PASSWORD'),
    'HOST': getenv('DB_HOST'),
    'PORT': getenv('DB_PORT', 5432),
    'OPTIONS': {
      'sslmode': 'require',
    },
  }
}

Поскольку я не смог найти причину, по которой Docker сохраняет этот кэш, я использовал следующее обходное решение на случай, если другие пользователи столкнутся с той же проблемой с тем же типом проекта.

Я перестал использовать команду управления и выделенный контейнер app cron.

Я перенес эти команды управления в REST-представления, которые может запустить только определенный пользователь с определенным маркером аутентификации.

Я создал новый простой контейнер для задания cron, которое выполняет запросы curl по этим URL-адресам.

Теперь все работает.

Вернуться на верх