跳转到内容

Docker Runner

复制本页

本页记录 2026-10-17 核过的 Docker Runner。示例的参考结果在这个 Debian Bookworm 容器里生成。仓库尚未公开,因此容器定义直接写在下面。

容器从官方语言镜像带入 Go、Node.js、Elixir 与 Erlang/OTP,基底是对应版本的 Python 镜像。libvips 按固定版本和校验和从上游源码包编译安装,使用的是这份容器内的库,而不是发行版软件包里的 libvips。

版本数字的对照表见版本兼容矩阵。在自己的机器上安装时,仍以安装与环境检查里实际打印出的版本为准。

角色 组件 版本 说明
容器 Go 1.27.0 来自 golang:1.27.0-bookworm
容器 Node.js 24.14.1 随官方 Node 镜像复制进来,供 sharp 使用
容器 pnpm 12.11.2 由 Corepack 启用
容器 Python 3.14.5 容器基底 python:3.14.5-bookworm
容器 Elixir / OTP 1.20.4 / 29.1.2 来自 hexpm/elixir:1.20.4-erlang-29.1.2-debian-bookworm-20261005
容器 libvips 8.17.3 容器内源码构建,安装到 /usr/local
参考 govips v2.20.0 链接上面的 libvips 8.17.3
比较 pyvips 3.2.0 链接同一份 libvips 8.17.3
比较 vips CLI 8.17.3 与参考实现共用这份库
比较 sharp 0.35.5 使用 npm 包自带的 libvips 8.18.7
比较 Image / Vix 0.72.0 / 0.42.0 链接同一份 libvips 8.17.3;只覆盖部分示例
  1. 容器提供上表里的编译器、语言运行时,以及一份源码安装的 libvips 8.17.3。
  2. Go/govips 作为参考实现,链接这份 libvips,对同一张输入执行处理,并写出图像和版本、尺寸等元数据。
  3. sharp、pyvips 和 vips CLI 按同一处理意图各自执行。Elixir 的 Image 通过 Vix 链接同一份 libvips,编译和运行都使用 VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS。Image 目前只覆盖缩略图、格式转换、几何、Alpha 和色彩调节,它不是与前四个入口并列的参考实现。
  4. 每个输出记录格式、尺寸、通道和字节大小。相同字节数仍只说明文件大小相同。像素是否一致,留在后续比较里单独结论。
  5. 这些输出会带着格式、尺寸和版本信息一起留下,供后续对照。

镜像的默认命令只打印 pnpm 版本。真正跑示例时会换成对应入口的命令。镜像设置 VIPS_RUNNER=1,用来标明进程处在这份示例环境中;这个变量不改变 libvips 的加载行为。

  • Go、Node.js、Elixir 与 OTP 从上面的官方镜像复制进最终镜像,避免在一份基底里分别安装四套语言工具链。
  • Node.js 镜像只提供 node 与 npm 模块。pnpm 12.11.2 由 Corepack 激活。
  • libsctp1 让 OTP 启动时不必为缺少 SCTP 库发出警告。
  • pyvips 3.2.0 在镜像构建结束时安装,并断言它加载的是 libvips 8.17.3。
  • Elixir 与 OTP 留在镜像里。Image 与 Vix 在运行对应示例时编译,并链接镜像内的 libvips,而不是 Vix 默认附带的预编译库。镜像不安装 FFmpeg、Nx、evision 或 image_vision。
  • 镜像不设置 VIPS_BLOCK_UNTRUSTED。Image 会在应用启动时自行设置它;若把它写成全局环境变量,govips、pyvips 和 CLI 的加载行为也会一起改变。
  • 文字示例使用已安装的 DejaVu Sans,并在构建结束时刷新字体缓存。

libvips 以 release 构建安装到 /usr/local,动态模块关闭。构建结束时会检查 JPEG、WebP、PNG、SVG 和 HEIF 加载器确实出现在 vips -l foreign 的输出里。

当前示例用到的能力保持启用:JPEG、PNG、WebP、TIFF、SVG(librsvg)、Pango/Cairo 文字、fontconfig、HEIF、cgif、libspng、OpenJPEG、libimagequant、libarchive、libexif、lcms2 和 fftw3。HEIF 保持启用,但不用动态模块。这样若 AVIF/HEIF 加载器缺失,会在加载器列表里直接看见,而不是拖到以后某次调用才失败。

以下加载器关闭:ImageMagick、OpenSlide、FITS、NIfTI、OpenEXR、Poppler、PDF、JPEG XL,以及 quantizr 与 Highway。当前示例使用 JPEG、PNG、WebP、TIFF 和 SVG。关掉 ImageMagick,是为了避免发行版里的 Magick 加载器插到原生 JPEG、PNG、WebP 保存器前面,改变外来加载器的优先级。

下面是 2026-10-17 这份示例容器的定义。

