飘易博客(作者:Flymorn)
订阅《飘易博客》RSS,第一时间查看最新文章!
飘易首页 | 留言本 | 关于我 | 订阅Feed

Docker容器配置LNMP Web服务器环境流程

Author:飘易 Source:飘易
Category:电脑技术 PostTime:2026/4/12 21:08:38 UpdateTime:2026/4/16
正 文:

Docker容器配置LNMP下WEB环境是目前推荐的方式,尽量避免那些LNMP一键包安装包之类,这些一键包之前或多或少出现过上下游产业链被黑被挂马的事故。建议大家尽量还是自己通过Docker安装自己的LNMP环境,避免无意中被挂马。

由于CentOS已经停止维护,飘易推荐使用 Rocky Linux 或 Alma Linux 来替换 Centos 系统,这2个新系统几乎完美兼容centos,你之前熟悉的yum等命令可以直接使用,学习迁移成本几乎为零。目前,Rocky社区的声量稍微比Alma高一些,但这2个系统随自己喜好任选即可。

另外,docker环境下也不建议部署LNMPA,直接部署 LNMP 架构接口。传统的LNMP架构容易出现502错误,但docker时代已经很少出现502了。

使用 Docker Compose 部署(内存一般需2G及以上)。

一、环境部署

第一步:环境准备(Rocky / Alma Linux)

这两款系统完全兼容,安装 Docker 的步骤是一致的:

# 1. 安装 Docker 引擎

yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin


# 2. 启动并设置开机自启

systemctl start docker
systemctl enable docker
或者二合一命令:
systemctl enable --now docker


通过 systemctl status docker 查看结果


第二步:创建项目目录结构

建议将所有网站数据统一管理:

mkdir -p /home/docker-web/npm       # 存放 Nginx Proxy Manager 数据
mkdir -p /home/docker-web/mysql     # 存放 MySQL 数据
mkdir -p /home/docker-web/wwwroot   # 存放多个网站的代码
mkdir -p /home/docker-web/php-conf  # 存放自定义php.ini-叠加方式
mkdir -p /home/docker-web/ftp       # 存放FTP
cd /home/docker-web


第三步:编写 docker-compose.yml 配置文件

/home/docker-web 目录下编写 docker-compose.yml

创建文件 vi docker-compose.yml,填入以下内容:

services:
  # 1. 数据库容器 (MySQL 5.7)
  db:
    image: mysql:5.7
    container_name: mysql_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 密码
    ports:
      - "3306:3306"
    volumes:
      - ./mysql:/var/lib/mysql

  # 2. PHP 容器 (PHP 7.4) 首次根据Dockerfile文件编译
  php74:
    build: 
      context: .
      dockerfile: Dockerfile
    container_name: php74_service
    restart: always
    volumes:
      - ./wwwroot:/www
      - ./php-conf/custom.ini:/usr/local/etc/php/conf.d/custom.ini # 叠加php.ini配置

  # 3. 反向代理与 SSL 管理器 (NPM) 已经包含了 Nginx
  nginx-proxy:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx_proxy
    restart: always
    ports:
      - '80:80'    # HTTP 端口
      - '443:443'  # HTTPS 端口
      - '60081:81'    # 管理后台端口
    volumes:
      - ./npm/data:/data
      - ./npm/letsencrypt:/etc/letsencrypt
      - ./wwwroot:/www  # 这里必须和 PHP 容器挂载同样的宿主机目录
    depends_on:
      - php74
      - db

  # 4.ftp
  ftp:
    image: stilliard/pure-ftpd
    container_name: ftp_service
    restart: always
    ports:
      - "60021:21"              # FTP 标准端口
      - "30000-30019:30000-30019" # 被动模式端口范围
    volumes:
      - ./wwwroot:/home/ftpusers  # 映射网页根目录
      - ./ftp/db:/etc/pure-ftpd/passwd # 存放 FTP 账号数据
    environment:
      PUBLICHOST: "公网IP"
      FTP_USER_NAME: "fadmin"      # 默认初始账号(可选)
      FTP_USER_PASS: "密码"   # 默认初始密码(可选)
      FTP_USER_HOME: "/home/ftpusers"
      # 指定用户的 UID 和 GID
      FTP_USER_UID: 82
      FTP_USER_GID: 82
      ADDED_FLAGS: -E # 仅允许已验证用户(即禁用匿名)

  # 5.phpmyadmin管理 --- 这里不推荐开启这个容器!!自带apache+php环境,内存占用过大!
  #phpmyadmin:
  #  image: phpmyadmin:latest
  #  container_name: phpmyadmin_service
  #  restart: always
  #  environment:
  #    - PMA_HOST=db     # 默认连接到你的 MySQL 容器
  #  ports:
  #    - "60082:80"

