前言
在传统部署方式中,搭建一套 LNMP(Linux + Nginx + MySQL + PHP)环境往往需要在服务器上逐一安装、配置各个组件,过程繁琐且难以在不同机器之间保持一致。Docker Compose 提供了一种声明式的解决方案:用一个 YAML 文件描述整套环境,一条命令即可完成启动、停止和重建。本文将以一个可直接落地的示例,演示如何用 Docker Compose 一键部署 LNMP 环境。
一、环境准备
开始之前,请确保服务器已安装 Docker 和 Docker Compose(或使用 Docker Desktop 自带的 compose 插件)。可以用以下命令验证安装情况:
docker --version
docker compose version
建议 Docker 版本在 20.10 以上,Compose 使用 v2 插件形式(命令为 docker compose,而非老的 docker-compose)。
二、项目目录结构
按照惯例,我们将配置文件、日志、站点代码分目录存放,结构清晰也方便后续维护:
lnmp/
├── docker-compose.yml # 编排文件
├── nginx/
│ ├── conf.d/
│ │ └── default.conf # 站点配置
│ └── logs/ # Nginx 日志
├── php/
│ ├── Dockerfile # 自定义 PHP 镜像
│ └── php.ini # PHP 配置
├── mysql/
│ ├── data/ # 数据库数据目录
│ └── conf/my.cnf # MySQL 配置
└── www/ # 网站根目录
└── index.php
三、编写 docker-compose.yml
这是整个方案的核心。我们定义三个服务:nginx、php、mysql,并让它们处于同一个自定义网络中互相通信。
version: "3.8"
services:
nginx:
image: nginx:1.25-alpine
container_name: lnmp-nginx
ports:
- "80:80"
volumes:
- ./www:/var/www/html
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/logs:/var/log/nginx
depends_on:
- php
networks:
- lnmp-net
restart: always
php:
build: ./php
container_name: lnmp-php
volumes:
- ./www:/var/www/html
- ./php/php.ini:/usr/local/etc/php/php.ini
networks:
- lnmp-net
restart: always
mysql:
image: mysql:8.0
container_name: lnmp-mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: your_root_password
MYSQL_DATABASE: app_db
MYSQL_USER: app_user
MYSQL_PASSWORD: your_app_password
TZ: Asia/Shanghai
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf
command: --default-authentication-plugin=mysql_native_password
networks:
- lnmp-net
restart: always
networks:
lnmp-net:
driver: bridge
几个关键点说明:
1. 卷挂载保持代码同步。./www 同时挂载给 Nginx 和 PHP 容器,这样修改本地代码后刷新页面立即生效,无需重新构建镜像。
2. 容器间通过服务名通信。在 PHP 代码中连接数据库时,主机名应写 mysql(即服务名),而不是 127.0.0.1,因为各容器有各自独立的网络栈。
3. depends_on 只保证启动顺序。它不保证 MySQL 已"就绪",应用侧应做好连接重试,或使用 healthcheck 配合 depends_on.condition。
四、自定义 PHP 镜像
官方 php:fpm 镜像缺少许多常用扩展(如 pdo_mysql、redis、gd),我们通过 Dockerfile 安装:
FROM php:8.2-fpm
# 换用国内镜像源加速(按需)
RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources
# 安装系统依赖
RUN apt-get update && apt-get install -y \
libpng-dev libjpeg-dev libfreetype6-dev libzip-dev zip unzip \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install gd pdo_mysql mysqli bcmath zip opcache \
&& rm -rf /var/lib/apt/lists/*
# 安装 Redis 扩展
RUN pecl install redis && docker-php-ext-enable redis
# 安装 Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
配套一个最小化的 php.ini:
[PHP]
memory_limit = 256M
upload_max_filesize = 50M
post_max_size = 50M
max_execution_time = 120
timezone = Asia/Shanghai
[opcache]
opcache.enable = 1
opcache.memory_consumption = 128
五、Nginx 站点配置
nginx/conf.d/default.conf 中将 PHP 请求通过 FastCGI 转发给 php 服务。注意 fastcgi_pass 使用服务名 php:9000:
server {
listen 80;
server_name localhost;
root /var/www/html;
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;
}
location ~ /\.ht {
deny all;
}
}
一个常见坑:由于 ./www 分别挂载到两个容器,Nginx 容器内看到的路径必须与 SCRIPT_FILENAME 传递给 PHP 容器的路径一致,否则会报 File not found。上面的配置中两边都挂载到了 /var/www/html,天然一致。
六、启动与验证
在项目根目录执行:
docker compose up -d --build
首次运行会构建 PHP 镜像,需要几分钟。完成后验证各容器状态:
docker compose ps
docker compose logs -f php
在 www/index.php 中放一个测试脚本,验证 PHP 与 MySQL 的连通性:
<?php
try {
$pdo = new PDO('mysql:host=mysql;dbname=app_db;charset=utf8mb4', 'app_user', 'your_app_password');
echo 'LNMP 环境部署成功!MySQL 版本:' . $pdo->query('SELECT VERSION()')->fetchColumn();
} catch (PDOException $e) {
echo '连接失败:' . $e->getMessage();
}
浏览器访问 http://服务器IP/,看到版本号输出即代表整套链路(Nginx → PHP-FPM → MySQL)全部打通。
七、日常运维命令
# 停止所有服务
docker compose down
# 修改配置后重启单个服务
docker compose restart php
# 查看 MySQL 数据(进入容器)
docker compose exec mysql mysql -uapp_user -p app_db
# 备份数据库(导出到宿主机当前目录)
docker compose exec mysql mysqldump -uroot -pyour_root_password app_db > backup.sql
八、几点生产环境建议
这套配置适合开发和小型项目,若要上生产,还需要补充一些措施:数据库密码不要明文写在 compose 文件里,可使用 .env 文件或 secrets 管理;MySQL 的 3306 端口不必对外暴露,可以删掉 ports 映射仅保留内部通信;为 nginx 配置 HTTPS 证书(可用 certbot 容器自动续期);给每个服务加上 healthcheck,并配置日志轮转(logging.options.max-size)避免磁盘被日志占满。
结语
用 Docker Compose 部署 LNMP 的最大价值在于"环境即代码":整套环境由一个 YAML 和几个配置文件完整描述,换一台服务器只需克隆目录、执行 docker compose up -d 即可复现。配合 Git 管理,团队协作时也不会再出现"我本地是好的"这类问题。建议大家从这个最小可用版本出发,按需逐步添加 Redis、队列、定时任务等服务。

发表评论 取消回复