多项目共用一台服务器部署实操技巧
·
多项目共用一台服务器部署实操技巧:一台服务器扛下所有,省钱又省心
初创公司、个人开发者、小团队——预算有限,服务器就一台,项目却有好几个。别慌,这篇文章就是你的"一机多项目"生存指南。
一、先看全局:整体架构长什么样?
┌─────────────────────────────┐
│ 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 个中小项目,完全没问题。 省下的服务器钱,够你吃一年的外卖了。🚀
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)