[存档] 一键脚本:Docker Compose 自动更新 & SOCKS5 部署 & 设置虚拟内存

1.Docker Compose 项目每日自动更新一键脚本

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
PATH=/usr/sbin:/usr/bin:/sbin:/bin

# === 配置:你的 Compose 项目目录 ===
DIRS=(
  "/home/ryan/gemini-balance"
  "/home/ryan/gpt-load"
)

# 如需连数据库服务也更新,把 SKIP_DB_SERVICES 改成 0
SKIP_DB_SERVICES=${SKIP_DB_SERVICES:-1}

# 避免并发(如果上一次没跑完)
exec 9>/var/run/compose-autoupdate.lock
flock -n 9 || { echo "Another run is in progress. Exit."; exit 0; }

# 解析 compose 命令(用数组避免空格问题)
declare -a COMPOSE
if docker compose version >/dev/null 2>&1; then
  COMPOSE=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
  COMPOSE=(docker-compose)
else
  echo "docker compose / docker-compose 未安装"; exit 1
fi

git_as_owner() {
  local dir="$1"; shift
  local owner
  owner=$(stat -c '%U' "$dir" 2>/dev/null || echo "")
  [ -z "$owner" ] && { echo "无法获取目录所有者:$dir"; return 1; }
  sudo -u "$owner" git -C "$dir" "$@"
}

update_one() {
  local d="$1"
  echo -e "\n=============================="
  echo ">>> 更新项目:$d @ $(date -Is)"
  echo "=============================="
  [ -d "$d" ] || { echo "目录不存在:$d"; return 1; }
  cd "$d"

  # 1) Git(有仓库且干净才 pull)
  if [ -d .git ]; then
    if ! git_as_owner "$d" diff --quiet || ! git_as_owner "$d" diff --cached --quiet; then
      echo "⚠️ 检测到未提交改动,跳过 git pull:$d"
    else
      git_as_owner "$d" fetch --all --prune
      local branch
      branch=$(git_as_owner "$d" symbolic-ref --short HEAD 2>/dev/null || echo "")
      if [ -n "$branch" ] && ! git_as_owner "$d" rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1; then
        if git_as_owner "$d" ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then
          git_as_owner "$d" branch --set-upstream-to "origin/$branch" "$branch" || true
        fi
      fi
      if ! git_as_owner "$d" pull --ff-only; then
        echo "⚠️ ff-only pull 失败(可能存在分叉),已跳过 pull。"
      fi
    fi
  else
    echo "ℹ️ 非 git 仓库,跳过 git。"
  fi

  # 2) Compose:只更新应用服务(默认跳过数据库类)
  "${COMPOSE[@]}" config >/dev/null

  local services
  services=$("${COMPOSE[@]}" config --services)
  if [ "${SKIP_DB_SERVICES}" = "1" ]; then
    services=$(echo "$services" | grep -Ev '^(mysql|mariadb|db|postgres|redis)$' || true)
  fi
  # 如果未匹配到(例如单服务项目),直接全量
  if [ -z "$services" ]; then
    services=$("${COMPOSE[@]}" config --services)
  fi

  echo "[Compose] 目标服务:"
  echo "$services" | sed 's/^/  - /'

  echo "[Compose] 拉取镜像…"
  "${COMPOSE[@]}" pull $services

  echo "[Compose] 滚动更新…"
  "${COMPOSE[@]}" up -d --remove-orphans $services

  echo "[Compose] 状态:"
  "${COMPOSE[@]}" ps

  echo "[Compose] 最近 80 行日志:"
  "${COMPOSE[@]}" logs --tail 80 --no-log-prefix $services || true

  echo "✅ 完成:$d"
}

for d in "${DIRS[@]}"; do
  update_one "$d"
done

# 可选:清理悬挂镜像,避免磁盘涨太快
docker image prune -f >/dev/null 2>&1 || true
echo -e "\n🎉 自动更新完成:$(date -Is)"

2.sing-box-yg 专用的 SOCKS5 一键部署脚本

# sing-box-yg 专用:复制这一个块,粘贴进终端即可完成 “SOCKS5 入站 → direct 出站” 独立服务
# 适用:已安装 https://github.com/yonggekkk/sing-box-yg 的 VPS(定制二进制通常在 /etc/s-box/sing-box)
# 用法:
#   1) 如需改端口/账号/监听地址,只需改动最上面的四个变量即可;其余保持不变。
#   2) 选中**整段**复制,粘贴到服务器终端回车执行。

