ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker Compose实战:从零搭建生产级LNMP环境并部署PHP应用

Docker Compose实战:从零搭建生产级LNMP环境并部署PHP应用 1. 项目概述与核心价值在Linux服务器上部署Web应用LNMPLinux, Nginx, MySQL, PHP是经典且高效的组合。传统方式需要手动安装、配置每个组件过程繁琐且容易出错尤其是在需要维护多个项目或不同PHP版本时依赖冲突和环境隔离问题更是让人头疼。Docker的出现彻底改变了这一局面。它通过容器化技术将应用及其所有依赖打包成一个标准化的单元实现了环境的一致性、隔离性和可移植性。今天我们就来手把手地用Docker在Linux上搭建一个生产可用的LNMP环境并完成一个PHP项目的部署。这不仅仅是“安装”更是一次现代化运维思维的实践。无论你是刚接触Docker的开发者还是希望优化现有部署流程的运维工程师这篇超详细的指南都将为你提供从零到一的完整路径和大量实战中积累的避坑经验。2. 环境整体设计与思路拆解2.1 为什么选择Docker Compose面对LNMP这样多服务的应用栈单纯使用docker run命令逐个启动容器会非常麻烦需要手动处理网络连接、数据卷挂载、启动顺序等问题。Docker Compose正是为解决多容器应用定义和运行而生的工具。它允许我们使用一个YAML格式的docker-compose.yml文件来配置所有服务、网络和卷。通过一条命令docker-compose up -d就能按照定义好的关系一键启动整个应用栈。这种“基础设施即代码”的方式使得我们的环境配置可以被版本化管理在任何一台安装了Docker和Compose的机器上都能快速、一致地重现。2.2 核心组件选型与版本考量在Docker的世界里每个组件都有官方镜像可供选择但版本和标签的选择至关重要。Nginx: 我们选择官方nginx:alpine镜像。alpine版本基于极简的Alpine Linux系统镜像体积非常小通常只有几十MB安全性也相对更高非常适合作为Web服务器。对于大多数Web应用稳定版如nginx:1.24-alpine足矣。MySQL: 选择官方mysql:8.0镜像。MySQL 8.0在性能、安全性和功能上相比5.7有显著提升是目前的主流选择。务必注意从MySQL 8开始默认的身份验证插件是caching_sha2_password一些老的客户端或PHP扩展可能不支持必要时可以在配置中改回mysql_native_password。PHP-FPM: 这是关键。我们需要一个集成了FPMFastCGI Process Manager和常用扩展的镜像。官方php:fpm镜像是一个很好的起点但它可能缺少你项目需要的特定扩展如gd,pdo_mysql,redis等。因此更常见的做法是使用基于官方镜像定制的社区镜像如php:8.2-fpm-alpine或者自己编写Dockerfile来构建包含所需扩展的镜像。本文将演示基于官方镜像的简单定制。注意版本对齐。确保你选择的PHP镜像版本如8.2与你项目代码所兼容的PHP版本一致。在生产环境中建议锁定具体的小版本号例如php:8.2.15-fpm-alpine以避免因镜像自动更新到新小版本而引入意外问题。2.3 目录结构规划清晰的目录结构是项目可维护性的基础。在开始之前我们先规划好工作目录。/opt/docker-lnmp/ # 项目根目录 ├── docker-compose.yml # Docker Compose 核心配置文件 ├── nginx/ │ ├── conf.d/ │ │ └── default.conf # Nginx 站点配置文件 │ └── ssl/ # 存放SSL证书可选 ├── mysql/ │ └── data/ # 挂载MySQL数据目录实现数据持久化 ├── php/ │ ├── Dockerfile # 用于构建自定义PHP-FPM镜像 │ └── php.ini # PHP自定义配置文件 └── wwwroot/ # 网站源代码目录 └── your_project/ # 你的具体项目目录这个结构将配置、数据、代码分离一目了然。docker-compose.yml是大脑其他目录是四肢和器官。3. 核心细节解析与实操要点3.1 Docker与Docker Compose安装与验证如果你的Linux系统尚未安装Docker以下是基于Ubuntu/Debian和CentOS/RHEL的快速安装步骤。请始终以root用户或具有sudo权限的用户操作。对于 Ubuntu/Debian:# 1. 更新软件包索引并安装依赖 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg # 2. 添加Docker官方GPG密钥 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg # 3. 设置稳定版仓库 echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 4. 安装Docker引擎 sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 5. 启动Docker并设置开机自启 sudo systemctl start docker sudo systemctl enable docker # 6. 验证安装 sudo docker run hello-world对于 CentOS/RHEL 7/8/9:# 1. 卸载旧版本如果存在 sudo yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # 2. 安装yum工具集并添加Docker仓库 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 3. 安装Docker引擎 sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 4. 启动Docker并设置开机自启 sudo systemctl start docker sudo systemctl enable docker # 5. 验证安装 sudo docker run hello-world安装独立版本的 Docker Compose (如果未安装插件版):上述命令已经安装了docker-compose-plugin你可以使用docker compose命令注意中间没有横线。如果你需要独立的docker-compose二进制文件可以执行# 下载最新稳定版请查看GitHub Release页面获取最新版本号 DOCKER_COMPOSE_VERSION$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep tag_name | cut -d\ -f4) sudo curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose sudo chmod x /usr/local/bin/docker-compose # 验证 docker-compose --version实操心得镜像加速。国内从Docker Hub拉取镜像速度可能很慢。务必配置国内镜像加速器。创建或修改/etc/docker/daemon.json加入以下内容以阿里云为例需注册后获取专属加速器地址{ registry-mirrors: [https://your-mirror.mirror.aliyuncs.com] }然后重启Docker服务sudo systemctl restart docker。这能为你节省大量等待时间。3.2 编写自定义PHP-FPM的Dockerfile官方php:fpm镜像很纯净但通常我们需要额外的扩展。在/opt/docker-lnmp/php/目录下创建Dockerfile# 使用官方PHP 8.2 FPM Alpine镜像作为基础 FROM php:8.2-fpm-alpine # 设置容器内时区可选但建议 RUN apk add --no-cache tzdata \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ echo Asia/Shanghai /etc/timezone # 安装系统依赖和常用的PHP扩展 # 注意docker-php-ext-install 是官方镜像提供的便捷脚本 RUN apk add --no-cache \ freetype-dev libjpeg-turbo-dev libpng-dev libwebp-dev \ libzip-dev zip unzip curl git supervisor \ # MySQL客户端库用于pdo_mysql和mysqli扩展 $PHPIZE_DEPS libxml2-dev oniguruma-dev openssl-dev # 编译安装GD库用于图像处理 RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ docker-php-ext-install gd # 安装其他核心PHP扩展 RUN docker-php-ext-install pdo_mysql mysqli mbstring exif pcntl bcmath zip sockets # 安装Redis扩展通过PECL RUN pecl install redis docker-php-ext-enable redis # 安装ComposerPHP包管理器 COPY --fromcomposer:latest /usr/bin/composer /usr/bin/composer # 清理缓存减小镜像体积 RUN apk del $PHPIZE_DEPS rm -rf /tmp/* /var/cache/apk/* # 设置工作目录 WORKDIR /var/www/html # 声明端口 EXPOSE 9000 # 使用PHP-FPM默认的启动命令 CMD [php-fpm]这个Dockerfile做了几件关键事1) 基于轻量Alpine系统2) 安装了项目常用的扩展gd, pdo_mysql, redis等3) 安装了Composer4) 设置了时区。你可以根据项目需求增删扩展。3.3 配置Nginx虚拟主机在/opt/docker-lnmp/nginx/conf.d/目录下创建default.conf这是Nginx的核心配置文件server { listen 80; # 如果你的域名已解析可以写域名本地测试可以用服务器IP或localhost server_name localhost your_domain.com; # 网站根目录此路径是容器内的路径会通过卷映射到宿主机的wwwroot root /var/www/html/your_project/public; # 注意这里指向了项目下的public目录这是Laravel等框架的常见入口 index index.php index.html index.htm; # 字符集 charset utf-8; # 日志配置 access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log warn; # 静态文件处理 location / { try_files $uri $uri/ /index.php?$query_string; } # 禁止访问隐藏文件如 .git, .env location ~ /\. { deny all; } # 将PHP请求转发给PHP-FPM容器处理 location ~ \.php$ { # 关键php-fpm是我们在docker-compose.yml中定义的PHP服务名容器间通过服务名通信 fastcgi_pass php-fpm:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # 一些安全设置 fastcgi_param HTTP_PROXY ; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; # 如果PHP脚本执行超时可以调整以下参数 fastcgi_read_timeout 300; fastcgi_send_timeout 300; } # 缓存静态资源 location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control public, immutable; log_not_found off; access_log off; } }这个配置是一个通用的、适用于现代PHP框架如Laravel、ThinkPHP的模板。关键点在于fastcgi_pass php-fpm:9000;它告诉Nginx将PHP文件交给名为php-fpm的容器处理。php-fpm这个主机名由Docker Compose在创建的网络中自动解析。4. 实操过程与核心环节实现4.1 编写Docker Compose核心配置文件现在让我们在项目根目录/opt/docker-lnmp/创建核心的docker-compose.yml文件。version: 3.8 # 定义服务容器 services: # Nginx 服务 nginx: image: nginx:alpine container_name: lnmp_nginx restart: unless-stopped ports: - 80:80 # 将宿主机的80端口映射到容器的80端口 - 443:443 # 如果需要HTTPS映射443端口 volumes: # 挂载网站源代码目录 - ./wwwroot:/var/www/html # 挂载自定义的Nginx配置覆盖容器内的默认配置 - ./nginx/conf.d:/etc/nginx/conf.d # 挂载SSL证书目录可选 - ./nginx/ssl:/etc/nginx/ssl # 挂载日志目录方便查看 - ./logs/nginx:/var/log/nginx depends_on: - php-fpm networks: - lnmp-network # MySQL 服务 mysql: image: mysql:8.0 container_name: lnmp_mysql restart: unless-stopped environment: # 强烈建议修改这些环境变量这是设置root密码和创建默认数据库的方式。 MYSQL_ROOT_PASSWORD: YourStrongRootPassword123! # 请务必修改为强密码 MYSQL_DATABASE: my_app_db # 容器启动时创建的数据库 MYSQL_USER: my_app_user # 创建的用户名 MYSQL_PASSWORD: YourStrongUserPassword456! # 对应用户的密码 volumes: # 持久化MySQL数据防止容器删除后数据丢失 - ./mysql/data:/var/lib/mysql # 可以挂载自定义配置文件如果需要 # - ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf ports: # 将宿主机的3306端口映射出去方便用宿主机上的客户端如Navicat连接 - 3306:3306 networks: - lnmp-network # 健康检查确保MySQL完全启动后再启动依赖它的服务虽然depends_on不等待就绪但这是好习惯 healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -u, root, -p$$MYSQL_ROOT_PASSWORD] interval: 10s timeout: 5s retries: 5 # PHP-FPM 服务使用自定义镜像 php-fpm: build: ./php # 指向包含Dockerfile的目录将根据Dockerfile构建镜像 container_name: lnmp_php-fpm restart: unless-stopped volumes: # 与Nginx共享网站源代码 - ./wwwroot:/var/www/html # 挂载自定义的php.ini配置文件 - ./php/php.ini:/usr/local/etc/php/conf.d/custom.ini # 挂载Composer缓存目录加速后续安装 - composer-cache:/root/.composer/cache depends_on: mysql: condition: service_healthy # 等待MySQL健康检查通过 networks: - lnmp-network # 可以在此定义健康检查但PHP-FPM通常不需要 # 定义网络让三个服务在同一个自定义网络内可以通过服务名互相访问 networks: lnmp-network: driver: bridge # 定义数据卷用于持久化数据 volumes: # MySQL数据卷已在服务中定义这里定义的是命名卷 composer-cache: # 用于Composer缓存的命名卷即使容器删除缓存仍在这个配置文件定义了三个服务并通过networks让它们处于同一网络。volumes实现了宿主机目录与容器目录的映射这是数据持久化和配置管理的核心。depends_on控制了启动顺序但请注意它只控制“启动”顺序不保证服务“就绪”。我们通过MySQL的healthcheck来部分解决这个问题。4.2 准备项目代码与自定义配置放置项目代码将你的PHP项目例如一个Laravel项目放入/opt/docker-lnmp/wwwroot/your_project/目录下。确保入口文件如index.php位于正确的路径例如public/目录下。配置PHP可选在/opt/docker-lnmp/php/目录下创建php.ini文件用于覆盖PHP的默认配置。例如调整上传文件大小限制和内存限制upload_max_filesize 100M post_max_size 100M memory_limit 256M date.timezone Asia/Shanghai配置MySQL可选如果需要调整MySQL配置可以在./mysql/目录下创建my.cnf文件并在docker-compose.yml中取消对应行的注释并挂载。4.3 启动与验证LNMP环境一切就绪后进入项目根目录启动服务cd /opt/docker-lnmp # 构建自定义PHP镜像并启动所有服务-d 表示后台运行 docker-compose up -d --build--build参数会强制重建PHP-FPM服务的镜像。首次运行或修改Dockerfile后需要此参数。后续启动只需docker-compose up -d。使用以下命令查看服务状态和日志# 查看所有容器状态 docker-compose ps # 查看Nginx日志实时 docker-compose logs -f nginx # 查看PHP-FPM日志 docker-compose logs -f php-fpm # 查看MySQL日志 docker-compose logs -f mysql如果看到所有容器状态都是Up并且没有明显的错误日志说明环境启动成功。验证服务连通性验证Nginx在浏览器访问你的服务器IP或配置的域名如http://your-server-ip。如果看到Nginx欢迎页或你的项目页面说明Nginx运行正常。验证PHP在wwwroot/your_project/目录下创建一个info.php文件内容为?php phpinfo(); ?。然后在浏览器访问http://your-server-ip/info.php。你应该能看到详细的PHP配置信息页面其中包含已安装的扩展如gd, pdo_mysql, redis。验证后请务必删除此文件以免泄露服务器信息。验证MySQL从宿主机或同一网络内的其他容器连接MySQL。# 在宿主机上连接因为映射了3306端口 mysql -h 127.0.0.1 -P 3306 -u my_app_user -p # 输入密码后应能进入MySQL命令行。 # 或者从PHP-FPM容器内部连接服务名mysql docker-compose exec php-fpm bash # 进入容器后 mysql -h mysql -u my_app_user -p4.4 项目初始化与配置假设你部署的是一个Laravel项目还需要进行一些初始化操作# 1. 进入PHP-FPM容器 docker-compose exec php-fpm bash # 2. 进入项目目录因为工作目录已设置为/var/www/html而项目在your_project子目录 cd your_project # 3. 安装Composer依赖如果vendor目录不存在 composer install --no-dev --optimize-autoloader # 4. 复制环境配置文件并编辑根据你的数据库配置修改 .env 文件 cp .env.example .env # 使用vim或cat编辑.env文件关键配置如下 # DB_CONNECTIONmysql # DB_HOSTmysql # 注意这里填服务名mysql不是localhost或127.0.0.1 # DB_PORT3306 # DB_DATABASEmy_app_db # DB_USERNAMEmy_app_user # DB_PASSWORDYourStrongUserPassword456! # 5. 生成应用密钥 php artisan key:generate # 6. 运行数据库迁移如果需要 php artisan migrate --force # 7. 设置存储目录权限Laravel要求 chmod -R 775 storage bootstrap/cache # 8. 退出容器 exit现在再次访问你的网站应该能看到正常运行的应用程序了。5. 常见问题与排查技巧实录在搭建和运行过程中你几乎一定会遇到一些问题。以下是我踩过坑后总结的排查清单。5.1 容器启动失败或不断重启问题现象docker-compose ps显示容器状态为Restarting或Exited。排查步骤查看详细日志docker-compose logs [service_name]。这是最直接的错误信息来源。常见错误包括端口冲突Bind for 0.0.0.0:80 failed: port is already allocated。说明宿主机80端口已被占用可能是已有的Nginx或Apache。解决修改docker-compose.yml中ports映射的宿主机端口如8080:80或停止占用端口的服务。权限问题Permission denied。挂载的宿主机目录权限不足容器内用户如www-data无法读写。解决确保宿主机目录对容器用户可读可写。一个快速但不建议用于生产的方法是sudo chmod -R 777 /opt/docker-lnmp/wwwroot。生产环境应仔细配置用户和组权限。镜像拉取失败网络问题导致。检查Docker镜像加速器配置。配置文件语法错误Nginx或docker-compose.yml文件格式错误。使用nginx -t命令测试Nginx配置需进入容器或使用docker-compose config检查Compose文件。5.2 Nginx显示“502 Bad Gateway”或“File not found”问题现象访问PHP页面出现502错误或直接显示PHP源代码。排查步骤检查PHP-FPM容器是否运行docker-compose ps确认php-fpm服务状态为Up。检查Nginx与PHP-FPM的连接在Nginx配置中fastcgi_pass指令的值必须是PHP-FPM容器的服务名和内部端口默认9000。确保docker-compose.yml中PHP-FPM的服务名是php-fpm且Nginx配置中写的是php-fpm:9000。检查文件路径“File not found”通常是因为SCRIPT_FILENAME参数指向的路径在容器内不存在。确认Nginx配置中的root指令路径正确。fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;这行存在且正确。在PHP-FPM容器内使用ls -la检查/var/www/html/your_project/public/index.php文件是否存在。检查PHP-FPM监听进入PHP-FPM容器查看进程和监听端口。docker-compose exec php-fpm bash netstat -tlnp | grep 9000 # 应该看到 php-fpm 进程在监听 9000 端口。 ps aux | grep php-fpm5.3 项目无法连接MySQL数据库问题现象项目日志或页面显示数据库连接错误如“SQLSTATE[HY000] [2002] Connection refused”。排查步骤确认MySQL容器健康docker-compose logs mysql查看有无启动错误。确保健康检查通过。确认连接参数在项目配置如Laravel的.env文件中DB_HOST必须填写服务名mysql而不是localhost或127.0.0.1。因为从PHP容器内部看localhost指的是自己而mysql这个主机名会被Docker的网络DNS解析到MySQL容器的IP。检查用户权限使用创建的用户如my_app_user从PHP容器内部手动连接测试。docker-compose exec php-fpm bash mysql -h mysql -u my_app_user -p如果连接失败可能是用户密码错误或权限不足。可以进入MySQL容器用root用户登录检查。docker-compose exec mysql mysql -u root -p # 输入root密码后 SHOW DATABASES; USE my_app_db; SHOW GRANTS FOR my_app_user%;MySQL 8.0身份验证插件问题如果错误信息包含caching_sha2_password可能是客户端不支持。可以在创建用户时指定旧插件或在docker-compose.yml的MySQL服务环境变量中添加MYSQL_DEFAULT_AUTHENTICATION_PLUGINmysql_native_password。5.4 性能优化与生产环境考量当前配置适合开发和测试。上生产环境前还需考虑以下几点资源限制在docker-compose.yml中为每个服务设置CPU和内存限制防止单个容器耗尽主机资源。services: mysql: deploy: resources: limits: cpus: 1.0 memory: 1G reservations: memory: 512M使用独立数据库对于重要项目考虑使用云数据库如RDS或独立部署的MySQL实例而非容器内数据库以获得更好的稳定性、性能和备份支持。配置SSL/TLS在nginx/conf.d/中配置HTTPS并挂载SSL证书到容器。可以使用Let‘s Encrypt免费证书。日志管理将容器日志通过driver配置发送到集中式日志系统如ELK而不是仅仅存储在宿主机目录。备份策略定期备份挂载的MySQL数据目录./mysql/data。可以使用cron任务执行docker-compose exec mysql mysqldump命令进行逻辑备份。使用.env文件管理敏感信息不要在docker-compose.yml中硬编码密码。创建一个.env文件在项目根目录内容如MYSQL_ROOT_PASSWORDYourStrongRootPassword123! MYSQL_PASSWORDYourStrongUserPassword456!然后在docker-compose.yml中使用变量MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}。并确保.env文件不被提交到版本库。5.5 日常运维命令速查表场景命令说明启动/停止/重启docker-compose up -d构建镜像如果需要并启动所有服务后台docker-compose stop停止所有服务不删除容器docker-compose start启动已停止的服务docker-compose restart [service]重启指定服务或所有服务docker-compose down停止并删除所有容器、网络默认不删除卷docker-compose down -v停止并删除所有容器、网络、卷数据会丢失查看状态与日志docker-compose ps查看服务状态docker-compose logs [service]查看服务日志docker-compose logs -f [service]实时跟踪日志进入容器docker-compose exec [service] bash进入指定服务的容器如php-fpm更新配置修改docker-compose.yml或服务配置后docker-compose up -d --build [service]重建并启动特定服务清理无用镜像docker image prune -a删除所有未被容器使用的镜像谨慎操作最后关于镜像更新如果只是修改了项目代码只需替换wwwroot目录下的文件然后重启Nginx和PHP-FPM服务即可docker-compose restart nginx php-fpm。如果修改了Dockerfile或docker-compose.yml中镜像相关的配置则需要重新构建docker-compose up -d --build。这套基于Docker的LNMP环境将部署从“手工艺术”变成了“标准化工程”一次搭建处处运行希望你也能享受到容器化带来的便利与高效。
返回列表