You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
8.8 KiB
184 lines
8.8 KiB
#!/usr/bin/env bash
|
|
# <服务> 部署脚本(每个服务目录各一份,服务名取自所在目录名)
|
|
#
|
|
# 用法: ./deploy.sh <env> [action] (在 deploy/<服务>/ 下运行)
|
|
# env : local | dev | prod
|
|
# action : dev/prod → deploy(默认) | up | build | pull | down | restart | logs | ps
|
|
# local → up(默认) | build | pull | down | restart | logs | ps
|
|
#
|
|
# deploy = 构建推送镜像 + 同步配置 + 远程 pull + up(一键发布,仅 dev/prod)
|
|
# up = 远程:仅同步配置 + 远程 pull + up(用已推送镜像);本机:起容器
|
|
#
|
|
# 例(在 deploy/console/ 下):
|
|
# ./deploy.sh prod # 一键发布生产 console
|
|
# ./deploy.sh prod logs # 看生产 console 日志
|
|
# ./deploy.sh local up # 本机起 console
|
|
# ./deploy.sh dev build # 只构建推送 dev 镜像
|
|
set -euo pipefail
|
|
|
|
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 本服务部署目录(deploy/<服务>)
|
|
SERVICE="$(basename "$SVC_DIR")" # 服务名 = 目录名(console / admin)
|
|
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根(deploy/<服务>/../..)
|
|
|
|
blue() { printf "\033[34m%s\033[0m\n" "$*"; }
|
|
green() { printf "\033[32m%s\033[0m\n" "$*"; }
|
|
red() { printf "\033[31m%s\033[0m\n" "$*"; }
|
|
|
|
# ╔══════════════════════════════════════════════════════════════════════╗
|
|
# ║ 部署目标 / 私有仓库配置 —— 写死在这里,改这里即可(按环境) ║
|
|
# ║ console、admin 同机不同目录;DEPLOY_DIR 自动 = /opt/yunyan/<服务名> ║
|
|
# ║ 注意:本文件纳入 git,下面的密码/IP 会随仓库提交(私有仓库内可接受) ║
|
|
# ╚══════════════════════════════════════════════════════════════════════╝
|
|
deploy_profile() {
|
|
DEPLOY_HOST=""; REGISTRY_PASS="" # 占位,local 用不到;dev/prod 在下面填
|
|
# —— 各环境通用默认 ——
|
|
REGISTRY=docker-registry.ideapsound.com # 私有镜像仓库地址
|
|
REGISTRY_USER=admin # 仓库账号
|
|
DEPLOY_PORT=22 # 服务器 SSH 端口
|
|
DEPLOY_USER=root # 服务器 SSH 用户
|
|
DEPLOY_KEY=~/.ssh/id_rsa # 本机访问服务器的 SSH 私钥路径
|
|
case "$ENV_NAME" in
|
|
dev)
|
|
DEPLOY_HOST="" # ← 开发服务器 IP(必填)
|
|
REGISTRY_PASS="" # ← 私有仓库密码(必填)
|
|
;;
|
|
prod)
|
|
DEPLOY_HOST="" # ← 生产服务器 IP(必填)
|
|
REGISTRY_PASS="" # ← 私有仓库密码(必填)
|
|
;;
|
|
local) ;; # 本机不走 SSH,无需填
|
|
esac
|
|
DEPLOY_DIR="/opt/yunyan/$SERVICE" # 远程部署目录(自动按服务名)
|
|
}
|
|
|
|
usage() {
|
|
cat >&2 <<EOF
|
|
用法: ./deploy.sh <env> [action] (在 deploy/$SERVICE/ 下运行)
|
|
env : local | dev | prod
|
|
action : dev/prod → deploy(默认) | up | build | pull | down | restart | logs | ps
|
|
local → up(默认) | build | pull | down | restart | logs | ps
|
|
例:
|
|
./deploy.sh prod # 一键发布生产 $SERVICE
|
|
./deploy.sh prod logs # 看生产 $SERVICE 日志
|
|
./deploy.sh local up # 本机起 $SERVICE
|
|
./deploy.sh dev build # 只构建推送 dev 镜像
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
# ── 参数解析与校验 ──
|
|
ENV_NAME="${1:-}"; ACTION="${2:-}"
|
|
[ -n "$ENV_NAME" ] || usage
|
|
[ -f "$SVC_DIR/docker-compose.yml" ] || { red "✗ $SERVICE 目录缺 docker-compose.yml(deploy.sh 须放在服务目录内)"; exit 1; }
|
|
case "$ENV_NAME" in
|
|
local|dev|prod) ;;
|
|
*) red "✗ 未知环境: ${ENV_NAME}(应为 local|dev|prod)"; usage ;;
|
|
esac
|
|
# 默认动作:远程发布默认 deploy,本机默认 up
|
|
[ -n "$ACTION" ] || { [ "$ENV_NAME" = local ] && ACTION=up || ACTION=deploy; }
|
|
|
|
# 从 ENV_FILE 安全读取单个变量值(去除可能的外层双引号)
|
|
_envget() {
|
|
{ grep -E "^$1=" "$ENV_FILE" || true; } | head -1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//'
|
|
}
|
|
|
|
# 载入 env/<环境>.env —— 现在只放容器运行时配置(DSN / Redis / NATS / 密钥 / 端口 / TAG / 网络)。
|
|
# 部署目标与仓库凭据已写死在 deploy_profile,不再放 env 文件。脚本只需从这里取 compose 仍要的 TAG/网络。
|
|
load_env() {
|
|
ENV_FILE="$SVC_DIR/env/$ENV_NAME.env"
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
red "✗ 缺少 $SERVICE/env/$ENV_NAME.env —— 请先从 $SERVICE/env/$ENV_NAME.env.example 复制并填写"
|
|
exit 1
|
|
fi
|
|
TAG="$(_envget TAG)"; TAG="${TAG:-latest}"
|
|
DOCKER_NETWORK="$(_envget DOCKER_NETWORK)"; DOCKER_NETWORK="${DOCKER_NETWORK:-1panel-network}"
|
|
}
|
|
|
|
# 生成 compose 用 .env = 服务运行时 env 文件 + 注入写死的 REGISTRY(镜像地址替换用)
|
|
# 先滤掉源文件里可能残留的 REGISTRY 行,保证唯一、不依赖 compose 的覆盖顺序。
|
|
gen_env() {
|
|
{ grep -v '^REGISTRY=' "$ENV_FILE" || true; } > "$SVC_DIR/.env"
|
|
printf 'REGISTRY=%s\n' "$REGISTRY" >> "$SVC_DIR/.env"
|
|
}
|
|
|
|
# 构建并推送本服务镜像(复用仓库根目录 build.sh,目标 = 服务名)。
|
|
# local 环境只构建不推送(PUSH=0),dev/prod 构建并推送到私有仓库。
|
|
build_push() {
|
|
local push=1; [ "$ENV_NAME" = local ] && push=0
|
|
blue ">>> 构建$([ "$push" = 1 ] && echo 推送) $SERVICE 镜像 (TAG=${TAG:-latest}, PUSH=$push)"
|
|
TAG="${TAG:-latest}" REGISTRY="$REGISTRY" PUSH="$push" "$REPO_ROOT/build.sh" "$SERVICE"
|
|
}
|
|
|
|
ensure_network_local() {
|
|
docker network inspect "$DOCKER_NETWORK" >/dev/null 2>&1 || docker network create "$DOCKER_NETWORK"
|
|
}
|
|
|
|
# 本机 docker compose(gen_env 落为同目录 .env,既做变量替换又做容器 env_file)
|
|
compose_local() {
|
|
gen_env
|
|
ensure_network_local
|
|
( cd "$SVC_DIR" && docker compose --env-file .env "$@" )
|
|
}
|
|
|
|
# ── 远程 SSH 基础 ──
|
|
_ssh() { ssh -p "${DEPLOY_PORT:-22}" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "${DEPLOY_USER:-root}@${DEPLOY_HOST}" "$@"; }
|
|
_scp() { scp -P "${DEPLOY_PORT:-22}" -i "${DEPLOY_KEY/#\~/$HOME}" -o StrictHostKeyChecking=accept-new "$@"; }
|
|
_dest() { echo "${DEPLOY_USER:-root}@${DEPLOY_HOST}:$1"; }
|
|
|
|
# 远程发布:同步配置 + 远程 login + pull + up
|
|
remote_deploy() {
|
|
: "${DEPLOY_HOST:?请在 deploy.sh 顶部 deploy_profile 的 $ENV_NAME 段填 DEPLOY_HOST}"
|
|
gen_env
|
|
|
|
blue ">>> 同步 $SERVICE 配置到 ${DEPLOY_USER:-root}@${DEPLOY_HOST}:${DEPLOY_DIR}"
|
|
_ssh "mkdir -p '$DEPLOY_DIR'"
|
|
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEPLOY_DIR/docker-compose.yml")"
|
|
_scp "$SVC_DIR/.env" "$(_dest "$DEPLOY_DIR/.env")"
|
|
# 带 confs/ 的服务(console)一并同步配置并预建 log 目录
|
|
if [ -d "$SVC_DIR/confs" ]; then
|
|
_ssh "mkdir -p '$DEPLOY_DIR/confs' '$DEPLOY_DIR/log'"
|
|
_scp -r "$SVC_DIR/confs/." "$(_dest "$DEPLOY_DIR/confs/")"
|
|
fi
|
|
|
|
blue ">>> 远程拉起 $SERVICE"
|
|
[ -n "${REGISTRY_PASS:-}" ] && \
|
|
_ssh "docker login '$REGISTRY' -u '${REGISTRY_USER:-admin}' -p '$REGISTRY_PASS'"
|
|
_ssh "docker network inspect '$DOCKER_NETWORK' >/dev/null 2>&1 || docker network create '$DOCKER_NETWORK'"
|
|
_ssh "cd '$DEPLOY_DIR' && docker compose --env-file .env pull && docker compose --env-file .env up -d --force-recreate"
|
|
}
|
|
|
|
# 远程运维(down/restart/logs/ps/pull)
|
|
remote_compose() {
|
|
: "${DEPLOY_HOST:?请在 deploy.sh 顶部 deploy_profile 的 $ENV_NAME 段填 DEPLOY_HOST}"
|
|
_ssh "cd '$DEPLOY_DIR' && docker compose --env-file .env $*"
|
|
}
|
|
|
|
# ── 分发 ──
|
|
deploy_profile # 写死的部署目标 / 仓库凭据
|
|
load_env # env 文件里的运行时配置(TAG / 网络)
|
|
|
|
if [ "$ENV_NAME" = local ]; then
|
|
case "$ACTION" in
|
|
up) compose_local up -d --force-recreate ;;
|
|
down) compose_local down ;;
|
|
restart) compose_local restart ;;
|
|
logs) compose_local logs -f --tail=120 ;;
|
|
ps) compose_local ps ;;
|
|
pull) compose_local pull ;;
|
|
build) build_push ;;
|
|
*) red "未知操作: ${ACTION}(local 支持 up|down|restart|logs|ps|pull|build)"; exit 1 ;;
|
|
esac
|
|
else
|
|
case "$ACTION" in
|
|
deploy) build_push; remote_deploy ;;
|
|
up) remote_deploy ;;
|
|
build) build_push ;;
|
|
pull) remote_compose pull ;;
|
|
down) remote_compose down ;;
|
|
restart) remote_compose restart ;;
|
|
ps) remote_compose ps ;;
|
|
logs) remote_compose logs -f --tail=120 ;;
|
|
*) red "未知操作: ${ACTION}(dev/prod 支持 deploy|up|build|pull|down|restart|logs|ps)"; exit 1 ;;
|
|
esac
|
|
fi
|
|
green "✓ $SERVICE $ENV_NAME $ACTION 完成"
|
|
|