531 lines
15 KiB
Bash
Executable File
531 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
||
# ===================================================================
|
||
# TopFans 打包上传部署脚本
|
||
# 功能:
|
||
# - 本地:构建镜像 → 推送到镜像仓库
|
||
# - 远程:拉取镜像 → 启动服务
|
||
# - 回滚:回滚到指定版本
|
||
# ===================================================================
|
||
#
|
||
# 使用前提:
|
||
# 1. 已安装 Docker
|
||
# 2. 已创建阿里云容器镜像仓库
|
||
# 3. 服务器已配置 SSH 免密登录(建议)
|
||
#
|
||
# 使用方式:
|
||
# # 本地构建 + 推送
|
||
# ./deploy.sh build v1.0.0
|
||
#
|
||
# # 远程部署(从仓库拉取 + 启动)
|
||
# ./deploy.sh deploy v1.0.0
|
||
#
|
||
# # 回滚到指定版本
|
||
# ./deploy.sh rollback v1.0.0
|
||
#
|
||
# # 查看部署历史
|
||
# ./deploy.sh history
|
||
#
|
||
# # 一键构建 + 推送 + 部署(本地构建完成后远程部署)
|
||
# ./deploy.sh all v1.0.0
|
||
#
|
||
# ===================================================================
|
||
|
||
set -e
|
||
|
||
# ==================== 颜色定义 ====================
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
MAGENTA='\033[0;35m'
|
||
NC='\033[0m'
|
||
|
||
# ==================== 路径配置 ====================
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
cd "$SCRIPT_DIR"
|
||
|
||
# ==================== 镜像配置 ====================
|
||
# 本地构建后打包传输到服务器,不需要镜像仓库
|
||
SERVICES=(
|
||
"gateway"
|
||
"userservice"
|
||
"socialservice"
|
||
"assetservice"
|
||
"galleryservice"
|
||
"activityservice"
|
||
)
|
||
|
||
# ==================== 服务器配置 ====================
|
||
# ⚠️ 修改为你的服务器信息
|
||
SERVER_HOST="101.132.250.62" # 服务器 IP 或域名
|
||
SERVER_PORT="22" # SSH 端口
|
||
SERVER_USER="root" # SSH 用户名
|
||
SERVER_PATH="/opt/topfans/docker" # 服务器上 docker 目录路径
|
||
|
||
# ==================== 打印函数 ====================
|
||
print_step() {
|
||
echo ""
|
||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${BLUE} $1${NC}"
|
||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
}
|
||
|
||
print_msg() {
|
||
local color=$1
|
||
local msg=$2
|
||
echo -e "${color}${msg}${NC}"
|
||
}
|
||
|
||
# ==================== 帮助信息 ====================
|
||
show_help() {
|
||
cat << EOF
|
||
${MAGENTA}TopFans 部署脚本${NC}
|
||
|
||
${YELLOW}用法:${NC}
|
||
$0 <命令> [版本号] [选项]
|
||
|
||
${YELLOW}命令:${NC}
|
||
${GREEN}build${NC} <版本号> 本地构建镜像并打包传输到服务器
|
||
${GREEN}deploy${NC} <版本号> 远程部署(加载镜像 + 启动服务)
|
||
${GREEN}rollback${NC} <版本号> 回滚到指定版本
|
||
${GREEN}history${NC} 查看部署历史
|
||
${GREEN}all${NC} <版本号> 一键构建 + 传输 + 部署
|
||
${GREEN}clean${NC} 清理本地镜像(谨慎使用)
|
||
|
||
${YELLOW}选项:${NC}
|
||
--server <IP> 指定服务器(覆盖配置文件)
|
||
--skip-build 跳过构建(用于已构建过的情况)
|
||
--skip-push 跳过传输(用于已传输过的情况)
|
||
--force 强制执行(不确认)
|
||
--help, -h 显示此帮助
|
||
|
||
${YELLOW}示例:${NC}
|
||
$0 build v1.0.0 # 构建并打包传输到服务器
|
||
$0 deploy v1.0.0 --server 192.168.1.100 # 部署到服务器
|
||
$0 rollback v0.9.0 # 回滚到 v0.9.0
|
||
$0 history # 查看部署历史
|
||
$0 all v1.0.0 --server 192.168.1.100 # 一键完成所有操作
|
||
|
||
${YELLOW}前提准备:${NC}
|
||
1. 修改 SERVER_HOST 为你的服务器 IP
|
||
2. 配置服务器 SSH 免密登录(建议)
|
||
|
||
EOF
|
||
}
|
||
|
||
# ==================== 配置检查 ====================
|
||
check_config() {
|
||
local errors=0
|
||
|
||
if [ -z "$SERVER_HOST" ]; then
|
||
print_msg "$YELLOW" "警告: SERVER_HOST 未设置,远程部署功能将不可用"
|
||
fi
|
||
|
||
return $errors
|
||
}
|
||
|
||
# ==================== 1. 构建镜像 ====================
|
||
do_build() {
|
||
print_step "🔨 构建 Docker 镜像"
|
||
|
||
# 调用构建脚本
|
||
# ./build.sh --no-cache
|
||
./build.sh
|
||
|
||
if [ $? -ne 0 ]; then
|
||
print_msg "$RED" "❌ 构建失败"
|
||
exit 1
|
||
fi
|
||
|
||
print_msg "$GREEN" "✅ 镜像构建完成"
|
||
}
|
||
|
||
# ==================== 2. 打包并传输镜像到服务器 ====================
|
||
do_push() {
|
||
local version=$1
|
||
|
||
if [ -z "$SERVER_HOST" ]; then
|
||
print_msg "$RED" "错误: 请设置 SERVER_HOST"
|
||
exit 1
|
||
fi
|
||
|
||
print_step "📦 打包镜像为 tar 文件"
|
||
|
||
local tmp_dir="/tmp/topfans-images-${version}"
|
||
mkdir -p "${tmp_dir}"
|
||
|
||
local failed=()
|
||
local packed=()
|
||
|
||
for SERVICE in "${SERVICES[@]}"; do
|
||
local local_image="topfans/${SERVICE}:latest"
|
||
local tar_file="${tmp_dir}/${SERVICE}.tar"
|
||
|
||
echo ""
|
||
print_msg "$YELLOW" "处理 ${SERVICE}..."
|
||
|
||
if docker save "${local_image}" -o "${tar_file}"; then
|
||
echo -e " ${GREEN}✅ 已打包${NC}"
|
||
packed+=("${SERVICE}")
|
||
else
|
||
echo -e " ${RED}❌ 打包失败${NC}"
|
||
failed+=("${SERVICE}")
|
||
fi
|
||
done
|
||
|
||
if [ ${#failed[@]} -ne 0 ]; then
|
||
print_msg "$RED" "❌ 打包失败: ${failed[*]}"
|
||
rm -rf "${tmp_dir}"
|
||
exit 1
|
||
fi
|
||
|
||
print_msg "$GREEN" "✅ 全部打包完成"
|
||
|
||
print_step "📤 传输镜像到服务器"
|
||
|
||
print_msg "$YELLOW" "正在传输到 ${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}..."
|
||
|
||
# 创建服务器目录
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "mkdir -p ${SERVER_PATH}/images && rm -f ${SERVER_PATH}/images/*.tar 2>/dev/null || true"
|
||
|
||
# 传输 tar 文件
|
||
for SERVICE in "${packed[@]}"; do
|
||
local tar_file="${tmp_dir}/${SERVICE}.tar"
|
||
print_msg "$YELLOW" "传输 ${SERVICE}.tar..."
|
||
scp -P "${SERVER_PORT}" "${tar_file}" "${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/images/"
|
||
print_msg "$GREEN" "✅ ${SERVICE}.tar 传输完成"
|
||
done
|
||
|
||
# 清理本地临时文件
|
||
rm -rf "${tmp_dir}"
|
||
|
||
print_msg "$GREEN" "✅ 镜像传输完成"
|
||
}
|
||
|
||
# ==================== 3. 远程部署 ====================
|
||
do_deploy() {
|
||
local version=$1
|
||
|
||
if [ -z "$SERVER_HOST" ]; then
|
||
print_msg "$RED" "错误: 请设置 SERVER_HOST(服务器 IP)"
|
||
print_msg "$YELLOW" "使用方法: $0 deploy ${version} --server <IP>"
|
||
exit 1
|
||
fi
|
||
|
||
print_step "🚀 远程部署到 ${SERVER_HOST}"
|
||
|
||
print_msg "$YELLOW" "检查 Docker 环境..."
|
||
|
||
ssh -p "${SERVER_PORT}" -T "${SERVER_USER}@${SERVER_HOST}" << 'ENDSSH'
|
||
set -e
|
||
|
||
echo '=== 1. 检查 Docker 环境 ==='
|
||
if ! command -v docker &> /dev/null; then
|
||
echo '❌ Docker 未安装'
|
||
exit 1
|
||
fi
|
||
|
||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||
echo '❌ docker-compose 未安装'
|
||
exit 1
|
||
fi
|
||
|
||
echo '✅ Docker 环境就绪'
|
||
ENDSSH
|
||
|
||
# 确保服务器目录存在
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "mkdir -p ${SERVER_PATH}/images"
|
||
print_msg "$GREEN" "✅ 服务器目录就绪"
|
||
|
||
# 从 tar 文件加载镜像
|
||
print_step "📥 从 tar 文件加载镜像"
|
||
for SERVICE in "${SERVICES[@]}"; do
|
||
print_msg "$YELLOW" "加载 ${SERVICE}..."
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
docker load -i ${SERVER_PATH}/images/${SERVICE}.tar
|
||
"
|
||
print_msg "$GREEN" "✅ ${SERVICE} 加载完成"
|
||
done
|
||
|
||
# 打 latest 标签
|
||
print_msg "$YELLOW" "打 latest 标签..."
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
for service in ${SERVICES[*]}; do
|
||
docker tag topfans/\${service}:latest topfans/\${service}:v${version}
|
||
docker tag topfans/\${service}:latest topfans/\${service}:latest
|
||
done
|
||
echo '标签完成'
|
||
"
|
||
|
||
# 停止旧服务
|
||
print_msg "$YELLOW" "停止旧服务..."
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
cd ${SERVER_PATH} && \
|
||
docker-compose -f docker-compose.prod.yml down 2>/dev/null || true
|
||
"
|
||
|
||
# 启动新服务
|
||
print_msg "$YELLOW" "启动服务..."
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
cd ${SERVER_PATH} && \
|
||
docker-compose -f docker-compose.prod.yml --profile prod up -d
|
||
"
|
||
|
||
# 等待并检查
|
||
print_msg "$YELLOW" "等待服务启动 (15s)..."
|
||
sleep 15
|
||
|
||
print_step "📊 部署结果"
|
||
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
echo ''
|
||
docker-compose -f docker-compose.prod.yml ps
|
||
echo ''
|
||
echo -n 'Gateway 健康检查: '
|
||
curl -s http://localhost:8080/health > /dev/null && echo '✅ OK' || echo '⚠️ 检查失败'
|
||
"
|
||
|
||
print_msg "$GREEN" "✅ 远程部署完成"
|
||
}
|
||
|
||
# ==================== 4. 回滚 ====================
|
||
do_rollback() {
|
||
local version=$1
|
||
|
||
if [ -z "$SERVER_HOST" ]; then
|
||
print_msg "$RED" "错误: 请设置 SERVER_HOST"
|
||
exit 1
|
||
fi
|
||
|
||
print_step "🔄 回滚到版本 v${version}"
|
||
|
||
print_msg "$YELLOW" "正在回滚 ${SERVER_HOST} 上的服务..."
|
||
|
||
# 停止服务
|
||
print_msg "$YELLOW" "停止服务..."
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
cd ${SERVER_PATH} && \
|
||
docker-compose -f docker-compose.prod.yml down
|
||
"
|
||
|
||
# 从已有的 tar 文件加载镜像并打标签
|
||
for SERVICE in "${SERVICES[@]}"; do
|
||
print_msg "$YELLOW" "加载 ${SERVICE}:v${version}..."
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
docker load -i ${SERVER_PATH}/images/${SERVICE}.tar
|
||
docker tag topfans/${SERVICE}:latest topfans/${SERVICE}:v${version}
|
||
docker tag topfans/${SERVICE}:latest topfans/${SERVICE}:latest
|
||
"
|
||
print_msg "$GREEN" "✅ ${SERVICE} 回滚完成"
|
||
done
|
||
|
||
# 启动服务
|
||
print_msg "$YELLOW" "启动服务..."
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
cd ${SERVER_PATH} && \
|
||
docker-compose -f docker-compose.prod.yml --profile prod up -d
|
||
"
|
||
|
||
sleep 10
|
||
|
||
print_step "📊 回滚结果"
|
||
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
echo ''
|
||
docker-compose -f docker-compose.prod.yml ps
|
||
echo ''
|
||
echo -n 'Gateway 健康检查: '
|
||
curl -s http://localhost:8080/health > /dev/null && echo '✅ OK' || echo '⚠️ 检查失败'
|
||
"
|
||
|
||
print_msg "$GREEN" "✅ 回滚完成!当前版本: v${version}"
|
||
}
|
||
|
||
# ==================== 5. 查看历史 ====================
|
||
do_history() {
|
||
if [ -z "$SERVER_HOST" ]; then
|
||
print_msg "$RED" "错误: 请设置 SERVER_HOST"
|
||
exit 1
|
||
fi
|
||
|
||
print_step "📜 部署历史"
|
||
|
||
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
|
||
if [ -f ${SERVER_PATH}/deploy_history.json ]; then
|
||
cat ${SERVER_PATH}/deploy_history.json
|
||
else
|
||
echo '暂无部署历史'
|
||
fi
|
||
"
|
||
|
||
echo ""
|
||
print_msg "$YELLOW" "查看实时日志: ssh ${SERVER_USER}@${SERVER_HOST} 'docker-compose -f ${SERVER_PATH}/docker-compose.prod.yml logs -f'"
|
||
}
|
||
|
||
# ==================== 6. 清理本地镜像 ====================
|
||
do_clean() {
|
||
print_step "🧹 清理本地镜像"
|
||
|
||
print_msg "$RED" "警告: 将删除所有 topfans 镜像"
|
||
print_msg "$YELLOW" "列出当前镜像:"
|
||
|
||
docker images | grep topfans || echo "无 topfans 镜像"
|
||
|
||
echo ""
|
||
read -p "确认删除? (y/N): " confirm
|
||
if [ "$confirm" != "y" ]; then
|
||
print_msg "$YELLOW" "已取消"
|
||
exit 0
|
||
fi
|
||
|
||
docker images | grep topfans | awk '{print $3}' | xargs -r docker rmi -f
|
||
print_msg "$GREEN" "✅ 清理完成"
|
||
}
|
||
|
||
# ==================== 主函数 ====================
|
||
main() {
|
||
if [ $# -eq 0 ]; then
|
||
show_help
|
||
exit 0
|
||
fi
|
||
|
||
local command=$1
|
||
shift
|
||
|
||
local version=""
|
||
local skip_build=false
|
||
local skip_push=false
|
||
local force=false
|
||
|
||
# 解析剩余参数
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
--server)
|
||
SERVER_HOST="$2"
|
||
shift 2
|
||
;;
|
||
--skip-build)
|
||
skip_build=true
|
||
shift
|
||
;;
|
||
--skip-push)
|
||
skip_push=true
|
||
shift
|
||
;;
|
||
--force)
|
||
force=true
|
||
shift
|
||
;;
|
||
-h|--help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
v*)
|
||
version="${1#v}"
|
||
shift
|
||
;;
|
||
*)
|
||
echo -e "${RED}错误: 未知参数 '$1'${NC}"
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 检查配置
|
||
check_config || true
|
||
|
||
case $command in
|
||
build)
|
||
# 构建 + 推送
|
||
if [ -z "$version" ]; then
|
||
echo -e "${RED}错误: 请指定版本号${NC}"
|
||
echo "用法: $0 build v1.0.0"
|
||
exit 1
|
||
fi
|
||
|
||
echo -e "${CYAN}版本: v${version}${NC}"
|
||
echo -e "${CYAN}目标: ${SERVER_USER}@${SERVER_HOST}${NC}"
|
||
|
||
if [ "$skip_build" = false ]; then
|
||
do_build
|
||
fi
|
||
|
||
if [ "$skip_push" = false ]; then
|
||
do_push "$version"
|
||
fi
|
||
;;
|
||
|
||
deploy)
|
||
# 远程部署
|
||
if [ -z "$version" ]; then
|
||
echo -e "${RED}错误: 请指定版本号${NC}"
|
||
echo "用法: $0 deploy v1.0.0"
|
||
exit 1
|
||
fi
|
||
|
||
echo -e "${CYAN}版本: v${version}${NC}"
|
||
echo -e "${CYAN}目标: ${SERVER_USER}@${SERVER_HOST}${NC}"
|
||
|
||
do_deploy "$version"
|
||
;;
|
||
|
||
rollback)
|
||
# 回滚
|
||
if [ -z "$version" ]; then
|
||
echo -e "${RED}错误: 请指定要回滚的版本${NC}"
|
||
echo "用法: $0 rollback v1.0.0"
|
||
exit 1
|
||
fi
|
||
|
||
echo -e "${RED}⚠️ 确认回滚到 v${version}?${NC}"
|
||
[ "$force" = false ] && read -p "确认? (y/N): " confirm && [ "$confirm" != "y" ] && exit 0
|
||
|
||
do_rollback "$version"
|
||
;;
|
||
|
||
history)
|
||
do_history
|
||
;;
|
||
|
||
clean)
|
||
do_clean
|
||
;;
|
||
|
||
all)
|
||
# 构建 + 推送 + 部署
|
||
if [ -z "$version" ]; then
|
||
echo -e "${RED}错误: 请指定版本号${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
if [ -z "$SERVER_HOST" ]; then
|
||
echo -e "${RED}错误: 请设置 SERVER_HOST 或使用 --server 参数${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
echo -e "${MAGENTA}一键部署流程:${NC}"
|
||
echo " 1. 构建镜像"
|
||
echo " 2. 打包传输到服务器"
|
||
echo " 3. 部署到 ${SERVER_HOST}"
|
||
echo ""
|
||
|
||
[ "$force" = false ] && read -p "继续? (y/N): " confirm && [ "$confirm" != "y" ] && exit 0
|
||
|
||
do_build
|
||
do_push "$version"
|
||
do_deploy "$version"
|
||
|
||
print_step "🎉 全部完成!"
|
||
;;
|
||
|
||
*)
|
||
echo -e "${RED}错误: 未知命令 '$command'${NC}"
|
||
show_help
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
main "$@"
|