0

Docker LNMP多站点配置教程:docker-compose编排多域名网站的完整实战

2026.05.21 | youres | 16次围观


一台服务器同时托管多个网站,是每个开发者进阶路上都会遇到的需求。传统方式需要在系统层面安装多个LNMP环境,配置复杂且难以隔离。使用Docker配合docker-compose,可以让每个站点拥有独立的运行环境,互不干扰,维护成本大大降低。本文详解如何用docker-compose编排LNMP多站点,包括Nginx多域名配置、数据持久化、以及实战中的常见坑。

为什么要用Docker跑LNMP多站点

直接装LNMP当然能用,但问题在于:

  • 多个PHP版本难以共存(老项目用PHP 7.x,新项目用PHP 8.x)
  • Nginx配置耦合在系统里,改坏一个影响全部
  • MySQL版本升级可能造成兼容性问题
  • 换服务器要重装整个环境,费时费力

用Docker做多站点隔离,每个「站点容器组」独立运行。升级PHP只影响对应容器,不影响其他站点。真正做到「删库跑路」也不慌——容器一删,环境干净。

整体架构设计

多站点LNMP的容器结构有两种主流思路:

方案一:共享MySQL,各自独立Nginx+PHP

所有站点共用一个MySQL容器,每个站点有自己独立的Nginx+PHP-FPM容器。这种方式适合业务数据需要集中管理的场景。

方案二:完全独立,每个站点一套LNMP

每个站点有完整的Nginx+PHP+MySQL组合,完全隔离,互不影响。适合站点之间需要强隔离的情况。

本文以方案一为主,因为更贴近实际使用场景——大多数情况下多个站点共享一个数据库实例是合理的选择。

整体目录结构如下:

~/lnmp-multi/
├── docker-compose.yml
├── nginx/
│   ├── conf.d/
│   │   ├── site1.conf
│   │   └── site2.conf
│   └── vhost/
│       ├── site1/
│       └── site2/
└── mysql/
    └── data/

docker-compose.yml完整配置

version: "3.8"

services:
  mysql:
    image: mysql:8.0
    container_name: lnmp-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: your_strong_password
      MYSQL_DATABASE: app_db
    volumes:
      - ./mysql/data:/var/lib/mysql
    networks:
      - lnmp-net
    command: --default-authentication-plugin=mysql_native_password

  site1-nginx:
    image: nginx:alpine
    container_name: site1-nginx
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx/vhost/site1:/usr/share/nginx/html
      - ./nginx/conf.d/site1.conf:/etc/nginx/conf.d/site1.conf
    depends_on:
      - site1-php
    networks:
      - lnmp-net

  site1-php:
    image: php:8.2-fpm-alpine
    container_name: site1-php
    restart: always
    volumes:
      - ./nginx/vhost/site1:/var/www/html
    networks:
      - lnmp-net
    depends_on:
      - mysql

  site2-nginx:
    image: nginx:alpine
    container_name: site2-nginx
    restart: always
    ports:
      - "8080:80"
    volumes:
      - ./nginx/vhost/site2:/usr/share/nginx/html
      - ./nginx/conf.d/site2.conf:/etc/nginx/conf.d/site2.conf
    depends_on:
      - site2-php
    networks:
      - lnmp-net

  site2-php:
    image: php:8.1-fpm-alpine
    container_name: site2-php
    restart: always
    volumes:
      - ./nginx/vhost/site2:/var/www/html
    networks:
      - lnmp-net
    depends_on:
      - mysql

networks:
  lnmp-net:
    driver: bridge

Nginx多域名配置详解

站点1配置(site1.conf)

server {
    listen 80;
    server_name www.site1.com site1.com;
    root /usr/share/nginx/html;
    index index.php index.html;

    access_log /var/log/nginx/site1_access.log;
    error_log /var/log/nginx/site1_error.log;

    location / {
        try_files `$uri `/index.php?`$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass site1-php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /var/www/html/`$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\. {
        deny all;
    }
}

站点2配置(site2.conf)

server {
    listen 80;
    server_name www.site2.com site2.com;
    root /usr/share/nginx/html;
    index index.php index.html;

    access_log /var/log/nginx/site2_access.log;
    error_log /var/log/nginx/site2_error.log;

    location / {
        try_files `$uri `/index.php?`$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass site2-php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /var/www/html/`$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\. {
        deny all;
    }
}

两个配置的核心区别是 fastcgi_pass 指向不同的 PHP-FPM 容器:site1-php 和 site2-php。这是最容易出错的地方,指向错误会导致502错误。

启动与验证

# 创建目录
mkdir -p ~/lnmp-multi/nginx/{conf.d,vhost/{site1,site2}} ~/lnmp-multi/mysql/data

# 写入测试文件到site1
echo "<?php echo "Site 1 - PHP " . phpversion(); ?>" > ~/lnmp-multi/nginx/vhost/site1/index.php

# 写入测试文件到site2
echo "<?php echo "Site 2 - PHP " . phpversion(); ?>" > ~/lnmp-multi/nginx/vhost/site2/index.php

# 启动所有容器
cd ~/lnmp-multi && docker-compose up -d

# 查看运行状态
docker-compose ps

启动后访问对应端口,看到PHP版本信息输出即为成功。site1显示 PHP 8.2.x,site2显示 PHP 8.1.x,说明两个站点已完全隔离。

数据持久化的关键点

  • MySQL数据目录:必须映射到宿主机本地目录(./mysql/data),否则容器删除后数据全部丢失
  • 站点文件目录:同样要映射到宿主机(./nginx/vhost/site1),方便直接编辑代码
  • Nginx配置文件:每次修改conf后reload Nginx容器即可生效:docker-compose exec site1-nginx nginx -s reload
  • 不要用匿名卷:docker-compose中不要省略volumes的主机路径映射

常用维护命令

# 重启所有容器
docker-compose restart

# 查看日志(跟随输出)
docker-compose logs -f site1-php

# 进入PHP容器排查问题
docker-compose exec site1-php sh

# 进入MySQL容器
docker-compose exec mysql mysql -uroot -p

# 完整停机(保留数据)
docker-compose down

# 清理数据重新开始
docker-compose down -v

常见问题排查

502 Bad Gateway:最常见的问题是PHP-FPM容器名称拼写错误,或PHP容器还没完全启动就开始处理请求。加一段健康检查即可:

site1-php:
  image: php:8.2-fpm-alpine
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost"]
    interval: 10s
    timeout: 5s
    retries: 3

MySQL连接被拒绝:PHP连接MySQL时host填写的是容器名mysql,不是localhost,也不是127.0.0.1。

文件权限问题:Docker容器以www-data用户运行,如果挂载的目录权限不对,会报403错误。先执行:chmod -R 755 ./nginx/vhost/

总结

Docker+docker-compose做LNMP多站点,本质是把每个站点的运行环境封装成容器,通过网络桥接实现互通。核心要点就三个:Nginx根据server_name区分不同域名、PHP-FPM通过容器名找到对应服务、数据通过volumes持久化到宿主机。

这套方案的可维护性远高于传统LNMP部署。一台2核4G的服务器,跑5-10个中小型站点绰绰有余。需要迁移服务器时,只需把整个目录scp过去,新机器装好Docker,一行命令全量恢复。

相关阅读:

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论
883文章数 0评论数
作者其它文章