首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >PHP 生产级 Docker 镜像部署与最佳实践

PHP 生产级 Docker 镜像部署与最佳实践

作者头像
Tinywan
发布于 2026-09-15 14:32:51
发布于 2026-09-15 14:32:51
1420
举报
文章被收录于专栏:开源技术小栈开源技术小栈

官方 PHP 镜像到底差了什么?

如果你曾经用 Docker 部署过 PHP 应用,大概率经历过这样的场景:拉一个 php:8.3-fpm 官方镜像,写一份几十行的 Dockerfile 装 Composer、装扩展、调权限,再挂一堆配置文件——php.ini、www.conf、nginx.conf——最后祈祷生产环境和本地表现一致。

这不是你一个人的问题。官方 PHP Docker 镜像本质上只是一个"能跑 PHP"的最小运行时:没有 Composer,没有生产级的安全加固,默认以 root 用户运行,FPM 参数是裸的默认值,健康检查需要自己写,OPcache 配置全靠自己摸索。对于本地开发勉强够用,但离"生产就绪"还有相当的距离。

于是大多数团队的做法是:fork 一份官方 Dockerfile,加上自己的补丁,维护一个内部镜像。时间一长,这个镜像就变成了没人敢动的"祖传配置"。

Serversideup/php 就是为了解决这个问题而生的。它不是另起炉灶造一个新镜像,而是在官方 PHP 镜像之上,补全了生产环境真正需要的一切。

项目概况

serversideup/php 由 Server Side Up 团队(Dan Pastori 和 Jay Rogers)维护,是一个完全开源(GPL-3.0)的 PHP Docker 镜像方案。截至目前,项目在 GitHub 上有 2,500+ stars,Docker Hub 累计拉取超过 100 万次,已有 46 位贡献者参与开发,最新版本为 v4.5.1(2026 年 7 月发布)。

它支持的 PHP 版本范围很广:PHP 7.4 到 8.5,同时提供 Debian 和 Alpine 两种基础系统。镜像变体覆盖了几乎所有主流部署场景:

变体

适用场景

镜像标签示例

cli

命令行脚本、定时任务、队列 Worker

serversideup/php:8.5-cli

fpm

自带 Web Server 的自定义方案

serversideup/php:8.5-fpm

fpm-nginx

FPM + NGINX(最常用)

serversideup/php:8.5-fpm-nginx

fpm-apache

FPM + Apache

serversideup/php:8.5-fpm-apache

frankenphp

现代高性能方案,基于 Caddy

serversideup/php:8.5-frankenphp

所有镜像同时发布在 Docker Hub 和 GitHub Packages 上,不会因为单一源的故障而拉不到镜像。

核心特性详解

与其罗列所有功能,不如挑几个真正改变开发体验的特性展开聊聊。

环境变量驱动配置

这是 serversideup/php 最核心的设计理念:几乎所有配置项都可以通过环境变量完成,不需要挂载或修改任何配置文件。

一个典型的例子。假设你需要调整 PHP 的内存限制、上传大小、执行时间,并开启 OPcache:

代码语言:javascript
复制
services:
  php:
    image: serversideup/php:8.5-fpm-nginx
    environment:
      PHP_MEMORY_LIMIT: "512M"
      PHP_UPLOAD_MAX_FILE_SIZE: "200M"
      PHP_MAX_EXECUTION_TIME: "180"
      PHP_OPCACHE_ENABLE: "1"

就这样。不需要 COPY php.ini /usr/local/etc/php/conf.d/,不需要在 Dockerfile 里写 RUN sed -i ...。改完重启容器即可生效。

环境变量覆盖了 PHP 本身的几乎所有可调参数,也覆盖了 NGINX/Apache/Caddy 的关键配置。举几个容易忽略但很有用的:

  • PHP_FPM_PM_MAX_CHILDREN:FPM 最大子进程数,默认 20,低配机器上可能需要调小
  • PHP_OPCACHE_JIT 和 PHP_OPCACHE_JIT_BUFFER_SIZE:PHP 8.x 的 JIT 编译器开关,CPU 密集型场景可以开启试试
  • NGINX_CLIENT_MAX_BODY_SIZE:NGINX 请求体大小限制,默认 100M
  • PHP_FPM_PM_CONTROL:FPM 进程管理策略,fpm-nginx 和 fpm-apache 默认是 ondemand(按需启动),这对低流量站点很友好

