FrankenPHP Docker 镜像实战:自定义镜像、扩展安装、Worker 模式与生产加固指南
FrankenPHP Docker 镜像实战自定义镜像、扩展安装、Worker 模式与生产加固指南【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp本指南以官方文档 docs/ru/docker.md 为主线系统讲解如何基于dunglas/frankenphp官方 Docker 镜像构建自己的 PHP 应用镜像从基础镜像选择与标签规则、安装 PHP 扩展与自定义 Caddy 模块到默认启用 Worker 模式、以非 root 用户运行以及基于 Distroless 的生产加固。读完本文你将能独立写出可直接构建、可投入开发与生产的完整 Dockerfile 与 Compose 配置。镜像概览基础、变体与标签规则FrankenPHP 的官方 Docker 镜像dunglas/frankenphp并非从零构建而是基于 PHP 官方镜像php:version-zts-os叠加而成这一点在仓库的 docker-bake.hcl 中可以直接验证contexts { php-base docker-image://php:${php-version}-zts-${os} golang-base docker-image://golang:${GO_VERSION}-${os} }镜像使用 PHP 的ZTSZend Thread Safety变体这是 FrankenPHP 以线程方式运行 PHP 解释器所必需的。官方同时提供Debian与Alpine Linux两种基础系统适用于主流 CPU 架构amd64、386、arm/v7、arm64Alpine 额外支持 arm/v6。文档明确建议优先使用 Debian 变体——Alpine 基于 musl libc在部分场景如 Symfony 应用下需要额外调优栈大小下文详述。PHP 版本支持镜像覆盖 PHP 8.2、8.3、8.4 和 8.5 四个版本。从 docker-bake.hcl 的构建矩阵可以看到这一事实variable PHP_VERSION { default 8.2,8.3,8.4,8.5 }且默认latest版本指向8.5DEFAULT_PHP_VERSION。标签Tag规则镜像标签遵循统一模板dunglas/frankenphp:frankenphp-version-phpphp-version-osfrankenphp-versionFrankenPHP 版本号可精确到主版本如1、次版本如1.2或补丁版本如1.2.3php-versionPHP 版本号同样支持从主版本到补丁版本的粒度os基础系统取值trixieDebian Trixie、bookwormDebian Bookworm或alpine最新稳定版 Alpine。在 docker-bake.hcl 的tag()函数中可以看到标签的生成逻辑默认 PHP 版本8.5 trixie 组合会额外生成省略-php8.5-trixie的简写标签如dunglas/frankenphp:1因此dunglas/frankenphp不带标签实际指向最新稳定版本镜像还支持latest-*形式标签。所有可用标签可通过 Docker Hub 的 tags 页面查看。快速上手构建并运行第一个镜像在项目根目录创建如下DockerfileFROM dunglas/frankenphp COPY . /app/publicFrankenPHP 镜像默认以/app为工作目录、/app/public为站点根目录对应 Dockerfile 中的WORKDIR /app与mkdir -p /app/public并把仓库内置的 package/content/index.php 预置为首页。接着构建并运行docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app--rm保证容器退出后自动清理适合快速验证。镜像的默认入口是在docker-php-entrypoint中把php替换为frankenphp run见 Dockerfile默认启动命令为CMD [--config, /etc/frankenphp/Caddyfile, --adapter, caddyfile]并暴露 80HTTP、443HTTPS、443/udpHTTP/3以及 2019Caddy 管理端口四个端口镜像还内置了HEALTHCHECK通过请求http://localhost:2019/metrics做健康检查。默认 Caddyfile 与开箱即用的环境变量镜像内预置了一份默认 caddy/frankenphp/Caddyfile它通过环境变量把常用配置参数化让你无需修改 Caddyfile 即可调整站点行为。关键片段{ skip_install_trust {$CADDY_GLOBAL_OPTIONS} frankenphp { {$FRANKENPHP_CONFIG} } } {$CADDY_EXTRA_CONFIG} {$SERVER_NAME:localhost} { root {$SERVER_ROOT:public/} encode zstd br gzip php_server { #worker /path/to/your/worker.php } } import Caddyfile.d/*.caddyfile这份文件暴露的核心环境变量与 docs/ru/config.md 一致环境变量作用默认值SERVER_NAME监听地址与站点主机名同时决定 TLS 证书生成localhostSERVER_ROOT站点根目录public/即/app/publicFRANKENPHP_CONFIG注入frankenphp指令块内容最常用于配置 Worker空CADDY_GLOBAL_OPTIONS注入 Caddy 全局选项如debug、servers块空CADDY_EXTRA_CONFIG/CADDY_SERVER_EXTRA_DIRECTIVES注入站点块内外的额外指令空此外镜像会把/etc/caddy/Caddyfile硬链接到/etc/frankenphp/Caddyfile见 Dockerfile两者等价。如果需要添加更多站点配置可以直接把自定义.caddyfile文件挂载或拷贝到/etc/caddy/Caddyfile.d/目录——默认配置已通过import自动加载该目录下的所有文件。要注意这份默认 Caddyfile 开启了encode zstd br gzip压缩并默认关闭了 HTTPS 自动证书的信任安装skip_install_trust。安装额外的 PHP 扩展官方镜像内置了社区维护的install-php-extensions脚本构建时从mlocati/docker-php-extension-installer的 latest 版本下载并赋予可执行权限见 Dockerfile。安装扩展只需在 Dockerfile 中追加FROM dunglas/frankenphp # 在此处添加需要的扩展 RUN install-php-extensions \ pdo_mysql \ gd \ intl \ zip \ opcache该脚本会自动处理扩展的编译依赖、启用对应ini配置等繁琐环节支持绝大多数主流 PECL 扩展与官方扩展。扩展最终被安装到/usr/local/lib/php/extensions/no-debug-zts-YYYYMMDD/目录对应 docs/ru/config.md 中 Docker 场景的 PHP 配置路径约定。用 xcaddy 安装自定义 Caddy 模块FrankenPHP 构建在 Caddy 之上因此Caddy 生态的全部模块都可以通过 xcaddy 编译进 FrankenPHP。官方镜像提供了名为builder的特殊目标stage其中已包含编译好的libphp静态库和完整的 Go 工具链供你在多阶段构建中定制二进制。官方推荐的模式是两阶段构建先在builder阶段用 xcaddy 打出带自定义模块的frankenphp二进制再在runner阶段替换官方二进制FROM dunglas/frankenphp:builder AS builder # 从 caddy 官方 builder 镜像复制 xcaddy 工具 COPY --fromcaddy:builder /usr/bin/xcaddy /usr/bin/xcaddy # 编译 FrankenPHP 必须启用 CGO RUN CGO_ENABLED1 \ XCADDY_SETCAP1 \ XCADDY_GO_BUILD_FLAGS-ldflags-w -s -tagsnobadger,nomysql,nopgx \ CGO_CFLAGS$(php-config --includes) \ CGO_LDFLAGS$(php-config --ldflags) $(php-config --libs) \ xcaddy build \ --output /usr/local/bin/frankenphp \ --with github.com/dunglas/frankenphp./ \ --with github.com/dunglas/frankenphp/caddy./caddy/ \ --with github.com/dunglas/caddy-cbrotli \ # Mercure 和 Vulcain 已包含在官方构建中但你可以移除它们 --with github.com/dunglas/mercure/caddy \ --with github.com/dunglas/vulcain/caddy # 在此处添加更多 Caddy 模块 FROM dunglas/frankenphp AS runner # 用包含自定义模块的二进制替换官方二进制 COPY --frombuilder /usr/local/bin/frankenphp /usr/local/bin/frankenphp几点关键说明CGO_ENABLED1与CGO_CFLAGS/CGO_LDFLAGS是必须的——FrankenPHP 需要把 PHP 解释器链接进二进制对应 docs/ru/compile.md 中go build的CGO_CFLAGS$(php-config --includes)用法--tagsnobadger,nomysql,nopgx用于关闭不需要的 Caddy 存储后端builder镜像与 runner 镜像一样覆盖所有 FrankenPHP/PHP 版本组合且同时提供 Debian 与 Alpine 变体官方构建默认已包含 Mercure 与 Vulcain 模块可按需在--with列表中增删。[!TIP]如果使用Alpine Linux Symfony可能遇到PHP Fatal error: Maximum call stack size of 83360 bytes reached之类的栈溢出错误需要在XCADDY_GO_BUILD_FLAGS中增大栈大小详见 docs/ru/compile.md 的 xcaddy 小节例如XCADDY_GO_BUILD_FLAGS$-ldflags -w -s -extldflags \-Wl,-z,stack-size0x80000\。仓库的 alpine.Dockerfile 在构建时同样通过-Wl,-z,stack-size0x80000处理了这一问题。默认启用 Worker 模式FrankenPHP 的 Worker 模式可以在启动时预加载应用、把请求处理耗时降到毫秒级。在 Docker 场景下只需通过FRANKENPHP_CONFIG环境变量注入worker指令该变量会被默认 Caddyfile 的{$FRANKENPHP_CONFIG}展开到frankenphp指令块中FROM dunglas/frankenphp # ... ENV FRANKENPHP_CONFIGworker ./public/index.php也可以在docker run时用-e指定并额外传入 worker 数量默认每个 CPU 启动 2 个 workerdocker run \ -e FRANKENPHP_CONFIGworker ./public/index.php 42 \ -v $PWD:/app \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp有关 worker 脚本编写、超级全局变量行为、重启策略等更完整的内容参见 docs/ru/worker.md。开发环境挂载源码卷与 Docker Compose开发时最常见的需求是让容器内的代码与宿主机实时同步直接挂载当前目录即可docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app[!TIP]加上--tty选项可以让 FrankenPHP 输出人类可读的彩色日志而不是默认的 JSON 格式显著提升开发体验。使用 Docker Compose 的等价配置如下compose.yamlservices: php: image: dunglas/frankenphp # 如需使用自定义 Dockerfile取消下面一行的注释 #build: . # 生产环境取消下面一行的注释 # restart: always ports: - 80:80 # HTTP - 443:443 # HTTPS - 443:443/udp # HTTP/3 volumes: - ./:/app/public - caddy_data:/data - caddy_config:/config # 生产环境请注释掉下面一行——它在开发环境提供可读日志 tty: true # Caddy 证书与配置所需的卷 volumes: caddy_data: caddy_config:这里有两个需要重点理解的卷caddy_data:/dataCaddy 的数据目录存放自动签发的 TLS 证书、OCSP 应答缓存等。镜像是通过XDG_DATA_HOME/data指向它的见 Dockerfilecaddy_config:/configCaddy 的配置目录XDG_CONFIG_HOME/config保存运行时生成的配置快照。持久化这两个卷后容器重建时证书与配置不会丢失生产环境务必保留。以普通用户运行容器出于安全考虑很多生产环境要求容器内不以 root 运行。FrankenPHP 官方镜像本身以 root 为默认用户因为需要在镜像构建时对二进制设置 capability但完全支持切换为普通用户只需注意两点绑定 80/443 需要CAP_NET_BIND_SERVICE能力以及/config/caddy、/data/caddy目录需要写入权限。保留特权端口设置 CAP_NET_BIND_SERVICEFROM dunglas/frankenphp ARG USERappuser RUN \ # Alpine 基础镜像请改用 adduser -D ${USER} useradd ${USER}; \ # 允许绑定 80 和 443 端口 setcap CAP_NET_BIND_SERVICEeip /usr/local/bin/frankenphp; \ # 授权写入 /config/caddy 和 /data/caddy chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER}setcap给二进制授予绑定特权端口的最小能力useradd创建普通用户最后USER切换。这也是官方镜像构建流程的做法——Dockerfile 中在编译后执行了setcap cap_net_bind_serviceep /usr/local/bin/frankenphp。完全去掉特权使用非特权端口即使在非 root 情况下FrankenPHP 要监听保留端口80/443仍需要CAP_NET_BIND_SERVICE。如果应用跑在1024 及以上的非特权端口则完全不需要任何额外能力FROM dunglas/frankenphp ARG USERappuser RUN \ # Alpine 基础镜像请改用 adduser -D ${USER} useradd ${USER}; \ # 移除默认设置的能力 setcap -r /usr/local/bin/frankenphp; \ # 授权写入 /config/caddy 和 /data/caddy chown -R ${USER}:${USER} /config/caddy /data/caddy USER ${USER}然后通过SERVER_NAME环境变量让服务器监听非特权端口例如docker run -e SERVER_NAME:8000 ...SERVER_NAME会被默认 Caddyfile 的{$SERVER_NAME:localhost}展开:8000即监听本机全部网卡的 8000 端口。在非特权端口模式下镜像也无需对二进制设置任何 capability攻击面进一步缩小。镜像更新机制官方 Docker 镜像会在以下时机自动重建发布新版本时FrankenPHP 版本发布触发每天 UTC 凌晨 4 点如果官方 PHP 镜像发布了新版本。这意味着基础镜像中的 PHP 安全补丁会以日更的节奏合入 FrankenPHP 镜像。生产环境建议为镜像固定使用具体的补丁版本标签如dunglas/frankenphp:1.2.3-php8.4.5-bookworm以锁定行为同时订阅更新通知以便及时跟进安全修复开发环境则可以直接使用latest或latest-php8.5-trixie。生产加固Distroless 与 Docker Hardened 基础镜像为了进一步缩小攻击面与镜像体积FrankenPHP 官方文档提供了基于Google Distroless或Docker Hardened镜像的加固方案。Distroless 只包含运行时所需的库与二进制没有 shell 和包管理器因此调试会困难得多仅建议在安全优先级很高的生产环境使用。由于这些极简基础镜像没有包管理器额外的 PHP 扩展必须在中间构建阶段安装并把扩展及其动态依赖的共享库一并复制过去。完整示例FROM dunglas/frankenphp AS builder # 在此添加额外 PHP 扩展 RUN install-php-extensions pdo_mysql pdo_pgsql #... # 将 frankenphp 二进制与所有已装扩展的共享库复制到临时目录 # 也可以手动分析二进制和每个 .so 的 ldd 输出来完成此步骤 RUN apt-get update apt-get install -y libtree \ EXT_DIR$(php -r echo ini_get(extension_dir);) \ FRANKENPHP_BIN$(which frankenphp); \ LIBS_TMP_DIR/tmp/libs; \ mkdir -p $LIBS_TMP_DIR; \ for target in $FRANKENPHP_BIN $(find $EXT_DIR -maxdepth 2 -type f -name *.so); do \ libtree -pv $target | sed s/.*── \(.*\) \[.*/\1/ | grep -v ^$target | while IFS read -r lib; do \ [ -z $lib ] continue; \ base$(basename $lib); \ destfile$LIBS_TMP_DIR/$base; \ if [ ! -f $destfile ]; then \ cp $lib $destfile; \ fi; \ done; \ done # Distroless Debian 基础镜像——确保 Debian 版本与基础镜像一致 FROM gcr.io/distroless/base-debian13 # 备选Docker Hardened Image # FROM dhi.io/debian:13 # 应用与 Caddyfile 的路径 ARG PATH_TO_APP. ARG PATH_TO_CADDYFILE./Caddyfile # 复制应用到 /app # 进一步加固确保只有 nonroot 用户拥有可写路径 COPY --chownnonroot:nonroot $PATH_TO_APP /app COPY $PATH_TO_CADDYFILE /etc/caddy/Caddyfile # 复制 frankenphp 与所需库 COPY --frombuilder /usr/local/bin/frankenphp /usr/local/bin/frankenphp COPY --frombuilder /usr/local/lib/php/extensions /usr/local/lib/php/extensions COPY --frombuilder /tmp/libs /usr/lib # 复制 php.ini 配置文件 COPY --frombuilder /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d COPY --frombuilder /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini # Caddy 数据目录——即使文件系统是只读的也必须让 nonroot 用户可写 ENV XDG_CONFIG_HOME/config \ XDG_DATA_HOME/data COPY --frombuilder --chownnonroot:nonroot /data/caddy /data/caddy COPY --frombuilder --chownnonroot:nonroot /config/caddy /config/caddy USER nonroot WORKDIR /app # 入口使用指定的 Caddyfile 运行 frankenphp ENTRYPOINT [/usr/local/bin/frankenphp, run, -c, /etc/caddy/Caddyfile]这段配置的要点第一阶段基于完整的dunglas/frankenphp镜像安装扩展并用libtree递归解析frankenphp二进制与所有.so扩展的共享库依赖去重复制到/tmp/libs第二阶段使用gcr.io/distroless/base-debian13注意 Debian 大版本必须与 FrankenPHP 基础镜像一致否则 glibc 版本不匹配会导致二进制无法运行通过XDG_CONFIG_HOME/XDG_DATA_HOME配合chown nonroot保证 Caddy 在只读文件系统下仍能写证书与配置需要把仓库根目录的 package/Caddyfile 这类站点配置作为PATH_TO_CADDYFILE传入并显式用frankenphp run -c指定。[!WARNING]Distroless / Docker Hardened 镜像不包含 shell 与包管理器容器内无法执行apt、sh等调试命令排障成本高请仅在安全优先级极高的生产环境使用。开发版本镜像dev除了稳定版镜像官方还维护独立的dunglas/frankenphp-dev镜像仓库用于追踪主干开发分支每次向 GitHub 仓库 main 分支推送代码都会自动触发构建标签latest*指向main分支的最新提交head同时提供sha-git-commit-hash形式的标签可精确锁定到某次提交。dev 镜像对应仓库中的 dev.Dockerfile它基于golang:1.26从源码编译带调试符号的 PHP--enable-debug、CFLAGS-ggdb3并安装了 gdb、valgrind、clang 等调试工具链适合在开发/调试 FrankenPHP 本身或其 worker 时使用不建议用于生产。小结选择合适镜像的决策路径日常开发使用dunglas/frankenphp:latest或latest-php8.5-trixie配合卷挂载与--tty可读日志生产部署默认使用 Debian 变体 固定版本标签通过FRANKENPHP_CONFIG启用 Worker用命名卷持久化/data与/config并按需以普通用户 setcap运行需要自定义 Caddy 模块走builder阶段 xcaddy 两阶段构建安全敏感的生产环境使用 Distroless / Docker Hardened 多阶段加固方案以nonroot用户运行调试 FrankenPHP 本身使用dunglas/frankenphp-dev的sha-*标签。以上所有配置均可在当前仓库中交叉验证构建逻辑见 docker-bake.hcl、Dockerfile 与 alpine.Dockerfile默认 Caddyfile 见 caddy/frankenphp/Caddyfile环境变量与 Caddyfile 指令的完整说明见 docs/ru/config.mdworker 模式的深入用法见 docs/ru/worker.md。【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →