多项目共用一台服务器部署实操技巧:一台服务器扛下所有,省钱又省心

初创公司、个人开发者、小团队——预算有限,服务器就一台,项目却有好几个。别慌,这篇文章就是你的"一机多项目"生存指南。


一、先看全局:整体架构长什么样?


                    ┌─────────────────────────────┐
                    │         Nginx (80/443)       │  ← 唯一入口,反向代理
                    │   统一域名 + SSL 证书管理      │
                    └──────────┬──────────────────┘
                               │
            ┌──────────────────┼──────────────────┐
            │                  │                  │
     ┌──────▼──────┐   ┌──────▼──────┐   ┌──────▼──────┐
     │  项目 A      │   │  项目 B      │   │  项目 C      │
     │  Spring Boot │   │  Vue 前端    │   │  Python Django│
     │  :8080       │   │  :3000       │   │  :5000       │
     └─────────────┘   └─────────────┘   └─────────────┘
            │                  │                  │
     ┌──────▼──────┐   ┌──────▼──────┐   ┌──────▼──────┐
     │  MySQL      │   │  Redis      │   │  PostgreSQL │
     │  :3306      │   │  :6379      │   │  :5432      │
     └─────────────┘   └─────────────┘   └─────────────┘

核心思路

层级 方案 作用
入口层 Nginx 反向代理 统一 80/443 端口,根据域名/路径分发到不同项目
应用层 不同端口 每个项目跑在独立端口,互不干扰
数据层 不同数据库实例或 Schema 数据隔离,避免互相踩踏
进程层 Systemd / Supervisor 守护进程,崩溃自动重启

二、Nginx 反向代理:一台服务器的灵魂

✅ 基础配置:多项目域名分发


nginx

# /etc/nginx/nginx.conf 或 /etc/nginx/conf.d/projects.conf

