builder阶段必须与final镜像php版本完全一致,否则必然运行时报错;如composer.lock依赖php 8.2的readonly语法而final用php 8.1,构建成功但启动即fatal error。

builder 阶段必须和 final 镜像 PHP 版本完全一致
版本错配不是“可能出问题”,而是必然导致运行时报错。比如 composer.lock 里记录了依赖于 PHP 8.2 的 readonly 属性语法,但 final 镜像用的是 PHP 8.1,构建不报错,启动时直接 Fatal error: Uncaught ParseError。
常见错误写法:FROM composer:2 AS builder 或 FROM php:8.3-cli AS builder + FROM php:8.1-fpm-alpine AS final —— 这类组合会让 autoload classmap 生成逻辑与运行时解析能力脱节。
- 正确做法:builder 和 final 的
FROM行 PHP 小版本(如 8.1)、发行版(bullseye/alpine3.19)必须一字不差对齐 - 若 final 用
php:8.1-fpm-bullseye,builder 就得是php:8.1-cli-bullseye - 别图省事用
composer:2:它绑定的 PHP 版本不可控,且不保证ext-mbstring、ext-xml等 Composer 必需扩展已启用
COPY 顺序错了,缓存就全废了
Docker 层缓存只认内容哈希 + 执行顺序。只要在 COPY composer.json composer.lock ./ 和 RUN composer install 之间插入任意一行指令(比如 RUN mkdir -p /app/logs),后续所有层都会失效,每次改代码都重装整个 vendor。
典型错误现象:CI 构建耗时从 40 秒飙升到 6 分钟,日志里反复看到 Downloading ...。
- 严格顺序只能是:
COPY composer.json composer.lock ./→RUN composer install --no-dev --optimize-autoloader --classmap-authoritative --no-scripts --no-progress -
COPY . /app必须放在RUN composer install之后,否则一改 README.md 就触发重装 - 如果项目需要
post-install-cmd(如生成配置文件),才考虑COPY . /app提前,但要清楚代价:失去 vendor 层缓存
final 阶段 COPY vendor 时权限和路径必须匹配
镜像构建成功,容器却报 Failed opening required 'vendor/autoload.php'?大概率不是代码问题,而是 WORKDIR 或 --chown 没对齐。
原因很直接:builder 阶段 composer dump-autoload 生成的 classmap 路径是基于其 WORKDIR 写死的;final 阶段若 WORKDIR 不同,或 vendor/ 属主是 root,FPM 进程(通常是 www-data 或 UID 1001)根本读不了。
- builder 和 final 必须设相同
WORKDIR,例如都写WORKDIR /app - final 阶段复制 vendor 时必须带
--chown:COPY --from=builder --chown=www-data:www-data /app/vendor /app/vendor - 若 final 镜像用非 root 用户(推荐),提前用
RUN adduser -D -u 1001 app创建用户,并在COPY --chown中对应使用app:app
BuildKit 缓存挂载不是可选项,是提速刚需
没启用 BuildKit 缓存挂载时,composer install 每次都要重新下载 ZIP 包、解压、校验,网络波动还容易中断。启用后,包缓存复用率可达 95%+,重复构建时间下降 60% 以上。
注意:光开 DOCKER_BUILDKIT=1 不够,Dockerfile 里必须显式声明挂载点,否则缓存仍不生效。
- 开头加
# syntax=docker/dockerfile:1 - 在 builder 阶段
RUN前加RUN --mount=type=cache,target=/root/.composer/cache composer install ... - 国内环境务必同步设镜像源:
RUN composer config -g repos.packagist composer https://mirrors.aliyun.com/composer/ - 别信
COMPOSER_CACHE_DIR环境变量——BuildKit 的--mount才是唯一可靠方式
实际执行时最容易被跳过的,是 builder 和 final 的 WORKDIR 对齐这件事。它不报错、不中断构建,但会导致 autoload 失效,问题只在容器启动后暴露,排查成本远高于写 Dockerfile 时多敲两行字。











