#!/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