前言

在传统部署方式中,搭建一套 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、队列、定时任务等服务。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部