Django [ОШИБКА] Рабочий (pid:7) получил SIGKILL! Возможно, из-за нехватки памяти? сообщение при загрузке больших файлов

Вопрос или проблема

Мое приложение Django в контейнере позволяет пользователям загружать файлы (загружаемые напрямую в мои DigitalOcean Spaces). При тестировании на моем локальном устройстве (и на развертывании в Heroku) я могу успешно загружать небольшие файлы без проблем. Однако при загрузке больших файлов, например, размером более 200 МБ, я получаю следующие логи ошибок:

 [2024-09-29 19:00:51 +0000] [1] [CRITICAL] ВРЕМЯ ОЖИДАНИЯ РАБОТНИКА (pid:7)
web-1     | [2024-09-29 19:00:52 +0000] [1] [ERROR] Работник (pid:7) был убит сигналом SIGKILL! Возможно, нехватка памяти?
web-1     | [2024-09-29 19:00:52 +0000] [29] [INFO] Запуск работника с pid: 29

Ошибка возникает примерно через 30 секунд после начала загрузки, поэтому я подозреваю, что тайм-аут вызывается gunicorn из-за отсутствия ответа. Я не уверен, что делать, чтобы решить эту проблему, кроме как увеличить время ожидания, что мне сказали, не рекомендуется. Вот мой код, обрабатывающий загрузку файлов:

views.py:

@csrf_protect
def transcribe_submit(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            uploaded_file = request.FILES['file']

            request.session['uploaded_file_name'] = uploaded_file.name
            request.session['uploaded_file_size'] = uploaded_file.size

            session_id = str(uuid.uuid4())
            request.session['session_id'] = session_id

            try:
                transcribed_doc, created = TranscribedDocument.objects.get_or_create(id=session_id)
                transcribed_doc.audio_file = uploaded_file
                transcribed_doc.save()
          ...

            except Exception as e:
                # Логируем ошибку и отвечаем статусом ошибки сервера
                print(f"Произошла ошибка: {str(e)}")
                return HttpResponse(status=500)

          ...
        else:
            return HttpResponse(status=500)
    else:
        form = UploadFileForm()
        
    return render(request, 'transcribe/transcribe-en.html', {"form": form})

forms.py:

def validate_audio_language(value):
    # код для проверки языка аудио
    if value not in allowed_languages:
        raise ValidationError("Ошибка")

def validate_output_file_type(value):
    # код для проверки типа файла
    if value not in output_file_type:
        raise ValidationError("Ошибка")
    
class UploadFileForm(forms.Form):
    file = forms.FileField(validators=[validate_file])

docker-compose.yml:

#version: "3.9"
services:
  web:
    build: .
    #command: python /code/manage.py runserver  0.0.0.0:8000
    command: gunicorn mysite.wsgi -b 0.0.0.0:8000 --reload
    volumes:
      - .:/code
    ports:
      - 8000:8000
    depends_on:
      - db
      - redis
      - celery
    environment:
      -  "DJANGO_SECRET_KEY="
      
    user: user-me
  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - "POSTGRES_HOST_AUTH_METHOD=trust"
  redis:
    image: redis:6
    ports:
      - 6379:6379
  celery:
    build: .
    command: celery -A mysite worker --loglevel=info 
    volumes:
      - .:/code
    depends_on:
      - redis
      - db
    environment:
      -  "DJANGO_SECRET_KEY="

    user: user-me
volumes:
  postgres_data:

Ответ или решение

Ошибка, с которой вы сталкиваетесь ([ERROR] Worker (pid:7) was sent SIGKILL! Perhaps out of memory?), указывает на то, что рабочий процесс приложения django, запущенный с Gunicorn, был завершен системой, скорее всего, из-за недостатка памяти во время обработки загрузки большого файла. Ваша программа пытается обработать файл объемом 200+ МБ, что может превышать лимиты, установленные на вашем сервере. Вот несколько шагов, которые могут помочь решить эту проблему:

1. Увеличение лимита на размер загружаемого файла

Проверьте настройки Django для допустимого размера загружаемого файла. Для этого вам нужно установить параметр DATA_UPLOAD_MAX_MEMORY_SIZE в settings.py. Убедитесь, что он установлен достаточно высоко, чтобы разрешить загрузку больших файлов. Например:

DATA_UPLOAD_MAX_MEMORY_SIZE = 250 * 1024 * 1024  # 250MB

2. Увеличение тайм-аута Gunicorn

Хотя увеличение тайм-аута не всегда является оптимальным решением, это может помочь в вашем случае. Попробуйте задать больший тайм-аут через параметр --timeout. Например, увеличьте его до 120 секунд:

command: gunicorn mysite.wsgi -b 0.0.0.0:8000 --timeout 120 --reload

3. Увеличение ресурсов в Docker

Поскольку ваше приложение работает в Docker, убедитесь, что контейнеру выделено достаточно ресурсов (CPU и память). Вам может потребоваться изменить конфигурацию docker-compose.yml, добавив ограничения на память, например:

  web:
    build: .
    command: gunicorn mysite.wsgi -b 0.0.0.0:8000 --timeout 120 --reload
    mem_limit: 512m  # Установите необходимый лимит памяти
    ...

4. Обработка загрузок в фоне

Для загрузки больших файлов стоит рассмотреть возможность обработки этих файлов в фоновом режиме, используя Celery. Это позволит вашему веб-приложению быстрее отвечать на запросы, избегая тайм-аутов из-за длительных операций. Вот примерный алгоритм:

  1. Загрузка файла на другой сервис (например, AWS S3 или DigitalOcean Spaces).
  2. Сохранение информации о загруженном файле в базе данных.
  3. Запуск задачи Celery для обработки файла в фоновом режиме.

5. Логирование и отладка

Убедитесь, что вы корректно обрабатываете ошибки и логируете их. Например, вы можете использовать logging вместо простого print(), чтобы лучше отслеживать и воспроизводить проблемы:

import logging

logger = logging.getLogger(__name__)

try:
    # код для обработки файла
except Exception as e:
    logger.error(f"Error occurred: {str(e)}")
    return HttpResponse(status=500)

6. Рекомендации по оптимизации

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

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

Оцените материал
Добавить комментарий

Капча загружается...