# ========== 项目 A:Spring Boot 后端 ==========
server {
    listen 80;
    server_name api.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

# ========== 项目 B:Vue 前端 ==========
server {
    listen 80;
    server_name www.yourdomain.com;

    root /var/www/project-b/dist;
    index index.html;

    # Vue Router history 模式:所有路由都指向 index.html
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

# ========== 项目 C:Python Django ==========
server {
    listen 80;
    server_name admin.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # Django 静态文件
    location /static/ {
        alias /var/www/project-c/static/;
        expires 30d;
    }

    # Django 媒体文件
    location /media/ {
        alias /var/www/project-c/media/;
        expires 7d;
    }
}

✅ 路径分发:一个域名,多个项目


nginx

# 一个域名下,用路径区分不同项目
server {
    listen 80;
    server_name app.yourdomain.com;

    # /api/* → Spring Boot
    location /api/ {
        proxy_pass http://127.0.0.1:8080/;
    }

    # /admin/* → Django
    location /admin/ {
        proxy_pass http://127.0.0.1:5000/admin/;
    }

    # / → Vue 前端
    location / {
        root /var/www/project-b/dist;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
}

✅ HTTPS 统一管理(SSL 证书)


nginx

# 用 Certbot 一键申请 Let's Encrypt 免费证书
sudo certbot --nginx -d api.yourdomain.com -d www.yourdomain.com -d admin.yourdomain.com

# 自动生成的配置(已包含 HTTPS)
server {
    listen 443 ssl;
    server_name api.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;

    # ... 其余配置同上
}

# HTTP 自动跳转 HTTPS
server {
    listen 80;
    server_name api.yourdomain.com;
    return 301 https://$host$request_uri;
}

💡 Let's Encrypt 免费 SSL,90 天续期一次,Certbot 自动续期,零成本。


三、进程守护:项目挂了自动拉起来

✅ 方案一:Systemd(推荐 ⭐⭐⭐⭐⭐)

每个项目一个 .service 文件:


ini

# /etc/systemd/system/project-a.service
[Unit]
Description=Project A - Spring Boot
After=network.target mysql.service

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/project-a
ExecStart=/usr/bin/java -jar /opt/project-a/app.jar \
    -Xms512m -Xmx512m \
    --server.port=8080
ExecStop=/bin/kill -15 $MAINPID
Restart=always
RestartSec=10
StandardOutput=append:/var/log/project-a.log
StandardError=append:/var/log/project-a-error.log

# 资源限制(防止一个项目吃光服务器)
MemoryMax=1G
CPUQuota=50%

[Install]
WantedBy=multi-user.target

bash

# 常用命令
sudo systemctl daemon-reload          # 重新加载配置
sudo systemctl start project-a        # 启动
sudo systemctl stop project-a         # 停止
sudo systemctl restart project-a      # 重启
sudo systemctl status project-a       # 查看状态
sudo systemctl enable project-a       # 开机自启
sudo journalctl -u project-a -f       # 实时查看日志

✅ 方案二:Supervisor(简单项目够用)


ini

# /etc/supervisor/conf.d/project-a.conf
[program:project-a]
command=/usr/bin/java -jar /opt/project-a/app.jar --server.port=8080
directory=/opt/project-a
user=appuser
autostart=true
autorestart=true
startretries=3
stdout_logfile=/var/log/project-a.log
stderr_logfile=/var/log/project-a-error.log
environment=SPRING_PROFILES_ACTIVE="prod"

bash

supervisorctl reread          # 重新读取配置
supervisorctl update          # 更新进程
supervisorctl status          # 查看状态
supervisorctl tail project-a  # 查看日志

✅ 方案三:PM2(Node.js / 前端项目首选)


bash

# 全局安装
npm install -g pm2

# 启动项目
pm2 start "npm run start" --name project-b
pm2 start "python manage.py runserver 0.0.0.0:5000" --name project-c

# 开机自启 + 保存进程列表
pm2 startup
pm2 save

# 监控
pm2 monit
pm2 logs project-b --lines 100
方案 适用场景 优点 缺点
Systemd Java/Go/任何语言 系统级集成,资源限制强 配置稍复杂
Supervisor Python/简单项目 配置简单,上手快 功能不如 Systemd
PM2 Node.js/前端 零配置,监控方便 只适合 Node 生态

四、数据库:多项目如何共存不打架?

✅ 方案一:不同数据库实例(最干净 ⭐⭐⭐⭐⭐)

项目 数据库 端口 用途
项目 A MySQL 3306 业务数据
项目 C PostgreSQL 5432 业务数据
共享 Redis 6379 缓存 / 会话

✅ 方案二:同一个 MySQL,不同 Schema(省资源)


sql

-- 为每个项目创建独立的数据库(Schema)
CREATE DATABASE project_a CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE project_b CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE project_c CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 创建独立用户,只授权对应数据库
CREATE USER 'app_a'@'localhost' IDENTIFIED BY 'StrongPass123!';
GRANT ALL PRIVILEGES ON project_a.* TO 'app_a'@'localhost';

CREATE USER 'app_b'@'localhost' IDENTIFIED BY 'StrongPass456!';
GRANT ALL PRIVILEGES ON project_b.* TO 'app_b'@'localhost';

yaml

# application.yml - 每个项目只连自己的库
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/project_a?useUnicode=true&characterEncoding=utf8
    username: app_a
    password: StrongPass123!

✅ 方案三:Docker 容器化隔离(最推荐 ⭐⭐⭐⭐⭐)


yaml

# docker-compose.yml — 一台服务器,多个项目,互不干扰
version: '3.8'

services:
  # ===== 项目 A =====
  project-a:
    image: openjdk:17-jdk-slim
    container_name: project-a
    ports:
      - "8080:8080"
    volumes:
      - /opt/project-a/app.jar:/app.jar
    command: java -jar /app.jar --server.port=8080
    restart: always
    networks:
      - app-network
    depends_on:
      - mysql-a

  mysql-a:
    image: mysql:8.0
    container_name: mysql-a
    environment:
      MYSQL_ROOT_PASSWORD: root123!
      MYSQL_DATABASE: project_a
    volumes:
      - mysql-a-data:/var/lib/mysql
    networks:
      - app-network
    restart: always

  # ===== 项目 B =====
  project-b:
    image: nginx:alpine
    container_name: project-b
    ports:
      - "3000:80"
    volumes:
      - /opt/project-b/dist:/usr/share/nginx/html
    restart: always
    networks:
      - app-network

  # ===== Redis 共享缓存 =====
  redis:
    image: redis:7-alpine
    container_name: redis
    ports:
      - "6379:6379"
    command: redis-server --requirepass RedisPass123!
    restart: always
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  mysql-a-data:

bash

# 一键启动所有项目
docker-compose up -d

# 查看日志
docker-compose logs -f project-a

# 停止所有
docker-compose down

💡 Docker 方案是终极解决方案:每个项目独立运行环境,不怕依赖冲突,不怕版本打架,迁移也方便。


五、日志管理:多项目日志不再一团乱麻

✅ 方案:按项目分类 + 日志轮转


bash

# /etc/logrotate.d/project-a
/var/log/project-a.log {
    daily                   # 每天轮转
    rotate 14               # 保留 14 天
    compress                # 压缩旧日志
    delaycompress           # 延迟一天压缩
    missingok               # 文件不存在不报错
    notifempty              # 空文件不轮转
    copytruncate            # 不重启进程,直接截断
    size 100M               # 超过 100M 也轮转
}

✅ 集中式日志(进阶推荐)


yaml

# docker-compose.yml 添加 ELK 或 Loki
loki:
  image: grafana/loki:latest
  ports:
    - "3100:3100"
  volumes:
    - /var/log:/var/log
  command: -config.file=/etc/loki/local-config.yaml

promtail:  # 日志采集器
  image: grafana/promtail:latest
  volumes:
    - /var/log:/var/log
    - /opt/promtail-config.yaml:/etc/promtail/config.yaml
  command: -config.file=/etc/promtail/config.yaml

yaml

# promtail-config.yaml — 按项目采集
scrape_configs:
  - job_name: project-a
    static_configs:
      - targets: [localhost]
        labels:
          job: project-a
          __path__: /var/log/project-a.log

  - job_name: project-b
    static_configs:
      - targets: [localhost]
        labels:
          job: project-b
          __path__: /var/log/project-b.log

六、CI/CD 自动化:改完代码一键部署

✅ GitHub Actions + SSH 部署(零成本方案)


yaml

# .github/workflows/deploy.yml
name: Deploy to Server

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: 部署项目 A
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/project-a
            git pull origin main
            systemctl restart project-a

      - name: 部署项目 B
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_IP }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/project-b
            npm install
            npm run build
            pm2 restart project-b --update-env

✅ Webhook 方式(最简单)


bash

# 服务器上监听 GitHub Webhook
# /opt/webhook/deploy.sh
#!/bin/bash
cd /opt/project-a && git pull && systemctl restart project-a
cd /opt/project-b && npm run build && pm2 restart project-b

# GitHub Webhook URL: http://your-server:9000/deploy
# 用 adnanh/webhook 快速搭建
docker run -d -p 9000:9000 \
  -v /opt/webhook:/etc/webhook \
  adnanh/webhook -hooks /etc/webhook/hooks.json -verbose

七、资源隔离:别让一个项目拖垮整台服务器

✅ Systemd 资源限制(防止内存爆炸)


ini

# /etc/systemd/system/project-a.service
[Service]
MemoryMax=1G              # 最多用 1G 内存,超了就 OOM Kill
CPUQuota=50%              # 最多用 50% CPU
TasksMax=100              # 最多 100 个线程
IOWeight=500              # IO 权重

✅ Docker 资源限制


yaml

# docker-compose.yml
services:
  project-a:
    deploy:
      resources:
        limits:
          cpus: '1.0'        # 最多 1 核 CPU
          memory: 1G         # 最多 1G 内存
        reservations:
          cpus: '0.5'        # 保证 0.5 核
          memory: 512M

✅ 监控告警(防患于未然)


bash

# 安装 Node Exporter + Grafana(监控服务器资源)
docker run -d -p 9090:9090 \
  --name node-exporter \
  prom/node-exporter

# 安装 cAdvisor(监控 Docker 容器资源)
docker run -d -p 8080:8080 \
  --name cadvisor \
  -v /:/rootfs:ro \
  -v /var/run:/var/run:ro \
  -v /sys:/sys:ro \
  -v /var/lib/docker/:/var/lib/docker:ro \
  google/cadvisor

八、常见坑 & 解决方案

现象 解决方案
端口冲突 两个项目都想用 8080 Nginx 统一入口,后端用不同端口
内存溢出 一个项目 OOM,全服务器卡死 Systemd MemoryMax 或 Docker 限制
日志撑爆磁盘 日志文件无限增长 logrotate 每日轮转 + 压缩
SSL 证书过期 网站突然变"不安全" certbot renew --dry-run 测试,crontab 自动续期
部署时服务中断 重启期间 502 错误 用蓝绿部署或 PM2 零停机重启
数据库连接池打满 一个项目把 MySQL 连接吃光 每个项目独立连接池,限制 maxPoolSize
时间不一致 日志时间戳对不上 统一 timedatectl set-timezone Asia/Shanghai + NTP 同步

九、终极方案对比

方案 复杂度 隔离性 推荐场景
裸机 + Nginx + Systemd ⭐⭐ ⭐⭐ 项目少(2-3个),团队有运维能力
裸机 + Docker Compose ⭐⭐⭐ ⭐⭐⭐⭐ 最推荐,项目 3-10 个,性价比最高
K8s(Kubernetes) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 项目 10+ 个,有专职运维

十、一键部署脚本(拿来就用)


bash

#!/bin/bash
# deploy-all.sh — 一键部署所有项目

PROJECTS=("project-a" "project-b" "project-c")

for project in "${PROJECTS[@]}"; do
    echo "🚀 Deploying $project ..."
    
    cd "/opt/$project"
    git pull origin main
    
    case $project in
        "project-a")
            systemctl restart project-a
            ;;
        "project-b")
            npm install && npm run build
            pm2 restart project-b --update-env
            ;;
        "project-c")
            docker-compose -f /opt/project-c/docker-compose.yml up -d --build
            ;;
    esac
    
    echo "✅ $project deployed!"
done

# 重载 Nginx
nginx -t && systemctl reload nginx

echo "🎉 All projects deployed!"

总结:一台服务器的生存法则


┌─────────────────────────────────────────────┐
│              一台服务器部署 Checklist         │
├─────────────────────────────────────────────┤
│ ✅ Nginx 反向代理 — 统一入口,域名分发          │
│ ✅ 每个项目独立端口 — 互不干扰                 │
│ ✅ Systemd/Docker 守护 — 崩溃自动重启          │
│ ✅ 数据库独立 Schema/实例 — 数据隔离           │
│ ✅ 资源限制(CPU/内存)— 防止一个拖垮全部       │
│ ✅ 日志轮转 — 防止磁盘撑爆                     │
│ ✅ Let's Encrypt 免费 SSL — 零成本 HTTPS       │
│ ✅ CI/CD 自动化 — 改完代码一键部署              │
│ ✅ 监控告警 — 出问题第一时间知道                │
└─────────────────────────────────────────────┘

🎯 一台 4C8G 的服务器,用 Docker Compose 跑 5-10 个中小项目,完全没问题。 省下的服务器钱,够你吃一年的外卖了。🚀

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