我个人的经验是:刚开始用的时候会不习惯"什么都靠环境变量"这种方式,觉得不如直接改配置文件直观。但用了一段时间之后,你会发现这恰恰是容器化应该有的样子——配置跟着部署走,而不是跟着镜像走。

非 root 运行与安全加固

官方 PHP 镜像默认以 root 用户运行。这意味着如果你的应用被攻破,攻击者拿到的就是容器内的 root 权限。在 Kubernetes 等编排平台中,root 容器通常会被安全策略直接拒绝。

serversideup/php 默认以 www-data(UID 33)用户运行,遵循最小权限原则。这在安全层面带来了几个好处:

  • 爆炸半径受限:即使应用被入侵,攻击者无法在容器内执行特权操作
  • Kubernetes 兼容:很多集群的 PodSecurityPolicy 或 OPA 策略要求非 root 运行
  • 符合 CIS/NIST 基准:安全审计时少一项整改

如果需要在构建阶段安装扩展,可以临时切换到 root:

代码语言:javascript
复制
FROM serversideup/php:8.5-cli

USER root
RUN install-php-extensions bcmath imagick redis mongodb
USER www-data

这里用到的 install-php-extensions 也是预装好的工具(来自 mlocati/docker-php-extension-installer),一行命令装扩展,自动处理依赖,不用再操心 apt-get 装一堆 dev 库。

另一个值得注意的安全细节:镜像默认关闭了 server_tokens(不暴露 NGINX 版本号),Session Cookie 默认要求 Secure 连接,SSL 模式支持 off/mixed/full 三种策略。这些都是"不需要你额外操心但确实有用"的加固。

S6 Overlay 进程管理

如果你用过 fpm-nginx 或 fpm-apache 变体,会发现容器里实际上跑着两个进程:PHP-FPM 和 Web Server(NGINX/Apache)。Docker 的哲学是"一个容器一个进程",但现实部署中把 FPM 和 Web Server 放在一起往往更实用——少一层网络跳转,配置也更简单。

问题是:谁来管理这两个进程的生命周期?如果一个挂了怎么办?

serversideup/php 选择了 S6 Overlay 作为 init 系统。S6 是一个轻量级的进程管理方案,专门为容器设计,比 Supervisor 更适合 Docker 场景。它提供了:

  • 进程监督:FPM 或 Web Server 异常退出时自动重启
  • 优雅关闭:收到 SIGTERM 时按正确顺序停止进程,支持零停机部署
  • 统一日志:所有进程的日志都输出到 STDOUT/STDERR,直接对接 Docker 日志驱动

对使用者来说,这些细节是透明的。你只需要知道:容器内的进程管理是可靠的,不需要自己写启动脚本或者额外装 Supervisor。

S6 的行为也可以通过环境变量调整。比如 S6_BEHAVIOUR_IF_STAGE2_FAILS 默认值是 2(服务启动失败时停止容器),确保编排系统能检测到故障并重启。排查问题时可以把 S6_VERBOSITY 从默认的 1 调高,会看到更详细的启动日志。

FrankenPHP 支持

FrankenPHP 是这个项目里最值得关注的变体之一。它不是一个传统的 FPM 进程管理器,而是把 PHP 直接嵌入到 Caddy Web Server 中运行,从根本上消除了 FPM 和 Web Server 之间的 FastCGI 通信开销。

用 FrankenPHP 意味着什么?

  • 架构简化:不再需要 FPM + NGINX 双进程,一个 FrankenPHP 进程就搞定了一切
  • HTTP/2、HTTP/3 原生支持:Caddy 自带这些能力,不需要额外配置
  • Worker 模式:可以把 PHP 应用常驻内存,省去每次请求的框架启动开销(对 Laravel 这类重型框架效果显著)
  • 性能提升:社区反馈在多种场景下比传统 FPM 快 2-3 倍

切换方式非常简单:

代码语言:javascript
复制
services:
  php:
    image: serversideup/php:8.5-frankenphp
    ports:
      - "80:8080"
    volumes:
      - ./:/var/www/html