脚本里 YOUR_PASSWORD 改成你的密码,对应宿主机映射的端口号比如60081这些,随自己喜好,建议改成高位端口号,避免被特征扫描。

phpmyadmin 如果要使用,建议直接利用NPM 挂在一个目录上去,可以直接利用现有的nginx+php环境,避免phpmyadmin容器里重复的apache+php环境,太重,内存也会多占用100-200MB。参考:https://www.piaoyi.org/computer/Docker-Phpmyadmin-npm.html 


PHP容器需要注意下,你安装的程序可能依赖一些特定的库。比如Laravel依赖:

BCMath PHP Extension
Ctype PHP Extension
JSON PHP Extension
Mbstring PHP Extension
OpenSSL PHP Extension
PDO PHP Extension
Tokenizer PHP Extension
XML PHP Extension
还有常用的GD库

在 docker-compose.yml 同级目录下创建文件:Dockerfile,编写内容如下:

# 使用轻量的 Alpine 版本
FROM php:7.4-fpm-alpine

# 1. 安装 GD 库所需的系统依赖(解压、字体、图片格式支持)
RUN apk add --no-cache libpng-dev libjpeg-turbo-dev freetype-dev libwebp-dev  libxpm-dev

# 2. 配置 GD 库
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp

# 3. 安装 Laravel 必须的扩展
# pdo_mysql: 数据库连接
# bcmath: 高精度计算
# gd: 图像处理
# (Ctype, JSON, Mbstring, OpenSSL, Tokenizer, XML 在 7.4 官方镜像中通常已内置)
RUN docker-php-ext-install pdo_mysql mysqli bcmath gd

# Redis 缓存
#RUN printf "\n" | pecl install redis && docker-php-ext-enable redis

请注意看注释。


第四步:启动docker

启动docker服务:

docker compose up -d

如果php有编译扩展,首次需带编译参数:

docker compose up -d --build

注意:由于涉及编译,第一次执行 --build 会耗时 1-3 分钟,后续启动则是秒开。编译生成本地镜像后,后续不需要再带参数 build。

首次安装之后的提示类似:

 ✔ Image mysql:5.7                       Pulled 49.0s
 ✔ Image jc21/nginx-proxy-manager:latest Pulled 56.5s
 ✔ Image stilliard/pure-ftpd             Pulled 26.8s
 ✔ Image docker-web-php74                Built  57.1s
 ✔ Network docker-web_default            Created    0.0s
 ✔ Container php74_service               Started    2.7s
 ✔ Container ftp_service                 Started    3.2s
 ✔ Container mysql_db                    Started    2.7s
 ✔ Container nginx_proxy                 Started    1.0s

看到上面的信息,就代表成功了。

二、LNMP环境相关配置

