Предоставьте процессорное время и память для подпроцесса

#python #c #docker #subprocess #resources

Вопрос:

Я изучал, как предоставить собственный Python subprocess time и memory .

 
import resource
import subprocess


def set_memory_time(seconds):
    limit_virtual_memory(seconds)
    usage_start = resource.getrusage(resource.RUSAGE_CHILDREN)
    print("usage_start ", usage_start)
    try:
        p = subprocess.check_output(
            ['docker exec -it cpp_compiler sh -c "g   -o Test1 prog1.cpp amp;amp; ./Test1 < input.txt"'],
            shell=True)
    except Exception as e:
        print(e)
    usage_end = resource.getrusage(resource.RUSAGE_CHILDREN)
    print("usage_end ", usage_end)
    cpu_time = usage_end.ru_utime - usage_start.ru_utime
    print("cpu_time ", cpu_time)


def limit_virtual_memory(seconds):
    max_virtual_memory = 10 * 1024 * 1024  # 10 MB
    usage_start = resource.getrusage(resource.RUSAGE_CHILDREN)
    resource.setrlimit(resource.RLIMIT_AS, (max_virtual_memory, resource.RLIM_INFINITY))
    resource.setrlimit(resource.RLIMIT_CPU, (seconds, usage_start.ru_utime   seconds))

 

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

  1. Общая цель, которую я пытаюсь достичь здесь, заключается subprocess.check_output(['docker exec -it cpp_compiler sh -c "g -o Test1 prog1.cpp amp;amp; ./Test1 < input.txt"'] в том, что эта строка не должна содержать больше ресурсов, чем выделено.
    Есть ли способ добиться этого в python ?
  2. Проблема, которую я пытаюсь решить здесь, заключается в том, чтобы распределять CPU time и memory ограничивать отправленный пользователем CPP код. CPP код в конечном итоге будет выполняться в контейнере docker для песочницы, но вы хотите ограничить используемый ресурс.

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

Спасибо

Ответ №1:

Вы не можете ограничить использование ресурсов произвольным docker exec процессом.

Docker использует модель клиент/сервер, поэтому при запуске docker exec он просто отправляет запрос демону Docker. Когда вы пытаетесь setrlimit ограничить память подпроцесса, это ограничивает только сам docker exec процесс; но это делает запрос демону Docker, который, в свою очередь, запускает новый процесс в пространстве имен контейнера. Ни один из этих процессов не является дочерним по отношению друг к другу, и ни один из них, выходящий за рамки исходного docker exec , не наследует эти ограничения ресурсов.

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

Как правило, вам следует избегать использования subprocess модуля для вызова docker команд. Создание команд оболочки и использование их выходных данных может быть сложным делом, и если ваш код не идеален, очень легко использовать атаку с использованием оболочки, чтобы использовать docker команду для укоренения хоста. Вместо этого используйте что-то вроде Docker SDK для Python.

Итак, если вы хотите запустить новый контейнер с фиксированным ограничением памяти и ограничить время его выполнения, вы можете сделать это с помощью чего-то вроде:

 import docker
import requests
client = docker.from_env()

container = client.containers.run(
  image='some/image:tag',
  command=['the', 'command', 'to', 'run'],
  detach=True,
  mem_limit=10485760 # 10 MiB
)
try:
  container.wait(timeout=30) # seconds
except requests.exceptions.ReadTimeout:
  # container ran over its time allocation
  container.kill()
  container.wait()
print(container.logs())
container.remove()
 

Комментарии:

1. Спасибо @David Maze , это хорошее объяснение. На самом деле ограничения на контейнер не будут работать в нашем случае, так как мы хотим запускать несколько тестовых случаев одновременно в контейнере и хотим ограничить ограничение по времени/памяти для каждого тестового случая в контейнере, но не для контейнера. Итак, у вас есть какие-нибудь идеи, как этого добиться?

2. Контейнеры не особенно дороги и не имеют жесткой изоляции ресурсов; если вы запускаете четыре процесса в четырех контейнерах, их производительность должна быть сопоставима с их запуском в одном контейнере, а настройка будет намного проще. Я бы запустил здесь по одному контейнеру на процесс.