php容器默认时区为utc,需在dockerfile中通过配置文件(如timezone.ini)设置date.timezone = "asia/shanghai"并重启服务生效,仅运行时调用date_default_timezone_set()或ini_set()不可靠。

php -r "echo date_default_timezone_get()" 返回 UTC 就说明时区没生效
PHP 容器默认时区是 UTC,哪怕宿主机设了 Asia/Shanghai,容器里 date()、strtotime() 这些函数仍按 UTC 走。ThinkPHP 的 default_timezone 配置或 Laravel 的 APP_TIMEZONE 只影响框架层格式化,不改底层 PHP 行为。
必须在 PHP 初始化阶段就锁定时区:
- 在 Dockerfile 中写入配置文件:
RUN echo "date.timezone = Asia/Shanghai" > /usr/local/etc/php/conf.d/timezone.ini - 别用
ini_set('date.timezone', ...)—— 它只在运行时生效,中间可能已有请求按 UTC 处理完 - Alpine 镜像注意路径:/etc/php8/php.ini 或 /etc/php7/php.ini,版本前缀要匹配;Debian/Ubuntu 一般统一走
/usr/local/etc/php/conf.d/ - 验证方式不是看 phpinfo() 页面,而是进容器执行:
php -r "echo date_default_timezone_get();"
docker-php-ext-install 报错“no such file or directory”或卡住
这不是扩展名写错了,而是系统级依赖缺失导致编译失败。常见于 Alpine 和 Debian 镜像中缺少头文件或 dev 包。
典型表现:
-
docker-php-ext-install pdo_mysql提示configure: error: Cannot find MySQL header files - 执行到一半停住,CPU 占用低,无错误输出(其实是卡在交互式提示)
- Alpine 上装
redis或grpc直接失败,因为 musl libc 不兼容 pecl 编译流程
实操建议:
- Debian/Ubuntu 基础镜像先装依赖:
RUN apt-get update && apt-get install -y libpq-dev default-libmysqlclient-dev libzip-dev libpng-dev libjpeg-dev - Alpine 用 apk 对应包:
RUN apk add --no-cache mysql-client-dev postgresql-dev zlib-dev libpng-dev libjpeg-turbo-dev - 加
DEBIAN_FRONTEND=noninteractive环境变量避免卡在 apt 提示 - 优先用
docker-php-ext-install装核心扩展(pdo、mbstring、opcache),非核心扩展如 redis 改用预编译包或换php:apache镜像
storage/logs 权限拒绝但 chown -R www-data:www-data 没用
问题不在“没改权限”,而在于“改错时机”和“UID 错位”。Docker 容器启动后执行 chown 是临时的,重启即失效;更关键的是,不同基础镜像中 www-data 用户的 UID 不同(php:8.2-fpm 是 82,php:8.2-apache 是 33,自定义镜像可能是 1001)。
正确做法是构建期固化权限:
- Dockerfile 中显式创建 runtime 目录并设宽松权限:
RUN mkdir -p /app/storage/{logs,cache,views} && chmod -R 777 /app/storage - 挂载时避免全量映射项目目录,改用只读源码 + 独立可写卷:
volumes: - ./storage:/app/storage:rw - Mac 用户额外加
:cached后缀(如-v $(pwd)/storage:/app/storage:cached),否则 opcache 不刷新,改代码不生效 - 别信 “宿主机 chown 33:33” —— Windows/macOS 没有 UID 概念,该操作实际无效;Linux 宿主机也得确认当前用户 UID 真是 33
docker-compose.yml 里 db 主机名能连通但 PDO 连接报错“SQLSTATE[HY000] [2002] Connection refused”
这通常不是网络不通,而是数据库服务没真正 ready 就被 PHP 应用去连了。Docker 启动顺序不等于服务就绪顺序。
常见误判点:
-
depends_on只控制启动顺序,不等待 db 容器内 MySQL 进程监听 3306 端口 - PHP 应用启动太快,在 MySQL 还没初始化完表结构、甚至没加载完配置时就发起连接
- MySQL 容器用了
mysql:8.0,但应用连接串没加?serverVersion=8.0,PDO 认不出新协议
解决方向:
- 在 PHP 启动脚本里加重试逻辑(比如用
while ! mysqladmin ping -hdb -P3306 --silent; do sleep 2; done) - MySQL 容器加健康检查:
healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] - Laravel/.env 里确保
DB_HOST=db(不是 localhost),且DB_PORT=3306显式写出 - 若用 MySQL 8+,PDO DSN 加上
serverVersion参数,例如:mysql:host=db;port=3306;dbname=laravel;charset=utf8mb4;serverVersion=8.0
时区、扩展、权限、连接这四块最容易在构建后“看似正常启动,实则暗病缠身”。尤其是 date_default_timezone_get() 和 php -m | grep opcache 这类 CLI 验证动作,必须在容器启动后第一时间手动跑一遍,不能只信日志或页面表现。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