1、NPM (Nginx Proxy Manager—— 图形化 Docker 网关

这是目前 Docker 社区流行的方案,它专门为一个目的而生:管理反向代理和 SSL。NPM 是一个带图形界面的 Docker 容器,它专门负责“反向代理”和“自动领证(Let's Encrypt)”。它能帮你自动处理 acme.sh 脚本、自动续签,且不影响 PHP 容器的纯净。

开设多个网站与配置 SSL

1. 登录管理后台

访问 http://服务器IP:60081。(注意,默认81,建议改成高位端口号)

首次登录会提醒设置管理员邮箱、密码,请务必记牢。


2. 开设第一个网站 (例如 www.site1.com)

点击 Hosts -> Proxy Hosts -> Add Proxy Host。


Details 选项卡:

Domain Names: 输入 www.site1.com
Forward Hostname / IP: 输入 php74_service (直接使用 Compose 里的服务名)
Forward Port: 输入 9000
勾选 Block Common Exploits。

说明:9000 是 PHP-FPM(FastCGI 进程管理器)的默认监听端口。镜像 php:7.4-fpm-alpine 在制作时,内部的配置文件(/usr/local/etc/php-fpm.d/www.conf)里就定义了:listen = 9000


Custom Locations 选项卡 :啥也不填。


SSL右侧小齿轮图标(Settings),填入以下 Nginx 配置:

# 第一种:默认标准php配置

fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /www/site1$fastcgi_script_name; # 注意这里的路径
fastcgi_param PHP_VALUE "open_basedir=/www/site1/:/tmp/"; # 限制 PHP 活动范围
include fastcgi_params;


# 第二种:laravel或lumen - 需root定义到public

# 显式定义 root
root /www/www.site1.com/public;
index index.html index.php;

# 强制让 Nginx 处理静态资源,不走 PHP
location ~* \.(jpg|jpeg|gif|png|css|js|ico|webp|tiff|gz|svg|svgz|zip|rar|7z|exe|mp3|mp4|ogg|ogv|woff|woff2|ttf)$ {
    access_log off;
    expires 30d;
    try_files $uri =404;
}

# 处理入口
location / {
    # 优雅的处理laravel,lumen路由规则
    try_files $uri $uri/ /index.php?$query_string;
}

# 核心 PHP 解析:不要包裹在额外的 location / 里面
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass php74_service:9000;
    fastcgi_index index.php;
    
    # 核心:确保这两行存在
    fastcgi_param SCRIPT_FILENAME /www/www.site1.com/public$fastcgi_script_name;
    fastcgi_param REQUEST_URI $request_uri;
    
    # 兼容 Lumen 路由,添加 PATH_INFO
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    # 建议加上这一行,增加隔离性
    fastcgi_param PHP_VALUE "open_basedir=/www/www.site1.com/:/tmp/";
    
    # 增加连接超时限制(防止接口执行耗时任务报错)
    fastcgi_read_timeout 300;
}


SSL 选项卡 (自动续签):

SSL Certificate: 选择 Request a new SSL Certificate。
勾选 Force SSL (强制跳转 HTTPS)。
勾选 I Agree to the Let's Encrypt Terms of Service。
点击 Save。

关于“自动续签”

您不需要进行任何操作。Nginx Proxy Manager (NPM) 内部集成了定时任务,它会每隔一段时间检查证书有效期。如果证书剩余时间不足 30 天,它会自动联系 Let's Encrypt 完成验证并替换新证书,整个过程对网站访问完全无感。


3. 开设第二个网站 (例如 www.site2.com)

重复上述步骤,只需在 Nginx 配置中,将 /www/site1 改为 /www/site2 即可。


注意:NPM创建一个网站后,并不会在自动创建对应的目录,需要自行创建目录,比如 /home/docker-web/wwwroot/site1


2、容器的UID、GID权限问题

如何确认 PHP 容器(尤其是 Alpine 版本)内部运行的用户及其 UID/GID?

直接查看容器内进程(最快)通过 ps 命令查看当前正在运行的 php-fpm 进程归属于哪个用户:

docker exec -it php74_service ps aux

输出示例:

PID   USER     TIME  COMMAND
    1 root      0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
    7 www-data  0:00 php-fpm: pool www
    8 www-data  0:00 php-fpm: pool www

Master process 通常显示为 root。

Pool www(工作进程)显示的用户(如 www-data)就是实际处理网页请求的用户。


查询特定用户的 UID/GID(最准确)如果你在上面的输出中看到了 www-data,可以用 id 命令查出它的具体数字:

docker exec -it php74_service id www-data

预期的返回结果(如果是 PHP Alpine 镜像):

uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)