# ======= 可按需修改的 4 个变量 =======
LISTEN_ADDR="0.0.0.0"   # 若仅本机使用改为 127.0.0.1
SOCKS_PORT=41233          # 端口可改为未占用的,如 41234
SOCKS_USER="ryanvan"     # Socks5 用户名
SOCKS_PASS=" "     # Socks5 密码

# ======= 以下内容无需改动,直接粘贴执行 =======
CFG_PATH="/etc/sing-box/socks.json"; UNIT_PATH="/etc/systemd/system/sing-box-socks.service"
SING_BOX_BIN=""; for c in \
  "/etc/s-box/sing-box" \
  "$(command -v sing-box 2>/dev/null || true)" \
  "/usr/local/bin/sing-box" \
  "/usr/bin/sing-box"; do [ -n "$c" ] && [ -x "$c" ] && SING_BOX_BIN="$c" && break; done; \
if [ -z "$SING_BOX_BIN" ]; then echo "[ERR] 未找到 sing-box 可执行文件,请先在 sb 面板(选项 8)安装/切换内核" >&2; exit 1; fi; \

sudo mkdir -p /etc/sing-box; \
sudo tee "$CFG_PATH" >/dev/null <<EOF
{
  "log": { "level": "info" },
  "inbounds": [{
    "type": "socks",
    "tag": "socks-in",
    "listen": "$LISTEN_ADDR",
    "listen_port": $SOCKS_PORT,
    "users": [{ "username": "$SOCKS_USER", "password": "$SOCKS_PASS" }]
  }],
  "outbounds": [{ "type": "direct", "tag": "direct" }],
  "route": { "rules": [{ "inbound": ["socks-in"], "outbound": "direct" }] }
}
EOF

sudo "$SING_BOX_BIN" check -c "$CFG_PATH"; \
sudo tee "$UNIT_PATH" >/dev/null <<EOF
[Unit]
Description=Sing-box SOCKS-only (yg) service
After=network.target

[Service]
ExecStart=$SING_BOX_BIN run -c $CFG_PATH
Restart=always
User=root
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload; \
sudo systemctl enable --now sing-box-socks; \
sleep 0.5; \
sudo systemctl status sing-box-socks --no-pager -l || true; \
if command -v ss >/dev/null 2>&1; then ss -lntp | grep ":$SOCKS_PORT" || true; fi; \
echo "
[OK] SOCKS5 已部署:$LISTEN_ADDR:$SOCKS_PORT  用户: $SOCKS_USER  密码: $SOCKS_PASS"; \
echo "提示:如需限制来源 IP,请执行:sudo ufw allow from <你的公网IP> to any port $SOCKS_PORT proto tcp";

3. 设置虚拟内存一键脚本

# 需 root
cat >/root/init-swap-zram512.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# ================== 可调参数 ==================
SWAP_FILE=${SWAP_FILE:-/swapfile.new}   # 磁盘swap路径
SWAP_SIZE=${SWAP_SIZE:-3G}              # 磁盘swap大小
ZRAM_TARGET_MIB=${ZRAM_TARGET_MIB:-512} # 目标 zram 大小(MiB)
ZRAM_PRIORITY=${ZRAM_PRIORITY:-100}     # zram 优先级
DISKSWAP_PRIORITY=${DISKSWAP_PRIORITY:-0} # 磁盘swap优先级
# =============================================

log() { echo -e "\e[32m[INFO]\e[0m $*"; }
warn(){ echo -e "\e[33m[WARN]\e[0m $*"; }
err() { echo -e "\e[31m[ERR ]\e[0m $*" >&2; }

[ "$(id -u)" -eq 0 ] || { err "请以 root 运行"; exit 1; }
export DEBIAN_FRONTEND=noninteractive

log "1/4 创建并启用 ${SWAP_SIZE} 磁盘Swap: ${SWAP_FILE}"
if [ ! -f "$SWAP_FILE" ]; then
  if ! fallocate -l "$SWAP_SIZE" "$SWAP_FILE" 2>/dev/null; then
    dd if=/dev/zero of="$SWAP_FILE" bs=1M count=$(( ${SWAP_SIZE%G} * 1024 )) status=progress
  fi
