from __future__ import annotations from typing import Dict, List, Optional from pathlib import Path import docker from ..core.config import settings class DockerRunner: """通过 docker.sock 在宿主机运行容器的轻量封装。""" def __init__(self) -> None: # 依赖 /var/run/docker.sock 已在 docker-compose 挂载至 worker 容器 self.client = docker.from_env() def run( self, image: Optional[str], command: List[str], workdir: Path, mounts: Dict[Path, str], env: Optional[Dict[str, str]] = None, platform: Optional[str] = None, detach: bool = False, remove: bool = True, ) -> str: """ 运行容器并返回容器日志(非 detach)或容器 ID(detach)。 mounts: {host_path: container_path} """ image_to_use = image or settings.DOCKER_IMAGE platform_to_use = platform or settings.DOCKER_PLATFORM volumes = {str(host): {"bind": container, "mode": "rw"} for host, container in mounts.items()} container = self.client.containers.run( image=image_to_use, command=command, working_dir=str(workdir), volumes=volumes, environment=env or {}, platform=platform_to_use, detach=detach, remove=remove if not detach else False, ) if detach: return container.id # 同步模式:等待并返回日志 result_bytes = container.logs(stream=False) return result_bytes.decode("utf-8", errors="ignore")