ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Docker容器化LNMP环境搭建:从原理到实战部署PHP项目

Docker容器化LNMP环境搭建:从原理到实战部署PHP项目 1. 项目概述与核心价值在服务器运维和Web开发领域环境搭建一直是个既基础又磨人的活儿。我记得刚入行那会儿为了在CentOS上配一个能跑的LNMPLinux, Nginx, MySQL, PHP得照着教程一行行敲命令处理各种依赖冲突、版本不匹配一个下午可能就搭进去了最后还可能因为某个配置文件里的一个分号没写对而前功尽弃。后来Docker的出现彻底改变了这个局面。它把应用和其运行环境打包成一个轻量级、可移植的“容器”让“一次构建处处运行”成为现实。今天要聊的就是在Linux系统下如何用Docker来搭建一个LNMP环境并部署一个PHP项目。这不仅仅是把几个服务跑起来那么简单而是一套现代化的、可复现的、易于管理的开发/部署工作流。对于个人开发者这意味着你可以在几分钟内复现一个和生产环境几乎一致的开发环境对于团队这意味着新成员入职不再需要痛苦的环境配置直接拉取镜像就能开工对于运维这意味着环境的一致性得到了保障再也不会出现“在我机器上是好的”这种经典问题。整个流程的核心思路是“容器化”和“编排”。我们不再直接在宿主机上安装Nginx、MySQL、PHP而是为每个服务创建独立的Docker容器然后通过Docker Compose这个工具用一个配置文件把它们组织、连接起来实现一键启动。这样做的好处是隔离性好、资源占用少、迁移和扩展极其方便。接下来我会从环境准备开始一步步带你完成从零到一的搭建并分享我在这个过程中踩过的坑和积累的技巧。2. 环境准备与Docker基础2.1 宿主机Linux系统选择与配置虽然Docker支持多种Linux发行版但为了获得最好的兼容性和社区支持我强烈推荐使用Ubuntu Server LTS如22.04或24.04或CentOS/Rocky Linux/AlmaLinux的较新版本。我个人更偏爱Ubuntu因为其软件源更新快遇到问题网上解决方案也多。这次演示就以Ubuntu 22.04 LTS为例。在开始安装Docker之前有几项准备工作要做。首先确保你的系统是最新的sudo apt update sudo apt upgrade -y这个命令会更新软件包列表并升级所有可升级的软件包。-y参数表示自动确认避免中途需要手动输入。其次虽然不是必须但我建议安装一些常用的工具方便后续操作和问题排查sudo apt install -y curl wget vim net-toolscurl/wget: 用于从网络下载文件。vim: 一个强大的文本编辑器后续修改配置文件会用到。net-tools: 包含netstat等网络诊断工具。注意如果你使用的是云服务器如阿里云ECS、腾讯云CVM请务必在安全组防火墙规则中提前放行你需要用到的端口例如80HTTP、443HTTPS、3306MySQL等。否则即使容器内的服务运行正常外部也无法访问。2.2 Docker引擎的安装与验证Docker的安装方式有很多最稳妥的是使用Docker官方提供的安装脚本或添加其APT源。这里我们采用官方推荐的方法。卸载旧版本如果是全新系统可跳过sudo apt remove docker docker-engine docker.io containerd runc安装依赖包和GPG密钥 Docker需要一些基础依赖并且我们需要添加Docker官方的软件源密钥以确保下载的软件包是可信的。sudo apt install -y ca-certificates curl gnupg lsb-release sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg这里通过管道|将curl下载的密钥流直接传递给gpg --dearmor命令进行解码并保存到指定位置。添加Docker的APT软件源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引擎sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugindocker-ce是社区版引擎docker-ce-cli是命令行工具containerd.io是底层的容器运行时docker-compose-plugin是Docker Compose的插件版本V2。验证安装并设置开机自启sudo systemctl start docker sudo systemctl enable docker sudo docker run hello-world运行hello-world镜像是一个经典测试。如果安装成功你会看到一串欢迎信息最后以Hello from Docker!结尾。这证明Docker引擎可以正常拉取镜像并运行容器。实操心得安装后默认只有root用户和sudo用户能运行Docker命令。为了避免每次都要输入sudo可以将当前用户加入docker用户组sudo usermod -aG docker $USER。但要注意这等同于赋予了该用户root权限因为Docker守护进程以root运行。在生产环境或多人使用的开发机上需谨慎操作。操作完成后需要退出当前SSH会话并重新登录用户组变更才会生效。2.3 Docker Compose的安装与核心概念在上一步我们通过docker-compose-plugin已经安装了Docker Compose V2。它的命令是docker compose注意中间没有横线。你可以通过docker compose version来验证。Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。它的核心是一个YAML格式的配置文件默认叫docker-compose.yml。在这个文件里你可以定义整个应用需要的所有服务容器、网络、数据卷等。之后只需要一个命令docker compose up就能启动所有定义好的服务。它的工作流程可以简单理解为定义在docker-compose.yml中描述你的应用架构需要哪些镜像、端口映射、环境变量、数据卷等。启动运行docker compose upCompose会帮你拉取镜像如果本地没有、创建网络、创建并启动所有容器。管理使用docker compose stop/start/restart/down等命令来管理整个应用的生命周期。相比于手动用docker run启动每一个容器并处理它们之间的链接Docker Compose极大地简化了多容器应用的管理是搭建LNMP这类组合环境的绝配。3. LNMP容器化方案设计与镜像选型3.1 整体架构设计思路传统的LNMP是几个软件安装在同一台机器上。在Docker方案里我们的思路是“一个服务一个容器”。因此我们需要三个容器Nginx容器负责处理HTTP/HTTPS请求反向代理到后端的PHP处理程序并托管静态文件。PHP-FPM容器运行PHP-FPMFastCGI Process Manager进程负责解析和执行PHP代码。MySQL容器提供数据库服务。这三个容器需要能够互相通信。Docker Compose会自动为它们创建一个独立的虚拟网络容器之间可以通过服务名service name作为主机名直接访问这比手动配置--link或自定义网络要方便和安全得多。此外我们还需要考虑数据的持久化MySQL数据绝对不能保存在容器内部否则容器删除数据就丢了。需要挂载一个宿主机目录或数据卷Volume到容器的数据目录如/var/lib/mysql。网站代码我们的PHP项目代码也需要挂载到PHP-FPM和Nginx容器中这样修改宿主机上的代码容器内能立即生效非常适合开发。Nginx配置可以将自定义的Nginx站点配置文件挂载到Nginx容器的配置目录。3.2 官方镜像与自定义镜像选型对于基础服务优先使用Docker官方镜像Official Image。它们由Docker官方或软件维护团队制作安全、稳定、更新及时文档也最全。Nginx我们选择nginx:alpine。alpine版本基于极简的Alpine Linux镜像体积非常小只有几MB安全性也更高。对于Web服务器功能完全足够。MySQL选择mysql:8.0或mysql:latest。MySQL 8.0是目前的主流版本。注意生产环境建议锁定具体的小版本号如mysql:8.0.33以避免自动升级带来的不兼容风险。PHP-FPM选择php:fpm-alpine。同样fpm标签表示包含PHP-FPMalpine表示小体积。我们需要的是带FPM的PHP环境。但是官方PHP-FPM镜像通常只包含最基本的PHP核心扩展。我们的项目很可能需要额外的扩展比如pdo_mysql连接MySQL、gd图像处理、zip等。因此我们需要以官方镜像为基础构建一个自定义的Docker镜像。构建自定义镜像需要编写一个Dockerfile。它的作用就像一份食谱告诉Docker如何一步步组装出我们需要的镜像。我们会在Dockerfile里指定基础镜像php:fpm-alpine然后运行安装扩展的命令最后生成一个包含所需扩展的新镜像。方案对比方案优点缺点适用场景全部使用官方镜像简单快捷无需构建PHP可能缺少必要扩展需在容器内手动安装不持久快速测试或项目仅需PHP核心功能自定义PHP镜像扩展预装环境固化可版本化管理需要编写和维护Dockerfile首次需要构建时间推荐。适合所有正式开发和生产环境确保环境一致性使用第三方集成镜像如bitnami/lnmp开箱即用一体化灵活性差镜像臃肿学习成本高更新可能滞后希望快速体验不关心内部细节显然为了项目的可维护性和一致性我们选择“自定义PHP镜像”方案。4. 详细搭建步骤与配置解析接下来我们创建一个项目目录比如~/docker-lnmp所有操作都在这个目录下进行。4.1 创建项目结构与编写Dockerfile首先创建必要的目录结构这有助于文件管理mkdir -p ~/docker-lnmp cd ~/docker-lnmp mkdir -p nginx/conf.d php mysql/data wwwnginx/conf.d: 存放自定义的Nginx站点配置文件。php: 存放自定义的PHP Dockerfile及其相关文件。mysql/data: 作为挂载点持久化MySQL数据库文件。www: 存放我们的PHP项目代码。现在我们来创建自定义的PHP镜像。在php目录下创建Dockerfilevim ~/docker-lnmp/php/Dockerfile文件内容如下# 使用官方PHP-FPM Alpine镜像作为基础 FROM php:8.2-fpm-alpine # 安装系统依赖包 # 这些包是编译PHP扩展或某些功能所必需的 RUN apk add --no-cache \ freetype-dev \ libjpeg-turbo-dev \ libpng-dev \ libzip-dev \ zip \ unzip \ curl \ git \ $PHPIZE_DEPS # 安装并启用常用的PHP扩展 # docker-php-ext-install 是官方镜像提供的便捷安装脚本 RUN docker-php-ext-configure gd --with-freetype --with-jpeg \ docker-php-ext-install -j$(nproc) \ gd \ pdo_mysql \ mysqli \ opcache \ zip \ bcmath \ docker-php-ext-enable \ opcache # 安装Composer (PHP的依赖管理工具) COPY --fromcomposer:latest /usr/bin/composer /usr/bin/composer # 设置工作目录 WORKDIR /var/www/html # 提示默认的www-data用户和组在Alpine中对应的是82:82 # 如果需要调整文件权限可以在这里修改 # RUN chown -R www-data:www-data /var/www/html这个Dockerfile做了以下几件事基于php:8.2-fpm-alpine构建。使用Alpine的包管理器apk安装编译和运行扩展所需的系统库。使用docker-php-ext-install编译安装gd配置了FreeType和JPEG支持、pdo_mysql、mysqli、opcache、zip、bcmath等扩展。使用多阶段构建从Composer官方镜像中直接拷贝composer二进制文件到我们的镜像中省去了安装步骤。设置容器启动后的默认工作目录。注意$PHPIZE_DEPS是一个包含gcc,make,autoconf等编译工具的包组安装扩展后我们可以通过apk del $PHPIZE_DEPS来删除它们以减小镜像体积。但为了后续可能临时安装其他扩展的灵活性这里选择保留。在生产镜像构建中可以考虑删除。4.2 编写Docker Compose核心配置文件在项目根目录~/docker-lnmp创建docker-compose.yml文件vim ~/docker-lnmp/docker-compose.yml文件内容如下我会逐段解释version: 3.8 services: # MySQL 服务 mysql: image: mysql:8.0 container_name: lnmp_mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: your_strong_root_password_here # 务必修改 MYSQL_DATABASE: myapp_db MYSQL_USER: myapp_user MYSQL_PASSWORD: your_strong_user_password_here # 务必修改 volumes: - ./mysql/data:/var/lib/mysql - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql # 可选初始SQL ports: - 3306:3306 networks: - lnmp-network healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -u, root, -p$$MYSQL_ROOT_PASSWORD] interval: 10s timeout: 5s retries: 3 # PHP-FPM 服务 (使用自定义镜像) php: build: ./php # 指向包含Dockerfile的目录 container_name: lnmp_php restart: unless-stopped volumes: - ./www:/var/www/html # 挂载项目代码 depends_on: mysql: condition: service_healthy # 等待MySQL健康检查通过 networks: - lnmp-network # Nginx 服务 nginx: image: nginx:alpine container_name: lnmp_nginx restart: unless-stopped ports: - 80:80 - 443:443 # 如需HTTPS提前准备证书并挂载 volumes: - ./www:/var/www/html:ro # 只读挂载代码目录 - ./nginx/conf.d:/etc/nginx/conf.d:ro # 挂载自定义配置 depends_on: - php networks: - lnmp-network # 定义自定义网络便于容器间通信 networks: lnmp-network: driver: bridge关键配置解析版本version: 3.8指定了Compose文件的语法版本。建议使用3.x以上版本以支持更多新特性。服务定义mysql:environment: 通过环境变量设置MySQL的root密码、创建默认数据库和用户。这是安全关键点务必在生产环境中使用强密码并通过.env文件管理避免密码硬编码在YAML中。volumes:./mysql/data:/var/lib/mysql将宿主机目录挂载到容器内实现数据持久化。./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql是一个技巧如果init.sql文件存在MySQL容器首次启动时会自动执行它可用于初始化表结构。ports:3306:3306将宿主机的3306端口映射到容器的3306端口这样你就能用宿主机上的数据库客户端如Navicat、MySQL Workbench连接了。如果仅容器间访问可以去掉此映射更安全。healthcheck: 配置健康检查Compose的depends_on的condition选项可以依赖于此确保PHP服务启动时MySQL已就绪。php:build: ./php: 这是关键它告诉Compose去./php目录下寻找Dockerfile并构建镜像。首次运行docker compose up时会自动构建。volumes:./www:/var/www/html将本地项目代码目录挂载到PHP容器的网站根目录实现代码实时同步。depends_on: 设置依赖关系condition: service_healthy确保等待MySQL健康状态为“健康”后再启动PHP。nginx:volumes: 挂载了两处。一是代码目录只读:ro因为Nginx只需要读取静态文件和将PHP请求转发给PHP-FPM。二是自定义配置目录./nginx/conf.d里面可以放我们自己的站点配置文件它会覆盖容器内默认的/etc/nginx/conf.d/下的配置。ports: 映射了80和443端口用于HTTP和HTTPS访问。网络networks定义了一个名为lnmp-network的桥接网络。所有服务都加入此网络后它们可以通过服务名如mysql,php直接相互访问无需知道IP地址。例如PHP代码中连接数据库的主机名就可以写mysql。4.3 配置Nginx与PHP通信Nginx需要通过FastCGI协议将PHP请求转发给PHP-FPM容器。我们需要创建一个Nginx的站点配置文件。在~/docker-lnmp/nginx/conf.d目录下创建文件default.conf这个文件名任意但后缀必须是.confvim ~/docker-lnmp/nginx/conf.d/default.conf内容如下server { listen 80; server_name localhost; # 或你的域名 root /var/www/html; index index.php index.html index.htm; # 日志配置可选方便调试 access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { try_files $uri $uri/ /index.php?$query_string; } # 处理PHP请求 location ~ \.php$ { fastcgi_pass php:9000; # 关键php是Compose中定义的服务名 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # 禁止访问 .htaccess 等隐藏文件 location ~ /\.ht { deny all; } }核心配置解析root /var/www/html;: 这是Nginx容器内的路径因为我们通过卷挂载它对应着宿主机的./www目录。fastcgi_pass php:9000;: 这是最关键的配置。它告诉Nginx将PHP请求转发到名为php的主机的9000端口。这里的php就是在docker-compose.yml中定义的PHP服务名。Docker的网络DNS会自动将其解析为PHP容器的IP地址。fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;: 这个参数告诉PHP-FPM要执行的具体脚本文件路径必须正确设置否则会出现File not found.错误。4.4 编写测试项目与启动验证现在我们在www目录下创建一个最简单的PHP项目来测试环境。创建入口文件index.phpvim ~/docker-lnmp/www/index.php内容如下?php phpinfo(); // 尝试连接MySQL数据库 $host mysql; // 使用服务名 $dbname myapp_db; $user myapp_user; $pass your_strong_user_password_here; // 替换为docker-compose.yml中设置的密码 try { $pdo new PDO(mysql:host$host;dbname$dbname;charsetutf8mb4, $user, $pass); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo h2 stylecolor: green;MySQL 连接成功/h2; } catch (PDOException $e) { echo h2 stylecolor: red;MySQL 连接失败: /h2 . $e-getMessage(); } ?这个脚本做了两件事显示phpinfo()信息尝试用我们在docker-compose.yml中配置的数据库信息连接MySQL。万事俱备启动所有服务在项目根目录~/docker-lnmp下运行docker compose up -dup: 创建并启动所有服务。-d: 后台运行detached mode。首次运行会经历以下步骤为php服务构建自定义镜像根据Dockerfile。为所有服务拉取所需的官方镜像nginx:alpine, mysql:8.0。创建lnmp-network网络。按依赖顺序启动容器mysql - php - nginx。使用以下命令查看容器状态和日志docker compose ps # 查看各服务状态 docker compose logs # 查看所有服务的日志 docker compose logs nginx # 只看nginx服务的日志如果一切正常docker compose ps应该显示所有容器的状态为Up。现在打开浏览器访问你的服务器IP地址或http://localhost如果你在本地虚拟机操作。你应该能看到标准的phpinfo()页面并且下方显示“MySQL 连接成功”的绿色字样。恭喜一个基于Docker的LNMP环境已经成功运行。5. 进阶配置、优化与日常管理5.1 数据持久化与备份策略数据是无价的。我们的配置已经通过volumes将MySQL数据持久化到了宿主机的./mysql/data目录。但还需要考虑备份。MySQL数据备份 你可以直接备份宿主机上的./mysql/data目录但更推荐使用mysqldump命令在容器内执行逻辑备份这样更安全、兼容性更好。# 进入mysql容器执行备份 docker exec lnmp_mysql mysqldump -u root -p你的root密码 myapp_db ~/backup_$(date %Y%m%d_%H%M%S).sql # 或者使用docker compose docker compose exec mysql mysqldump -u root -p你的root密码 --all-databases ~/full_backup.sql可以将此命令加入crontab实现定时备份。项目代码备份./www目录本身就在宿主机常规文件备份方式即可。使用Docker命名卷Named Volume对于生产环境更推荐使用Docker管理的命名卷而不是绑定挂载./mysql/data。命名卷由Docker完全管理性能更好且更容易迁移。修改docker-compose.yml中MySQL的卷配置volumes: - mysql_data:/var/lib/mysql # 替换原来的 ./mysql/data # 在文件底部 volumes: 部分声明 volumes: mysql_data:然后通过docker volume ls和docker volume inspect lnmp_mysql_data来管理卷。5.2 性能调优与安全加固1. 资源限制 在docker-compose.yml中可以为每个服务设置CPU和内存限制防止某个容器耗尽主机资源。services: mysql: # ... 其他配置 deploy: # 注意在Compose v3中resources放在deploy下 resources: limits: cpus: 1.0 # 最多使用1个CPU核心 memory: 1G # 最多使用1GB内存 reservations: cpus: 0.5 memory: 512M对于非Swarm模式可以使用cpus和mem_limit等旧参数但deploy.resources是更现代的方式在docker compose up时生效需Compose V2。2. PHP-FPM调优 PHP-FPM的配置位于容器内的/usr/local/etc/php-fpm.d/www.conf。我们可以通过挂载自定义配置文件来覆盖默认值。首先在宿主机创建配置文件mkdir -p ~/docker-lnmp/php/php-fpm.d vim ~/docker-lnmp/php/php-fpm.d/zz-docker.conf内容示例根据服务器配置调整[www] pm dynamic pm.max_children 50 pm.start_servers 5 pm.min_spare_servers 5 pm.max_spare_servers 35 pm.max_requests 500然后在docker-compose.yml中PHP服务的volumes部分添加挂载volumes: - ./www:/var/www/html - ./php/php-fpm.d/zz-docker.conf:/usr/local/etc/php-fpm.d/zz-docker.conf3. 安全加固修改默认端口将docker-compose.yml中MySQL的端口映射改为3307:3306避免使用默认的3306端口减少被扫描的风险。使用.env文件管理密码创建.env文件确保在.gitignore中将密码写入MYSQL_ROOT_PASSWORDyour_very_strong_password MYSQL_PASSWORDanother_strong_password在docker-compose.yml中引用MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}。Nginx禁用服务器令牌在Nginx配置中添加server_tokens off;隐藏Nginx版本信息。容器用户非root运行在Dockerfile中可以使用USER指令指定一个非root用户来运行进程例如在PHP的Dockerfile末尾添加USER www-data。5.3 常用Docker Compose管理命令掌握这些命令你就能轻松管理整个LNMP环境# 启动服务后台模式 docker compose up -d # 停止服务 docker compose down # 停止服务并删除所有相关容器、网络不会删除镜像和卷 docker compose down # 停止服务并删除容器、网络、以及docker-compose.yml中定义的匿名卷谨慎 docker compose down -v # 查看运行状态 docker compose ps # 查看实时日志 docker compose logs -f # 查看某个服务的日志 docker compose logs -f nginx # 在运行中的容器内执行命令例如进入MySQL命令行 docker compose exec mysql mysql -u root -p # 重新构建并启动服务例如修改了Dockerfile后 docker compose up -d --build # 重启某个服务 docker compose restart php # 暂停和恢复服务 docker compose pause docker compose unpause5.4 项目部署与更新流程当你的PHP项目开发完成需要更新到线上环境时流程非常简单更新代码将最新的代码上传到宿主机的./www目录或通过Git拉取。更新依赖如果你的项目使用Composer可以在PHP容器内执行更新docker compose exec php composer install --no-dev --optimize-autoloader执行数据库迁移如果有Laravel之类的框架需要运行迁移docker compose exec php php artisan migrate --force清理缓存清理应用缓存、视图缓存等。docker compose exec php php artisan optimize:clear重启服务如有必要通常代码更新无需重启容器因为目录是挂载的。但如果修改了PHP环境如.env文件或需要重启FPM进程可以重启PHP服务docker compose restart php如果修改了Nginx配置需要重启或重载Nginxdocker compose exec nginx nginx -s reload # 平滑重载不影响在线服务整个更新过程无需停机体现了容器化部署的巨大优势。6. 常见问题排查与实战技巧即使按照步骤操作也可能会遇到问题。这里记录了一些我踩过的坑和解决方法。6.1 容器启动失败与日志分析问题运行docker compose up -d后使用docker compose ps发现某个容器状态是Exit或Restarting。解决这是最常遇到的问题。第一步永远是查看日志。docker compose logs [service_name] # 查看特定服务日志 docker compose logs --tail50 -f # 查看最后50行并实时跟随MySQL启动失败常见原因是宿主机上./mysql/data目录的权限问题。确保目录存在且MySQL容器内的mysql用户通常是999:999有读写权限。可以尝试sudo chown -R 999:999 ./mysql/data。也可能是旧数据与新版本不兼容。PHP构建失败查看构建日志。常见原因是网络问题导致apk add或docker-php-ext-install下载包失败。可以尝试更换Alpine的软件源或者在Dockerfile中增加构建超时和重试逻辑。Nginx启动失败通常是default.conf配置文件有语法错误。可以通过命令测试配置docker compose exec nginx nginx -t。6.2 网络连接问题502 Bad Gateway问题浏览器访问显示502错误Nginx日志docker compose logs nginx显示connect() failed (111: Connection refused) while connecting to upstream。解决这几乎总是Nginx无法连接到PHP-FPM服务。检查PHP-FPM容器是否运行docker compose ps确保php服务状态为Up。检查Nginx配置中的fastcgi_pass确认配置文件中写的是php:9000服务名端口并且端口是PHP-FPM监听的端口默认9000。检查PHP-FPM监听设置进入PHP容器检查/usr/local/etc/php-fpm.d/www.conf确认listen 9000并且listen.allowed_clients注释掉或包含Nginx的IP。检查Docker网络确保nginx和php服务在同一个自定义网络lnmp-network中。使用docker network inspect lnmp-network查看容器连接情况。6.3 文件权限与目录挂载问题问题PHP应用无法写入日志文件、上传文件或者Nginx报Permission denied错误。解决这是宿主机用户IDUID与容器内用户ID不匹配导致的经典问题。容器内PHP-FPM和Nginx通常以www-data用户UID 82运行而宿主机上的./www目录所有者可能是你的当前用户如UID 1000。方案一简单在宿主机上修改项目目录权限为777不推荐用于生产chmod -R 777 ./www。方案二推荐让容器内的进程以宿主机当前用户的UID运行。修改PHP的Dockerfile在最后添加RUN apk add --no-cache shadow \ usermod -u 1000 www-data \ groupmod -g 1000 www-data并将./www目录的UID/GID改为1000。同时在docker-compose.yml的PHP和Nginx服务中添加用户映射user: 1000:1000这样容器内的进程就会以UID 1000运行与宿主机用户匹配。6.4 性能问题与调试技巧问题网站访问速度慢数据库连接超时。解决查看容器资源使用docker stats可以实时查看所有容器的CPU、内存使用情况。进入容器调试docker compose exec php sh # 进入PHP容器 docker compose exec mysql bash # 进入MySQL容器在容器内你可以运行top、free -m或者使用mysql命令行工具检查数据库状态。分析慢查询在MySQL容器内启用慢查询日志或在PHP应用中配置数据库调试模式。调整PHP-FPM配置如前所述根据服务器硬件情况调整pm.max_children等参数。一个粗略的计算公式max_children 可用内存 / 单个PHP进程平均内存占用。6.5 镜像构建优化与清理随着开发进行会产生很多中间镜像和停止的容器占用磁盘空间。清理所有停止的容器、未使用的网络、构建缓存docker system prune -f清理未使用的镜像docker image prune -a优化Dockerfile构建利用Docker的构建缓存。将不经常变动的操作如安装系统包放在Dockerfile前面将经常变动的操作如拷贝应用代码放在后面。合并RUN命令以减少镜像层数。我个人在实际操作中的体会是Docker化LNMP环境最大的价值不在于第一次搭建的速度而在于其带来的确定性和可移植性。这份docker-compose.yml和相关的配置文件就是你的环境“蓝图”。无论是换一台新电脑还是在新服务器上部署只需要git clone代码和这份配置文件然后一个docker compose up -d一个完全一致的环境就在几分钟内准备就绪。它把环境配置从一门“手艺”变成了可版本控制的“代码”这对于团队协作和持续集成/持续部署CI/CD来说是革命性的。最后一个小技巧可以把常用的docker compose命令写成Makefile或Shell脚本别名进一步提升效率。
返回列表