Celery в docker для создания файлов в каталоге приложения
У меня есть приложение Django с Celery в качестве механизма отложенных задач. Есть несколько файловых операций, которые я хотел бы перенести в Celery, чтобы избавить Django от трудоемких процессов. Вот как выглядит мой docker-compose:
...
services:
django: &django
build:
context: .
dockerfile: ./compose/local/django/Dockerfile
image: my_project_local_django
container_name: my_project_local_django
depends_on:
- postgres
- redis
volumes:
- .:/app:z
- ./media:/app/media # To make sure that /media hooks up to Celery.
env_file:
- ./.envs/.local/.django
- ./.envs/.local/.postgres
ports:
- "8000:8000"
command: /start
postgres:
...
redis:
image: redis:6
container_name: my_project_local_redis
volumes:
- my_project_local_local_redis_data:/data
celeryworker:
<<: *django
image: my_project_local_celeryworker
container_name: my_project_local_celeryworker
volumes:
- ./media:/app/media # To ensure Celery has access to local /media directory.
depends_on:
- redis
- postgres
ports: []
command: /start-celeryworker
celerybeat:
<<: *django
image: my_project_local_celerybeat
container_name: my_project_local_celerybeat
volumes:
- ./media:/app/media # To ensure Celery has access to local /media directory.
depends_on:
- redis
- postgres
- django
restart: always
ports: []
command: /start-celerybeat
flower:
<<: *django
image: my_project_local_flower
container_name: my_project_local_flower
ports:
- "5555:5555"
command: /start-flower
И вот задача. Пока что я просто тестирую простой сценарий создания пустого файла.
...
@celery_app.task()
def generate_file(object_id):
lock_key = f"generate_file_task_lock:{object_id}"
# Try to acquire the lock
if redis_client.set(lock_key, "locked", ex=60, nx=True): # `nx=True` ensures the lock is only set if it doesn't exist
try:
object = Model.objects.get(pk=object_id)
dir_path = os.path.join(f"media/user_files/{object.user.username}/")
file_path = dir_path + str(object.pk)
# Create a blank file
os.makedirs(dir_path, exist_ok=True)
with open(file_path, "w+") as file:
file.write("")
if os.path.exists(file_path):
logger.info(f"File created successfully: {file_path}")
else:
logger.error(f"File not found after writing: {file_path}")
return f"Object {object_id} processed."
except Exception as e:
logger.error(f"Error creating file: {str(e)}", exc_info=True)
raise # Re-raise the exception so Celery logs it
finally:
redis_client.delete(lock_key) # Release the lock when the task is finished
else:
return f"Object {object_id} already has a running task."
Я немного упростил этот код, чтобы было меньше путаницы.
При выполнении этой задачи в логах отображается сообщение об успешном создании файла, но он не отображается в файлах моего проекта. Я предполагаю, что он может быть создан в контейнере Celery. Я пытался убедиться, что у Celery есть доступ к каталогу /media, но, должно быть, я что-то упускаю и у меня нет идей, как это решить.
Кстати, если я решу создать файлы в каталоге, отличном от /media (например, в корневом каталоге проекта), они появятся в моем проекте. Единственная проблема в том, что они недоступны для загрузки, поэтому логично использовать папку /media, если я хочу, чтобы они были доступны.