Docker容器化TeX Live:打造可移植的LaTeX论文编译环境
2026/9/14 18:50:08 网站建设 项目流程

说实话,作为一个常年和论文、排版打交道的人,我以前听到“装 LaTeX 环境”就头大。不是因为 LaTeX 难学,而是因为 TeX Live 这玩意一装就是几个 GB,升级一次还得折腾半小时,换台电脑又得重来一遍。直到我把整个编译环境塞进了 Docker,用容器跑 TeX Live 编译 LaTeX 论文,这套流程才算真正消停了。这篇文章就详细讲讲我是怎么做的,包括镜像怎么选、Dockerfile 怎么写、日常怎么编译,以及踩过的那些坑。适合本地不想装全家桶、又需要稳定编译环境的朋友,尤其是经常换电脑写论文的学生党。

1. 为什么选 Docker 跑 TeX Live——论文编译环境到底有哪些坑

1.1 本地装 TeX Live 的四大烦恼

先说结论:在本地直接安装 TeX Live 不是不行,但维护成本真的高。我总结下来主要有四类问题一直在反复消耗我的时间。

第一是版本碎片化。TeX Live 每年更新一个大版本,2022、2023、2024,不同版本对宏包的支持不完全一样。期刊投稿的时候,很多模板会明确要求“请使用 TeX Live 2022 编译”,你机器上装的是 2024,编译出来的格式可能就跟编辑部的要求有细微差别。为了一个返修稿去装一个旧版本,说实话很劝退。

第二是卸载和清理困难。Windows 上的 TeX Live 卸载还算好,macOS 和 Linux 上如果你当时是手动装到/usr/local/texlive或者用户目录下,时间一长根本记不清哪些文件是 TeX 的、哪些是后来其他工具放的。等你意识到需要清理的时候,往往已经和系统混在一起了。

第三是系统环境的干扰。同一个.tex文件,换一台机器编译结果可能完全不一样。原因可能是 A 机器装了某个字体,B 机器没装;A 机器tlmgr更新过宏包,B 机器还是老版本。尤其是我这种经常用笔记本加台式机来回写的人,这种差异会让人怀疑人生。

第四是团队协作和 CI 的需求。如果你跟同学合写论文,或者想把论文构建过程接入自动化流程,统一编译环境几乎是刚需。大家本地环境各不一样,只有把环境固化成镜像,才能保证“我这儿编译过,你那儿也编译过”。

1.2 Docker 方案的取舍分析

用 Docker 跑 TeX Live,本质上是把“编译工具链”和“你的操作系统环境”彻底隔离。镜像里是什么版本就是什么版本,宿主机的环境再乱也影响不到编译结果。

我自己最直观的感受是:Docker 方案是在“体积”和“省心”之间做了取舍。TeX Live 镜像一般很大,官方镜像解压后好几个 GB,第一次拉取确实慢。但好处是拉一次以后就不用管了,论文写完了、环境要升级了,重新拉一个新 tag 的镜像就行,宿主机上一点残留都没有。

性能上也不需要担心。编译论文本身是 CPU 密集型的小任务,Docker 的容器化开销几乎可以忽略不计。我拿一篇十几页的中文毕业论文试过,纯xelatex编译也就几十秒,和裸机跑几乎没有体感差异。真正的大工程,比如好几本几百页的书,容器化也不会有明显瓶颈,毕竟编译不涉及 GPU、不涉及高频 I/O。

1.3 什么场景适合用 Docker 编译 LaTeX

不是所有情况都必须上 Docker,但下面这几类场景我是强烈建议用的。

  • 多设备切换:笔记本、台式机、公司电脑,随时拉镜像就行,保证所有设备编译结果一致。
  • 论文模板复现:跑期刊或学校的模板时,一个模板配一个容器,模板之间的宏包冲突完全隔离。
  • 自动化和批量编译:比如用 GitHub Actions 或自己搭的构建服务,Docker 镜像天然适合作为执行环境。
  • 给不会装环境的朋友写教程:我帮学弟学妹搭环境时,直接给他们一个 Docker 命令,比让他们安装 Visual Studio Code、再装 TeX Live 全家桶省心太多。

反过来,如果你的使用频率极低,一个月就编译两三次,而且只在一台固定的电脑上用,那本地装一个精简版也行,没必要非得引入 Docker 这个概念。

2. 核心配置解析:镜像选型与 Dockerfile 编写

2.1 官方镜像和社区镜像怎么选

目前跑 TeX Live 的 Docker 镜像主要有两类来源。

