一台服务器如何同时部署前端React应用和Node.js后端服务?

在一台服务器上同时部署前端React应用和Node.js后端服务是一个常见需求。以下是详细的部署方案:

1. 项目结构建议

/server
  ├── app.js (或 server.js)
  ├── routes/
  ├── controllers/
  └── package.json
/client
  ├── public/
  ├── src/
  └── package.json
package.json (根目录,用于统一管理)

2. 前端构建配置

React应用构建

# 构建生产版本
npm run build

构建后的文件会生成在 client/build 目录。

3. 后端服务配置

Express服务器示例

// server/app.js
const express = require('express');
const path = require('path');
const app = express();

// 静态文件服务 - 前端
app.use(express.static(path.join(__dirname, '../client/build')));

// API路由
app.use('/api', require('./routes/api'));

// 处理所有其他请求,返回index.html
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, '../client/build', 'index.html'));
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

4. 跨域解决方案

开发环境(CORS)

// server/app.js
const cors = require('cors');

// 允许前端访问
app.use(cors({
  origin: 'http://localhost:3000',
  credentials: true
}));

生产环境(反向)

# Nginx配置
server {
    listen 80;
    server_name your-domain.com;

    # 前端静态文件
    location / {
        root /var/www/your-app/client/build;
        try_files $uri $uri/ /index.html;
    }

    # API到Node.js
    location /api {
        proxy_pass http://localhost: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;
    }
}

5. 环境变量管理

.env文件

# .env
NODE_ENV=production
PORT=5000
DATABASE_URL=mongodb://localhost:27017/myapp
API_BASE_URL=/api
FRONTEND_BUILD_PATH=../client/build

在代码中使用

// server/config.js
require('dotenv').config();

module.exports = {
  port: process.env.PORT || 5000,
  dbUrl: process.env.DATABASE_URL,
  env: process.env.NODE_ENV
};

6. 启动脚本配置

package.json

{
  "scripts": {
    "build": "cd client && npm run build",
    "start": "node server/app.js",
    "dev": "concurrently "cd server && nodemon app.js" "cd client && npm start"",
    "prod": "npm run build && npm run start"
  },
  "dependencies": {
    "express": "^4.18.0",
    "dotenv": "^16.0.0"
  },
  "devDependencies": {
    "concurrently": "^7.0.0"
  }
}

7. 进程管理

使用PM2管理进程

# 安装PM2
npm install -g pm2

# 启动应用
pm2 start server/app.js --name "my-app"

# 设置开机自启
pm2 startup
pm2 save

# 查看状态
pm2 status
pm2 logs

PM2配置文件

// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'my-app',
      script: './server/app.js',
      instances: 1,
      autorestart: true,
      watch: false,
      max_memory_restart: '1G',
      env: {
        NODE_ENV: 'development'
      },
      env_production: {
        NODE_ENV: 'production'
      }
    }
  ]
};

8. Docker部署方案

Dockerfile

FROM node:16-alpine

WORKDIR /app

# 安装依赖
COPY package*.json ./
RUN npm ci --only=production

# 复制前端构建文件
COPY client/build ./client/build

# 复制服务器文件
COPY server ./server

# 暴露端口
EXPOSE 5000

# 启动命令
CMD ["node", "server/app.js"]

docker-compose.yml

version: '3.8'
services:
  app:
    build: .
    ports:
      - "5000:5000"
    environment:
      - NODE_ENV=production
    volumes:
      - ./logs:/app/logs
    restart: unless-stopped

9. 安全最佳实践

HTTPS配置

// server/https-server.js
const https = require('https');
const fs = require('fs');
const express = require('express');

const options = {
  key: fs.readFileSync('/path/to/private.key'),
  cert: fs.readFileSync('/path/to/certificate.crt')
};

const app = express();
// ... 应用配置

https.createServer(options, app).listen(443);

安全中间件

const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

app.use(helmet());
app.use(rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100 // 限制每个IP 100次请求
}));

10. 监控和日志

// 日志中间件
const morgan = require('morgan');
const winston = require('winston');

// 请求日志
app.use(morgan('combined'));

// 错误处理
app.use((err, req, res, next) => {
  winston.error(err.stack);
  res.status(500).send('Something broke!');
});

部署流程总结

  1. 准备阶段:确保服务器环境配置好Node.js、Nginx等
  2. 构建前端npm run build 生成生产版本
  3. 启动后端:使用PM2启动Node.js服务
  4. 配置反向:使用Nginx处理静态文件和API请求
  5. 设置SSL:配置HTTPS证书
  6. 监控维护:设置日志监控和错误报警

这样部署既保证了性能,又实现了前后端的合理分离和集成。