#!/usr/bin/env bash # yunyan-sas 多环境部署入口 # local —— 本机 docker compose # dev / prod —— SSH 同步配置到目标机后远程 docker compose # # 配置来源: # env/<环境>.env 运行时变量(镜像/网络/DB/Redis/NATS/Console 等) # targets/<环境>.conf 远程目标(SSH 主机/密钥/部署目录/镜像仓库账号) set -euo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$ROOT/.." && pwd)" ENV="${1:-}" ACTION="${2:-}" green() { printf "\033[32m%s\033[0m\n" "$*"; } blue() { printf "\033[34m%s\033[0m\n" "$*"; } red() { printf "\033[31m%s\033[0m\n" "$*"; } usage() { cat <<'EOF' 用法: ./deploy.sh <环境> <操作> 环境: local | dev | prod 操作: up 启动(local 直接起;dev/prod 同步配置后远程起) down 停止并移除容器 pull 拉取最新镜像 restart 重启容器 logs 跟随日志 ps 查看容器状态 build 构建并推送镜像到私有仓库(调用 ../build.sh,TAG 取自 env) deploy 一键发布:build + 同步配置 + 远程 pull + up 示例: ./deploy.sh local up # 本机起服务 ./deploy.sh dev build # 构建推送 dev 镜像 ./deploy.sh dev deploy # 一键发布到开发服务器 ./deploy.sh prod deploy # 一键发布到生产服务器 ./deploy.sh prod logs # 跟随生产日志 EOF } [ -z "$ENV" ] && { usage; exit 1; } case "$ENV" in local|dev|prod) ;; *) red "✗ 未知环境: $ENV"; usage; exit 1 ;; esac [ -z "$ACTION" ] && { usage; exit 1; } ENV_FILE="$ROOT/env/$ENV.env" [ -f "$ENV_FILE" ] || { red "✗ 缺少环境变量文件: deploy/env/$ENV.env(参考 env/example.env)"; exit 1; } # 从 env 取某个 key 的值 env_val() { grep -E "^$1=" "$ENV_FILE" | head -1 | cut -d= -f2-; } TAG="$(env_val TAG)"; TAG="${TAG:-latest}" # ── 构建并推送镜像(复用根目录 build.sh)── do_build() { blue ">>> [$ENV] 构建并推送镜像 (TAG=$TAG)" TAG="$TAG" "$REPO_ROOT/build.sh" all } # ── 本机 compose ── local_compose() { cp "$ENV_FILE" "$ROOT/.env" ( cd "$ROOT" && docker compose --env-file "$ROOT/.env" "$@" ) } local_ensure_network() { local net; net="$(env_val DOCKER_NETWORK)"; net="${net:-1panel-network}" docker network inspect "$net" >/dev/null 2>&1 || docker network create "$net" } # ── 远程目标 ── load_target() { local conf="$ROOT/targets/$ENV.conf" [ -f "$conf" ] || { red "✗ 缺少部署目标: deploy/targets/$ENV.conf(参考 targets/example.conf)"; exit 1; } # shellcheck disable=SC1090 source "$conf" [ -z "${SSH_HOST:-}" ] && { red "✗ targets/$ENV.conf 未配置 SSH_HOST"; exit 1; } [ -z "${REMOTE_DIR:-}" ] && { red "✗ targets/$ENV.conf 未配置 REMOTE_DIR"; exit 1; } local port="${SSH_PORT:-22}" local key="${SSH_KEY/#\~/$HOME}" local common="-i $key -o StrictHostKeyChecking=accept-new" SSH="ssh -p $port $common ${SSH_USER}@${SSH_HOST}" SCP="scp -P $port $common" } remote_sync() { blue ">>> [$ENV] 同步配置到 ${SSH_USER}@${SSH_HOST}:${REMOTE_DIR}" $SSH "mkdir -p '$REMOTE_DIR/console/confs' '$REMOTE_DIR/console/log'" $SCP "$ROOT/docker-compose.yml" "${SSH_USER}@${SSH_HOST}:$REMOTE_DIR/docker-compose.yml" $SCP "$ENV_FILE" "${SSH_USER}@${SSH_HOST}:$REMOTE_DIR/.env" $SCP "$ROOT/console/confs/console.yaml" "${SSH_USER}@${SSH_HOST}:$REMOTE_DIR/console/confs/console.yaml" } remote_ensure_network() { local net; net="$(env_val DOCKER_NETWORK)"; net="${net:-1panel-network}" $SSH "docker network inspect '$net' >/dev/null 2>&1 || docker network create '$net'" } remote_login() { [ -n "${REGISTRY_PASS:-}" ] && \ $SSH "docker login '${REGISTRY:-docker-registry.ideapsound.com}' -u '${REGISTRY_USER:-admin}' -p '$REGISTRY_PASS'" } remote_compose() { $SSH "cd '$REMOTE_DIR' && docker compose --env-file .env $*"; } # ================= 分发 ================= if [ "$ENV" = "local" ]; then case "$ACTION" in up) local_ensure_network; local_compose up -d ;; down) local_compose down ;; pull) local_compose pull ;; restart) local_compose restart ;; logs) local_compose logs -f --tail=120 ;; ps) local_compose ps ;; build) do_build ;; deploy) do_build; local_ensure_network; local_compose pull; local_compose up -d ;; *) red "✗ 未知操作: $ACTION"; usage; exit 1 ;; esac else load_target case "$ACTION" in up) remote_sync; remote_ensure_network; remote_login; remote_compose pull; remote_compose up -d ;; down) remote_compose down ;; pull) remote_login; remote_compose pull ;; restart) remote_compose restart ;; logs) remote_compose logs -f --tail=120 ;; ps) remote_compose ps ;; build) do_build ;; deploy) do_build; remote_sync; remote_ensure_network; remote_login; remote_compose pull; remote_compose up -d ;; *) red "✗ 未知操作: $ACTION"; usage; exit 1 ;; esac fi green "✓ [$ENV] $ACTION 完成"