一类是官方仓库,比如texlive/texlive(Docker Hub 上)和ghcr.io/texlive/texlive(GitHub Container Registry 上的新镜像)。注意,texlive/texlive这个仓库的 tag 规则有点特殊,早期的 tag 是latest2023.1这种带年份的,后来官方换到了 GHCR,推荐用ghcr.io/texlive/texlive:latest或指定年份的 tag。这类镜像是基于官方 TeX Live 的install-tl脚本构建的,包含完整的宏包集合,体积大但是最省心。

另一类是社区精简镜像,比如mawippel/texlive或者各种针对中文优化的镜像。这类镜像我实际用得不多,因为搞不清它到底放了哪些宏包、去掉了哪些,排查问题会更费劲。

我给新手的建议是:直接用官方镜像,不要折腾精简版。TeX Live 编译报错里最多的就是 “Package xxx not found”,精简镜像是为了省体积砍了宏包,你写论文的时候根本猜不到模板下一秒要用哪个宏包。官方完整版镜像虽然大,但基本涵盖 CTAN 上绝大多数常用宏包,能把你从“缺啥装啥”的循环里解放出来。

另外提一句,用ghcr.io拉取镜像在国内网络环境下有时候很慢。常用的办法是给 Docker 配置 registry mirror,或者把ghcr.io/texlive/texlive换成docker.io/texlive/texlive的旧镜像。但是版本 tag 会老一些,自己权衡就好。

2.2 基于官方镜像定制 Dockerfile

直接拉官方镜子也能用,但我个人还是习惯自己写一个 Dockerfile。原因有两个:一是为了额外装一些模板需要但镜像里没有的字体或宏包;二是为了方便在团队里分发,其他人docker build一下就有一模一样的环境。

下面是我一直在用的 Dockerfile,基于ghcr.io/texlive/texlive:latest修改而来:

FROM ghcr.io/texlive/texlive:latest # 设置工作目录 WORKDIR /work # 安装常用系统工具,方便排查问题 RUN apk add --no-cache \ fontconfig \ ttf-freefont \ curl \ git # 设置 CTAN 镜像源(用于 tlmgr) RUN tlmgr option repository https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/tlnet # 安装额外的常用宏包(按需添加) RUN tlmgr install \ latexmk \ ctex \ xecjk \ fandol \ biblatex \ biber \ algorithm2e \ listings # 更新字体缓存 RUN fc-cache -f # 默认执行命令 CMD ["bash"]

这里有几个值得说明的点。

FROM我用了 GHCR 的最新镜像,如果你需要精确的版本,可以指定 tag,比如ghcr.io/texlive/texlive:2025.1这样的格式。我建议写论文时固定一个 tag,别用latest,不然某天重新拉镜像可能就和你之前编译的环境不一样了。

tlmgr option repository这一行是把宏包源切到国内 CTAN 镜像,实测下载宏包速度快很多。当然这个命令不是必须的,如果你不管网络环境,也可以不切换。要注意的是新版 TeX Live 的tlmgr默认仓库地址是https://mirror.ctan.org/systems/texlive/tlnet,有些网络环境下不稳定,切到国内镜像纯粹是出于可用性考虑。

apk add装的是 Alpine 的包,因为ghcr.io/texlive/texlive:latest基础镜像是 Alpine Linux。如果你的基础镜像换成了 Debian 系的,那就要用apt-get,不要照抄。

2.3 中文字体与 ctex 配置的细节

中文论文绕不开ctex宏包和字体问题。在 Docker 里跑中文 LaTeX,最省事的方式就是用ctex宏包 +xelatex编译,它默认会使用Fandol字体(FandolSong、FandolHei 等),这些字体在 TeX Live 自带的宏包集合里就有,不需要额外安装系统字体。

所以上面 Dockerfile 里我装fandol是有原因的。很多模板会显式指定\setCJKmainfont{FandolSong}或依赖 ctex 的默认字体设置,如果镜像里没有 Fandol,编译就会报 “Font FandolSong not found”。这个报错在本地机器上很常见,因为 Fandol 并不是操作系统字体,它在 TeX Live 的字体目录里。

如果你的论文要求使用特定中文字体,比如“宋体”或“黑体”,那就涉及安装或挂载系统字体的问题。有两个思路:

  • 把字体文件直接复制进镜像,然后在 Dockerfile 里RUN fc-cache -f,一劳永逸,但镜像会变大。
  • 在运行容器时把宿主机的字体目录挂载进去,比如-v /usr/share/fonts:/usr/share/fonts:ro,Windows 下可以挂载C:\Windows\Fonts。这种方式更灵活,但不适合团队分发。

