Spaces:
Configuration error
Configuration error
| # 优化版多阶段构建Dockerfile | |
| # 这个版本针对生产环境进行了优化,镜像更小,安全性更高 | |
| # ================================ | |
| # 阶段1: 前端构建 | |
| # ================================ | |
| FROM node:18-alpine as frontend-builder | |
| # 设置工作目录 | |
| WORKDIR /app | |
| # 只复制package文件,利用Docker缓存 | |
| COPY client/package*.json ./ | |
| # 安装依赖 | |
| RUN npm ci --only=production && \ | |
| npm cache clean --force | |
| # 复制源代码 | |
| COPY client/ ./ | |
| # 构建前端 | |
| RUN npm run build | |
| # ================================ | |
| # 阶段2: 后端构建 | |
| # ================================ | |
| FROM node:18-alpine as backend-builder | |
| WORKDIR /app | |
| # 复制package文件 | |
| COPY server/package*.json ./ | |
| # 安装依赖 | |
| RUN npm ci --only=production && \ | |
| npm cache clean --force | |
| # 复制源代码 | |
| COPY server/ ./ | |
| # ================================ | |
| # 阶段3: 最终生产镜像 | |
| # ================================ | |
| FROM node:18-alpine | |
| # 安装运行时依赖 | |
| RUN apk add --no-cache \ | |
| nginx \ | |
| supervisor \ | |
| wget \ | |
| curl \ | |
| dumb-init \ | |
| && rm -rf /var/cache/apk/* | |
| # 创建应用用户 | |
| RUN addgroup -g 1001 -S appuser && \ | |
| adduser -S appuser -u 1001 -G appuser | |
| # 创建必要的目录 | |
| RUN mkdir -p /app /var/log/nginx /var/log/supervisor /run/nginx && \ | |
| chown -R appuser:appuser /app /var/log/nginx /var/log/supervisor /run/nginx | |
| # 设置工作目录 | |
| WORKDIR /app | |
| # 复制后端文件 | |
| COPY --from=backend-builder --chown=appuser:appuser /app ./ | |
| # 复制前端构建文件 | |
| COPY --from=frontend-builder --chown=appuser:appuser /app/dist ./public | |
| # 创建nginx配置 | |
| COPY --chown=appuser:appuser <<EOF /etc/nginx/nginx.conf | |
| user appuser; | |
| worker_processes auto; | |
| pid /run/nginx/nginx.pid; | |
| events { | |
| worker_connections 1024; | |
| use epoll; | |
| multi_accept on; | |
| } | |
| 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; | |
| error_log /var/log/nginx/error.log warn; | |
| # 性能优化 | |
| sendfile on; | |
| tcp_nopush on; | |
| tcp_nodelay on; | |
| keepalive_timeout 65; | |
| types_hash_max_size 2048; | |
| # Gzip压缩 | |
| gzip on; | |
| gzip_vary on; | |
| gzip_min_length 1024; | |
| gzip_comp_level 6; | |
| gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; | |
| server { | |
| listen 80; | |
| server_name localhost; | |
| root /app/public; | |
| index index.html; | |
| # 前端路由 | |
| location / { | |
| try_files \$uri \$uri/ /index.html; | |
| } | |
| # API代理 | |
| location /api/ { | |
| proxy_pass http://127.0.0.1:5000; | |
| proxy_http_version 1.1; | |
| proxy_set_header Upgrade \$http_upgrade; | |
| proxy_set_header Connection 'upgrade'; | |
| proxy_set_header Host \$host; | |
| proxy_set_header X-Real-IP \$remote_addr; | |
| proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; | |
| proxy_set_header X-Forwarded-Proto \$scheme; | |
| proxy_cache_bypass \$http_upgrade; | |
| } | |
| # Socket.IO代理 | |
| location /socket.io/ { | |
| proxy_pass http://127.0.0.1:5000; | |
| proxy_http_version 1.1; | |
| proxy_set_header Upgrade \$http_upgrade; | |
| proxy_set_header Connection "upgrade"; | |
| proxy_set_header Host \$host; | |
| proxy_set_header X-Real-IP \$remote_addr; | |
| proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; | |
| proxy_set_header X-Forwarded-Proto \$scheme; | |
| } | |
| # 静态资源缓存 | |
| location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)\$ { | |
| expires 1y; | |
| add_header Cache-Control "public, immutable"; | |
| } | |
| } | |
| } | |
| EOF | |
| # 创建supervisor配置 | |
| COPY --chown=appuser:appuser <<EOF /etc/supervisor/conf.d/supervisord.conf | |
| [supervisord] | |
| nodaemon=true | |
| user=appuser | |
| logfile=/var/log/supervisor/supervisord.log | |
| pidfile=/run/supervisord.pid | |
| childlogdir=/var/log/supervisor | |
| [program:nginx] | |
| command=nginx -g "daemon off;" | |
| autostart=true | |
| autorestart=true | |
| stderr_logfile=/var/log/supervisor/nginx_error.log | |
| stdout_logfile=/var/log/supervisor/nginx_access.log | |
| user=appuser | |
| [program:node] | |
| command=node index.js | |
| directory=/app | |
| autostart=true | |
| autorestart=true | |
| stderr_logfile=/var/log/supervisor/node_error.log | |
| stdout_logfile=/var/log/supervisor/node_access.log | |
| user=appuser | |
| environment=NODE_ENV=production,PORT=5000 | |
| EOF | |
| # 设置权限 | |
| RUN chown -R appuser:appuser /etc/nginx /etc/supervisor | |
| # 切换到应用用户 | |
| USER appuser | |
| # 暴露端口 | |
| EXPOSE 80 | |
| # 环境变量 | |
| ENV NODE_ENV=production | |
| ENV PORT=5000 | |
| # 健康检查 | |
| HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ | |
| CMD wget --no-verbose --tries=1 --spider http://localhost:80/api/health || exit 1 | |
| # 使用dumb-init作为PID 1 | |
| ENTRYPOINT ["/usr/bin/dumb-init", "--"] | |
| # 启动supervisor | |
| CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] | |