我在使用 Django REST Framework 的 Django 应用程序中面临持续存在的问题。该应用程序有多个模型,其中一个名为 Project ,带有 created_at 时间戳。如果用户在项目创建后 72 小时内未对其采取操作,manage.py 中有一个管理命令,该命令会将项目存档。该命令由生产中的 cronjob 执行,整个设置在 Docker 容器中运行。
以前,我把后端环境分成两个Docker容器:
一个容器运行 Django 应用程序并提供 API。 另一个容器专门用于运行执行这些管理命令的 cronjob。
此设置与过时的设置存在相同的问题,并且单独管理两个容器会增加大量开销,因为我需要清除两个容器的缓存并频繁重建它们。为了简化,我将 API 和 cronjob 合并到一个容器中,并使用 Supervisor 来管理该容器内的 cronjob。请注意,Supervisor 运行正常,因为 cronjob 本身按计划运行(日志证实了这一点),但这只是 cronjob 中的环境变量问题。
cronjob 使用过时的设置,特别是指向测试数据库的旧 DB_HOST,而不是 Docker Compose 设置的环境变量中定义的正确数据库 URL。但是,Django 应用程序本身(正常访问时)可以正确连接到这些环境变量中指定的生产数据库。
奇怪的是,当我使用
手动运行archive_old_projects命令时docker exec -it backend python3 manage.py archive_expired_new_project
它工作正常并使用正确的 DB_HOST 变量。只有当 cronjob 执行时,它才会回退到旧的、无法访问的数据库配置。这迫使我经常清理 Docker、删除缓存数据并重建映像以恢复功能,这对于持续维护来说是不切实际的解决方案。
对于每个本地构建,我始终使用
--pull
和 --no-cache
参数来确保一切都尽可能新鲜。不幸的是,这似乎对解决这个问题没有帮助——尽管采取了这些措施,cronjob 仍会间歇性地回退到过时的环境配置。
以下是有关我在测试和生产中使用环境变量的更多详细信息:
似乎 django 命令在从 cron 执行时依赖于某些“缓存”,但我无法找到并理解在哪里以及如何执行。
为什么 cronjob 会使用过时的设置运行,即使它和 Django 应用程序都驻留在同一个 Docker 容器中并且应该有权访问相同的环境变量?
Docker Cleanup:重建 Docker 镜像,清除缓存层,并确保 Docker Compose 环境变量是最新的。这暂时解决了问题,但问题又出现了。
配置检查:验证 DB_HOST 和其他关键设置是否从 settings.py 中的环境中正确加载。
Cronjob 命令:检查 cronjob 条目以确保它使用正确的 management.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:
...
设置.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 保留此缓存的任何原因,因此我使用了以下解决方法,以防其他用户在同一类型的项目中遇到相同的问题。
我停止使用管理命令和专用应用程序 cron 容器。
我将这些管理命令移至 REST 视图,只有具有特定身份验证令牌的特定用户才能启动。
我为 cron 作业创建了一个新的简单容器,用于在这些 URL 上执行curl 请求。
现在一切正常。