我个人在团队协作时偏向于把字体“固化进镜像”,因为成员之间的字体差异很容易踩坑。你要是自己单干,挂载宿主字体就够用了。

3. 实操过程:构建镜像与日常编译命令

3.1 构建并验证镜像

Dockerfile 准备好后,构建镜像非常简单:

docker build -t my-texlive:2025 .

这里-t指定镜像名和 tag,我用my-texlive:2025方便自己识别版本。构建过程中如果网络不好,tlmgr install可能失败,建议配置好国内镜像源再重试。

构建完成后,先验证一下环境是否正常:

docker run --rm my-texlive:2025 tex --version

正常能看到 TeX Live 的版本信息。再验证一下 xelatex:

docker run --rm my-texlive:2025 xelatex --version

到这里,你的 TeX Live 编译环境就已经是一个“可移植的工具箱”了,去哪台机器都能用。

3.2 日常编译:挂载目录与 latexmk 自动编译

写论文的时候,工作目录里会有主.tex文件、图片文件夹、.bib文献库,编译过程中还会生成一堆中间文件。所以每次运行容器时,最关键的一步是把当前论文目录挂载进容器,并且让容器在挂载目录下执行编译命令。

我常用的编译命令长这样:

docker run --rm \ -v "$PWD":/work \ -w /work \ my-texlive:2025 \ latexmk -xelatex -interaction=nonstopmode -halt-on-error main.tex

这里每个参数解释一下:

  • --rm:容器结束后自动删除,不留垃圾容器。
  • -v "$PWD":/work:把当前目录挂载到容器里的/work目录,这样容器里生成的 PDF 和中间文件会直接落在宿主机当前目录。
  • -w /work:进入容器后的工作目录。
  • -interaction=nonstopmode:编译遇到错误时不要停下来等待交互,直接把日志打出来。
  • -halt-on-error:遇到第一个错误就停止,避免无意义的继续编译。
  • latexmk:自动判断编译次数,跑完xelatexbibtex、再跑xelatex直到交叉引用稳定,比手动连续跑两三遍xelatex省心太多。

如果你不想用latexmk,手动跑命令也行:

docker run --rm -v "$PWD":/work -w /work my-texlive:2025 xelatex -interaction=nonstopmode main.tex docker run --rm -v "$PWD":/work -w /work my-texlive:2025 bibtex main docker run --rm -v "$PWD":/work -w /work my-texlive:2025 xelatex -interaction=nonstopmode main.tex

但这样效率低,而且需要你记得每步的顺序。latexmk是 LaTeX 生态里的标准自动构建工具,强烈建议直接用它。

3.3 编译产物管理:中间文件不搞乱目录

LaTeX 编译会生成大量的中间文件:.aux.log.toc.bbl.bcf.out等等。如果全堆在当前目录,时间长了目录会非常乱。而通过 Docker 挂载目录时,容器在当前目录下生成的这些中间文件也会直接落到宿主机,所以“乱目录”的问题并不会因为用了 Docker 就自动消失。

解决方式有两种。一种是在latexmkrc里配置输出目录,把中间文件放到一个子目录里:

# .latexmkrc $xelatex = 'xelatex -interaction=nonstopmode -halt-on-error -outdir=build %O %S'; $pdf_mode = 5; # 使用 xelatex

这样编译时中间文件都会生成在build目录下,目录干净很多。

另一种方式是编译完了直接在宿主机清理临时文件。可以写一个小脚本,把main.auxmain.log这类垃圾文件删掉。我在实际项目里是把两种方式都用上了,.latexmkrc里指定输出目录build,另外在.gitignore里把build/忽略掉,这样团队协作时不会误提交中间文件。

3.4 tlmgr 补装宏包的正确姿势

文章写了几个月,模板突然用到一个镜像里没有的宏包,这是常态。遇到这种情况,不用改 Dockerfile 重新 build,直接在现有容器里临时用tlmgr安装就行。不过要注意,容器默认是无状态的,直接跑docker run my-texlive:2025 tlmgr install xxx装完就没了,下次容器还是老样子。

如果只是想临时验证一下某个宏包,可以这么跑:

docker run --rm my-texlive:2025 tlmgr install xxx

