在阿里云ECS上同时运行多个Spring Boot应用是完全可行的,以下是详细的步骤和最佳实践:
1. 基础环境准备
# 更新系统
sudo yum update -y
# 安装Java(以OpenJDK为例)
sudo yum install -y java-1.8.0-openjdk-devel
# 验证安装
java -version
2. 应用配置 – 端口分离
每个Spring Boot应用必须使用不同的端口:
应用1 (application1) – application.yml
server:
port: 8080
spring:
application:
name: app1-service
应用2 (application2) – application.yml
server:
port: 8081
spring:
application:
name: app2-service
应用3 (application3) – application.yml
server:
port: 8082
spring:
application:
name: app3-service
3. 目录结构规划
/home/apps/
├── app1/
│ ├── app1.jar
│ ├── application.yml
│ └── start.sh
├── app2/
│ ├── app2.jar
│ ├── application.yml
│ └── start.sh
├── app3/
│ ├── app3.jar
│ ├── application.yml
│ └── start.sh
└── logs/
├── app1.log
├── app2.log
└── app3.log
4. 启动脚本示例
start.sh 示例
#!/bin/bash
APP_NAME="app1"
JAR_NAME="app1.jar"
LOG_DIR="/home/apps/logs"
LOG_FILE="${LOG_DIR}/${APP_NAME}.log"
# 创建日志目录
mkdir -p $LOG_DIR
# JVM参数优化
JVM_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -Dspring.profiles.active=prod"
case "$1" in
start)
echo "Starting $APP_NAME..."
nohup java $JVM_OPTS -jar $JAR_NAME > $LOG_FILE 2>&1 &
echo $! > ${APP_NAME}.pid
echo "$APP_NAME started with PID $(cat ${APP_NAME}.pid)"
;;
stop)
if [ -f "${APP_NAME}.pid" ]; then
PID=$(cat ${APP_NAME}.pid)
echo "Stopping $APP_NAME (PID: $PID)..."
kill $PID
rm -f ${APP_NAME}.pid
echo "$APP_NAME stopped"
else
echo "$APP_NAME is not running"
fi
;;
restart)
$0 stop
sleep 2
$0 start
;;
status)
if [ -f "${APP_NAME}.pid" ]; then
PID=$(cat ${APP_NAME}.pid)
if ps -p $PID > /dev/null; then
echo "$APP_NAME is running (PID: $PID)"
else
echo "$APP_NAME is not running (stale PID file)"
rm -f ${APP_NAME}.pid
fi
else
echo "$APP_NAME is not running"
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
5. 使用Systemd服务管理
创建systemd服务文件
# /etc/systemd/system/app1.service
[Unit]
Description=App1 Spring Boot Application
After=network.target
[Service]
Type=simple
User=apps
WorkingDirectory=/home/apps/app1
ExecStart=/usr/bin/java -Xms512m -Xmx1024m -jar app1.jar
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
启用和管理服务
# 重新加载systemd配置
sudo systemctl daemon-reload
# 启用开机自启
sudo systemctl enable app1.service
sudo systemctl enable app2.service
sudo systemctl enable app3.service
# 启动服务
sudo systemctl start app1.service
sudo systemctl start app2.service
sudo systemctl start app3.service
# 查看状态
sudo systemctl status app1.service
6. Nginx反向X_X配置
# /etc/nginx/conf.d/apps.conf
upstream app1 {
server 127.0.0.1:8080;
}
upstream app2 {
server 127.0.0.1:8081;
}
upstream app3 {
server 127.0.0.1:8082;
}
server {
listen 80;
server_name your-domain.com;
location /app1/ {
proxy_pass http://app1/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /app2/ {
proxy_pass http://app2/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /app3/ {
proxy_pass http://app3/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
7. 资源监控和优化
检查端口占用
# 查看所有监听端口
netstat -tlnp | grep java
# 检查特定端口
lsof -i :8080
内存监控脚本
#!/bin/bash
# monitor_apps.sh
echo "=== Spring Boot Apps Status ==="
for pid_file in /home/apps/*/app*.pid; do
if [ -f "$pid_file" ]; then
APP_NAME=$(basename $(dirname $pid_file))
PID=$(cat $pid_file)
if ps -p $PID > /dev/null; then
MEM_USAGE=$(ps -o pid,ppid,cmd,%mem --no-headers -p $PID | awk '{print $4}')
echo "$APP_NAME: PID=$PID, Memory=${MEM_USAGE}%"
else
echo "$APP_NAME: Not running (PID file exists but process dead)"
fi
fi
done
8. 安全建议
# 防火墙配置
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=8081/tcp
sudo firewall-cmd --permanent --add-port=8082/tcp
sudo firewall-cmd --reload
# 或者只开放Nginx端口
sudo firewall-cmd --permanent --remove-port=8080-8082/tcp
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --reload
9. 性能调优建议
- 内存分配:根据ECS实例规格合理分配JVM内存
- 线程池:配置合适的线程池大小
- 数据库连接池:合理设置连接池参数
- 日志级别:生产环境使用INFO或WARN级别
通过以上配置,你可以在同一台ECS实例上稳定运行多个Spring Boot应用。建议根据实际业务需求和服务器资源进行适当调整。
CLOUD云枢