286 lines
6.6 KiB
Bash
286 lines
6.6 KiB
Bash
#!/bin/bash
|
||
|
||
# Jenkins Demo 部署脚本
|
||
# 用于生产环境部署
|
||
|
||
set -e
|
||
|
||
# 配置变量
|
||
APP_NAME="jenkins-demo"
|
||
APP_VERSION="1.0.0"
|
||
DOCKER_IMAGE="${APP_NAME}:${APP_VERSION}"
|
||
CONTAINER_NAME="${APP_NAME}-prod"
|
||
APP_PORT="80"
|
||
CONTAINER_PORT="8080"
|
||
|
||
# 颜色输出
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 日志函数
|
||
log_info() {
|
||
echo -e "${BLUE}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# 检查Docker是否安装
|
||
check_docker() {
|
||
if ! command -v docker &> /dev/null; then
|
||
log_error "Docker未安装,请先安装Docker"
|
||
exit 1
|
||
fi
|
||
log_info "Docker检查通过"
|
||
}
|
||
|
||
# 停止并移除现有容器
|
||
stop_existing_container() {
|
||
if docker ps -a --format 'table {{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||
log_info "停止现有容器: ${CONTAINER_NAME}"
|
||
docker stop ${CONTAINER_NAME} || true
|
||
docker rm ${CONTAINER_NAME} || true
|
||
log_success "现有容器已停止并移除"
|
||
else
|
||
log_info "未发现现有容器: ${CONTAINER_NAME}"
|
||
fi
|
||
}
|
||
|
||
# 备份现有镜像
|
||
backup_current_image() {
|
||
if docker images | grep -q "${APP_NAME}.*latest"; then
|
||
BACKUP_TAG="backup-$(date +%Y%m%d-%H%M%S)"
|
||
log_info "备份当前镜像为: ${APP_NAME}:${BACKUP_TAG}"
|
||
docker tag ${APP_NAME}:latest ${APP_NAME}:${BACKUP_TAG}
|
||
log_success "镜像备份完成"
|
||
fi
|
||
}
|
||
|
||
# 拉取最新镜像
|
||
pull_latest_image() {
|
||
log_info "拉取最新镜像: ${DOCKER_IMAGE}"
|
||
if docker pull ${DOCKER_IMAGE}; then
|
||
docker tag ${DOCKER_IMAGE} ${APP_NAME}:latest
|
||
log_success "镜像拉取完成"
|
||
else
|
||
log_error "镜像拉取失败"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 启动新容器
|
||
start_new_container() {
|
||
log_info "启动新容器: ${CONTAINER_NAME}"
|
||
|
||
docker run -d \
|
||
--name ${CONTAINER_NAME} \
|
||
--restart unless-stopped \
|
||
-p ${APP_PORT}:${CONTAINER_PORT} \
|
||
-e SPRING_PROFILES_ACTIVE=prod \
|
||
-e JAVA_OPTS="-Xms512m -Xmx1024m" \
|
||
-v /var/log/${APP_NAME}:/app/logs \
|
||
--health-cmd="curl -f http://localhost:${CONTAINER_PORT}/api/health || exit 1" \
|
||
--health-interval=30s \
|
||
--health-timeout=10s \
|
||
--health-retries=3 \
|
||
--health-start-period=60s \
|
||
${APP_NAME}:latest
|
||
|
||
log_success "容器启动完成"
|
||
}
|
||
|
||
# 健康检查
|
||
health_check() {
|
||
log_info "执行健康检查..."
|
||
|
||
local max_attempts=30
|
||
local attempt=1
|
||
|
||
while [ $attempt -le $max_attempts ]; do
|
||
if curl -f -s http://localhost:${APP_PORT}/api/health > /dev/null; then
|
||
log_success "应用程序健康检查通过"
|
||
return 0
|
||
fi
|
||
|
||
log_info "健康检查失败,重试 (${attempt}/${max_attempts})"
|
||
sleep 10
|
||
((attempt++))
|
||
done
|
||
|
||
log_error "健康检查失败,应用程序可能未正常启动"
|
||
return 1
|
||
}
|
||
|
||
# 回滚函数
|
||
rollback() {
|
||
log_warning "开始回滚操作..."
|
||
|
||
# 停止失败的容器
|
||
docker stop ${CONTAINER_NAME} || true
|
||
docker rm ${CONTAINER_NAME} || true
|
||
|
||
# 查找最新的备份镜像
|
||
BACKUP_IMAGE=$(docker images --format "table {{.Repository}}:{{.Tag}}" | grep "${APP_NAME}:backup-" | head -1)
|
||
|
||
if [ -n "$BACKUP_IMAGE" ]; then
|
||
log_info "使用备份镜像回滚: ${BACKUP_IMAGE}"
|
||
docker tag ${BACKUP_IMAGE} ${APP_NAME}:latest
|
||
start_new_container
|
||
|
||
if health_check; then
|
||
log_success "回滚成功"
|
||
else
|
||
log_error "回滚后健康检查仍然失败"
|
||
exit 1
|
||
fi
|
||
else
|
||
log_error "未找到备份镜像,无法回滚"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 清理旧镜像
|
||
cleanup_old_images() {
|
||
log_info "清理旧的Docker镜像..."
|
||
|
||
# 保留最近的5个备份镜像
|
||
OLD_BACKUPS=$(docker images --format "table {{.Repository}}:{{.Tag}}" | grep "${APP_NAME}:backup-" | tail -n +6)
|
||
|
||
if [ -n "$OLD_BACKUPS" ]; then
|
||
echo "$OLD_BACKUPS" | while read -r image; do
|
||
log_info "删除旧备份镜像: $image"
|
||
docker rmi "$image" || true
|
||
done
|
||
fi
|
||
|
||
# 清理悬挂镜像
|
||
docker image prune -f
|
||
|
||
log_success "镜像清理完成"
|
||
}
|
||
|
||
# 显示部署信息
|
||
show_deployment_info() {
|
||
echo
|
||
log_success "=== 部署信息 ==="
|
||
echo "应用名称: ${APP_NAME}"
|
||
echo "应用版本: ${APP_VERSION}"
|
||
echo "容器名称: ${CONTAINER_NAME}"
|
||
echo "访问地址: http://localhost:${APP_PORT}"
|
||
echo "健康检查: http://localhost:${APP_PORT}/api/health"
|
||
echo "API文档: http://localhost:${APP_PORT}/api/users"
|
||
echo
|
||
log_success "=== 部署完成 ==="
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
log_info "开始部署 ${APP_NAME} v${APP_VERSION}"
|
||
|
||
# 检查环境
|
||
check_docker
|
||
|
||
# 备份现有镜像
|
||
backup_current_image
|
||
|
||
# 停止现有容器
|
||
stop_existing_container
|
||
|
||
# 拉取新镜像
|
||
pull_latest_image
|
||
|
||
# 启动新容器
|
||
start_new_container
|
||
|
||
# 健康检查
|
||
if health_check; then
|
||
# 清理旧镜像
|
||
cleanup_old_images
|
||
|
||
# 显示部署信息
|
||
show_deployment_info
|
||
else
|
||
# 健康检查失败,执行回滚
|
||
rollback
|
||
fi
|
||
}
|
||
|
||
# 脚本使用说明
|
||
usage() {
|
||
echo "用法: $0 [选项]"
|
||
echo
|
||
echo "选项:"
|
||
echo " deploy 执行部署(默认)"
|
||
echo " rollback 回滚到上一个版本"
|
||
echo " status 查看应用状态"
|
||
echo " logs 查看应用日志"
|
||
echo " stop 停止应用"
|
||
echo " help 显示帮助信息"
|
||
echo
|
||
}
|
||
|
||
# 查看应用状态
|
||
status() {
|
||
echo "=== 容器状态 ==="
|
||
docker ps -a --filter name=${CONTAINER_NAME}
|
||
echo
|
||
echo "=== 镜像列表 ==="
|
||
docker images | grep ${APP_NAME}
|
||
}
|
||
|
||
# 查看应用日志
|
||
logs() {
|
||
if docker ps --filter name=${CONTAINER_NAME} --format '{{.Names}}' | grep -q ${CONTAINER_NAME}; then
|
||
docker logs -f ${CONTAINER_NAME}
|
||
else
|
||
log_error "容器 ${CONTAINER_NAME} 未运行"
|
||
fi
|
||
}
|
||
|
||
# 停止应用
|
||
stop() {
|
||
log_info "停止应用..."
|
||
docker stop ${CONTAINER_NAME} || true
|
||
docker rm ${CONTAINER_NAME} || true
|
||
log_success "应用已停止"
|
||
}
|
||
|
||
# 根据参数执行不同操作
|
||
case "${1:-deploy}" in
|
||
deploy)
|
||
main
|
||
;;
|
||
rollback)
|
||
rollback
|
||
;;
|
||
status)
|
||
status
|
||
;;
|
||
logs)
|
||
logs
|
||
;;
|
||
stop)
|
||
stop
|
||
;;
|
||
help)
|
||
usage
|
||
;;
|
||
*)
|
||
log_error "未知参数: $1"
|
||
usage
|
||
exit 1
|
||
;;
|
||
esac
|