但这是改到容器层,容器一删除就没了。如果确认这个宏包以后一直要用,正确做法是把它加进 Dockerfile,重新 build 一遍镜像。所以在开始做项目时,我习惯每隔一段时间就把常用宏包固化进 Dockerfile,这样重新构建的镜像会越来越“顺手”。

4. 用 VSCode + LaTeX Workshop 在容器里一键编译

4.1 为什么要让 LaTeX Workshop 调 Docker 编译

如果你只是偶尔编译一下,纯命令行就够了。但我写论文的频率高,每次都在终端敲一长串docker run命令,挺烦的。所以我把 VSCode 里的 LaTeX Workshop 扩展也配置成了走 Docker 编译,这样在编辑器里点一下按钮就能出 PDF,日志也能直接跳转到出错的行。

核心思路其实不复杂:LaTeX Workshop 本质上是调用一个编译命令,我们只要把“本地latexmk”换成“docker run ... latexmk”就行。

4.2 tools 和 recipes 的配置详解

在 VSCode 的设置里加下面这段配置:

{ "latex-workshop.latex.tools": [ { "name": "latexmk-docker", "command": "docker", "args": [ "run", "--rm", "-v", "%DIR%:/work", "-w", "/work", "my-texlive:2025", "latexmk", "-xelatex", "-interaction=nonstopmode", "-halt-on-error", "%DOC%" ], "env": {} } ], "latex-workshop.latex.recipes": [ { "name": "latexmk (Docker)", "tools": ["latexmk-docker"] } ] }

关键点在%DIR%%DOC%这两个占位符。LaTeX Workshop 会自动把它们替换成当前文件的目录路径和文件名。注意 Windows 环境下,%DIR%会变成类似C:\Users\xxx\paper这样的路径,Docker Desktop 里挂载 Windows 路径时一般也能自动转换,但路径带中文或空格的时候会出问题,建议论文目录用英文名。

配好之后,打开一个.tex文件,切到 LaTeX Workshop 侧边栏,点一下 “latexmk (Docker)” 或者用快捷键Ctrl+Alt+B,就会在终端里看到 Docker 容器启动、编译、输出日志的全过程。

4.3 PDF 预览与正反向同步

编译完 PDF 后,LaTeX Workshop 默认会调用内置的 PDF 查看器,直接可以预览。需要注意的是,PDF 文件是容器生成的,挂载目录后它会直接出现在宿主机当前目录里,VSCode 打开它没有任何问题。反向同步(从 PDF 点击跳转到源码)也正常,因为 LaTeX Workshop 读的是.synctex.gz文件,这个文件由编译过程生成,跟 Docker 环境的隔离没有关系。

我实际用下来,整个链路是:源码文件在 Windows 宿主机 → VSCode 点击编译 → Docker 里跑latexmk→ 生成 PDF 在宿主机目录 → VSCode 自动刷新预览。整个过程很像是在本地编译,但真正干活的其实是容器。

4.4 日常使用体验和注意事项

用这套方案写作几个月,我的体会是:编译速度取决于论文规模,中等长度的论文(几十页)几秒到十几秒就能完成,和本地环境几乎没有差别。真正要注意的是首次拉镜像或更新镜像时的等待,这个时间无法避免,毕竟 TeX Live 完整版太大。

Windows 下还需要确保 Docker Desktop 处于运行状态。如果你突然点编译没反应,第一反应应该是去看 Docker Desktop 是否正常启动,很大概率是它没跑起来,而不是你配置写错了。macOS 和 Linux 下相对省心一些,没有这个状态检查环节。

5. 常见问题与排查技巧实录

5.1 编译报“Package xxx not found”

这是最常见的错误,原因就是镜像里没有对应的宏包。我的排查思路是三步走:

第一步,先用docker run --rm my-texlive:2025 tlmgr search --global --all "宏包名"查看这个宏包在 CTAN 上的存在情况。如果搜不到,可能是宏包名写错了。

第二步,确认名字后装进当前镜像临时试用:

docker run --rm my-texlive:2025 tlmgr install 宏包名

第三步,试过能用之后,把tlmgr install加进 Dockerfile,重新构建镜像。这样一劳永逸。

这里有个坑:有些论文模板用了很久不更新的宏包,可能在新的 TeX Live 里已经被合并到别的宏包里,名字变化了。遇到这种情况,搜索时除了搜包名,还要去模板目录下的.cls文件里看它到底RequirePackage了什么,然后逐个检查。

5.2 “Fatal error occurred, no output PDF file produced”

这种报错通常说明编译在某个环节彻底失败了。不要只看最后一行,要往.log文件里翻。我一般用这种姿势来定位:

