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-адресам.
Теперь все работает.