
1. 项目概述为什么选择Docker来搭建LNMP如果你是一个经常在Linux服务器上折腾的开发者或运维肯定对LNMPLinux, Nginx, MySQL, PHP这套黄金组合不陌生。传统上我们会在服务器上直接安装Nginx、编译PHP、配置MySQL整个过程繁琐不说一旦某个环节出错或者需要切换PHP版本那简直就是一场灾难。更别提不同项目依赖不同环境时端口冲突、版本不兼容的问题有多让人头疼了。我最初也是这么过来的直到开始用Docker。简单来说Docker把LNMP的每个组件Nginx, MySQL, PHP-FPM都打包成了独立的、轻量的“集装箱”容器。每个容器都拥有自己独立的运行环境互不干扰。你想用PHP 7.4跑老项目同时用PHP 8.2测试新特性用Docker可以轻松实现只需要运行两个不同的PHP容器即可。项目部署和迁移也变得极其简单因为整个环境及其配置都被“容器化”了在任何安装了Docker的Linux机器上都能一键还原。所以这篇内容就是带你从零开始在Linux系统上使用Docker一步步搭建一个干净、隔离、可复用的LNMP开发/测试环境并最终部署一个PHP项目。整个过程我会把每一步的原理、背后的考量以及我踩过的坑都讲清楚目标是让你看完就能自己动手搭起来并且理解为什么要这么做。2. 环境准备与Docker基础在开始搭建之前我们需要确保基础环境是就绪的。这里假设你使用的是一台干净的Linux服务器如CentOS 7/8, Ubuntu 20.04/22.04并且拥有root或sudo权限。2.1 安装与配置DockerDocker的安装其实很简单但有几个关键点需要注意这直接关系到后续使用的稳定性和效率。首先卸载旧版本如果有的话。这步很重要避免残留文件导致冲突。sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine对于Ubuntu/Debian系统使用apt-get remove。接下来是安装。我强烈建议使用Docker官方提供的仓库进行安装而不是使用系统自带的旧版本软件包。官方仓库的版本更新兼容性更好。安装必要的工具包# CentOS/RHEL sudo yum install -y yum-utils # Ubuntu/Debian sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release添加Docker官方GPG密钥和仓库# CentOS/RHEL sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # Ubuntu/Debian sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null安装Docker引擎# CentOS/RHEL sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin # Ubuntu/Debian sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin启动并设置开机自启sudo systemctl start docker sudo systemctl enable docker验证安装运行sudo docker run hello-world。如果能看到欢迎信息说明Docker安装成功并可以正常运行。注意如果你在虚拟机或某些云主机上安装Docker后运行docker info时遇到类似WARNING: No swap limit support或内核参数警告通常不影响基础使用但生产环境建议根据文档调整。如果遇到“virtualization support not detected”这类错误说明你的系统特别是某些VPS或虚拟机没有开启虚拟化支持如Intel VT-x/AMD-V或者Docker Desktop所需的后台服务未启动。对于Linux服务器我们安装的是Docker Engine不依赖桌面环境的虚拟化所以这个问题较少见但如果是在Windows WSL2或某些特殊虚拟化平台上可能需要检查主机的BIOS设置或虚拟化平台配置。2.2 配置Docker镜像加速器直接从Docker官方Hub拉取镜像在国内速度可能很慢。配置一个国内镜像加速器是必不可少的步骤。这里以阿里云镜像加速器为例你需要有一个阿里云账号登录阿里云容器镜像服务控制台。在“镜像工具” - “镜像加速器”中你会看到针对不同系统的加速器地址形如https://xxxx.mirror.aliyuncs.com。在Linux服务器上编辑Docker的守护进程配置文件sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [https://xxxx.mirror.aliyuncs.com] } EOF请将https://xxxx.mirror.aliyuncs.com替换为你从阿里云控制台获取的实际地址。你也可以添加多个镜像源例如同时使用中科大源[https://docker.mirrors.ustc.edu.cn]。重新加载配置并重启Dockersudo systemctl daemon-reload sudo systemctl restart docker验证是否生效sudo docker info在输出中查找Registry Mirrors部分应该能看到你配置的镜像地址。实操心得镜像加速器不仅能加快拉取速度还能提高稳定性。我曾经在没配置加速器的情况下拉取一个几百MB的镜像中途超时失败了好几次配置后基本是满速下载。这是搭建环境前必做的一步能节省大量等待时间。2.3 理解Docker核心概念镜像与容器在动手之前花两分钟理解两个核心概念后面操作会顺畅很多。镜像Image一个只读的模板类似于虚拟机的快照。它包含了运行某个软件所需的所有内容代码、运行时、库、环境变量和配置文件。我们接下来要拉的nginx:latest,mysql:8.0,php:8.2-fpm都是镜像。容器Container镜像的运行实例。你可以把它理解为一个轻量级的、隔离的进程。当我们运行docker run nginx时Docker会基于nginx镜像创建一个可读写的容器层然后启动它。一个镜像可以创建多个容器。我们的目标就是用这三个镜像创建三个相互协作的容器组成LNMP服务。3. 核心组件部署MySQL、PHP-FPM与Nginx我们将采用“分而治之”的策略先分别启动MySQL和PHP-FPM容器最后配置Nginx容器将它们连接起来。这里我不会使用docker run后面跟一长串参数的方式而是采用更清晰、更易维护的docker-compose方式。但为了让你理解底层发生了什么我会先讲解单容器运行的命令和原理。3.1 部署MySQL数据库容器数据库是应用的核心数据持久化是关键。我们不能让数据保存在容器内部因为容器停止或删除后里面的数据就没了。所以必须使用“数据卷”将容器内的数据目录挂载到宿主机上。单容器启动命令解析docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyour_strong_password \ -e MYSQL_DATABASEmyapp \ -v /path/on/host/mysql_data:/var/lib/mysql \ mysql:8.0-d后台运行容器。--name mysql8给容器起个名字方便后续管理。-p 3306:3306端口映射。将宿主机的3306端口映射到容器的3306端口。这样你就能通过宿主机IP:3306访问MySQL了。-e MYSQL_ROOT_PASSWORD...设置环境变量这里是MySQL root用户的密码。务必替换成一个强密码-e MYSQL_DATABASEmyapp容器启动时自动创建一个名为myapp的数据库方便后续使用。-v /host/path:/container/path数据卷挂载。将宿主机目录/path/on/host/mysql_data挂载到容器内的MySQL数据目录/var/lib/mysql。这样数据库文件就实际保存在宿主机上了。mysql:8.0指定使用的镜像及其标签。不写标签默认是latest但生产环境建议指定具体版本如8.0。运行后可以用docker ps查看容器状态用docker logs mysql8查看启动日志。确认运行正常后可以尝试连接# 进入容器内的MySQL命令行 docker exec -it mysql8 mysql -uroot -p # 然后输入上面设置的密码注意事项密码安全永远不要在命令中直接使用简单密码更不要将带密码的命令写入脚本提交到代码库。在实际生产或团队协作中应使用Docker Secrets或环境变量文件来管理敏感信息。数据卷路径确保宿主机上的挂载路径如/data/mysql存在且Docker进程有读写权限通常Docker daemon以root运行没问题。你可以先mkdir -p /data/mysql。版本选择MySQL 8.0默认使用了新的身份验证插件caching_sha2_password一些老的客户端或PHP版本可能不支持。如果遇到连接问题可以在创建容器时通过环境变量-e MYSQL_DEFAULT_AUTHENTICATION_PLUGINmysql_native_password指定使用旧的插件或者在容器内修改用户插件。3.2 部署PHP-FPM容器PHP-FPMFastCGI Process Manager是PHP的进程管理器负责处理Nginx转发过来的PHP脚本请求。单容器启动命令解析docker run -d \ --name php-fpm8 \ -p 9000:9000 \ -v /path/on/host/www:/var/www/html \ --link mysql8:mysql \ php:8.2-fpm-p 9000:9000暴露PHP-FPM的FastCGI服务端口9000。这样Nginx容器才能与之通信。-v /host/path/www:/var/www/html这是最关键的一步。我们将宿主机上的项目代码目录/path/on/host/www挂载到PHP容器的/var/www/html。这意味着你在宿主机上编辑代码容器内立即生效。--link mysql8:mysql这是旧的容器链接方式它允许PHP容器通过主机名mysql访问MySQL容器。在更现代的Docker网络实践中我们倾向于使用自定义网络但--link在简单场景下依然直观。它会将MySQL容器的IP等信息以环境变量和主机名的方式注入到PHP容器中。php:8.2-fpm使用官方PHP 8.2 FPM镜像。你也可以选择php:7.4-fpm等版本。启动后我们需要进入PHP容器安装一些必要的扩展比如pdo_mysql否则PHP无法连接MySQL。# 进入PHP容器 docker exec -it php-fpm8 /bin/bash # 在容器内安装扩展基于Debian的镜像 apt-get update apt-get install -y libpng-dev libzip-dev docker-php-ext-install pdo_mysql mysqli gd zip # 安装完成后退出 exit提示官方PHP镜像提供了非常方便的docker-php-ext-install脚本来安装核心扩展。对于更复杂的扩展如redis,mongodb可能需要使用pecl安装。实操心得挂载代码卷时要特别注意宿主机和容器内的用户权限问题。PHP-FPM进程通常以www-data用户运行而你在宿主机上可能是root或另一个普通用户。如果PHP容器没有权限读取或写入挂载目录的文件会导致网站报“Permission denied”错误。一个简单的解决办法是在宿主机上修改项目目录的权限为755或者更精细地调整所有者和组。我通常会在宿主机上执行chmod -R 755 /path/on/host/www来避免初期权限麻烦。3.3 部署Nginx容器并配置反向代理Nginx作为Web服务器和反向代理接收用户请求如果是静态文件如.jpg, .css则直接返回如果是PHP请求则转发给PHP-FPM容器处理。单容器启动命令解析docker run -d \ --name nginx \ -p 80:80 \ -p 443:443 \ -v /path/on/host/www:/usr/share/nginx/html \ -v /path/on/host/nginx.conf:/etc/nginx/nginx.conf:ro \ -v /path/on/host/conf.d:/etc/nginx/conf.d:ro \ --link php-fpm8:php-fpm \ nginx:latest-p 80:80 -p 443:443映射HTTP和HTTPS端口。-v /host/path/www:/usr/share/nginx/html同样挂载项目代码目录。Nginx和PHP容器必须挂载同一个宿主机目录到各自容器内的Web根目录这样它们才能访问到相同的文件。-v /host/path/nginx.conf:/etc/nginx/nginx.conf:ro挂载自定义的Nginx主配置文件。:ro表示只读防止容器意外修改宿主机文件。-v /host/path/conf.d:/etc/nginx/conf.d:ro挂载自定义的站点配置文件目录。这是更常见的做法每个项目一个独立的.conf文件放在这里。--link php-fpm8:php-fpm链接到PHP-FPM容器使其可以通过主机名php-fpm访问。现在我们需要在宿主机上创建Nginx的配置文件。首先创建目录结构mkdir -p /path/on/host/nginx/conf.d然后创建站点配置文件/path/on/host/nginx/conf.d/myapp.confserver { listen 80; server_name localhost; # 或你的域名 root /usr/share/nginx/html; index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php-fpm:9000; # 这里使用链接的容器名 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } }这个配置做了几件事监听80端口。设置Web根目录为/usr/share/nginx/html即我们挂载的目录。定义了PHP请求的处理规则将所有以.php结尾的请求转发到php-fpm:9000即我们之前启动的PHP-FPM容器。fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;这一行至关重要它告诉PHP-FPM要执行的文件路径在哪里。如果这行配置错误你会看到File not found.的错误。配置完成后重启Nginx容器使其生效docker restart nginx4. 使用Docker Compose编排LNMP服务虽然上面分步启动的方式能工作但管理三个容器的启动参数、链接关系非常麻烦。Docker Compose正是为了解决这个问题而生它允许我们用一个YAML文件docker-compose.yml来定义和运行多容器应用。4.1 编写docker-compose.yml文件在项目根目录例如/path/on/host下创建docker-compose.yml文件version: 3.8 services: nginx: image: nginx:latest container_name: lnmp_nginx ports: - 80:80 - 443:443 volumes: - ./www:/usr/share/nginx/html - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/logs:/var/log/nginx depends_on: - php networks: - lnmp-network php: image: php:8.2-fpm container_name: lnmp_php volumes: - ./www:/var/www/html - ./php/custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro depends_on: - mysql networks: - lnmp-network # 使用命令在容器启动后安装扩展 command: sh -c docker-php-ext-install pdo_mysql mysqli gd zip php-fpm mysql: image: mysql:8.0 container_name: lnmp_mysql ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: your_strong_password_here MYSQL_DATABASE: myapp MYSQL_USER: myuser MYSQL_PASSWORD: myuser_password volumes: - ./mysql/data:/var/lib/mysql - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro networks: - lnmp-network networks: lnmp-network: driver: bridge文件解析与优势版本指定Compose文件格式版本。服务定义定义了三个服务nginx,php,mysql。网络创建了一个名为lnmp-network的自定义桥接网络。所有服务加入此网络后可以通过服务名如mysql,php直接相互访问无需旧的--link参数更清晰、更现代。在Nginx配置中fastcgi_pass就可以写成php:9000。数据卷所有持久化数据代码、数据库、日志、配置都挂载到宿主机当前目录下的子目录中结构清晰易于备份。依赖使用depends_on控制启动顺序确保MySQL先于PHP启动PHP先于Nginx启动尽管网络联通后服务启动有微小延迟应用层可能需要重试逻辑。PHP扩展安装通过覆盖command在容器启动时自动安装所需的PHP扩展。这是一种方式另一种更好的方式是基于php:8.2-fpm镜像构建一个包含扩展的自定义镜像使用Dockerfile。MySQL初始化通过将SQL脚本挂载到/docker-entrypoint-initdb.d/目录容器首次启动时会自动执行该脚本可用于初始化表结构等。4.2 启动与管理LNMP堆栈在包含docker-compose.yml的目录下执行以下命令启动所有服务后台模式docker-compose up -d-d代表 detached后台运行。首次运行会拉取镜像然后创建网络、卷并启动容器。查看运行状态docker-compose ps查看所有容器日志docker-compose logs查看特定服务日志docker-compose logs nginx停止所有服务docker-compose down这个命令会停止并删除容器、网络默认创建的网络。但不会删除数据卷所以你的数据库文件和项目代码都还在。停止并删除所有资源包括数据卷docker-compose down -v警告这会删除docker-compose.yml中定义的所有匿名卷导致数据库数据丢失请谨慎使用。重启某个服务docker-compose restart nginx进入容器docker-compose exec php /bin/bash使用Docker Compose后整个LNMP环境的生命周期管理变得极其简单和统一配置文件即代码非常适合团队协作和版本管理。5. 项目部署与验证环境搭好了我们来部署一个实际的PHP项目进行验证。5.1 部署一个简单的PHP应用在宿主机上的项目目录/path/on/host/www下创建以下文件index.php(用于测试PHP环境和Nginx配置)?php phpinfo(); ?db_test.php(用于测试PHP连接MySQL)?php $host mysql; // 使用docker-compose中的服务名 $dbname myapp; // 与docker-compose中MYSQL_DATABASE一致 $user myuser; // 与docker-compose中MYSQL_USER一致 $pass myuser_password; // 与docker-compose中MYSQL_PASSWORD一致 try { $pdo new PDO(mysql:host$host;dbname$dbname;charsetutf8mb4, $user, $pass); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo 数据库连接成功br; // 尝试创建一个简单的表并插入数据 $pdo-exec(CREATE TABLE IF NOT EXISTS test (id INT AUTO_INCREMENT PRIMARY KEY, message VARCHAR(255))); $stmt $pdo-prepare(INSERT INTO test (message) VALUES (?)); $stmt-execute([date(Y-m-d H:i:s) . - 来自Docker LNMP的问候]); echo 数据插入成功br; // 查询数据 $stmt $pdo-query(SELECT * FROM test ORDER BY id DESC); while ($row $stmt-fetch(PDO::FETCH_ASSOC)) { echo ID: . $row[id] . - Message: . $row[message] . br; } } catch (PDOException $e) { die(数据库连接失败: . $e-getMessage()); } ?5.2 验证服务验证Nginx与PHP打开浏览器访问http://你的服务器IP。你应该能看到完整的phpinfo()输出页面这表明Nginx成功将PHP请求转发给了PHP-FPM容器处理。验证PHP连接MySQL访问http://你的服务器IP/db_test.php。如果一切配置正确你会看到“数据库连接成功”和插入的数据记录。如果看到连接失败的错误请按以下步骤排查检查docker-compose ps确保三个容器都在运行Up状态。检查PHP容器内是否安装了pdo_mysql扩展docker-compose exec php php -m | grep pdo_mysql。检查db_test.php中的数据库连接参数主机名、数据库名、用户名、密码是否与docker-compose.yml中的environment定义完全一致。注意在Docker Compose网络中主机名就是服务名mysql而不是localhost。进入MySQL容器验证用户和数据库是否创建成功docker-compose exec mysql mysql -uroot -p输入root密码后执行SHOW DATABASES;和SELECT user, host FROM mysql.user;。5.3 部署真实项目如WordPress对于像WordPress这样的真实项目步骤也非常类似下载WordPress在宿主机www目录下下载并解压WordPress最新版。配置数据库确保docker-compose.yml中MySQL服务已创建了WordPress需要的数据库和用户可以通过MYSQL_DATABASE,MYSQL_USER,MYSQL_PASSWORD环境变量自动创建或通过初始化SQL脚本创建。调整文件权限WordPress需要向wp-content目录写入文件。你可能需要在宿主机上调整权限chmod -R 755 /path/on/host/www和chown -R www-data:www-data /path/on/host/www/wp-content但要注意宿主机用户与容器内用户的映射更复杂的权限管理可能需要研究Docker的user命名空间映射。访问安装向导浏览器访问你的服务器IP就会进入WordPress著名的“五分钟安装”界面根据提示填写数据库信息数据库主机填mysql数据库名、用户名、密码填你在docker-compose.yml中设置的即可完成安装。6. 进阶配置与优化基础环境跑通后我们可以进行一些优化让它更适用于生产或开发场景。6.1 自定义PHP配置与扩展官方PHP镜像的默认配置可能不符合我们的要求。我们可以通过挂载自定义的php.ini文件来修改配置。在宿主机创建php/custom.ini文件对应docker-compose.yml中的挂载点upload_max_filesize 64M post_max_size 64M memory_limit 256M date.timezone Asia/Shanghai在docker-compose.yml的php服务中我们已经挂载了这个文件- ./php/custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro。PHP会自动读取conf.d目录下的所有.ini文件。对于更多扩展建议创建Dockerfile来构建自定义PHP镜像。在项目根目录创建Dockerfile.phpFROM php:8.2-fpm RUN apt-get update apt-get install -y \ libpng-dev \ libzip-dev \ libonig-dev \ docker-php-ext-install \ pdo_mysql \ mysqli \ gd \ zip \ mbstring \ pecl install redis docker-php-ext-enable redis然后修改docker-compose.yml将php服务的image: php:8.2-fpm替换为build: context: . dockerfile: Dockerfile.php。这样每次启动都会基于你的Dockerfile构建镜像确保环境一致性。6.2 Nginx优化与多项目配置对于多项目我们可以在nginx/conf.d/目录下为每个项目创建独立的.conf文件。例如新增一个blog.confserver { listen 80; server_name blog.yourdomain.com; root /usr/share/nginx/html/blog; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }然后在www目录下创建blog文件夹放入你的第二个项目代码。重启Nginx容器即可docker-compose restart nginx。6.3 数据备份与迁移这是Docker化部署最大的优势之一。备份整个环境的状态由docker-compose.yml和几个宿主机目录定义。备份就是备份这些文件docker-compose.yml环境定义。www/项目代码。mysql/data/数据库文件确保MySQL容器已停止或使用mysqldump进行热备份。nginx/conf.d/和php/custom.ini配置文件。迁移在新服务器上安装好Docker和Docker Compose将上述备份的目录和文件拷贝过去直接运行docker-compose up -d一个完全相同的LNMP环境就起来了。6.4 使用环境变量文件管理敏感信息将数据库密码等敏感信息直接写在docker-compose.yml中不安全。我们可以使用环境变量文件。创建一个.env文件在docker-compose.yml同级目录MYSQL_ROOT_PASSWORDSuperSecretRootPass123! MYSQL_DATABASEmyapp MYSQL_USERmyuser MYSQL_PASSWORDMyUserSecretPass456!修改docker-compose.yml中的mysql服务环境变量部分environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD}确保.env文件不被提交到版本控制系统在.gitignore中添加.env。7. 常见问题与故障排查实录在实际操作中你几乎一定会遇到一些问题。这里记录了我遇到的一些典型问题及其解决方法。7.1 容器启动失败或不断重启现象docker-compose ps显示容器状态为Restarting或Exited。排查查看日志这是第一步也是最重要的一步。docker-compose logs [服务名]会输出详细的错误信息。常见原因有端口冲突Bind for 0.0.0.0:80 failed: port is already allocated。说明宿主机80端口已被占用可能是已有的Nginx或Apache。修改docker-compose.yml中的端口映射例如改为- 8080:80或者停止占用端口的服务。权限问题Permission denied挂载卷。检查宿主机上挂载的目录如./mysql/data是否存在以及Docker进程是否有读写权限。可以尝试sudo chmod -R 777 /path/on/host仅用于测试生产环境需精细控制。镜像拉取失败网络问题或镜像不存在。检查镜像名和标签是否正确尝试docker pull [镜像名]手动拉取。配置错误例如Nginx配置文件语法错误。可以通过docker-compose exec nginx nginx -t来测试配置文件语法。7.2 Nginx报错 “502 Bad Gateway” 或 “File not found.”现象访问PHP页面出现502错误或者显示“File not found.”排查检查PHP-FPM容器确保php服务正在运行 (docker-compose ps)。确保PHP-FPM监听在9000端口 (docker-compose exec php netstat -tlnp | grep 9000)。检查Nginx配置确认fastcgi_pass指向正确。在Docker Compose网络中应使用服务名php:9000。检查SCRIPT_FILENAME参数是否正确指向了文件真实路径。一个常见的坑是$document_root变量未设置或设置错误确保root指令在server或location块中已正确配置。检查文件路径和权限进入Nginx容器 (docker-compose exec nginx /bin/sh)查看/usr/share/nginx/html/index.php文件是否存在。进入PHP容器查看/var/www/html/index.php是否存在。确认PHP容器内的用户通常是www-data有读取该文件的权限。7.3 PHP无法连接MySQL现象PHP页面显示 “SQLSTATE[HY000] [2002] Connection refused” 或 “SQLSTATE[HY000] [1045] Access denied”。排查检查MySQL容器确保mysql服务正在运行并且3306端口在容器内监听 (docker-compose exec mysql netstat -tlnp | grep 3306)。检查连接参数主机名在PHP代码或配置中主机名必须是mysqlDocker Compose服务名而不是localhost或127.0.0.1。因为localhost在PHP容器内指向容器自己而不是MySQL容器。用户名、密码、数据库名必须与docker-compose.yml或.env文件中定义的环境变量完全一致注意大小写。检查MySQL用户权限使用root用户进入MySQL检查为应用创建的用户是否拥有从任意主机%或特定网络访问的权限。在Docker网络中PHP容器会使用一个独立的IP连接MySQL。CREATE USER myuser% IDENTIFIED BY password; GRANT ALL PRIVILEGES ON myapp.* TO myuser%; FLUSH PRIVILEGES;检查防火墙如果MySQL服务映射了宿主机的端口- 3306:3306并且你尝试从宿主机外部连接请确保云服务器安全组或宿主机的防火墙如firewalld,ufw允许3306端口入站。7.4 性能问题与资源限制默认情况下容器可以使用宿主机的所有资源。在生产环境你可能需要限制容器的CPU和内存使用防止单个容器耗尽资源影响其他服务。可以在docker-compose.yml中为每个服务添加资源限制services: mysql: ... deploy: # 注意在Compose v3中resources放在deploy下且通常用于swarm模式。单机模式更推荐使用以下方式 # 对于 docker-compose up使用以下格式 # cpus: 0.5 # 不推荐已弃用 # mem_limit: 512m # 不推荐已弃用 # 推荐使用 ulimits: nproc: 65535 nofile: soft: 20000 hard: 40000 # 对于CPU和内存限制在单机模式下更标准的做法是在 docker run 时使用 --cpus 和 --memory 参数。 # 在docker-compose中对于非Swarm模式社区更倾向于使用 cpu_shares 和 mem_limit尽管部分属性在v3中标记为遗留。 cpu_shares: 512 # 相对权重默认1024 mem_limit: 512m # 内存硬限制 mem_reservation: 256m # 内存软限制设置资源限制是一个需要根据实际负载进行测试和调整的过程。过小的限制会导致服务性能低下过大的限制则失去了限制的意义。从最初的单个容器手动启动到使用Docker Compose一键编排再到自定义配置和优化这套基于Docker的LNMP环境搭建流程已经相当完整。它最大的价值在于提供了一种可重复、可移植、隔离的开发部署环境。一旦你熟悉了这套流程无论是搭建本地开发环境还是在新的服务器上部署生产应用效率都会得到质的提升。记住容器化不是银弹它引入了新的复杂度网络、存储、编排但带来的环境一致性和运维便捷性在大多数场景下绝对是利大于弊的。下次当你需要为不同项目配置不同PHP版本时你会庆幸自己用了Docker。