You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:50:20