Browse Source

fix: 把artisan serve (单进程) 改成 nginx + php-fpm

大侠咬超人 6 days ago
parent
commit
a74a257d76
6 changed files with 155 additions and 11 deletions
  1. 14 8
      Dockerfile
  2. 4 1
      app/Services/ExamPdfExportService.php
  3. 2 2
      docker-compose.yml
  4. 7 0
      docker-entrypoint.sh
  5. 89 0
      docker/nginx.conf
  6. 39 0
      docker/www.conf

+ 14 - 8
Dockerfile

@@ -26,9 +26,9 @@ COPY resources/ ./resources/
 RUN bun run build
 
 # ========================================
-# 第二阶段:PHP 运行时
+# 第二阶段:PHP 运行时(使用 php-fpm)
 # ========================================
-FROM php:8.3-alpine AS runtime
+FROM php:8.3-fpm-alpine AS runtime
 
 # 安装系统依赖 - 使用阿里云镜像
 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
@@ -44,6 +44,8 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
         icu-dev \
         libzip-dev \
         sqlite-dev \
+        # Nginx(高性能 Web 服务器)
+        nginx \
         # Chrome/Chromium 依赖(PDF 生成必须)
         chromium \
         nss \
@@ -64,7 +66,7 @@ RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
         npm \
         && rm -rf /var/cache/apk/* \
         && fc-cache -fv \
-        && mkdir -p /run/dbus
+        && mkdir -p /run/dbus /run/nginx /var/log/nginx
 
 # 设置 Chrome 环境变量
 ENV CHROME_BIN=/usr/bin/chromium-browser \
@@ -116,15 +118,19 @@ RUN chown -R www-data:www-data /app && \
     chmod -R 755 /app && \
     chmod -R 777 /app/storage /app/bootstrap/cache
 
+# 复制 Nginx 和 PHP-FPM 配置
+COPY docker/nginx.conf /etc/nginx/nginx.conf
+COPY docker/www.conf /usr/local/etc/php-fpm.d/www.conf
+
 EXPOSE 8000
 
-# 健康检查
-HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
-    CMD php artisan --version || exit 1
+# 健康检查(检查 Nginx 是否响应)
+HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
+    CMD curl -f http://127.0.0.1:8000/health || exit 1
 
-# 启动脚本:先启动 dbus,再运行主命令
+# 启动脚本:启动 dbus、php-fpm、nginx
 COPY docker-entrypoint.sh /usr/local/bin/
 RUN chmod +x /usr/local/bin/docker-entrypoint.sh
 
 ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
-CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
+CMD ["nginx", "-g", "daemon off;"]

+ 4 - 1
app/Services/ExamPdfExportService.php

@@ -845,7 +845,10 @@ class ExamPdfExportService
         ]);
 
         // 【修复】直接从MySQL数据库获取题目详情(不通过API)
-        $questionDetails = $this->getQuestionDetailsFromMySQL($paper);
+        // 只有当 $paper 是 Paper 模型时才查询题目详情
+        $questionDetails = ($paper instanceof \App\Models\Paper)
+            ? $this->getQuestionDetailsFromMySQL($paper)
+            : [];
 
         // 处理题目数据
         $questions = $this->processQuestionsForReport($paper, $questionDetails, $kpNameMap);

+ 2 - 2
docker-compose.yml

@@ -1,9 +1,9 @@
 services:
-  # 主应用(Web API 服务)
+  # 主应用(Web API 服务)- 使用 Nginx + PHP-FPM
   app:
     build: .
     container_name: math_cms_app
-    command: php artisan serve --host=0.0.0.0 --port=8000
+    # 使用 Dockerfile 中的默认 CMD(nginx + php-fpm)
     ports:
       - "5019:8000"
     env_file:

+ 7 - 0
docker-entrypoint.sh

@@ -8,5 +8,12 @@ if [ -x /usr/bin/dbus-daemon ]; then
     dbus-daemon --system --fork 2>/dev/null || true
 fi
 
+# 启动 PHP-FPM(后台运行)
+if [ "$1" = "nginx" ]; then
+    echo "Starting PHP-FPM..."
+    php-fpm -D
+    echo "PHP-FPM started"
+fi
+
 # 执行传入的命令
 exec "$@"

+ 89 - 0
docker/nginx.conf

@@ -0,0 +1,89 @@
+# 4核服务器:设置 4 个 worker(或 auto 自动检测)
+worker_processes auto;
+error_log /var/log/nginx/error.log warn;
+pid /var/run/nginx.pid;
+
+events {
+    # 每个 worker 的最大连接数
+    worker_connections 1024;
+    # 同时接受多个连接
+    multi_accept on;
+    # 使用 epoll(Linux 高性能事件模型)
+    use epoll;
+}
+
+http {
+    include /etc/nginx/mime.types;
+    default_type application/octet-stream;
+
+    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+                    '$status $body_bytes_sent "$http_referer" '
+                    '"$http_user_agent" "$http_x_forwarded_for"';
+
+    access_log /var/log/nginx/access.log main;
+
+    sendfile on;
+    tcp_nopush on;
+    tcp_nodelay on;
+    keepalive_timeout 65;
+    types_hash_max_size 2048;
+
+    # Gzip 压缩
+    gzip on;
+    gzip_vary on;
+    gzip_proxied any;
+    gzip_comp_level 6;
+    gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
+
+    server {
+        listen 8000;
+        server_name _;
+        root /app/public;
+        index index.php index.html;
+
+        # 请求体大小限制(上传文件)
+        client_max_body_size 100M;
+
+        # Laravel 路由
+        location / {
+            try_files $uri $uri/ /index.php?$query_string;
+        }
+
+        # PHP-FPM 处理
+        location ~ \.php$ {
+            fastcgi_pass 127.0.0.1:9000;
+            fastcgi_index index.php;
+            fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
+            include fastcgi_params;
+
+            # 超时设置
+            fastcgi_connect_timeout 60s;
+            fastcgi_send_timeout 300s;
+            fastcgi_read_timeout 300s;
+
+            # 缓冲区设置
+            fastcgi_buffer_size 128k;
+            fastcgi_buffers 4 256k;
+            fastcgi_busy_buffers_size 256k;
+        }
+
+        # 静态文件缓存
+        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+            expires 1y;
+            add_header Cache-Control "public, immutable";
+            access_log off;
+        }
+
+        # 禁止访问隐藏文件
+        location ~ /\. {
+            deny all;
+        }
+
+        # 健康检查
+        location /health {
+            access_log off;
+            return 200 "OK";
+            add_header Content-Type text/plain;
+        }
+    }
+}

+ 39 - 0
docker/www.conf

@@ -0,0 +1,39 @@
+[www]
+; Unix user/group of processes
+user = www-data
+group = www-data
+
+; The address on which to accept FastCGI requests
+listen = 127.0.0.1:9000
+
+; ===========================================
+; Process manager settings(4核8G服务器优化)
+; ===========================================
+pm = dynamic
+
+; 最大子进程数:每个进程约 50-80MB,留 4G 给 PHP
+; 4096MB / 60MB ≈ 68,但考虑其他容器,设 30
+pm.max_children = 30
+
+; 启动时的进程数(等于 CPU 核心数)
+pm.start_servers = 4
+
+; 最小空闲进程
+pm.min_spare_servers = 2
+
+; 最大空闲进程
+pm.max_spare_servers = 10
+
+; 每个进程处理 500 个请求后重启(防止内存泄漏)
+pm.max_requests = 500
+
+; 慢请求日志(超过 5 秒记录)
+request_slowlog_timeout = 5s
+slowlog = /var/log/php-fpm-slow.log
+
+; Logging
+catch_workers_output = yes
+decorate_workers_output = no
+
+; Security
+clear_env = no