11
This commit is contained in:
parent
ef56008ccb
commit
3446b08781
@ -140,6 +140,9 @@ bash scripts/migrate_container_db_to_volume.sh
|
||||
# 指定旧容器名
|
||||
SOURCE_CONTAINER=old-alphax-web bash scripts/migrate_container_db_to_volume.sh
|
||||
|
||||
# 旧容器已经停止也可以复制主 DB(如果仍在运行,脚本会用更安全的 SQLite backup)
|
||||
ALLOW_STOPPED=1 SOURCE_CONTAINER=old-alphax-web bash scripts/migrate_container_db_to_volume.sh
|
||||
|
||||
# 只复制,不自动重启 compose 服务
|
||||
RECREATE=0 bash scripts/migrate_container_db_to_volume.sh
|
||||
|
||||
@ -147,6 +150,19 @@ RECREATE=0 bash scripts/migrate_container_db_to_volume.sh
|
||||
FORCE=1 bash scripts/migrate_container_db_to_volume.sh
|
||||
```
|
||||
|
||||
如果脚本提示找不到 `alphax-web`,先查看服务器上的真实容器名:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
|
||||
```
|
||||
|
||||
然后用真实容器名执行:
|
||||
|
||||
```bash
|
||||
SOURCE_CONTAINER=<真实容器名> bash scripts/migrate_container_db_to_volume.sh
|
||||
```
|
||||
|
||||
## 打包迁移到新服务器
|
||||
|
||||
建议只打包代码和配置骨架,不把 DB 直接打进镜像。可以用 tar 打包整个副本目录,排除本地缓存和归档备份:
|
||||
|
||||
@ -17,6 +17,7 @@ set -euo pipefail
|
||||
# SOURCE_CONTAINER=old-alphax-web bash scripts/migrate_container_db_to_volume.sh
|
||||
# SERVICE=alphax-web TARGET_DB=data/altcoin_monitor.db bash scripts/migrate_container_db_to_volume.sh
|
||||
# RECREATE=0 bash scripts/migrate_container_db_to_volume.sh
|
||||
# ALLOW_STOPPED=1 SOURCE_CONTAINER=old-alphax-web bash scripts/migrate_container_db_to_volume.sh
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
@ -31,6 +32,7 @@ BACKUP_DIR="${BACKUP_DIR:-data/backups}"
|
||||
RECREATE="${RECREATE:-1}"
|
||||
STOP_SCHEDULER="${STOP_SCHEDULER:-1}"
|
||||
FORCE="${FORCE:-0}"
|
||||
ALLOW_STOPPED="${ALLOW_STOPPED:-1}"
|
||||
|
||||
compose() {
|
||||
${COMPOSE_CMD} "$@"
|
||||
@ -49,6 +51,15 @@ service_exists() {
|
||||
compose config --services 2>/dev/null | grep -qx "$1"
|
||||
}
|
||||
|
||||
container_running() {
|
||||
local cid="$1"
|
||||
[ "$(docker inspect -f '{{.State.Running}}' "$cid" 2>/dev/null || echo false)" = "true" ]
|
||||
}
|
||||
|
||||
container_name_for_log() {
|
||||
docker inspect -f '{{.Name}}' "$1" 2>/dev/null | sed 's#^/##'
|
||||
}
|
||||
|
||||
container_id() {
|
||||
if [ -n "$SOURCE_CONTAINER" ]; then
|
||||
docker inspect -f '{{.Id}}' "$SOURCE_CONTAINER" >/dev/null 2>&1 || die "SOURCE_CONTAINER not found: $SOURCE_CONTAINER"
|
||||
@ -57,8 +68,30 @@ container_id() {
|
||||
fi
|
||||
local cid
|
||||
cid="$(compose ps -q "$SERVICE" 2>/dev/null || true)"
|
||||
[ -n "$cid" ] || die "service '$SERVICE' is not running. Start it first, or set SOURCE_CONTAINER=<container_name>."
|
||||
echo "$cid"
|
||||
if [ -n "$cid" ]; then
|
||||
echo "$cid"
|
||||
return
|
||||
fi
|
||||
|
||||
# Fallback for servers where the old container was not created by the current
|
||||
# compose project, or the compose service name/project name changed.
|
||||
for name in "$SERVICE" alphax-web alphax_web alphax; do
|
||||
cid="$(docker ps -aq --filter "name=^/${name}$" | head -n 1)"
|
||||
if [ -n "$cid" ]; then
|
||||
echo "$cid"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
echo "ERROR: service '$SERVICE' is not running and no fallback container was found." >&2
|
||||
echo "" >&2
|
||||
echo "Run one of these on the server:" >&2
|
||||
echo " docker compose ps" >&2
|
||||
echo " docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'" >&2
|
||||
echo "" >&2
|
||||
echo "Then rerun with the actual container name, for example:" >&2
|
||||
echo " SOURCE_CONTAINER=<container_name> bash scripts/migrate_container_db_to_volume.sh" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
is_source_db_under_mount() {
|
||||
@ -124,17 +157,27 @@ STAMP="$(date +%Y%m%d_%H%M%S)"
|
||||
TMP_IN_CONTAINER="/tmp/alphax_container_db_${STAMP}.db"
|
||||
TMP_ON_HOST="${BACKUP_DIR}/altcoin_monitor.from_container.${STAMP}.tmp.db"
|
||||
|
||||
info "source container: ${SOURCE_CONTAINER:-$SERVICE} ($CID)"
|
||||
SOURCE_LABEL="${SOURCE_CONTAINER:-$(container_name_for_log "$CID")}"
|
||||
info "source container: ${SOURCE_LABEL:-$SERVICE} ($CID)"
|
||||
info "source db: $SOURCE_DB"
|
||||
info "target db: $TARGET_DB"
|
||||
|
||||
docker exec -e SOURCE_DB="$SOURCE_DB" "$CID" sh -lc 'test -s "$SOURCE_DB"' \
|
||||
|| die "source database does not exist or is empty inside container: $SOURCE_DB"
|
||||
RUNNING=0
|
||||
if container_running "$CID"; then
|
||||
RUNNING=1
|
||||
docker exec -e SOURCE_DB="$SOURCE_DB" "$CID" sh -lc 'test -s "$SOURCE_DB"' \
|
||||
|| die "source database does not exist or is empty inside container: $SOURCE_DB"
|
||||
|
||||
if is_source_db_under_mount "$CID" && [ "$FORCE" != "1" ]; then
|
||||
info "container path '$SOURCE_DB' is already under a mounted volume/bind mount."
|
||||
info "No migration needed. Set FORCE=1 only if you intentionally want to copy it anyway."
|
||||
exit 0
|
||||
if is_source_db_under_mount "$CID" && [ "$FORCE" != "1" ]; then
|
||||
info "container path '$SOURCE_DB' is already under a mounted volume/bind mount."
|
||||
info "No migration needed. Set FORCE=1 only if you intentionally want to copy it anyway."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
if [ "$ALLOW_STOPPED" != "1" ]; then
|
||||
die "source container is stopped. Start it first, or set ALLOW_STOPPED=1 to docker cp the DB from the stopped container."
|
||||
fi
|
||||
info "source container is stopped; falling back to docker cp. SQLite WAL files cannot be checkpointed in this mode."
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$TARGET_DB")" "$BACKUP_DIR"
|
||||
@ -144,11 +187,12 @@ if [ "$STOP_SCHEDULER" = "1" ] && service_exists "$SCHEDULER_SERVICE"; then
|
||||
compose stop "$SCHEDULER_SERVICE" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
info "creating SQLite backup inside container"
|
||||
docker exec \
|
||||
-e SOURCE_DB="$SOURCE_DB" \
|
||||
-e TMP_DB="$TMP_IN_CONTAINER" \
|
||||
"$CID" python - <<'PY'
|
||||
if [ "$RUNNING" = "1" ]; then
|
||||
info "creating SQLite backup inside running container"
|
||||
docker exec \
|
||||
-e SOURCE_DB="$SOURCE_DB" \
|
||||
-e TMP_DB="$TMP_IN_CONTAINER" \
|
||||
"$CID" python - <<'PY'
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
@ -173,9 +217,19 @@ if result != "ok":
|
||||
print(f"backup_created={dst}")
|
||||
PY
|
||||
|
||||
info "copying backup from container to host"
|
||||
docker cp "${CID}:${TMP_IN_CONTAINER}" "$TMP_ON_HOST"
|
||||
docker exec -e TMP_DB="$TMP_IN_CONTAINER" "$CID" sh -lc 'rm -f "$TMP_DB"' >/dev/null 2>&1 || true
|
||||
info "copying backup from container to host"
|
||||
docker cp "${CID}:${TMP_IN_CONTAINER}" "$TMP_ON_HOST"
|
||||
docker exec -e TMP_DB="$TMP_IN_CONTAINER" "$CID" sh -lc 'rm -f "$TMP_DB"' >/dev/null 2>&1 || true
|
||||
else
|
||||
info "copying raw DB from stopped container"
|
||||
docker cp "${CID}:${SOURCE_DB}" "$TMP_ON_HOST" \
|
||||
|| die "failed to copy $SOURCE_DB from stopped container. Check SOURCE_DB path."
|
||||
for suffix in "-wal" "-shm"; do
|
||||
if docker cp "${CID}:${SOURCE_DB}${suffix}" "${TMP_ON_HOST}${suffix}" >/dev/null 2>&1; then
|
||||
info "copied sidecar ${SOURCE_DB}${suffix}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
verify_db_on_host_if_possible "$TMP_ON_HOST"
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user