把 image 从 fpm-nginx 换成 frankenphp,其他配置基本不变。Caddy 相关的配置(端口、日志级别、Server Root 等)通过 CADDY_* 前缀的环境变量控制。

踩坑提醒:FrankenPHP 目前支持 PHP 8.3+,如果你的项目还跑在 8.2 或更低版本,暂时用不了。另外,FrankenPHP 的 Worker 模式需要应用做少量适配(比如正确处理数据库连接的持久化),不是所有项目都能"零改动迁移"。建议先在 staging 环境充分测试。

快速上手

废话少说,直接上手。以下是一个最小可运行的 Laravel 项目部署示例。

1. 创建项目

代码语言:javascript
复制
mkdir -p my-app/public
cd my-app

在 public/ 下创建 index.php:

代码语言:javascript
复制
<?php
phpinfo();

2. 编写 compose.yml

代码语言:javascript
复制
services:
  php:
    image: serversideup/php:8.5-fpm-nginx
    ports:
      - "80:8080"
    volumes:
      - ./:/var/www/html
    environment:
      PHP_UPLOAD_MAX_FILE_SIZE: "250M"
      PHP_OPCACHE_ENABLE: "0"  # 开发环境关闭,方便调试

注意端口映射:容器内 Web Server 监听 8080(非特权端口),映射到宿主机的 80。这是刻意为之——非 root 用户无法绑定 1024 以下的端口。

3. 启动

代码语言:javascript
复制
docker compose up

启动成功,输出

访问 http://localhost,看到 phpinfo() 页面就算成功。页面上应该能看到 PHP 8.5、upload_max_filesize = 250M、opcache.enable = Off。

4. 构建部署镜像

在生产环境中,为了安全性、可靠性和可移植性,你会直接将代码集成到镜像中

对于一个简单的 PHP 应用程序来说,你的 Dockerfile 可能如下所示

代码语言:javascript
复制
services:
  php:
    image: serversideup/php:8.5-frankenphp
    ports:
      - "80:8080"
    volumes:
      - ./:/var/www/html
    environment:
      PHP_UPLOAD_MAX_FILE_SIZE: "250M"
      PHP_OPCACHE_ENABLE: "1"  # 生产环境开启

一旦你有了 Dockerfile,就可以使用以下命令构建镜像:

代码语言:javascript
复制
docker build -t docker-php-tinywan-hello:latest .

在部署之前,请先在当地测试你的生产镜像。

compose.prod.yml

代码语言:javascript
复制
services:
  php:
    image: docker-php-tinywan-hello:latest
    ports:
      - "8799:8080"
    environment:
      PHP_OPCACHE_ENABLE: "1"
      PHP_MEMORY_LIMIT: "512M"

运行

代码语言:javascript
复制
docker compose -f compose.prod.yml up

这样你就可以在将镜像部署到实际服务器之前,确认其是否能够正确运行。

性能表现

关于性能,Server Side Up 官方引用了 Rabbit Company CEO Žiga Zajc 的测试数据:在相同条件下,serversideup/php 的 FPM 镜像可以处理约 484 请求/秒,而官方 PHP 镜像只有约 68 请求/秒。

这个差距主要来自几个方面:

  • OPcache 预配置:官方镜像不带 OPcache 优化配置,serversideup/php 提供了经过调优的默认参数
  • FPM 进程管理:默认使用 ondemand 策略(fpm-nginx/fpm-apache 变体),按需启动 worker,比 dynamic 更省资源
  • 安全加固的副作用:非 root 运行、正确的文件权限、关闭不必要的功能,这些都间接减少了开销

当然,基准测试的数据需要结合自己的业务场景来看。但至少可以确认:使用这个镜像不会因为"封装了太多东西"而变慢,恰恰相反,合理的默认配置反而比裸镜像跑得更好。

在生产环境中,还可以通过环境变量进一步调优:

代码语言:javascript
复制
environment:
  PHP_OPCACHE_ENABLE: "1"
  PHP_OPCACHE_MEMORY_CONSUMPTION: "256"
  PHP_OPCACHE_MAX_ACCELERATED_FILES: "20000"
  PHP_OPCACHE_JIT: "1255"
  PHP_OPCACHE_JIT_BUFFER_SIZE: "128M"
  PHP_FPM_PM_MAX_CHILDREN: "50"
  PHP_FPM_PM_CONTROL: "dynamic"
  PHP_MEMORY_LIMIT: "512M"

