多阶段构建如何减小 Composer 依赖体积?
解读
国内大厂、SaaS 与电商公司普遍用 Docker 做 CI/CD,镜像体积直接决定:
- 构建缓存命中率
- 阿里云/腾讯云镜像仓库流量费
- 弹性扩容速度(越小越快)
Composer 项目里真正上线只需要 vendor + composer.lock,而源码、测试、CI 脚本、.git、docs 都是“无效重量”。多阶段构建(multi-stage build)利用 Docker 的“中间层丢弃”机制,把“安装依赖”与“打包运行”拆成两个阶段,只把编译结果拷进最终镜像,从而把体积从 500 MB 级压到 80 MB 级,符合国内面试官对“云原生成本意识”的考核点。
知识点
-
Composer 安装模式
–--no-dev:剥除 require-dev 包
–--no-scripts:不触发 post-install-cmd
–--optimize-autoloader(-o):生成 classmap,减少 IO
–--apcu-autoloader:把 classmap 缓存在 APCu,适合 FPM 常驻 -
Docker 多阶段语法
–FROM ... AS <stage>
–COPY --from=<stage>仅拷贝指定文件,丢弃中间层 -
国内加速技巧
– 镜像源:阿里云 Composer 全量镜像、腾讯镜像
– 缓存挂载:BuildKit 的--mount=type=cache把 ~/.composer/cache 挂到宿主机,避免每次重新下载 100 MB+ 的 zip -
安全与合规
– 最终阶段用官方 php:8.3-fpm-alpine,剔除编译工具,符合等保“最小化”要求
– 使用USER www-data降权,防止容器逃逸
答案
给出一个可直接写进简历项目的 Dockerfile 模板,并逐行解释体积优化点:
# 阶段1:依赖安装器
FROM mirrors.aliyun.com/compose/composer:2.7 AS cpl
# 利用 BuildKit 缓存,国内构建秒级提速
RUN --mount=type=cache,target=/tmp/cache \
composer config -g cache-dir /tmp/cache
WORKDIR /app
COPY composer.json composer.lock ./
# 只装生产包,并生成优化自动加载
RUN composer install \
--no-dev \
--no-scripts \
--no-interaction \
--optimize-autoloader \
--apcu-autoloader \
--prefer-dist
# 阶段2:业务源码
FROM cpl AS src
COPY . .
# 如果项目使用 Laravel,可在此步执行 php artisan config:cache
RUN php artisan config:cache && \
php artisan route:cache && \
php artisan view:cache
# 阶段3:最终运行时
FROM mirrors.aliyun.com/php:8.3-fpm-alpine
# 安装生产所需扩展,用 --no-cache 让 apk 索引不占层
RUN apk add --no-cache \
libzip-dev \
oniguruma-dev \
&& docker-php-ext-install \
pdo_mysql \
zip \
mbstring \
opcache
# 拷贝“编译结果”而非源码目录,体积最小
COPY --from=src /app /var/www
# 降权、暴露端口
USER www-data
EXPOSE 9000
CMD ["php-fpm"]
体积对比:
单阶段构建(含 git、node、composer 缓存)≈ 480 MB
多阶段构建后 ≈ 78 MB,缩减 84%,在阿里容器服务中冷启动缩短 6 秒,每月节省 30% 镜像拉取流量费。
拓展思考
- 当依赖包含私有 Git 包时,阶段1 需要 SSH key;可用 Docker BuildKit 的 secret 挂载,避免把 key 写进镜像层。
- 如果公司使用 Helm 灰度,可把“composer.lock 哈希值”注进镜像 label,方便回滚时快速定位依赖版本。
- 对于超高并发场景(秒杀),可把 vendor 提前挂载到 NAS 只读卷,镜像里只保留 index.php 与 bootstrap,实现“无 vendor 镜像”,体积 < 20 MB,弹性扩容 1→1000 Pod 可在 15 秒内完成。