# Recipe runner for the VIPS Handbook.
# Versions come from toolchain.lock.yml. Node is pinned to 24.14.1, the patch
# already checked on the reference workstation; the lock file accepts any Node 24.
#
# Do not set VIPS_BLOCK_UNTRUSTED here. elixir-image/image sets it when that
# application starts, and a global setting would change govips, pyvips, and CLI loads.
FROM golang:1.27.0-bookworm AS go-toolchain
FROM node:24.14.1-bookworm AS node-toolchain
FROM hexpm/elixir:1.20.4-erlang-29.1.2-debian-bookworm-20261005 AS elixir-toolchain
FROM python:3.14.5-bookworm
ENV DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8 \
PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
VIPS_RUNNER=1 \
VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS \
GOCACHE=/tmp/vips-handbook-go-cache \
GOPROXY=https://proxy.golang.org,direct \
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig \
COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
cmake \
fontconfig \
fonts-dejavu-core \
git \
libarchive-dev \
libcairo2-dev \
libcgif-dev \
libexif-dev \
libexpat1-dev \
libfftw3-dev \
libfontconfig1-dev \
libglib2.0-dev \
libheif-dev \
libimagequant-dev \
libjpeg62-turbo-dev \
liblcms2-dev \
libopenjp2-7-dev \
libpango1.0-dev \
libpng-dev \
libsctp1 \
librsvg2-dev \
libspng-dev \
libtiff-dev \
libwebp-dev \
meson \
ninja-build \
pkg-config \
time \
xz-utils \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*
COPY --from=go-toolchain /usr/local/go /usr/local/go
COPY --from=node-toolchain /usr/local/bin/node /usr/local/bin/node
COPY --from=node-toolchain /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -sfn ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -sfn ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
&& ln -sfn ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack \
&& corepack enable \
&& corepack prepare pnpm@12.11.2 --activate
COPY --from=elixir-toolchain /usr/local/lib/erlang /usr/local/lib/erlang
COPY --from=elixir-toolchain /usr/local/lib/elixir /usr/local/lib/elixir
RUN ln -sfn ../lib/erlang/bin/erl /usr/local/bin/erl \
&& ln -sfn ../lib/erlang/bin/erlc /usr/local/bin/erlc \
&& ln -sfn ../lib/erlang/bin/escript /usr/local/bin/escript \
&& ln -sfn ../lib/erlang/bin/epmd /usr/local/bin/epmd \
&& ln -sfn ../lib/elixir/bin/elixir /usr/local/bin/elixir \
&& ln -sfn ../lib/elixir/bin/elixirc /usr/local/bin/elixirc \
&& ln -sfn ../lib/elixir/bin/iex /usr/local/bin/iex \
&& ln -sfn ../lib/elixir/bin/mix /usr/local/bin/mix
# libvips 8.17.3 is built in-image so `vips --version` matches the lock file.
# ImageMagick, OpenSlide, FITS, NIfTI, and JPEG XL are left out: current recipes
# use JPEG, PNG, WebP, TIFF, and SVG, and a distro Magick loader would change
# foreign-loader priority. HEIF stays enabled so a missing AVIF/HEIF loader is
# visible in `vips -l foreign` rather than failing silently later.
ARG LIBVIPS_VERSION=8.17.3
ARG LIBVIPS_SHA256=41e9a1439cd57dcc6d4435a085e2cfe181d9da1962fa84a484f09e8b536e4b77
ADD --checksum=sha256:${LIBVIPS_SHA256} \
https://github.com/libvips/libvips/releases/download/v${LIBVIPS_VERSION}/vips-${LIBVIPS_VERSION}.tar.xz \
/tmp/vips.tar.xz
RUN tar -xJf /tmp/vips.tar.xz -C /tmp \
&& cd /tmp/vips-${LIBVIPS_VERSION} \
&& meson setup build \
--prefix=/usr/local \
--libdir=lib \
--buildtype=release \
-Ddeprecated=false \
-Dexamples=false \
-Dintrospection=disabled \
-Dmodules=disabled \
-Dmagick=disabled \
-Dmagick-module=disabled \
-Dcfitsio=disabled \
-Dmatio=disabled \
-Dnifti=disabled \
-Dopenexr=disabled \
-Dopenslide=disabled \
-Dopenslide-module=disabled \
-Dpdfium=disabled \
-Dpoppler=disabled \
-Dpoppler-module=disabled \
-Djpeg-xl=disabled \
-Djpeg-xl-module=disabled \
-Dquantizr=disabled \
-Dhighway=disabled \
-Djpeg=enabled \
-Dpng=enabled \
-Dwebp=enabled \
-Dtiff=enabled \
-Dexif=enabled \
-Dlcms=enabled \
-Dfftw=enabled \
-Dfontconfig=enabled \
-Dpangocairo=enabled \
-Drsvg=enabled \
-Dheif=enabled \
-Dheif-module=disabled \
-Dcgif=enabled \
-Dspng=enabled \
-Dopenjpeg=enabled \
-Dimagequant=enabled \
-Darchive=enabled \
-Dzlib=enabled \
-Dnsgif=true \
&& meson compile -C build \
&& meson install -C build \
&& echo /usr/local/lib > /etc/ld.so.conf.d/usr-local.conf \
&& ldconfig \
&& fc-cache -f \
&& mkdir -p /opt/vips-handbook \
&& vips -l foreign > /opt/vips-handbook/foreign-loaders.txt \
&& grep -q 'jpegload' /opt/vips-handbook/foreign-loaders.txt \
&& grep -q 'webpload' /opt/vips-handbook/foreign-loaders.txt \
&& grep -q 'pngload' /opt/vips-handbook/foreign-loaders.txt \
&& grep -q 'svgload' /opt/vips-handbook/foreign-loaders.txt \
&& grep -q 'heifload' /opt/vips-handbook/foreign-loaders.txt \
&& rm -rf /tmp/vips-${LIBVIPS_VERSION} /tmp/vips.tar.xz
RUN pip install --no-cache-dir 'pyvips==3.2.0' \
&& python3 -c "import pyvips; v='.'.join(str(pyvips.version(i)) for i in range(3)); assert pyvips.__version__=='3.2.0', pyvips.__version__; assert v=='8.17.3', v" \
&& test "${VIX_COMPILATION_MODE}" = "PLATFORM_PROVIDED_LIBVIPS" \
&& test -z "${VIPS_BLOCK_UNTRUSTED:-}" \
&& vips --version | grep -qx 'vips-8.17.3'
WORKDIR /work
CMD ["pnpm", "--version"]