内置健康检查

生产环境中,负载均衡器和编排系统需要知道你的应用是否还活着。serversideup/php 内置了健康检查端点(默认路径 /healthcheck),Web 类变体(fpm-nginx、fpm-apache)会自动响应。

这意味着在 Kubernetes 中配置 livenessProbe 和 readinessProbe 变得极其简单:

代码语言:javascript
复制
livenessProbe:
  httpGet:
    path: /healthcheck
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 15
readinessProbe:
  httpGet:
    path: /healthcheck
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

健康检查路径可以通过 HEALTHCHECK_PATH 环境变量自定义。注意 cli 和 frankenphp 变体没有内置这个端点——前者因为没有 Web Server,后者因为 FrankenPHP 有自己的机制。

原生 CloudFlare 支持

如果你的站点走 CloudFlare CDN,获取用户真实 IP 是一个常见问题。CloudFlare 的请求经过代理转发后,Web Server 看到的来源 IP 是 CloudFlare 的节点 IP,不是用户真实 IP。

serversideup/php 的 fpm-nginx 和 fpm-apache 变体预配置了 CloudFlare 的可信代理列表,开箱即用。你不需要手动维护 CloudFlare IP 列表,也不用在 NGINX 里写一堆 set_real_ip_from 指令。

适用场景与局限性

适合的场景:

  • Laravel、WordPress、Symfony 等主流 PHP 框架的新项目或迁移项目
  • 需要从开发到生产保持环境一致性的团队
  • Kubernetes、Docker Swarm 等容器编排环境
  • 想要快速切换 PHP 版本测试兼容性的 CI/CD 流水线
  • 希望降低运维成本、减少"祖传配置"的小中型团队

需要注意的地方:

  • 端口差异:默认监听 8080 而非 80/443,已有的反向代理配置可能需要调整
  • Webroot 路径:默认文档根目录是 /var/www/html/public,如果你的项目结构不同,需要设置 NGINX_WEBROOT 或 APACHE_DOCUMENT_ROOT
  • FrankenPHP 成熟度:虽然性能出色,但 Worker 模式需要应用适配,部分老旧项目可能不兼容
  • 学习成本:如果你的团队对 Docker 不熟悉,从"直接装 LNMP"切换到容器化部署需要一定的适应期
  • 自定义深度:环境变量覆盖了绝大多数场景,但如果你有非常特殊的 NGINX 或 FPM 配置需求,最终还是需要挂载自定义配置文件或基于此镜像二次构建

总结

serversideup/php 解决的不是"能不能用 Docker 跑 PHP"的问题,而是"怎么把 PHP 容器跑好"的问题。它把散落在各种博客、Stack Overflow 回答和团队内部文档里的最佳实践,打包成了一个开箱即用的镜像。

对于 PHP 开发者来说,它的价值在于:你不再需要成为 Docker 和 NGINX 专家才能部署生产级的 PHP 应用。几个环境变量,一个 compose.yml,就能得到一个安全、高性能、易维护的运行环境。

项目完全开源,维护活跃(最近一次更新就在 2026 年 7 月),社区规模也在持续增长。如果你正在为 PHP 容器化部署头疼,或者想给现有的 Docker 方案做一次升级,值得认真试一试。

相关链接:

  • 项目官网:https://serversideup.net/open-source/docker-php
  • GitHub:https://github.com/serversideup/docker-php
  • Docker Hub:https://hub.docker.com/r/serversideup/php
  • 文档:https://serversideup.net/open-source/docker-php/docs/getting-started
  • 社区 Discord:https://serversideup.net/discord
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-08-08,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 官方 PHP 镜像到底差了什么?
  • 项目概况
  • 核心特性详解
  • 环境变量驱动配置
  • 非 root 运行与安全加固
  • S6 Overlay 进程管理
  • FrankenPHP 支持
    • 快速上手
  • 1. 创建项目
  • 2. 编写 compose.yml
  • 3. 启动
  • 4. 构建部署镜像
    • 性能表现
    • 内置健康检查
    • 原生 CloudFlare 支持
    • 适用场景与局限性
    • 总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档