如何从Docker另一容器调用Tectonic编译TeX文件?
解决Web容器调用Tectonic编译TeX文件的方案
你当前的Tectonic容器仅保持运行但未提供可调用的服务接口,轮询方案确实低效,以下是几个更优的实现方式:
方案1:Web容器通过Docker API直接调用Tectonic命令
无需修改Tectonic容器的核心逻辑,让Web容器通过Docker服务触发编译命令。
Docker Compose配置修改
给Web容器挂载Docker socket,使其能访问宿主的Docker服务:
version: '3.8' services: tectonic: image: rekka/tectonic volumes: - tex-volume:/data/ command: tail -f /dev/null # 让容器保持运行状态 tectonic-rest-serv: image: tectonic-rest:latest ports: - 8080:8080 volumes: - tex-volume:/var/lib/latex - /var/run/docker.sock:/var/run/docker.sock # 挂载Docker socket environment: - DOCKER_HOST=unix:///var/run/docker.sock
Web应用实现示例(Python)
使用Docker SDK调用Tectonic容器执行编译:
import docker client = docker.from_env() def compile_tex(tex_file_path): # 转换为Tectonic容器内的路径 container_tex_path = f"/data/{tex_file_path.split('/')[-1]}" container_pdf_path = container_tex_path.replace('.tex', '.pdf') # 执行编译命令 client.containers.run( 'rekka/tectonic', command=f"{container_tex_path} --output={container_pdf_path}", volumes={'tex-volume': {'bind': '/data', 'mode': 'rw'}}, remove=True ) # 从共享卷读取生成的PDF return f"/var/lib/latex/{tex_file_path.split('/')[-1].replace('.tex', '.pdf')}"
优缺点:实现简单,无需额外服务;但存在安全风险(Web容器拥有Docker权限),适合测试或低安全要求场景。
方案2:将Tectonic包装为HTTP服务
给Tectonic添加HTTP接口,Web容器通过HTTP请求触发编译,这是更安全的微服务架构方式。
步骤1:构建Tectonic HTTP服务镜像
写一个简单的Flask服务包装Tectonic命令:
Dockerfile
FROM rekka/tectonic RUN apk add --no-cache python3 py3-pip RUN pip install flask WORKDIR /app COPY app.py . EXPOSE 8000 CMD ["python3", "app.py"]
app.py
from flask import Flask, request, send_file import subprocess import os from uuid import uuid4 app = Flask(__name__) UPLOAD_DIR = "/data" @app.post("/compile") def compile_tex(): tex_file = request.files.get("tex_file") if not tex_file or not tex_file.filename.endswith('.tex'): return {"error": "无效的.tex文件"}, 400 # 生成唯一文件名避免冲突 file_id = str(uuid4()) tex_path = os.path.join(UPLOAD_DIR, f"{file_id}.tex") pdf_path = tex_path.replace('.tex', '.pdf') tex_file.save(tex_path) # 执行编译 try: subprocess.run( ["tectonic", tex_path, "--output", pdf_path], check=True, capture_output=True ) except subprocess.CalledProcessError as e: return {"error": f"编译失败: {e.stderr.decode('utf-8')}"}, 500 # 返回PDF文件并清理临时文件 response = send_file(pdf_path, as_attachment=True, download_name=f"{file_id}.pdf") os.remove(tex_path) os.remove(pdf_path) return response if __name__ == "__main__": app.run(host="0.0.0.0", port=8000)
步骤2:修改Docker Compose配置
version: '3.8' services: tectonic-service: build: ./tectonic-service # 指向存放Dockerfile和app.py的目录 ports: - 8000:8000 volumes: - tex-volume:/data/ tectonic-rest-serv: image: tectonic-rest:latest ports: - 8080:8080 volumes: - tex-volume:/var/lib/latex
Web应用调用示例(Python)
import requests def compile_via_http(tex_file_path): with open(tex_file_path, 'rb') as f: response = requests.post( "http://tectonic-service:8000/compile", files={"tex_file": f} ) if response.status_code == 200: with open("output.pdf", 'wb') as f: f.write(response.content) return "output.pdf" else: raise Exception(f"编译失败: {response.json()}")
优缺点:安全合规,符合微服务设计;需要额外维护一个小服务,适合生产环境。
方案3:使用任务队列实现异步编译
适合高并发或编译耗时较长的场景,解耦Web请求与编译任务。
Docker Compose配置修改
添加Redis作为消息队列:
version: '3.8' services: redis: image: redis:alpine ports: - 6379:6379 tectonic-worker: image: rekka/tectonic volumes: - ./worker.py:/data/worker.py - tex-volume:/data/ command: sh -c "apk add --no-cache python3 py3-pip && pip install redis && python /data/worker.py" depends_on: - redis tectonic-rest-serv: image: tectonic-rest:latest ports: - 8080:8080 volumes: - tex-volume:/var/lib/latex environment: - REDIS_HOST=redis depends_on: - redis
编写消费者脚本(worker.py)
import redis import subprocess import os import time r = redis.Redis(host='redis', port=6379, db=0) def process_task(task): tex_filename = task.decode('utf-8') tex_path = f"/data/{tex_filename}" pdf_path = tex_path.replace('.tex', '.pdf') if not os.path.exists(tex_path): return try: subprocess.run(["tectonic", tex_path, "--output", pdf_path], check=True) r.set(f"task:{tex_filename}", "completed") except subprocess.CalledProcessError: r.set(f"task:{tex_filename}", "failed") while True: task = r.brpop('tex_compile_queue')[1] process_task(task) time.sleep(1)
Web应用任务提交示例(Python)
import redis import os r = redis.Redis(host='redis', port=6379, db=0) def submit_compile_task(tex_file_path): tex_filename = os.path.basename(tex_file_path) dest_path = f"/var/lib/latex/{tex_filename}" # 保存文件到共享卷 with open(tex_file_path, 'rb') as src, open(dest_path, 'wb') as dst: dst.write(src.read()) # 提交任务到队列 r.lpush('tex_compile_queue', tex_filename) return tex_filename def check_task_status(task_id): status = r.get(f"task:{task_id}") return status.decode('utf-8') if status else "pending"
优缺点:支持高并发,异步处理不阻塞Web请求;架构稍复杂,适合大流量场景。
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

