docker nginx php mysql
用 Docker 跑 Nginx + PHP + MySQL,最容易踩的坑不是配置本身,而是容器之间怎么"看见"对方。我见过太多人把三个容器分别跑起来,然后 PHP 连不上 MySQL,报错 解决办法是自定义一个 bridge 网络。一条命令: 之后所有容器都加 先起数据库,因为后面 PHP 要用它。命令不复杂: 挂载 volume 是必须的。不挂的话,容器一删数据全没。我第一次搭环境时没注意这点,重装了一次容器,之前建的表全丢了,只能从头来。 PHP 容器需要 Nginx 能访问到它的 PHP-FPM 端口。用官方的 写个 Dockerfile: 构建、运行: 目录挂载路径要和 Nginx 那边一致,否则 Nginx 找不到 PHP 文件,返回 404。这个路径不匹配的问题非常隐蔽——Nginx 日志里只显示 file not found,不会告诉你是因为两个容器挂载点不同。 Nginx 需要两样东西:自己的配置文件,以及和 PHP 容器共享的网站目录。 配置文件里关键的一段: 文件权限。PHP-FPM 默认以 端口冲突。本机如果已经装了 MySQL 占着 3306,容器映射端口时会报错。要么改映射,要么直接不映射——PHP 容器通过内部网络连 MySQL,根本不需要把 3306 暴露到宿主机。 这套组合跑起来之后,改 PHP 代码即时生效,不用重启容器。Nginx 配置改了则要 Connection refused,折腾一下午。问题出在网络模式上——默认的 bridge 网络里,容器之间只能用 IP 通信,而 IP 每次重启都可能变。docker network create webnet--network webnet,它们就能用容器名当主机名互相访问。PHP 里写 new mysqli('mysql', ...) 就能连上,不用管 IP。这个细节看起来小,但它决定了你这套环境是能长期用还是一次性的玩具。MySQL 容器
docker run -d --name mysql --network webnet \ -e MYSQL_ROOT_PASSWORD=root123 \ -e MYSQL_DATABASE=testdb \ -v mysql_data:/var/lib/mysql \ mysql:8.0mysql:8.0 的认证插件默认是 caching_sha2_password,老版本的 PHP mysqli 扩展可能不认,如果连接报认证错误,加一个 --default-authentication-plugin=mysql_native_password 参数就能解决。PHP 容器
php:fpm 镜像,它自带 FPM 监听 9000 端口。但官方镜像里没有 mysqli 和 pdo_mysql 扩展,得自己构建。FROM php:8.2-fpm RUN docker-php-ext-install mysqli pdo pdo_mysqldocker build -t myphp . docker run -d --name php --network webnet \ -v /host/www:/var/www/html \ myphpNginx 容器
docker run -d --name nginx --network webnet \ -p 8080:80 \ -v /host/www:/var/www/html \ -v /host/nginx.conf:/etc/nginx/conf.d/default.conf \ nginx:alpinelocation ~ \.php$ { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name; include fastcgi_params; }fastcgi_pass php:9000 里的 php 就是 PHP 容器的名字,靠自定义网络解析。如果写成 localhost:9000,Nginx 会去连自己容器的 9000 端口,必然失败。几个实际会遇到的问题
www-data 用户运行,如果挂载的目录权限不对,写入操作会失败。开发环境图省事可以 chmod 777,生产环境老老实实调 uid。docker exec nginx nginx -s reload。我一般会在项目根目录放一个 docker-compose.yml,把这些参数固化下来,下次换机器直接 docker compose up -d,比手敲一堆 docker run 靠谱得多。