Files
Nexus/deploy/mysql_dump_to_file.sh
T
2026-07-08 22:31:31 +08:00

197 lines
5.0 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Nexus — MySQL dumpDocker 1Panel / 宿主机 mysqldump 通用)
#
# bash deploy/mysql_dump_to_file.sh --output /path/dump.sql [--root /opt/nexus]
# bash deploy/mysql_dump_to_file.sh --output /path/dump.sql --db-url 'mysql+aiomysql://...'
#
# 策略(按序):
# 1. DB host 为运行中容器名 → docker exec 该容器内 mysqldump(连 127.0.0.1
# 2. 宿主机有 mysqldump 且 host 可解析 → 本机 mysqldump
# 3. 1panel-network 存在 → docker run --rm mysql:8 客户端
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
NEXUS_ROOT="${NEXUS_ROOT:-/opt/nexus}"
OUTPUT=""
DB_URL=""
usage() {
cat <<'EOF'
用法: mysql_dump_to_file.sh --output FILE.sql [--root /opt/nexus] [--db-url URL]
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--output|-o) OUTPUT="$2"; shift 2 ;;
--root) NEXUS_ROOT="$2"; shift 2 ;;
--db-url) DB_URL="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "未知参数: $1" >&2; usage; exit 1 ;;
esac
done
if [[ -z "$OUTPUT" ]]; then
echo "缺少 --output" >&2
usage
exit 1
fi
resolve_db_url() {
if [[ -n "$DB_URL" ]]; then
echo "$DB_URL"
return 0
fi
local cname url
cname="$(docker ps --format '{{.Names}}' 2>/dev/null | grep -E 'nexus-prod-nexus|nexus-nexus-1' | head -1 || true)"
if [[ -n "$cname" ]]; then
url="$(docker exec "$cname" grep -E '^NEXUS_DATABASE_URL=' /app/.env 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'" || true)"
[[ -n "$url" ]] && echo "$url" && return 0
fi
for f in \
"${NEXUS_ROOT}/docker/.env.prod" \
"${NEXUS_ROOT}/.env" \
; do
[[ -f "$f" ]] || continue
url="$(grep -E '^NEXUS_DATABASE_URL=' "$f" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'" || true)"
[[ -n "$url" ]] && echo "$url" && return 0
done
return 1
}
DB_URL="$(resolve_db_url || true)"
if [[ -z "$DB_URL" ]]; then
echo "未找到 NEXUS_DATABASE_URL" >&2
exit 1
fi
python3 - "$DB_URL" "$OUTPUT" <<'PY'
import shutil
import subprocess
import sys
import urllib.parse
def docker_names() -> set[str]:
r = subprocess.run(
["docker", "ps", "--format", "{{.Names}}"],
capture_output=True,
text=True,
check=False,
)
return {ln.strip() for ln in (r.stdout or "").splitlines() if ln.strip()}
def network_exists(name: str) -> bool:
return (
subprocess.run(
["docker", "network", "inspect", name],
capture_output=True,
check=False,
).returncode
== 0
)
def write_dump(cmd: list[str], env: dict[str, str], out: str) -> int:
r = subprocess.run(cmd, env=env, capture_output=True, text=True)
if r.returncode != 0:
sys.stderr.write(r.stderr or r.stdout or "mysqldump failed\n")
return r.returncode
with open(out, "w", encoding="utf-8") as fh:
fh.write(r.stdout)
return 0
def dump_flags(db: str) -> list[str]:
return [
"--single-transaction",
"--routines",
"--triggers",
"--set-gtid-purged=OFF",
db,
]
url = sys.argv[1].replace("mysql+aiomysql://", "mysql://", 1)
url = url.replace("mysql+asyncmy://", "mysql://", 1)
out = sys.argv[2]
p = urllib.parse.urlparse(url)
user = urllib.parse.unquote(p.username or "")
password = urllib.parse.unquote(p.password or "")
host = p.hostname or "127.0.0.1"
if host == "host.docker.internal":
host = "127.0.0.1"
port = str(p.port or 3306)
db = (p.path or "/nexus").lstrip("/").split("?")[0]
env = {**__import__("os").environ, "MYSQL_PWD": password}
names = docker_names()
# 1) MySQL 跑在 Docker 容器内(1Panel 常见:URL host = 容器名)
if host in names:
cmd = [
"docker",
"exec",
"-e",
f"MYSQL_PWD={password}",
host,
"mysqldump",
"-h",
"127.0.0.1",
"-P",
port,
"-u",
user,
*dump_flags(db),
]
rc = write_dump(cmd, env, out)
if rc == 0:
sys.exit(0)
sys.stderr.write(f"docker exec {host} mysqldump 失败,尝试其它方式…\n")
# 2) 宿主机 mysqldump
if shutil.which("mysqldump"):
cmd = [
"mysqldump",
"-h",
host,
"-P",
port,
"-u",
user,
*dump_flags(db),
]
rc = write_dump(cmd, env, out)
if rc == 0:
sys.exit(0)
# 3) 临时 mysql 客户端容器(1panel-network
for net in ("1panel-network", "bridge"):
if not network_exists(net):
continue
cmd = [
"docker",
"run",
"--rm",
"--network",
net,
"-e",
f"MYSQL_PWD={password}",
"mysql:8.0",
"mysqldump",
"-h",
host,
"-P",
port,
"-u",
user,
*dump_flags(db),
]
rc = write_dump(cmd, env, out)
if rc == 0:
sys.exit(0)
sys.stderr.write("所有 mysqldump 策略均失败(无宿主机客户端且 Docker 回退不可用)\n")
sys.exit(1)
PY