一、环境架构设计
环境分支策略:
-
master分支 -> 生产环境(production)
-
develop分支 -> 测试环境(testing)
-
feature/*分支 -> 开发环境(development)
服务器结构:
text
GitLab Server (可托管或自建)
↓
GitLab Runner (注册到GitLab)
↓
部署目标服务器:
- 192.168.1.10:8080 (开发环境)
- 192.168.1.20:8080 (测试环境)
- 192.168.1.30:8080 (生产环境)
二、准备工作
1. Spring Boot项目配置
application.yml (多环境配置):
yaml
spring:
profiles:
active: @activatedProperties@
---
spring:
config:
activate:
on-profile: development
server:
port: 8080
app:
environment: development
---
spring:
config:
activate:
on-profile: testing
server:
port: 8080
app:
environment: testing
---
spring:
config:
activate:
on-profile: production
server:
port: 8080
app:
environment: production
pom.xml (配置profile):
xml
<profiles>
<profile>
<id>development</id>
<properties>
<activatedProperties>development</activatedProperties>
</properties>
</profile>
<profile>
<id>testing</id>
<properties>
<activatedProperties>testing</activatedProperties>
</properties>
</profile>
<profile>
<id>production</id>
<properties>
<activatedProperties>production</activatedProperties>
</properties>
</profile>
</profiles>
2. GitLab Runner安装配置
bash
# 在Runner服务器安装
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt-get install gitlab-runner
# 注册Runner
sudo gitlab-runner register
# 输入GitLab URL: https://gitlab.example.com
# 输入注册令牌(从GitLab Admin > Runners获取)
# 标签: shell-runner,linux,springboot
# 执行器: shell
三、GitLab CI/CD配置
.gitlab-ci.yml:
yaml
stages:
- build
- test
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
APP_NAME: "my-springboot-app"
DEPLOY_USER: "deploy"
# 缓存Maven依赖
cache:
paths:
- .m2/repository/
- target/
# 1. 构建阶段
build-job:
stage: build
script:
- echo "Building application..."
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
expire_in: 1 hour
only:
- branches
tags:
- shell-runner
# 2. 测试阶段
unit-test:
stage: test
script:
- echo "Running unit tests..."
- mvn test
only:
- develop
- master
tags:
- shell-runner
# 3. 开发环境部署
deploy-development:
stage: deploy
variables:
DEPLOY_SERVER: "192.168.1.10"
DEPLOY_PATH: "/opt/apps/development"
PROFILE: "development"
script:
- |
echo "Deploying to development environment..."
# 停止现有应用
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && ./stop.sh || true"
# 传输文件
scp target/*.jar $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/
scp deploy-scripts/start-development.sh $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/start.sh
scp deploy-scripts/stop.sh $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/
# 启动应用
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && chmod +x *.sh && ./start.sh"
environment:
name: development
url: http://192.168.1.10:8080
only:
- /^feature\/.*$/
tags:
- shell-runner
# 4. 测试环境部署
deploy-testing:
stage: deploy
variables:
DEPLOY_SERVER: "192.168.1.20"
DEPLOY_PATH: "/opt/apps/testing"
PROFILE: "testing"
script:
- |
echo "Deploying to testing environment..."
# 构建测试包
mvn clean package -Ptesting -DskipTests
# 传输文件
scp target/*.jar $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/
scp deploy-scripts/start-testing.sh $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/start.sh
# 部署
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && ./stop.sh || true"
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && chmod +x *.sh && ./start.sh"
environment:
name: testing
url: http://192.168.1.20:8080
only:
- develop
tags:
- shell-runner
when: manual # 测试环境需要手动触发
# 5. 生产环境部署
deploy-production:
stage: deploy
variables:
DEPLOY_SERVER: "192.168.1.30"
DEPLOY_PATH: "/opt/apps/production"
PROFILE: "production"
before_script:
- echo "Starting production deployment..."
script:
- |
# 构建生产包
mvn clean package -Pproduction -DskipTests
# 备份当前版本
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && cp $APP_NAME.jar $APP_NAME.jar.backup.$(date +%Y%m%d%H%M%S) || true"
# 传输文件
scp target/*.jar $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/$APP_NAME.jar
scp deploy-scripts/start-production.sh $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/start.sh
scp deploy-scripts/stop.sh $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/
scp deploy-scripts/health-check.sh $DEPLOY_USER@$DEPLOY_SERVER:$DEPLOY_PATH/
# 滚动部署
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && ./stop.sh"
sleep 10
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && chmod +x *.sh && ./start.sh"
# 健康检查
ssh $DEPLOY_USER@$DEPLOY_SERVER "cd $DEPLOY_PATH && ./health-check.sh"
environment:
name: production
url: http://192.168.1.30:8080
only:
- master
tags:
- shell-runner
when: manual # 生产环境需要手动触发
四、部署脚本
deploy-scripts/start-development.sh:
bash
#!/bin/bash
APP_NAME="my-springboot-app"
DEPLOY_PATH="/opt/apps/development"
LOG_PATH="/var/log/$APP_NAME"
# 创建日志目录
mkdir -p $LOG_PATH
# 启动应用(开发环境)
nohup java -jar \
-Dspring.profiles.active=development \
-Dserver.port=8080 \
$DEPLOY_PATH/$APP_NAME.jar \
> $LOG_PATH/app.log 2>&1 &
echo $! > $DEPLOY_PATH/app.pid
echo "Application started with PID: $(cat $DEPLOY_PATH/app.pid)"
deploy-scripts/start-testing.sh:
bash
#!/bin/bash
APP_NAME="my-springboot-app"
DEPLOY_PATH="/opt/apps/testing"
LOG_PATH="/var/log/$APP_NAME"
JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"
mkdir -p $LOG_PATH
nohup java $JAVA_OPTS -jar \
-Dspring.profiles.active=testing \
-Dserver.port=8080 \
$DEPLOY_PATH/$APP_NAME.jar \
> $LOG_PATH/app.log 2>&1 &
echo $! > $DEPLOY_PATH/app.pid
deploy-scripts/start-production.sh:
bash
#!/bin/bash
APP_NAME="my-springboot-app"
DEPLOY_PATH="/opt/apps/production"
LOG_PATH="/var/log/$APP_NAME"
JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOG_PATH"
mkdir -p $LOG_PATH
nohup java $JAVA_OPTS -jar \
-Dspring.profiles.active=production \
-Dserver.port=8080 \
-Dlogging.file.path=$LOG_PATH \
$DEPLOY_PATH/$APP_NAME.jar \
> $LOG_PATH/console.log 2>&1 &
echo $! > $DEPLOY_PATH/app.pid
echo "Production application started with PID: $(cat $DEPLOY_PATH/app.pid)"
deploy-scripts/stop.sh:
bash
#!/bin/bash
APP_NAME="my-springboot-app"
DEPLOY_PATH="/opt/apps"
# 根据当前目录确定环境
CURRENT_ENV=$(basename $(pwd))
PID_FILE="$DEPLOY_PATH/$CURRENT_ENV/app.pid"
if [ -f "$PID_FILE" ]; then
PID=$(cat $PID_FILE)
if ps -p $PID > /dev/null; then
echo "Stopping application (PID: $PID)..."
kill $PID
sleep 10
if ps -p $PID > /dev/null; then
echo "Force stopping application..."
kill -9 $PID
fi
fi
rm -f $PID_FILE
fi
deploy-scripts/health-check.sh:
bash
#!/bin/bash
APP_URL="http://localhost:8080/actuator/health"
MAX_RETRY=30
RETRY_INTERVAL=5
for i in $(seq 1 $MAX_RETRY); do
response=$(curl -s -o /dev/null -w "%{http_code}" $APP_URL)
if [ "$response" = "200" ]; then
echo "Application is healthy!"
exit 0
fi
echo "Health check attempt $i/$MAX_RETRY failed. Retrying in $RETRY_INTERVAL seconds..."
sleep $RETRY_INTERVAL
done
echo "Health check failed after $MAX_RETRY attempts"
exit 1
五、服务器准备
1. 部署服务器配置(每台环境服务器):
bash
# 创建部署用户
sudo useradd -m -s /bin/bash deploy
sudo passwd deploy
# 创建应用目录
sudo mkdir -p /opt/apps/{development,testing,production}
sudo chown -R deploy:deploy /opt/apps
# 安装Java
sudo apt update
sudo apt install -y openjdk-11-jdk
# 配置SSH密钥认证(用于GitLab Runner连接)
# 在GitLab Runner服务器生成密钥,并将公钥复制到部署服务器的~/.ssh/authorized_keys
2. 配置免密SSH登录:
bash
# 在GitLab Runner服务器执行
sudo -u gitlab-runner ssh-keygen -t rsa
sudo -u gitlab-runner cat ~/.ssh/id_rsa.pub
# 将公钥添加到各环境服务器的deploy用户
# 在目标服务器执行:
mkdir -p /home/deploy/.ssh
echo "ssh-rsa [PUBLIC_KEY]" >> /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
六、高级配置
1. 环境变量管理
在GitLab项目的 Settings > CI/CD > Variables 中添加:
-
PRODUCTION_SERVER_PRIVATE_KEY(生产服务器私钥) -
TESTING_SERVER_PRIVATE_KEY(测试服务器私钥) -
DB_PRODUCTION_PASSWORD -
DB_TESTING_PASSWORD
2. 回滚脚本
deploy-scripts/rollback.sh:
bash
#!/bin/bash
ENV=${1:-production}
DEPLOY_PATH="/opt/apps/$ENV"
APP_NAME="my-springboot-app"
# 查找最新的备份文件
BACKUP_FILE=$(ls -t $DEPLOY_PATH/$APP_NAME.jar.backup.* 2>/dev/null | head -1)
if [ -z "$BACKUP_FILE" ]; then
echo "No backup file found for rollback"
exit 1
fi
echo "Rolling back to: $BACKUP_FILE"
# 停止当前应用
cd $DEPLOY_PATH && ./stop.sh
# 恢复备份
cp $BACKUP_FILE $DEPLOY_PATH/$APP_NAME.jar
# 启动应用
cd $DEPLOY_PATH && ./start.sh
七、监控与维护
1. 日志轮转配置
/etc/logrotate.d/my-springboot-app:
bash
/var/log/my-springboot-app/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0644 deploy deploy
postrotate
# 重新打开日志文件
/bin/kill -HUP $(cat /opt/apps/production/app.pid 2>/dev/null) 2>/dev/null || true
endscript
}
2. 服务管理脚本
/etc/systemd/system/my-springboot-app.service (生产环境建议使用systemd):
ini
[Unit]
Description=My Spring Boot Application
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/opt/apps/production
ExecStart=/usr/bin/java -jar -Dspring.profiles.active=production my-springboot-app.jar
ExecStop=/bin/kill -15 $MAINPID
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
八、使用流程
-
开发流程:
-
从develop创建feature分支
-
开发完成后提交Merge Request到develop
-
CI会自动部署到开发环境验证
-
-
测试流程:
-
develop分支合并后,手动触发测试环境部署
-
测试通过后,创建release分支
-
-
生产发布:
-
release分支合并到master
-
手动触发生产环境部署
-
监控应用健康状态
-
这个方案提供了完整的非容器化部署流程,具有以下特点:
-
✅ 清晰的Git分支策略
-
✅ 环境隔离配置
-
✅ 自动化构建和部署
-
✅ 手动审批控制(测试和生产)
-
✅ 健康检查和监控
-
✅ 支持回滚操作
-
✅ 日志管理和轮转