0Pricing
PHP Academy · 课时

将 PHP 应用容器化

编写可用于生产环境的 PHP Dockerfile

将 PHP 应用容器化 是 CoddyKit 上的免费 PHP Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 PHP Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 PHP Academy 课程共包含 4 节课。

为什么要将 PHP 容器化

要可复现地交付 PHP,就意味着要将确切的解释器版本、扩展和 OS 库与代码一起固定下来。Docker 镜像可以让每个环境——开发者笔记本、持续集成和生产环境——使用相同的 php -v 结果和相同的 ext-* 集合。

本课将构建一个可用于生产环境的镜像:PHP-FPM、仅安装所需扩展、经过调整的配置、非 root 用户,以及健康检查。

基础镜像:FPM 还是 Apache

官方 PHP 镜像提供多种变体。对于部署在 nginx/traefik 后面的生产 Web 应用,优先选择 php:8.3-fpm-alpine(体积小)或 php:8.3-fpm(基于 Debian,使用 glibc,与原生库一起使用时意外更少)。

  • cli——工作进程、队列、定时任务
  • fpm——FastCGI 进程管理器,与 nginx 搭配使用
  • apache——内置 Apache,使用方便但更笨重

固定次版本号。生产环境中绝不要使用 :latest。

# Base image choice in your Dockerfile
FROM php:8.3-fpm-alpine AS base

# Why alpine? ~30MB base vs ~140MB Debian.
# Tradeoff: musl libc, occasional native-extension friction.

安装扩展

绝不要在这些镜像中执行 apt install php-xxx——请使用镜像自带的辅助工具 docker-php-ext-install、docker-php-ext-configure 和 pecl。install-php-extensions 脚本(由 mlocati 提供)是事实上的快捷方案,会为您提取正确的开发头文件。

FROM php:8.3-fpm-alpine

# Grab the helper that resolves build deps automatically
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions \
        pdo_mysql \
        opcache \
        intl \
        zip \
        redis \
        bcmath

在镜像中使用 Composer

请从官方镜像复制 Composer 二进制文件,而不是使用 curl 下载安装程序。生产环境中运行 composer install 时使用 --no-dev 和 --optimize-autoloader,并且先只复制 composer.json/composer.lock,这样依赖层就能独立于源代码变更进行缓存。

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /app

# Cache-friendly: deps layer invalidates only when lock changes
COPY composer.json composer.lock ./
RUN composer install \
        --no-dev \
        --no-scripts \
        --no-autoloader \
        --prefer-dist

COPY . .
RUN composer dump-autoload --optimize --classmap-authoritative

调整 php.ini

基础镜像提供 php.ini-production 和 php.ini-development 模板。启用生产模板,然后将自定义覆盖配置放入 conf.d——该目录最后合并,因此其中的设置优先。

生产环境的关键设置包括:opcache.enable=1、opcache.validate_timestamps=0(镜像中的代码不可变),以及合理的 memory_limit。

# Activate production ini
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Custom overrides win because conf.d loads last
COPY docker/php/zz-app.ini $PHP_INI_DIR/conf.d/zz-app.ini

OPcache 覆盖文件

这是生产环境中收益最大的单项优化。设置 validate_timestamps=0 后,PHP 不会在每次请求时检查文件状态——但这意味着您MUST重新构建镜像才能部署更改(对于不可变容器来说,这正是我们想要的效果)。

; docker/php/zz-app.ini
memory_limit = 256M
expose_php = Off

opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
opcache.preload = /app/preload.php
opcache.preload_user = www-data

以非 root 用户运行

镜像已经定义了 www-data。以 root 用户运行 FPM 会无谓地扩大攻击面。请设置可写路径(缓存、日志)的所有权,并在 CMD 之前使用 USER 切换用户。

FPM 的主进程仍然需要绑定特权端口吗?不需要——FPM 监听 9000 端口(非特权端口),因此使用非 root 用户很直接。

