FrankenPHP 已知问题排查指南不兼容扩展、musl libc 限制与 Docker TLS 配置实战【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp本篇技术指南系统梳理 FrankenPHPThe modern PHP app server的官方已知问题清单与对应解决方案。你将了解到哪些 PHP 扩展与 FrankenPHP 不兼容或存在缺陷、musl libc 静态二进制与 Alpine Docker 镜像的兼容性边界、如何在 Docker 中让https://127.0.0.1正常工作、如何让 Composer 的php脚本跑通以及静态二进制下 TLS/SSL 证书验证失败的修复方法。读完即可在部署 FrankenPHP 时提前规避这些坑位并在遇到问题时按图索骥快速定位。不支持的 PHP 扩展FrankenPHP 以 ZTSZend Thread Safety线程安全模式运行 PHP因此非线程安全not thread-safe的扩展无法兼容。官方文档明确列出以下扩展已知不兼容扩展名原因替代方案imap非线程安全javanile/php-imap2、webklex/php-imapnewrelic非线程安全无需要说明的是英文原版文档docs/known-issues.md中该表格还包含第三项pcov代码覆盖率扩展同样因非线程安全在经典模式下触发 SIGSEGV 崩溃见其上游 issue而不兼容。官方给出的替代方案是在 FrankenPHP 之外使用 CLI SAPI 收集覆盖率即在 CLI 模式下运行 PHPUnit/Pest。本文以西班牙语文档docs/es/known-issues.md为主体特此补充这条来自英文原版的重要条目。存在缺陷的 PHP 扩展除了完全不兼容的扩展外还有一批扩展存在已知缺陷或意外行为官方文档同样给出了清单其中英文原版比西班牙语版记录更全扩展名已知问题ext-openssl使用 musl libc 时OpenSSL 扩展在重负载下可能崩溃使用更主流的 GNU libc 时不会出现该问题。此错误由 PHP 官方跟踪php/php-src#13648datadog对 FrankenPHP 进行 profiling 时存在不稳定性由 DataDog 官方跟踪dd-trace-php#3729blackfireFrankenPHP 支持处于 beta 阶段功能尚未完整见 Blackfire 官方文档imagickImageMagick 的 OpenMP 线程与 FrankenPHP 的线程冲突会导致不稳定甚至崩溃。可通过\Imagick::setResourceLimit(\Imagick::RESOURCETYPE_THREAD, 1)禁用 ImageMagick 线程或以--disable-openmp重新编译 ImageMagick 缓解。静态二进制与官方apt/apk/rpm包已禁用 OpenMP因此仅 Docker 镜像和 Homebrew 安装受影响get_browser() 性能退化get_browser() 用于解析浏览器 User-Agent官方文档指出它在运行一段时间后表现不佳。由于浏览器识别结果本质上是静态的同一 User-Agent 永远映射到同一结果推荐方案是按 User-Agent 缓存结果例如使用 APCu 扩展$result apcu_fetch(browser_ . md5($_SERVER[HTTP_USER_AGENT]), $hit); if (!$hit) { $result get_browser(); apcu_store(browser_ . md5($_SERVER[HTTP_USER_AGENT]), $result); }这样绝大多数请求只需一次 APCu 内存读取绕开了 get_browser() 的退化路径。独立二进制与 Alpine 基础镜像musl libc 兼容性完全静态的独立二进制和 Alpine 基础镜像dunglas/frankenphp:*-alpine为了保持更小的体积使用的是musl libc而非 glibc。这带来了一些兼容性隐患其中最常见的是glob 标志GLOB_BRACE不可用见 php.net/glob。仓库中的 build-static.sh 印证了这一点默认以SPC_LIBCmusl构建只有显式设置SPC_LIBCglibc才会走 GNU 工具链同时 musl 构建还会自动附加额外的编译参数例如--disable-opcache-jit相关处理说明两种 libc 的构建路径与能力确有差异。建议如果遇到 musl 相关问题优先改用GNU 变体的静态二进制和Debian 基础镜像。官方文档 docs/es/static.md 进一步解释了三种构建形态的区别可作为决策参考基于 musl 的完全静态构建static-builder-musl不依赖任何系统库甚至能跑在scratch镜像中但无法加载动态 PHP 扩展如 Xdebug且有上述 musl 限制基于 glibc 的主要静态构建static-builder-gnu只依赖 glibc支持 2.17 及以上版本可以加载动态扩展但不能在基于 musl 的系统如 Alpine Linux上运行官方建议尽可能使用基于 glibc 的主要静态构建。另外注意性能文档docs/es/performance.md同样建议生产环境不要使用 musl优先使用链接 glibc 并以适当优化级别编译的 FrankenPHP。在 Docker 中使用https://127.0.0.1默认情况下FrankenPHP 为localhost生成 TLS 证书——这是本地开发最简单、最推荐的方式。如果你确实想用127.0.0.1作为主机名可以将服务器名设为127.0.0.1以生成对应证书。但仅此还不够由于 Docker 自身的网络机制你会遇到类似下面的 TLS 错误curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error方案一Linux 下使用 host 网络驱动docker run \ -e SERVER_NAME127.0.0.1 \ -v $PWD:/app/public \ --network host \ dunglas/frankenphp方案二Mac / Windows 下猜测容器 IPhost 网络驱动在 Mac 和 Windows 上不受支持你需要猜测容器的 IP 并加入服务器名。步骤运行docker network inspect bridge查看Containers键找到IPv4Address键下当前最后分配的 IP 地址并加 1如果没有容器在运行第一个分配的 IP 通常是172.17.0.2将预测的 IP 写入SERVER_NAMEdocker run \ -e SERVER_NAME127.0.0.1, 172.17.0.3 \ -v $PWD:/app/public \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp[!CAUTION] 务必把172.17.0.3替换为你的容器实际会被分配到的 IP。此后即可从宿主机访问https://127.0.0.1。仍无法访问开启调试模式如果还是不行用调试模式启动以定位问题docker run \ -e CADDY_GLOBAL_OPTIONSdebug \ -e SERVER_NAME127.0.0.1 \ -v $PWD:/app/public \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp这两个环境变量的语义在 docs/es/config.md 中有精确定义SERVER_NAME用于改变监听地址同时其中的主机名会被用于生成 TLS 证书CADDY_GLOBAL_OPTIONS用于向 Caddy 注入全局选项debug即开启全局调试日志。理解这一点后上面的排错思路就一目了然证书绑定的是SERVER_NAME中的主机名而容器网络隔离导致宿主机的127.0.0.1与容器内的127.0.0.1不是同一地址因此必须把容器实际 IP 也纳入证书与监听范围。引用php的 Composer 脚本Composer 脚本中运行php artisan package:discover --ansi。这在 FrankenPHP 下会失败原因有二Composer 不知道如何调用 FrankenPHP 二进制Composer 可能通过-d标志向命令追加 PHP 配置而 FrankenPHP 的 CLI 模式尚不支持-d参数。从源码看php-cli子命令注册在 caddy/php-cli.go 中frankenphp php-cli script.php [args ...]会以类似 CLI SAPI 的方式执行脚本最终调用 cli.go 中的frankenphp.ExecuteScriptCLI()进入 C 层执行。官方在 README.md 中也给出了标准用法frankenphp php-cli /path/to/your/script.php。因此桥接方案是写一个伪装成php的 shell 脚本剥掉-d参数后再转发给 FrankenPHP。创建/usr/local/bin/php#!/usr/bin/env bash args($) index0 for i in $ do if [ $i -d ]; then unset args[$index] unset args[$index1] fi index$((index1)) done /usr/local/bin/frankenphp php-cli ${args[]}然后设置环境变量PHP_BINARY指向该脚本并运行 Composerexport PHP_BINARY/usr/local/bin/php composer installLaravel 用户还可以参考 docs/es/laravel.md 中 Octane 的集成方式php artisan octane:install --serverfrankenphp、php artisan octane:frankenphp其中涉及的 artisan 调用同样可以通过上述PHP_BINARY桥接在容器或服务器环境中正常工作。静态二进制的 TLS/SSL 故障排查使用静态二进制时可能会遇到与 TLS 相关的错误——例如通过 STARTTLS 发送邮件时Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: error:80000002:system library::No such file or directory error:80000002:system library::No such file or directory error:80000002:system library::No such file or directory error:0A000086:SSL routines::certificate verify failed根因静态二进制没有内置 TLS 根证书OpenSSL 找不到 CA 证书导致证书校验失败。修复步骤确定 CA 证书应放置的位置执行openssl_get_cert_locations()查看输出中的默认路径把 CA 证书放到该位置?php var_export(openssl_get_cert_locations());[!CAUTION] Web 上下文与 CLI 上下文的配置可能不同。请务必在正确的上下文出问题的那一侧中运行openssl_get_cert_locations()。获取 CA 证书包可以从 cURL 官网下载从 Mozilla 提取的 CA 证书包或者直接使用各发行版提供的ca-certificates包Debian、Ubuntu、Alpine 均有。或用环境变量指定证书位置通过SSL_CERT_FILE与SSL_CERT_DIR告诉 OpenSSL 去哪里找 CA 证书# 设置 TLS 证书环境变量 export SSL_CERT_FILE/etc/ssl/certs/ca-certificates.crt export SSL_CERT_DIR/etc/ssl/certs以 Debian/Ubuntu 系为例/etc/ssl/certs/ca-certificates.crt正是ca-certificates包安装后的标准路径在 Alpine 上对应的证书目录同样是/etc/ssl/certs。设置后重启对应进程web 或 CLI再验证即可。总结与决策速查场景结论需要 imap / newrelic / pcov 扩展不兼容改用替代扩展或 CLI SAPI使用 Alpine 镜像或静态二进制注意 musl 限制如GLOB_BRACE不可用、OpenSSL 重负载崩溃优先 Debian 镜像与 glibc 构建需要https://127.0.0.1开发Linux 用 host 网络Mac/Windows 用docker network inspect bridge推算容器 IP 加入SERVER_NAMEComposer 脚本含php用剥离-d的 shell 包装脚本 PHP_BINARY环境变量桥接静态二进制出现 TLS 证书校验失败安装 CA 证书或用SSL_CERT_FILE/SSL_CERT_DIR指向证书位置以上就是 FrankenPHP 官方记录的已知问题全集及官方推荐解法配合本文引用的 docs/es/known-issues.md、docs/es/config.md、docs/es/static.md 与源码 caddy/php-cli.go 等文件你可以在部署前提前规避这些陷阱也能在线上出问题时快速定位根因。【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考