
FrankenPHP Docker 镜像完全指南从自定义构建、扩展安装到生产加固【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp本文围绕 FrankenPHP 官方 Docker 镜像的使用展开覆盖镜像标签体系、自定义 Dockerfile 构建、PHP 扩展与 Caddy 模块安装、worker 模式、开发期卷挂载、非 root 运行以及基于 distroless 的生产加固等完整实战方案。读完本文你将能够基于 Dockerfile 与 caddy/frankenphp/Caddyfile 的仓库实现独立构建一个贴合自身业务需求、可安全投产的 FrankenPHP 容器镜像。FrankenPHP 官方镜像的构成与标签体系FrankenPHP 的 Docker 镜像基于 PHP 官方镜像构建并针对主流 CPU 架构提供Debian与Alpine Linux两种变体。官方建议优先选用 Debian 变体因为其 glibc 生态兼容性更好、调试与排障更便利。镜像同时提供PHP 8.2、8.3、8.4 与 8.5四个 PHP 大版本的变体。这一矩阵在仓库的 docker-bake.hcl 中通过PHP_VERSION 8.2,8.3,8.4,8.5变量声明并以DEFAULT_PHP_VERSION 8.5指定默认版本。理解镜像标签Tag镜像标签遵循统一模式dunglas/frankenphp:frankenphp-version-phpphp-version-osfrankenphp-version与php-version分别为 FrankenPHP 与 PHP 的版本号粒度覆盖主版本如1、次版本如1.2直至补丁版本如1.2.3os取值如下trixie基于 Debian Trixiebookworm基于 Debian Bookwormalpine基于最新稳定版 Alpine Linux。docker-bake.hcl 中的构建矩阵显示Debian 系trixie/bookworm镜像覆盖linux/amd64、linux/386、linux/arm/v7与linux/arm64而 Alpine 变体额外支持linux/arm/v6。所有 tag 的实际列表可在 Docker Hub 的镜像 tags 页面浏览。说明本仓库为 GitHub 热门项目镜像实际拉取镜像请使用docker pull dunglas/frankenphp并从官方 Docker Hub 获取具体 tag 以线上列表为准。快速上手构建并运行你的第一个 FrankenPHP 镜像在项目根目录创建DockerfileFROM dunglas/frankenphp COPY . /app/public然后依次执行构建与运行命令docker build -t my-php-app . docker run -it --rm --name my-running-app my-php-app容器启动后即默认监听 HTTP 80 端口、HTTPS 443 端口及 HTTP/3 的443/udp端口这三个端口在 Dockerfile 中通过EXPOSE声明另暴露 2019 管理端口。FrankenPHP 会自动为配置的主机名签发本地 HTTPS 证书因此直接通过浏览器访问即获得完整的 HTTPS 体验。镜像内的默认入口行为定义在 Dockerfile 与 alpine.Dockerfile 中通过sed将 PHP 官方镜像的docker-php-entrypoint中的php替换为frankenphp run并以CMD [--config, /etc/frankenphp/Caddyfile, --adapter, caddyfile]作为默认启动参数。镜像内的目录约定从构建文件可知镜像内约定如下路径/app/public站点根目录应用代码应挂载或拷贝到这里/etc/caddy/Caddyfile与/etc/frankenphp/Caddyfile主配置文件后者是前者的硬链接/config与/data分别对应XDG_CONFIG_HOME与XDG_DATA_HOME存放 Caddy 配置与证书数据见 Dockerfile。通过环境变量调整 FrankenPHP Docker 配置为方便使用官方镜像内置了一份包含常用环境变量的默认Caddyfile其源码即仓库中的 caddy/frankenphp/Caddyfile。该文件利用 Caddy 的占位符语法将以下环境变量作为配置入口环境变量作用默认值SERVER_NAME设置站点监听地址与 TLS 证书主机名localhostSERVER_ROOT设置站点根目录public/FRANKENPHP_CONFIG向全局frankenphp指令注入配置如 worker 脚本空CADDY_GLOBAL_OPTIONS注入 Caddy 全局选项空CADDY_SERVER_EXTRA_DIRECTIVES注入站点块内的附加指令空CADDY_EXTRA_CONFIG注入 Caddyfile 顶层附加配置空例如启用 Caddy 调试模式只需docker run -v $PWD:/app/public \ -e CADDY_GLOBAL_OPTIONSdebug \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp默认 Caddyfile 还开启了encode zstd br gzip压缩并预留了 Mercure注释状态与 Vulcain 模块位以及Caddyfile.d/*.caddyfile目录的自动导入方便用户以追加文件的方式扩展配置而不修改主文件。安装额外的 PHP 扩展基础镜像内置了docker-php-extension-installer脚本路径为/usr/local/bin/install-php-extensions安装逻辑见 Dockerfile。添加扩展非常直接FROM dunglas/frankenphp # 在此添加其他扩展 RUN install-php-extensions \ pdo_mysql \ gd \ intl \ zip \ opcache该脚本会自动处理扩展依赖的编译与安装无需手工docker-php-ext-configure/docker-php-ext-install。安装后的扩展会被放置到 PHP 官方镜像约定的extension_dir即/usr/local/lib/php/extensions/no-debug-zts-YYYYMMDD/参见 docs/config.md 的 Docker 配置位置说明。此外docs/config.md 建议在需要自定义 PHP 运行时行为时从官方模板复制一份php.iniFROM dunglas/frankenphp # 生产环境 RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini # 或开发环境 RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini安装自定义 Caddy 模块xcaddy builder 镜像FrankenPHP 构建于 Caddy 之上因此所有 Caddy 模块都可与 FrankenPHP 一起使用。官方提供名为builder的构建镜像包含编译好的libphp及完整 Go 工具链配合 xcaddy 可以轻松定制二进制FROM dunglas/frankenphp:builder AS builder # 将 xcaddy 复制进 builder 镜像 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要点说明builder镜像内置了编译好的libphp共享库因此无需在构建阶段重新编译 PHP所有 FrankenPHP/PHP 版本、Debian 与 Alpine 变体均提供对应的builder镜像CGO_ENABLED1是硬性要求——FrankenPHP 通过 CGO 将 PHP 以共享库方式链接进 Go 二进制参见 docs/compile.md 中 xcaddy 构建一节官方二进制默认已内置 Mercure 与 Vulcain 模块上例只是展示保留它们的方式可在--with列表中自由增删XCADDY_GO_BUILD_FLAGS中的-tagsnobadger,nomysql,nopgx用于关闭 Caddy 内置存储模块以减少体积。提示若使用 Alpine Linux 运行 Symfony 应用musl libc 下可能需要增大默认栈大小否则可能出现PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation错误。具体做法参见 docs/it/compile.md对应英文文档 docs/compile.md即将栈大小通过-extldflags -Wl,-z,stack-size0x80000调大。Alpine 官方镜像的构建文件 alpine.Dockerfile 中正包含这一链接参数。默认启用 worker 模式设置FRANKENPHP_CONFIG环境变量即可让容器以 worker 脚本模式启动应用只需引导一次并常驻内存请求响应延迟可降至毫秒级原理详见 docs/worker.mdFROM dunglas/frankenphp # ... ENV FRANKENPHP_CONFIGworker ./public/index.php该值会被默认 Caddyfile 注入到全局frankenphp指令中。worker 模式同样支持指定启动线程数默认每 CPU 2 个例如ENV FRANKENPHP_CONFIGworker ./public/index.php 42可参考 docs/worker.md 的说明。开发阶段使用卷挂载将宿主机中的应用源码目录以卷方式挂载进容器即可实现修改即生效的开发循环docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app提示--tty选项会让容器输出人类可读的日志而非 JSON 格式日志。使用 Docker Compose 时推荐配置如下# compose.yaml services: 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与caddy_config两个命名卷分别对应镜像内的/data与/config即XDG_DATA_HOME与XDG_CONFIG_HOME用于持久化 Caddy 自动签发的 TLS 证书与运行时配置避免容器重建后重新签发。以非 root 用户运行FrankenPHP 支持在 Docker 中以非 root 用户运行。以下 Dockerfile 创建应用用户并保留绑定特权端口的 capabilityFROM dunglas/frankenphp ARG USERappuser RUN -EOF # 基于 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 EOF USER ${USER}无 capability 运行即使以非 root 运行FrankenPHP 仍需要CAP_NET_BIND_SERVICE能力才能绑定 80、443 等特权端口。若改用非特权端口1024 及以上则可完全去掉 capabilityFROM dunglas/frankenphp ARG USERappuser RUN -EOF # 基于 Alpine 的发行版请使用 adduser -D ${USER} useradd ${USER} # 移除默认能力 setcap -r /usr/local/bin/frankenphp # 授予 /config/caddy 与 /data/caddy 写权限 chown -R ${USER}:${USER} /config/caddy /data/caddy EOF USER ${USER}随后通过SERVER_NAME环境变量指定非特权端口例如docker run -e SERVER_NAME:8000 -p 8000:8000 my-php-app注意此时容器内的服务端口变为 8000映射到宿主机的-p 8000:8000。默认镜像二进制本身带有cap_net_bind_serviceep能力这是由官方构建流程在 Dockerfile 与 alpine.Dockerfile 中通过setcap设置的。镜像更新机制与开发版本更新时机FrankenPHP 官方 Docker 镜像在以下两种情况下自动重建每当发布新版本tag 新 release时每天 UTC 时间 4:00若官方 PHP 镜像有新版本可用时。前者保证功能迭代及时跟上后者保证安全补丁如 PHP 安全修复能在 24 小时内落地。开发版本dev每日构建的开发版本发布在独立的dunglas/frankenphp-dev仓库中每当 GitHub 仓库的main分支收到新提交就会触发一次新构建。其标签约定为latest*系列标签指向main分支最新提交head形如sha-git-commit-hash的标签对应具体提交哈希便于复现某个快照。仓库中的 dev.Dockerfile 展示了开发镜像的构建方式它从 PHP 8.5 源码编译带调试符号的libphp并预装 gdb、valgrind、neovim 等调试工具主要用于 FrankenPHP 本身的开发调试。生产加固distroless 与 Docker 加固镜像为进一步缩小攻击面与镜像体积可以基于 Google distroless 或 Docker 加固镜像构建最终镜像。[!WARNING] 这类最小化基础镜像不包含 shell 与包管理器调试难度显著增加因此仅建议在安全优先级极高的生产环境使用。由于最小基础镜像没有包管理器安装 PHP 扩展必须在中间构建阶段完成再手工拷贝扩展及其共享库依赖FROM dunglas/frankenphp AS builder # 在此添加 PHP 扩展 RUN install-php-extensions pdo_mysql pdo_pgsql #... # 将 frankenphp 与所有已安装扩展的共享库复制到临时位置 # 也可以手工分析 frankenphp 二进制与每个扩展 .so 文件的 ldd 输出来完成此步骤 RUN -EOF apt-get update apt-get install -y --no-install-recommends libtree mkdir -p /tmp/libs for target in $(which frankenphp) \ $(find $(php -r echo ini_get(extension_dir);) -maxdepth 2 -name *.so); do libtree -pv $target 2/dev/null | grep -oP (?:── )\K/\S(? \[) | while IFS read -r lib; do [ -f $lib ] cp -n $lib /tmp/libs/ done done EOF # 使用与 builder 相同 Debian 版本的 Distroless 基础镜像 FROM gcr.io/distroless/base-debian13 # Docker 加固镜像替代方案 # FROM dhi.io/debian:13 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 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 # 配置与数据目录必须对非 root 用户可写即使根文件系统为只读 ENV XDG_CONFIG_HOME/config XDG_DATA_HOME/data COPY --frombuilder --chownnonroot:nonroot /data /data COPY --frombuilder --chownnonroot:nonroot /config /config # 复制应用保持 root 所有权与 Caddyfile COPY . /app COPY Caddyfile /etc/caddy/Caddyfile USER nonroot WORKDIR /app ENTRYPOINT [/usr/local/bin/frankenphp, run, --config, /etc/caddy/Caddyfile]该方案的关键步骤与原理多阶段构建builder阶段复用官方镜像完成扩展安装并借助libtree递归解析出frankenphp二进制与所有扩展.so文件依赖的动态库统一收集到/tmp/libs文件搬运将二进制、PHP 扩展目录、conf.d配置、生产版php.ini以及收集到的动态库分别复制到 distroless 镜像的对应位置只读根文件系统适配通过XDG_CONFIG_HOME/config与XDG_DATA_HOME/data将 Caddy 的运行时写入重定向到挂载卷且/data、/config以nonroot用户所有保证非 root 可写非 root 与固定入口最终以nonroot用户运行ENTRYPOINT直接指向frankenphp run --config /etc/caddy/Caddyfile无 shell 可用、无提权路径攻击面最小化。注意由于运行时无setcap工具此镜像依赖非特权端口通过SERVER_NAME配置或由容器运行时注入 capability这是 distroless 方案与常规镜像在端口绑定方式上的主要差异。常见问题与排查建议想确认镜像内的版本信息执行docker run --rm dunglas/frankenphp frankenphp version与frankenphp build-info官方构建流程 Dockerfile 在构建时即运行这两个命令校验产物健康检查官方镜像内置HEALTHCHECK通过curl -f http://localhost:2019/metrics探测 Caddy 管理端点见 Dockerfile自定义镜像时建议保留或参照实现修改默认站点行为优先通过SERVER_NAME、SERVER_ROOT、FRANKENPHP_CONFIG等环境变量注入或向Caddyfile.d/目录追加.caddyfile文件避免直接修改主配置非特权端口与 TLS改用:8000这类非特权端口后默认自动 HTTPS 行为会相应调整开发环境如需完全禁用 HTTPS可将SERVER_NAME设为http://或:80参见 docs/config.md。通过以上从基础运行到生产加固的完整链路你可以根据业务场景自由组合官方镜像的各个能力点构建出安全、可维护且性能优异的 FrankenPHP 容器化应用。【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考