这里的uid、gid分别是镜像里的用户、组的id。


为什么这个 82 很重要?

在 Rocky/Alma Linux 宿主机上,当你执行 ls -l 查看 ./wwwroot 下的文件时:

如果显示数字 82:说明宿主机系统不认识这个用户,但 PHP 容器内部能够正常读写。

权限冲突:如果你用宿主机的 root 创建了文件,权限通常是 0:0。此时 PHP 容器(UID 82)尝试写入日志或上传图片时,会报 Permission Denied。


运维建议:

如果你发现 PHP 的 UID 确实是 82,那么在宿主机上操作网页文件后,记得执行以下命令来保持权限一致:

# 在宿主机的 /home/docker-web 目录下执行

chown -R 82:82 wwwroot/

宿主机不能执行:chown -R www-data:www-data wwwroot/ (因为Rocky/Alma Linux 默认没有 www-data 用户)


3、FTP配置

在 Docker 架构下为每个网站配置 FTP,最安全且主流的做法是使用 pure-ftpd 镜像。由于 FTP 协议比较特殊(涉及主动/被动模式及端口范围),在 Docker 中部署需要注意端口映射。为了保持低学习成本和高安全性,建议将 FTP 作为一个独立的容器加入到你的 docker-compose.yml 中。


为每个网站开设 FTP 账号

该镜像允许你通过简单的命令在容器内创建虚拟用户,并限制该用户只能访问特定的子目录(例如 site1)。


开设 Site1 的 FTP 账号:

执行以下命令 pure-pw

docker exec -it ftp_service pure-pw useradd site1_user -f /etc/pure-ftpd/passwd/pureftpd.passwd -u 82 -g 82 -d /home/ftpusers/site1

site1_user: 你想设置的 FTP 账号名。

/home/ftpusers/site1: 该账号登录后看到的根目录(对应宿主机的 ./wwwroot/site1)。

-u 82 -g 82 是为了匹配 PHP-FPM (Alpine) 的权限。通常 pure-ftpd 默认使用 UID 1000,如果你的 PHP 容器(Alpine 版)使用 UID 82,你可以在创建 FTP 用户时指定 -u 82。

输入以上命令后,会提示你输入FTP用户的密码


生效配置 mkdb

每次添加或修改用户后,必须运行以下命令更新数据库:

docker exec -it ftp_service pure-pw mkdb

或者明确指定路径(pure-pw mkdb [目标pdb] -f [源passwd]):

docker exec -it ftp_service pure-pw mkdb /etc/pure-ftpd/passwd/pureftpd.pdb -f /etc/pure-ftpd/passwd/pureftpd.passwd


其他:如何修改已经存在的ftp用户的uid、gid

# 假设你的用户名是 fadmin

docker exec -it ftp_service pure-pw usermod fadmin -u 82 -g 82 -f /etc/pure-ftpd/passwd/pureftpd.passwd

docker-compose.yml把密码文件映射到了 /etc/pure-ftpd/passwd,但命令默认在 /etc/pure-ftpd/ 目录下寻找。需要显式地指定密码文件的路径。

提示:不能直接映射到/etc/pure-ftpd/,因为这个目录下还有ftp的conf等其他配置文件。直接映射,到被宿主机覆盖,导致ftp崩溃。

# 必须执行 mkdb 才能生效(同样需要指定文件路径)

