RU EN HE
Обо мне Проекты Блог
← Назад к блогу
Автоматизация на Python

Универсальный контейнерный диспетчер на Python

Введение

При автоматизации разнородных задач часто возникает паттерн, когда Python-воркер должен запускать различные контейнеры в зависимости от типа задачи. Вместо хардкодинга каждого контейнера мы создаём универсальный диспетчер, управляемый YAML-конфигурацией.

YAML-конфигурация с секцией runners

# config.yaml
runners:
jira-processor:
image: localhost/jira-proc:latest
command: [“python”, process.py]
podman_args:
- “–pod”
- “main-stack”
- “–secret”
- “jira-token”
timeout: 300

asterisk-handler: image: localhost/asterisk-ari:latest command: [“python”, handle.py] podman_args: - “–pod” - “voip-stack” timeout: 60

report-generator: image: localhost/reports:latest command: [“python”, generate.py] podman_args: [] timeout: 600

EphemeralContainer Context Manager

Контекстный менеджер управляет полным жизненным циклом контейнера — от создания до удаления:

import subprocess, json, uuid

class EphemeralContainer: def init(self, runner_config): self.config = runner_config self.name = f"task-{uuid.uuid4().hex[:8]}" self.process = None

def enter(self): return self

def run(self, input_data): cmd = [“podman”, “run”, “–rm”, “-i”, “–name”, self.name] cmd.extend(self.config.get(‘podman_args’, [])) cmd.append(self.config[‘image’]) cmd.extend(self.config.get(‘command’, []))

result = subprocess.run( cmd, input=json.dumps(input_data).encode(), capture_output=True, timeout=self.config.get(‘timeout’, 300) )

if result.returncode != 0: raise RuntimeError( f"Container failed: {result.stderr.decode()}" ) return json.loads(result.stdout)

def exit(self, *args): # Гарантируем остановку при ошибке subprocess.run( [“podman”, “rm”, “-f”, self.name], capture_output=True )

Передача JSON через stdin

Данные задачи сериализуются в JSON и передаются через stdin контейнера. Контейнер читает stdin, обрабатывает данные и записывает результат в stdout. Этот подход обеспечивает полную изоляцию и универсальность — контейнер не зависит от конкретного транспорта.

Захват результатов из stdout

Результат обработки парсится из stdout контейнера. Stderr используется для логов и отладки. При ненулевом коде возврата выбрасывается исключение с содержимым stderr.

Обработка ошибок с блоком finally

Блок finally в контекстном менеджере гарантирует удаление контейнера даже при исключениях. Это предотвращает утечку ресурсов — зависшие контейнеры не будут накапливаться после сбоев.

Интеграция с RQ

Универсальная задача для RQ выглядит элементарно — она принимает имя раннера и данные, загружает конфигурацию и запускает соответствующий контейнер. Это позволяет добавлять новые типы задач без изменения кода воркера.

Заключение

Универсальный контейнерный диспетчер — элегантное решение для систем с разнородными задачами. YAML-конфигурация, context manager и передача данных через stdin/stdout обеспечивают гибкость, надёжность и полную изоляцию выполнения.