fi
chmod 600 "$SWAP_FILE"
mkswap "$SWAP_FILE" >/dev/null 2>&1 || true
# 以指定优先级启用
swapon -p "$DISKSWAP_PRIORITY" "$SWAP_FILE" 2>/dev/null || swapon "$SWAP_FILE"

# fstab 持久化(移除旧行,新增新行)
sed -i "\|^[[:space:]]*${SWAP_FILE}[[:space:]]\+none[[:space:]]\+swap|d" /etc/fstab
grep -q "^${SWAP_FILE} " /etc/fstab || echo "${SWAP_FILE} none swap sw,pri=${DISKSWAP_PRIORITY} 0 0" >> /etc/fstab

log "2/4 安装并配置 zram-tools,目标≈${ZRAM_TARGET_MIB}MiB(LZ4)"
apt-get update -y >/dev/null
apt-get install -y "linux-modules-extra-$(uname -r)" zram-tools >/dev/null || true

# 计算一个接近目标的百分比(用于大多数机器)
MemTotalKB=$(awk '/MemTotal/{print $2}' /proc/meminfo)
# 四舍五入: 百分比 = 目标MiB *1024*100 / MemTotalKB
Percent=$(( (ZRAM_TARGET_MIB*1024*100 + MemTotalKB/2) / MemTotalKB ))
# 设置合理边界,避免异常
[ "$Percent" -lt 10 ] && Percent=10
[ "$Percent" -gt 200 ] && Percent=200

cat >/etc/default/zramswap <<EOF_CFG
PERCENT=${Percent}
ALGO=lz4
PRIORITY=${ZRAM_PRIORITY}
EOF_CFG

systemctl enable --now zramswap >/dev/null 2>&1 || true
sleep 1

# 读取实际大小(MiB)
zsize_bytes=$(cat /sys/block/zram0/disksize 2>/dev/null || echo 0)
zsize_mib=$(( zsize_bytes/1024/1024 ))
log "zramswap 计算结果:${zsize_mib}MiB(目标≈${ZRAM_TARGET_MIB}MiB)"

# 若偏离过大(<目标-16 或 >目标+128),启用“固定 512MiB”兜底方案(自定义unit,重启后持久)
if [ "$zsize_mib" -lt $((ZRAM_TARGET_MIB-16)) ] || [ "$zsize_mib" -gt $((ZRAM_TARGET_MIB+128)) ]; then
  warn "结果偏离较大,改用固定 ${ZRAM_TARGET_MIB}MiB 方案并禁用 zramswap,以确保一致性(含开机自启)"
  systemctl disable --now zramswap >/dev/null 2>&1 || true

  mkdir -p /usr/local/sbin
  cat >/usr/local/sbin/configure-zram-fixed <<'EOSH'
#!/usr/bin/env bash
set -euo pipefail
modprobe zram || true
swapoff /dev/zram0 2>/dev/null || true
echo 1 > /sys/block/zram0/reset 2>/dev/null || true
echo lz4 > /sys/block/zram0/comp_algorithm 2>/dev/null || true
echo __SIZE_BYTES__ > /sys/block/zram0/disksize
mkswap /dev/zram0 >/dev/null
swapon -p __ZPRIORITY__ /dev/zram0
EOSH
  sed -i "s/__SIZE_BYTES__/$((ZRAM_TARGET_MIB*1024*1024))/; s/__ZPRIORITY__/${ZRAM_PRIORITY}/" /usr/local/sbin/configure-zram-fixed
  chmod +x /usr/local/sbin/configure-zram-fixed

  cat >/etc/systemd/system/zram-fixed.service <<'EOFU'
[Unit]
Description=Configure zram0 with fixed size
DefaultDependencies=no
After=local-fs.target
Before=swap.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/configure-zram-fixed
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOFU

  systemctl daemon-reload
  systemctl enable --now zram-fixed.service
fi

log "3/4 设置 vm.swappiness=60(持久)"
echo 'vm.swappiness=60' >/etc/sysctl.d/99-swap-tuning.conf
sysctl --system >/dev/null

log "4/4 最终状态:"
echo
swapon --show
echo
free -h
echo
log "完成!zram 目标≈${ZRAM_TARGET_MIB}MiB,磁盘Swap=${SWAP_SIZE},可重复运行且重启持久。"
EOF

chmod +x /root/init-swap-zram512.sh
bash /root/init-swap-zram512.sh