docker exec -it ftp_service pure-pw mkdb /etc/pure-ftpd/passwd/pureftpd.pdb -f /etc/pure-ftpd/passwd/pureftpd.passwd

#验证是否成功

docker exec -it ftp_service pure-pw show fadmin -f /etc/pure-ftpd/passwd/pureftpd.passwd

在输出结果中查找 UID 和 GID 这一行,确认是否已经变成了 82。


4、PHP配置文件 php.ini

如何修改php.ini?可利用镜像自带的 conf.d 目录(最优雅)

PHP 镜像默认会读取 /usr/local/etc/php/conf.d/ 目录下的所有 .ini 文件。你不需要修改主文件,只需叠加你的配置。


在宿主机创建一个目录 php-conf/,并在其中新建 custom.ini:

; 必须设置为 0,防止 FPM 尝试猜测不存在的路径
cgi.fix_pathinfo=0

; 开发或频繁变动环境下建议关闭 opcache 的文件路径缓存
;opcache.enable=0

; 确保文件上传和执行内存足够,防止 pma 处理大数据时崩溃
memory_limit=256M
upload_max_filesize = 64M
post_max_size = 64M
date.timezone = Asia/Shanghai


在 docker-compose.yml 中挂载这个 custom.ini 文件(注意:不能挂载整个目录,否则你安装的一些扩展就会被覆盖禁用):

php74:
  volumes:
    - ./php-conf/custom.ini:/usr/local/etc/php/conf.d/custom.ini

这样做的好处是:主配置文件保持原汁原味,你的改动一目了然。

修改后重启docker:docker compose up -d


5. 防火墙配置

上面的这些容器涉及到的宿主机的端口号需要对外开放了。防火墙开启,以及云服务器(阿里云、腾讯云、亚马逊等)的防火墙也要开放的。

# 确保防火墙放行并永久保存  

firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --permanent --add-port=60081/tcp
firewall-cmd --permanent --add-port=60082/tcp
firewall-cmd --reload

由于 FTP 需要被动端口进行数据传输,你需要在 Rocky/Alma Linux 的防火墙中开放相关主动、被动2种端口:

firewall-cmd --permanent --add-port=60021/tcp
firewall-cmd --permanent --add-port=30000-30019/tcp
firewall-cmd --reload


6、安全加固

这种方案下,即使 PHP 容器被挂马,它也无法直接修改宿主机的 crond 或系统文件。

建议定期运行  来更新官方镜像补丁:

docker compose pull && docker compose up -d


7、运维习惯对比

代码位置:您的网页文件放在 /home/docker-web/wwwroot/site1 下即可。

数据库连接:代码中连接数据库的地址不再是 localhost 或 127.0.0.1,而是直接写 db(即 Compose 中的服务名)。


进阶小技巧:

如果你只是修改了 php.ini 的配置文件,而没有修改 Dockerfile,你甚至不需要 up -d,直接重启对应的容器即可:

docker compose restart php74 【推荐:操作 Compose 定义的服务】
或者
docker restart php74_service

这比重新构建要快得多,且不会中断其他容器(如 MySQL)的运行。


重启所有服务:

docker compose restart

查看运行状态:

docker compose ps

实时查看日志(排查启动错误):

docker compose logs -f --tail 20


本文结束。

作者:飘易
来源:飘易
版权所有。转载时必须以链接形式注明作者和原始出处及本声明。
上一篇:AI大模型之探测人脸姿态:左右、上下旋转,头部倾斜角度
下一篇:如何给阿里云OSS对象存储配置CDN加速
0条评论 “Docker容器配置LNMP Web服务器环境流程”
No Comment .
发表评论
名称(*必填)
邮件(选填)
网站(选填)

记住我,下次回复时不用重新输入个人信息
目 录
飘易搜索
最新文章
相关文章
随机文章
© 2007-2030 飘易博客 Www.Piaoyi.Org 版权所有 Sitemap