#!/usr/bin/env bash # 拉取代码并重启:缺什么自动安装(curl、Git、Docker、Docker Compose),再 git 拉取 + docker compose 构建启动 # 用法:cd 项目根 && ./pull-and-restart.sh(若 Permission denied 则先执行 bash pull-and-restart.sh 或 chmod +x pull-and-restart.sh restart.sh) # 行尾:LF set -e ROOT="${PROJECT_ROOT:-$(cd "$(dirname "$0")" && pwd)}" cd "$ROOT" # 拉取后可能无可执行权限,先自修复以便下次可直接 ./ 执行 chmod +x "$ROOT/pull-and-restart.sh" "$ROOT/restart.sh" 2>/dev/null || true run_sudo() { sudo "$@"; } # ---------- 检测并安装 curl(下载 Docker Compose 等需要)---------- ensure_curl() { if command -v curl >/dev/null 2>&1; then return 0 fi echo "未检测到 curl,正在安装..." if command -v apt-get >/dev/null 2>&1; then run_sudo apt-get update -qq run_sudo apt-get install -y curl elif command -v dnf >/dev/null 2>&1; then run_sudo dnf install -y curl elif command -v yum >/dev/null 2>&1; then run_sudo yum install -y curl else echo "无法自动安装 curl,请先安装 curl 后重试." exit 1 fi echo "curl 已安装." } # ---------- 检测并安装 Git(国内服务器用系统源)---------- ensure_git() { if command -v git >/dev/null 2>&1; then return 0 fi echo "未检测到 Git,正在安装..." if command -v apt-get >/dev/null 2>&1; then run_sudo apt-get update -qq run_sudo apt-get install -y git elif command -v dnf >/dev/null 2>&1; then run_sudo dnf install -y git elif command -v yum >/dev/null 2>&1; then run_sudo yum install -y git else echo "无法自动安装 Git,请先安装 Git 后重试." exit 1 fi echo "Git 已安装." } # ---------- 检测并安装 Docker(用 run_sudo 检测,与后续 compose 一致;支持 Podman 兼容层)---------- ensure_docker() { if command -v docker >/dev/null 2>&1 && run_sudo docker info >/dev/null 2>&1; then echo "Docker 已就绪." return 0 fi if command -v docker >/dev/null 2>&1; then echo "Docker/Podman 守护进程未连接,尝试启动..." run_sudo systemctl start podman 2>/dev/null || true run_sudo systemctl start docker 2>/dev/null || true if run_sudo docker info >/dev/null 2>&1; then echo "Docker 已就绪." return 0 fi echo "错误:无法连接 Docker/Podman 守护进程,请执行: sudo systemctl start podman 或 sudo systemctl start docker" >&2 exit 1 fi echo "未检测到 Docker 或未启动,正在安装..." if command -v apt-get >/dev/null 2>&1; then run_sudo apt-get update -qq run_sudo apt-get install -y docker.io docker-compose-plugin 2>/dev/null || run_sudo apt-get install -y docker.io docker-compose run_sudo systemctl start docker run_sudo systemctl enable docker elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then if command -v dnf >/dev/null 2>&1; then run_sudo dnf install -y docker else run_sudo yum install -y docker fi run_sudo systemctl start docker run_sudo systemctl enable docker else echo "无法自动安装 Docker,请先安装 Docker 与 Docker Compose 后重试." exit 1 fi echo "Docker 安装完成." } # ---------- 配置 Docker Hub 镜像加速(国内拉取超时时使用 Podman 镜像)---------- ensure_registry_mirror() { REG_CONF_D="/etc/containers/registries.conf.d" REG_MIRROR_CONF="$REG_CONF_D/99-docker-mirror.conf" echo "配置 Docker Hub 镜像加速(Podman)..." run_sudo mkdir -p "$REG_CONF_D" run_sudo tee "$REG_MIRROR_CONF" >/dev/null <<'REGEOF' # 国内 Docker Hub 拉取加速,由 pull-and-restart.sh 生成(多镜像备用) unqualified-search-registries = ["docker.io"] [[registry]] location = "docker.io" [[registry.mirror]] location = "docker.m.daocloud.io" [[registry.mirror]] location = "docker.1ms.run" [[registry.mirror]] location = "docker.xuanyuan.me" REGEOF echo "已写入 $REG_MIRROR_CONF(docker.io 镜像: daocloud / 1ms / xuanyuan)." } # ---------- 检测并安装 Docker Compose(优先插件 docker compose,否则独立二进制)---------- ensure_docker_compose() { run_sudo docker compose version >/dev/null 2>&1 && return 0 command -v docker-compose >/dev/null 2>&1 && return 0 [ -x /usr/local/bin/docker-compose ] && return 0 # 优先尝试用包管理器安装插件,避免独立二进制架构不符导致 Bus error echo "未检测到 Docker Compose,正在尝试安装(优先插件)..." if command -v dnf >/dev/null 2>&1; then run_sudo dnf install -y docker-compose-plugin 2>/dev/null || true if ! run_sudo docker compose version >/dev/null 2>&1; then echo "系统源无插件,尝试添加 Docker CE 源(阿里云镜像)..." run_sudo dnf install -y dnf-plugins-core 2>/dev/null || true run_sudo dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 2>/dev/null || \ run_sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 2>/dev/null || true run_sudo dnf install -y docker-compose-plugin 2>/dev/null || true fi elif command -v yum >/dev/null 2>&1; then run_sudo yum install -y docker-compose-plugin 2>/dev/null || true if ! run_sudo docker compose version >/dev/null 2>&1; then echo "系统源无插件,尝试添加 Docker CE 源(阿里云镜像)..." run_sudo yum install -y yum-utils 2>/dev/null || true run_sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 2>/dev/null || \ run_sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 2>/dev/null || true run_sudo yum install -y docker-compose-plugin 2>/dev/null || true fi elif command -v apt-get >/dev/null 2>&1; then run_sudo apt-get update -qq 2>/dev/null; run_sudo apt-get install -y docker-compose-plugin 2>/dev/null || true fi run_sudo docker compose version >/dev/null 2>&1 && echo "Docker Compose 插件已就绪." && return 0 # 回退:下载独立版(国内 DaoCloud 镜像) echo "正在安装独立版 Docker Compose(国内 DaoCloud 镜像)..." COMPOSE_ARCH="$(uname -m)" case "$COMPOSE_ARCH" in x86_64) COMPOSE_ARCH=x86_64 ;; aarch64|arm64) COMPOSE_ARCH=aarch64 ;; *) COMPOSE_ARCH=x86_64 ;; esac COMPOSE_VER="v2.24.0" COMPOSE_URL_CN="https://get.daocloud.io/docker/compose/releases/download/${COMPOSE_VER}/docker-compose-linux-${COMPOSE_ARCH}" if ! run_sudo curl -sfL --connect-timeout 20 --max-time 90 "$COMPOSE_URL_CN" -o /usr/local/bin/docker-compose; then COMPOSE_URL="https://github.com/docker/compose/releases/download/${COMPOSE_VER}/docker-compose-linux-${COMPOSE_ARCH}" run_sudo curl -sfL --max-time 90 "$COMPOSE_URL" -o /usr/local/bin/docker-compose fi run_sudo chmod +x /usr/local/bin/docker-compose # 若运行即崩溃(如 Bus error),删除以免后续误用 run_sudo /usr/local/bin/docker-compose version >/dev/null 2>&1 || { run_sudo rm -f /usr/local/bin/docker-compose; echo "独立版运行失败(可能架构不符),请尝试: dnf install -y docker-compose-plugin 或 yum install -y docker-compose-plugin" >&2; return 0; } echo "Docker Compose 已安装." } # ---------- 检测并安装 Nginx(反代 + 强制 HTTPS,证书按域名存 /etc/ssl/yh_web/<域名>/)---------- ensure_nginx() { if command -v nginx >/dev/null 2>&1; then return 0 fi echo "未检测到 Nginx,正在安装..." if command -v apt-get >/dev/null 2>&1; then run_sudo apt-get update -qq run_sudo apt-get install -y nginx elif command -v dnf >/dev/null 2>&1; then run_sudo dnf install -y nginx elif command -v yum >/dev/null 2>&1; then run_sudo yum install -y nginx else echo "无法自动安装 Nginx,请手动安装后重试." exit 1 fi run_sudo systemctl enable nginx 2>/dev/null || true run_sudo systemctl start nginx 2>/dev/null || true echo "Nginx 已安装." } ensure_curl ensure_git ensure_docker ensure_docker_compose ensure_registry_mirror ensure_nginx # 确定要用的 compose 命令;测试独立二进制时用 || true 避免 Bus error 导致脚本退出 resolve_compose_cmd() { run_sudo docker compose version >/dev/null 2>&1 && echo "docker compose" && return if [ -x /usr/local/bin/docker-compose ]; then r=0; run_sudo /usr/local/bin/docker-compose version >/dev/null 2>&1 || r=1 if [ "$r" -eq 0 ]; then echo "/usr/local/bin/docker-compose"; return; fi echo "检测到 /usr/local/bin/docker-compose 无法运行(可能架构不符),正在重装..." >&2 run_sudo rm -f /usr/local/bin/docker-compose ensure_docker_compose || true fi run_sudo docker compose version >/dev/null 2>&1 && echo "docker compose" && return run_sudo docker-compose version >/dev/null 2>&1 && echo "docker-compose" && return ensure_docker_compose || true run_sudo docker compose version >/dev/null 2>&1 && echo "docker compose" && return if [ -x /usr/local/bin/docker-compose ]; then r=0; run_sudo /usr/local/bin/docker-compose version >/dev/null 2>&1 || r=1 [ "$r" -eq 0 ] && echo "/usr/local/bin/docker-compose" || echo "" else echo "" fi } COMPOSE_CMD="" compose_cmd() { if [ -z "$COMPOSE_CMD" ]; then COMPOSE_CMD="$(resolve_compose_cmd)" fi if [ -z "$COMPOSE_CMD" ]; then echo "错误:无法找到 docker compose 或 docker-compose,请手动安装到 /usr/local/bin/docker-compose" exit 1 fi # sudo 默认不传环境变量,显式传入以便 compose 使用 REGISTRY_MIRROR / GOPROXY run_sudo env REGISTRY_MIRROR="${REGISTRY_MIRROR}" GOPROXY="${GOPROXY}" $COMPOSE_CMD "$@" } echo "==========================================" echo " yh_web 拉取并重启" echo " 路径: $ROOT" echo "==========================================" # 环境配置:缺失时从 server/.env.example 复制(Docker 部署用 mongo:27017) if [ ! -f server/.env ]; then if [ -f server/.env.example ]; then cp server/.env.example server/.env echo "已从 server/.env.example 创建 server/.env(可按需修改)." else mkdir -p server NGINX_DEFAULT_DOMAIN="${NGINX_DOMAIN:-yuheng.yuxindazhineng.com}" cat > server/.env </dev/null 2>&1; then git fetch origin --progress git reset --hard "origin/$BRANCH" else echo "未检测到 Git 仓库,正在克隆..." export GIT_TERMINAL_PROMPT=0 # 默认仓库地址(仅本地/服务器使用,不提交到仓库;可覆盖:export GIT_REPO_URL=...) REPO_URL="${GIT_REPO_URL:-https://whm:02f8ceeee5f1aeb197ff400e4d97abbcf5550015@gitea.yuxindazhineng.com/whm/web.git}" SELF="$(basename "$0")" tmp_backup="/tmp/yh_web_deploy_$$" mkdir -p "$tmp_backup" [ -f "$SELF" ] && cp -a "$SELF" "$tmp_backup/" [ -f server/.env ] && cp -a server/.env "$tmp_backup/" 2>/dev/null || true git init -b "$BRANCH" git remote add origin "$REPO_URL" git fetch origin --progress git reset --hard "origin/$BRANCH" [ -f "$tmp_backup/$SELF" ] && cp -a "$tmp_backup/$SELF" "$SELF" && chmod +x "$SELF" [ -f "$tmp_backup/.env" ] && mkdir -p server && cp -a "$tmp_backup/.env" server/.env rm -rf "$tmp_backup" fi echo "" echo "[2/3] 重新构建并启动..." # 宿主机 9527 常被 sshd 占用,compose 必须使用 8088 且 api 不映射宿主机端口 if grep -q '9527' "$ROOT/docker-compose.yml" 2>/dev/null; then echo "错误: 当前 docker-compose.yml 仍含 9527,会与 sshd 冲突导致启动失败。请以 Gitea 为准拉取最新代码后再执行本脚本:" >&2 echo " git fetch origin && git reset --hard origin/master" >&2 echo "若 Gitea 上已为 8088 仍报错,请本地提交并 push 后再在服务器执行上述命令。" >&2 exit 1 fi export GOPROXY="${GOPROXY:-https://goproxy.cn,direct}" # 国内直连 Docker Hub 超时时,用镜像拉取基础镜像(Dockerfile FROM 与 mongo 镜像) export REGISTRY_MIRROR="${REGISTRY_MIRROR:-docker.m.daocloud.io/library/}" compose_cmd build --no-cache # 先显式从镜像站拉 mongo,避免 compose up 时直连 Docker Hub 超时 echo "拉取 mongo 镜像..." run_sudo docker pull "${REGISTRY_MIRROR}mongo:7" || true # 证书目录在 compose up 前就要就绪(compose 内 nginx 容器会挂载) NGINX_DOMAIN="${NGINX_DOMAIN:-yuheng.yuxindazhineng.com}" NGINX_SSL_DIR="/etc/ssl/yh_web/$NGINX_DOMAIN" run_sudo mkdir -p "$NGINX_SSL_DIR" if [ -f "$ROOT/nginx/$NGINX_DOMAIN.pem" ] && [ -f "$ROOT/nginx/$NGINX_DOMAIN.key" ]; then run_sudo cp -f "$ROOT/nginx/$NGINX_DOMAIN.pem" "$NGINX_SSL_DIR/fullchain.pem" run_sudo cp -f "$ROOT/nginx/$NGINX_DOMAIN.key" "$NGINX_SSL_DIR/privkey.pem" run_sudo chmod 644 "$NGINX_SSL_DIR/fullchain.pem" run_sudo chmod 600 "$NGINX_SSL_DIR/privkey.pem" elif [ -f "$ROOT/nginx/fullchain.pem" ] && [ -f "$ROOT/nginx/privkey.pem" ]; then run_sudo cp -f "$ROOT/nginx/fullchain.pem" "$ROOT/nginx/privkey.pem" "$NGINX_SSL_DIR/" run_sudo chmod 644 "$NGINX_SSL_DIR/fullchain.pem" run_sudo chmod 600 "$NGINX_SSL_DIR/privkey.pem" elif [ -f "$ROOT/nginx/$NGINX_DOMAIN/fullchain.pem" ] && [ -f "$ROOT/nginx/$NGINX_DOMAIN/privkey.pem" ]; then run_sudo cp -f "$ROOT/nginx/$NGINX_DOMAIN/fullchain.pem" "$ROOT/nginx/$NGINX_DOMAIN/privkey.pem" "$NGINX_SSL_DIR/" run_sudo chmod 644 "$NGINX_SSL_DIR/fullchain.pem" run_sudo chmod 600 "$NGINX_SSL_DIR/privkey.pem" fi compose_cmd down 2>/dev/null || true compose_cmd up -d --force-recreate echo "" echo "[3/3] 证书与宿主机 Nginx(可选)..." NGINX_CONF_NAME="${NGINX_DOMAIN}.conf" if [ -f "$ROOT/nginx/$NGINX_CONF_NAME" ]; then run_sudo cp -f "$ROOT/nginx/$NGINX_CONF_NAME" /etc/nginx/conf.d/ 2>/dev/null || true if run_sudo nginx -t 2>/dev/null; then run_sudo systemctl reload nginx 2>/dev/null && echo "宿主机 Nginx 已重载." || true fi fi echo "" echo "完成. 对外仅 443;反代: https://$NGINX_DOMAIN"