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
|
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 服务
|
# 只复制,不自动重启 compose 服务
|
||||||
RECREATE=0 bash scripts/migrate_container_db_to_volume.sh
|
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
|
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 打包整个副本目录,排除本地缓存和归档备份:
|
建议只打包代码和配置骨架,不把 DB 直接打进镜像。可以用 tar 打包整个副本目录,排除本地缓存和归档备份:
|
||||||
|
|||||||
@ -17,6 +17,7 @@ set -euo pipefail
|
|||||||
# SOURCE_CONTAINER=old-alphax-web bash scripts/migrate_container_db_to_volume.sh
|
# 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
|
# 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
|
# 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)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "$ROOT_DIR"
|
cd "$ROOT_DIR"
|
||||||
@ -31,6 +32,7 @@ BACKUP_DIR="${BACKUP_DIR:-data/backups}"
|
|||||||
RECREATE="${RECREATE:-1}"
|
RECREATE="${RECREATE:-1}"
|
||||||
STOP_SCHEDULER="${STOP_SCHEDULER:-1}"
|
STOP_SCHEDULER="${STOP_SCHEDULER:-1}"
|
||||||
FORCE="${FORCE:-0}"
|
FORCE="${FORCE:-0}"
|
||||||
|
ALLOW_STOPPED="${ALLOW_STOPPED:-1}"
|
||||||
|
|
||||||
compose() {
|
compose() {
|
||||||
${COMPOSE_CMD} "$@"
|
${COMPOSE_CMD} "$@"
|
||||||
@ -49,6 +51,15 @@ service_exists() {
|
|||||||
compose config --services 2>/dev/null | grep -qx "$1"
|
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() {
|
container_id() {
|
||||||
if [ -n "$SOURCE_CONTAINER" ]; then
|
if [ -n "$SOURCE_CONTAINER" ]; then
|
||||||
docker inspect -f '{{.Id}}' "$SOURCE_CONTAINER" >/dev/null 2>&1 || die "SOURCE_CONTAINER not found: $SOURCE_CONTAINER"
|
docker inspect -f '{{.Id}}' "$SOURCE_CONTAINER" >/dev/null 2>&1 || die "SOURCE_CONTAINER not found: $SOURCE_CONTAINER"
|
||||||
@ -57,8 +68,30 @@ container_id() {
|
|||||||
fi
|
fi
|
||||||
local cid
|
local cid
|
||||||
cid="$(compose ps -q "$SERVICE" 2>/dev/null || true)"
|
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>."
|
if [ -n "$cid" ]; then
|
||||||
echo "$cid"
|
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() {
|
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_IN_CONTAINER="/tmp/alphax_container_db_${STAMP}.db"
|
||||||
TMP_ON_HOST="${BACKUP_DIR}/altcoin_monitor.from_container.${STAMP}.tmp.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 "source db: $SOURCE_DB"
|
||||||
info "target db: $TARGET_DB"
|
info "target db: $TARGET_DB"
|
||||||
|
|
||||||
docker exec -e SOURCE_DB="$SOURCE_DB" "$CID" sh -lc 'test -s "$SOURCE_DB"' \
|
RUNNING=0
|
||||||
|| die "source database does not exist or is empty inside container: $SOURCE_DB"
|
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
|
if is_source_db_under_mount "$CID" && [ "$FORCE" != "1" ]; then
|
||||||
info "container path '$SOURCE_DB' is already under a mounted volume/bind mount."
|
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."
|
info "No migration needed. Set FORCE=1 only if you intentionally want to copy it anyway."
|
||||||
exit 0
|
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
|
fi
|
||||||
|
|
||||||
mkdir -p "$(dirname "$TARGET_DB")" "$BACKUP_DIR"
|
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
|
compose stop "$SCHEDULER_SERVICE" >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "creating SQLite backup inside container"
|
if [ "$RUNNING" = "1" ]; then
|
||||||
docker exec \
|
info "creating SQLite backup inside running container"
|
||||||
-e SOURCE_DB="$SOURCE_DB" \
|
docker exec \
|
||||||
-e TMP_DB="$TMP_IN_CONTAINER" \
|
-e SOURCE_DB="$SOURCE_DB" \
|
||||||
"$CID" python - <<'PY'
|
-e TMP_DB="$TMP_IN_CONTAINER" \
|
||||||
|
"$CID" python - <<'PY'
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
@ -173,9 +217,19 @@ if result != "ok":
|
|||||||
print(f"backup_created={dst}")
|
print(f"backup_created={dst}")
|
||||||
PY
|
PY
|
||||||
|
|
||||||
info "copying backup from container to host"
|
info "copying backup from container to host"
|
||||||
docker cp "${CID}:${TMP_IN_CONTAINER}" "$TMP_ON_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
|
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"
|
verify_db_on_host_if_possible "$TMP_ON_HOST"
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user