# Make runtime-writable dirs owned by the runtime user
RUN chown -R www-data:www-data /app/var /app/storage 2>/dev/null || true

USER www-data

EXPOSE 9000
CMD ["php-fpm"]

健康检查

编排器需要一个信号来确认 FPM 确实处于运行状态,而不只是进程存在。cgi-fcgi 可以请求 FPM 的 /status 或 /ping 端点。请先在 FPM 进程池中启用 pm.status_path 和 ping.path。

# In www.conf pool config:
;   ping.path = /ping
;   ping.response = pong

# Dockerfile HEALTHCHECK using cgi-fcgi
RUN install-php-extensions @composer >/dev/null 2>&1 || true

HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
  CMD SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET \
      cgi-fcgi -bind -connect 127.0.0.1:9000 || exit 1

组装 Dockerfile

下面是一个结构清晰的单阶段生产 Dockerfile。下一课中,我们会将其拆分为多阶段构建,以移除构建工具。请注意顺序:依赖项 → 配置 → 源代码 → 自动加载 → 用户切换。

FROM php:8.3-fpm-alpine

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
    install-php-extensions pdo_mysql opcache intl zip redis

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY docker/php/zz-app.ini $PHP_INI_DIR/conf.d/

COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
COPY . .
RUN composer dump-autoload --optimize --classmap-authoritative && \
    chown -R www-data:www-data /app/var

USER www-data
EXPOSE 9000
CMD ["php-fpm"]

验证构建

构建完成后,请快速检查实际写入镜像的内容:PHP 版本、已加载的扩展,以及 OPcache 验证是否已关闭。一个简短的 CLI 脚本即可确认应用所依赖的运行时契约。

<?php
// Run inside the container: php verify.php
echo 'PHP ' . PHP_VERSION . PHP_EOL;

$required = ['pdo_mysql', 'opcache', 'intl', 'zip'];
foreach ($required as $ext) {
    printf("%-12s %s\n", $ext, extension_loaded($ext) ? 'OK' : 'MISSING');
}

var_dump((bool) ini_get('opcache.enable'));
?>

整理构建上下文

.dockerignore 可以缩小构建上下文,防止密钥和过大的依赖目录泄露到镜像中并破坏缓存。请排除 vendor、VCS、env 文件和本地工具。

# .dockerignore
.git
.gitignore
vendor/
node_modules/
.env
.env.*
tests/
*.md
docker-compose*.yml
storage/logs/*
var/cache/*

快速检查

为什么要在生产镜像中设置 opcache.validate_timestamps=0?

回顾

您构建了一个生产环境 PHP-FPM 镜像:使用固定版本的 8.3-fpm-alpine 基础镜像,仅通过安装脚本安装所需扩展,将 Composer 和缓存的依赖项复制到各自的层中,启用生产环境 php.ini 并添加 OPcache 覆盖配置,切换到 www-data 用户,并添加了 FPM 健康检查。

关键习惯:固定版本、缓存依赖项层、以非 root 用户运行、关闭时间戳验证,并通过 .dockerignore 保持构建上下文精简。

常见问题解答

「将 PHP 应用容器化」课时是免费的吗?

是的 — 「将 PHP 应用容器化」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 PHP Academy 课程的其余内容,请升级到 CoddyKit PRO。 PHP Academy 课程共包含 4 节课。

「将 PHP 应用容器化」这节课中我会学到什么?

编写可用于生产环境的 PHP Dockerfile 你通过在浏览器中直接运行的动手代码来练习 PHP Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 PHP Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 PHP Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「将 PHP 应用容器化」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 PHP Academy 课中编写并运行代码吗?

能。每节 PHP Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 将 PHP 应用容器化
  2. 多阶段构建与优化
  3. 使用 Docker Compose 构建本地技术栈
  4. 使用 GitHub Actions 实现 CI/CD
← 返回 PHP Academy