diff --git a/docker/dify/.gitignore b/docker/dify/.gitignore index c67d0b7..eec8ce3 100644 --- a/docker/dify/.gitignore +++ b/docker/dify/.gitignore @@ -5,6 +5,7 @@ # 1. 默认忽略 volumes 下所有内容 volumes/** nginx/ +backups/* # 2. 保留当前已存在的目录本身(目录结构),方便写入 .gitkeep / 初始化文件 !volumes/ !volumes/app/ @@ -24,4 +25,4 @@ nginx/ !volumes/oceanbase/init.d/vec_memory.sql !volumes/opensearch/opensearch_dashboards.yml !volumes/sandbox/conf/config.yaml -!volumes/sandbox/conf/config.yaml.example \ No newline at end of file +!volumes/sandbox/conf/config.yaml.example diff --git a/docker/dify/backup.sh b/docker/dify/backup.sh new file mode 100755 index 0000000..9cbb857 --- /dev/null +++ b/docker/dify/backup.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Dify docker backup script +# 备份内容: +# 1) Postgres 数据库 dify +# 2) 整个 volumes/ 目录(包括插件、向量库等数据) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +BACKUP_DIR="$SCRIPT_DIR/backups" +TIMESTAMP="$(date +%Y%m%d_%H%M%S)" +DB_FILE="$BACKUP_DIR/dify_db_${TIMESTAMP}.sql" +VOL_FILE="$BACKUP_DIR/dify_volumes_${TIMESTAMP}.tar.gz" + +mkdir -p "$BACKUP_DIR" + +DB_USER="${POSTGRES_USER:-postgres}" +DB_NAME="${POSTGRES_DB:-dify}" + +# 自动检测 db_postgres 容器名,匹配形如 "-db_postgres-1" +DB_CONTAINER="${DB_CONTAINER:-}" +if [ -z "$DB_CONTAINER" ]; then + DB_CONTAINER="$(docker ps --format '{{.Names}}' | grep 'db_postgres-1$' | head -n1 || true)" +fi + +if [ -z "$DB_CONTAINER" ]; then + echo "错误:未找到名称类似 '*-db_postgres-1' 的运行中容器,请确认 db_postgres 服务已启动。" >&2 + exit 1 +fi + +echo "[1/2] 导出 Postgres 数据库 ${DB_NAME} (容器: ${DB_CONTAINER}) ..." + +docker exec -t "${DB_CONTAINER}" \ + pg_dump -U "${DB_USER}" -d "${DB_NAME}" > "${DB_FILE}" + +echo "数据库已备份到: ${DB_FILE}" + +echo "[2/2] 打包 volumes/ 目录 ..." +if [ ! -d "volumes" ]; then + echo "警告:当前目录下不存在 volumes/ 目录,跳过卷备份。" >&2 +else + tar czf "${VOL_FILE}" volumes/ + echo "卷数据已打包到: ${VOL_FILE}" +fi + +echo "备份完成。" diff --git a/docker/dify/restore.sh b/docker/dify/restore.sh new file mode 100755 index 0000000..b1bd2e8 --- /dev/null +++ b/docker/dify/restore.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Dify docker restore script +# 使用 backups/ 中的备份恢复: +# 1) volumes/ 目录 +# 2) Postgres 数据库 dify +# +# 用法: +# ./restore.sh # 使用最新备份 +# ./restore.sh 20251124_120000 # 使用指定时间戳的一组备份 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +BACKUP_DIR="$SCRIPT_DIR/backups" +TIMESTAMP="${1:-latest}" + +if [ ! -d "$BACKUP_DIR" ]; then + echo "错误:未找到备份目录 $BACKUP_DIR" >&2 + exit 1 +fi + +# 选择备份文件 +if [ "$TIMESTAMP" = "latest" ]; then + DB_FILE="$(ls -1t "$BACKUP_DIR"/dify_db_*.sql 2>/dev/null | head -n1 || true)" + VOL_FILE="$(ls -1t "$BACKUP_DIR"/dify_volumes_*.tar.gz 2>/dev/null | head -n1 || true)" +else + DB_FILE="$BACKUP_DIR/dify_db_${TIMESTAMP}.sql" + VOL_FILE="$BACKUP_DIR/dify_volumes_${TIMESTAMP}.tar.gz" +fi + +if [ -z "${DB_FILE:-}" ] || [ ! -f "$DB_FILE" ]; then + echo "错误:未找到数据库备份文件 (dify_db_*.sql),请检查 $BACKUP_DIR" >&2 + exit 1 +fi + +if [ -z "${VOL_FILE:-}" ] || [ ! -f "$VOL_FILE" ]; then + echo "错误:未找到卷备份文件 (dify_volumes_*.tar.gz),请检查 $BACKUP_DIR" >&2 + exit 1 +fi + +DB_USER="${POSTGRES_USER:-postgres}" +DB_NAME="${POSTGRES_DB:-dify}" + +# 自动检测 db_postgres 容器名,匹配形如 "-db_postgres-1" +DB_CONTAINER="${DB_CONTAINER:-}" +if [ -z "$DB_CONTAINER" ]; then + DB_CONTAINER="$(docker ps --format '{{.Names}}' | grep 'db_postgres-1$' | head -n1 || true)" +fi + +if [ -z "$DB_CONTAINER" ]; then + echo "错误:未找到名称类似 '*-db_postgres-1' 的运行中容器,请先启动 dify 的 db_postgres 服务。" >&2 + exit 1 +fi + +echo "将使用备份:" +echo " DB: $DB_FILE" +echo " Vol: $VOL_FILE" +read -rp "确认恢复这些备份? 这可能覆盖现有数据 [y/N]: " CONFIRM +if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then + echo "已取消恢复。" + exit 0 +fi + +echo "[1/2] 恢复 volumes/ 目录 ..." +tar xzf "$VOL_FILE" -C "$SCRIPT_DIR" + +echo "[2/2] 恢复 Postgres 数据库 ${DB_NAME} ..." +# 可选:先清空数据库,按需修改 +# docker exec -i "$DB_CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;' + +docker exec -i "$DB_CONTAINER" \ + psql -U "$DB_USER" -d "$DB_NAME" < "$DB_FILE" + +echo "恢复完成。"