docker run --rm -v "$PWD":/work -w /work my-texlive:2025 xelatex -interaction=nonstopmode main.tex 2>&1 | grep -A 10 "^!"

!开头的行是 LaTeX 报错的关键行,通常后面跟着错误类型和行号。常见的比如! Undefined control sequence,说明有命令拼写错误;! LaTeX Error: File not found,说明\includegraphics引用的图片路径不对。

如果你用的是 VSCode 的 LaTeX Workshop,直接在输出面板点错误信息就能跳到源码对应行,定位起来很方便。

5.3 中文字体找不到的问题

中文论文里“Font FandolSong not found”是高频报错。出现这个问题的原因一般是镜像里没装 Fandol 字体,或者装了但字体缓存没更新。验证方法:

docker run --rm my-texlive:2025 fc-list | grep Fandol

如果有输出,说明字体在;没有输出,说明镜像缺字体。解决办法就是把fandol宏包装上:

docker run --rm my-texlive:2025 tlmgr install fandol

但注意,就算宏包装了,某些场景下 ctex 依然找不到字体。这通常是字体缓存的问题,需要重新构建镜像时执行fc-cache -f。我在 Dockerfile 里特意加了这一行,就是为了避免这种玄学问题。

5.4 文件权限问题:生成的文件是 root 拥有

用 Docker 编译有一个不可避免的小麻烦:容器默认以 root 用户运行,生成的 PDF 和中间文件在宿主机上显示的所有者是 root。这意味着你想删掉或修改这些文件时,普通用户权限经常不够,得手动sudo

解决方式是在docker run时指定当前用户的 UID 和 GID:

docker run --rm \ -v "$PWD":/work \ -w /work \ --user "$(id -u):$(id -g)" \ my-texlive:2025 \ latexmk -xelatex -interaction=nonstopmode main.tex

Linux 和 macOS 下这个命令很通用。Windows 下的 Docker Desktop 权限模型不太一样,一般不需要这样处理,但我见过有些用户遇到文件只读的问题,通常重置文件属性即可。

5.5 Docker Desktop 启动失败与虚拟化问题

Windows 用户比较容易遇到 Docker Desktop 无法启动,提示虚拟化未开启。这个属于 Docker 环境本身的问题,排查方式比较固定:打开任务管理器确认虚拟化是否开启,如果没有,需要在 BIOS/UEFI 里开启硬件虚拟化,然后再重新启动 Docker Desktop。

还有一类情况是更新 Docker Desktop 之后突然起不来,多半是 WSL 内核和 Docker Desktop 版本不匹配。处理办法是去“控制面板 - 启用或关闭 Windows 功能”里确认“适用于 Linux 的 Windows 子系统”和“虚拟机平台”两项处于开启状态,或者执行wsl --update更新内核。macOS 上遇到 Docker Desktop 启动问题相对少,重启一下 Docker Desktop 往往就能解决。

5.6 磁盘占用过大与镜像清理方法

TeX Live 镜像加上各种 tag,时间久了很占磁盘空间。我建议定期清理一下不再使用的镜像:

docker system df docker image prune -f docker system prune -f

docker system df可以查看镜像、容器、卷占用的具体空间;docker image prune -f清理悬空镜像;docker system prune -f清理没用的构建缓存、停止的容器和不在使用的网络。注意不要乱加-adocker system prune -af会把所有没在使用的镜像全删掉,如果你不是想彻底回归干净状态,别轻易用。

另外,构建新镜像时如果改了 Dockerfile,旧镜像可以保留一两个版本,方便回退。我自己一般保留最近两个 tag,太旧的就删掉。

6. 回归一下“为什么值得这么干”

最后从一个个人使用者的角度说点实在的。用 Docker 来跑 TeX Live,最让我受益的不是“技术多炫”,而是它把写论文过程中最不稳定的环境因素给抹平了。我不用再担心模板缺某个宏包、不用在不同电脑之间同步环境、不用在换机之后花一下午重装软件。容器挂载目录、生成 PDF、VSCode 直接预览,这套流程一旦跑顺,日常使用的体感其实和本地编译几乎一样。

我踩过最典型的坑,反而是最开始想省体积、用精简镜像,结果每次编译都在“装宏包”的路上折腾,浪费时间比省下的那点磁盘空间多得多。所以如果你也准备用这套方案,我的建议是直接上官方完整版镜像,把环境一次性配置到位。后面写论文时,你会感谢自己当初这个决定。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询