๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

:::๊ฒฝ๊ณ  ์ด ํŠœํ† ๋ฆฌ์–ผ์€ ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ธฐ์—ฌ๋กœ ์ œ์ž‘๋˜์—ˆ์œผ๋ฉฐ Open WebUI ํŒ€์—์„œ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํŠน์ • ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋งž์ถฐ Open WebUI๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ฐ์—๋งŒ ๋ชฉ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”? ๊ธฐ์—ฌ ํŠœํ† ๋ฆฌ์–ผ์„ ํ™•์ธํ•˜์„ธ์š”. :::

์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐฑ์—…ํ•˜๊ธฐ

์•„๋ฌด๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์žƒ๊ณ  ์‹ถ์–ดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!

Open WebUI๋ฅผ ์ž๊ฐ€ ํ˜ธ์ŠคํŒ…ํ•˜๋Š” ๊ฒฝ์šฐ๋ผ๋ฉด, ๊ตฌ์„ฑ ์š”์†Œ ์ผ๋ถ€๋ฅผ ๋ณด์กดํ•˜๊ธฐ ์œ„ํ•ด ์ •์‹ ๋ฐฑ์—… ๊ณ„ํš์„ ์‹œํ–‰ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฐ€์ด๋“œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ธฐ๋ณธ ๊ถŒ์žฅ ์‚ฌํ•ญ์„ ์ œ๊ณตํ•˜๋ ค๋Š” ์˜๋„๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฐ€์ด๋“œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ Docker๋ฅผ ํ†ตํ•ด Open WebUI๋ฅผ ์„ค์น˜ํ–ˆ๊ฑฐ๋‚˜ ์„ค์น˜ํ•  ์˜๋„๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ์ง€์†์„ฑ ํ™•๋ณดโ€‹

๋จผ์ € Docker๋ฅผ ์‚ฌ์šฉํ•ด ์Šคํƒ์„ ๋ฐฐํฌํ•˜๊ธฐ ์ „์—, Docker Compose๊ฐ€ ์ง€์† ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ์Šคํ† ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. Docker Compose Github ์ €์žฅ์†Œ์—์„œ ์ œ๊ณต๋œ ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ด๋ฏธ ์ด๋Ÿฌํ•œ ์ž‘์—…์ด ์ฒ˜๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋ณ€ํ˜•ํ•˜์—ฌ ์ด ๋ถ€๋ถ„์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ์žŠ๊ธฐ ์‰ฌ์šด ์ ์„ ์œ ์˜ํ•˜์„ธ์š”.

Docker ์ปจํ…Œ์ด๋„ˆ๋Š” ์ผ์‹œ์ ์ด๋ฉฐ ๋ฐ์ดํ„ฐ๋Š” ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์‹œ์Šคํ…œ์—์„œ ์ƒ์กดํ•˜๊ธฐ ์œ„ํ•ด ์ง€์†์ ์œผ๋กœ ์œ ์ง€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Docker ๋ณผ๋ฅจ ์‚ฌ์šฉโ€‹

ํ”„๋กœ์ ํŠธ ์ €์žฅ์†Œ์—์„œ ์ œ๊ณต๋œ Docker Compose๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ Open Web UI๋ฅผ Docker ๋ณผ๋ฅจ์„ ์‚ฌ์šฉํ•ด ๋ฐฐํฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Ollama ๋ฐ Open WebUI์˜ ์„ค์น˜ ๊ฒฝ๋กœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

ollama:
volumes:
- ollama:/root/.ollama
open-webui:
volumes:
- open-webui:/app/backend/data

ํ˜ธ์ŠคํŠธ์—์„œ ์‹ค์ œ ๊ฒฝ๋กœ๋ฅผ ์ฐพ์œผ๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”:

docker volume inspect ollama

๊ทธ๋ฆฌ๊ณ 

docker volume inspect open-webui

ํ˜ธ์ŠคํŠธ ์ง์ ‘ ๋ฐ”์ธ๋“œ ์‚ฌ์šฉโ€‹

์ผ๋ถ€ ์‚ฌ์šฉ์ž๋Š” Open Web UI๋ฅผ ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์‹œ์Šคํ…œ์— ์ง์ ‘ (๊ณ ์ •๋œ) ๋ฐ”์ธ๋“œ๋กœ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค, ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค:

services:
ollama:
container_name: ollama
image: ollama/ollama:${OLLAMA_DOCKER_TAG-latest}
volumes:
- /opt/ollama:/root/.ollama
open-webui:
container_name: open-webui
image: ghcr.io/open-webui/open-webui:${WEBUI_DOCKER_TAG-main}
volumes:
- /opt/open-webui:/app/backend/data

์ด ๋ฐฉ์‹์œผ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐฐํฌํ–ˆ๋‹ค๋ฉด, ๋ฃจํŠธ ๊ฒฝ๋กœ๋“ค์„ ๊ธฐ๋กํ•ด ๋‘์„ธ์š”.

๋ฐฑ์—… ์ž‘์—…์˜ ์Šคํฌ๋ฆฝํŒ…โ€‹

์–ด๋–ป๊ฒŒ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ตฌ์„ฑ๋˜์—ˆ๋“ ์ง€ ๊ฐ„์— ์„œ๋ฒ„์˜ ์•ฑ ๋ฐ์ดํ„ฐ ์Šคํ† ์–ด๋ฅผ ๊ฒ€์‚ฌํ•˜์—ฌ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฑ์—…ํ•  ๊ฒƒ์ธ์ง€ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

โ”œโ”€โ”€ audit.log
โ”œโ”€โ”€ cache/
โ”œโ”€โ”€ uploads/
โ”œโ”€โ”€ vector_db/
โ””โ”€โ”€ webui.db

์ง€์† ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ์Šคํ† ์–ด์˜ ํŒŒ์ผ๋“คโ€‹

ํŒŒ์ผ/๋””๋ ‰ํ„ฐ๋ฆฌ์„ค๋ช…
audit.log์ด๋ฒคํŠธ ๊ฐ์‚ฌ ๊ธฐ๋ก ํŒŒ์ผ
cache/์บ์‹œ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋””๋ ‰ํ„ฐ๋ฆฌ
uploads/์‚ฌ์šฉ์ž ์—…๋กœ๋“œ ํŒŒ์ผ ์ €์žฅ ๋””๋ ‰ํ„ฐ๋ฆฌ
vector_db/ChromaDB ๋ฒกํ„ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํฌํ•จํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ
webui.db๊ธฐํƒ€ ์ธ์Šคํ„ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

ํŒŒ์ผ ์ˆ˜์ค€ ๋ฐฑ์—… ์ ‘๊ทผ๋ฒ•

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฑ์—…ํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ํŒŒ์ผ ์ˆ˜์ค€ ๋ฐฑ์—… ์ ‘๊ทผ๋ฒ•์œผ๋กœ, Open Web UI์˜ ์ง€์†์ ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๋Œ€๋กœ ๋ฐฑ์—…ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ธฐ์ˆ  ์„œ๋น„์Šค๋Š” ๊ฑฐ์˜ ๋ฌดํ•œํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฐฑ์—…๋  ์ˆ˜ ์žˆ์œผ๋‚˜, ์ฆ๋ถ„ ์ž‘์—…์„ ์œ„ํ•ด rsync๋Š” ์—ฌ์ „ํžˆ ์ธ๊ธฐ๊ฐ€ ์žˆ์œผ๋ฉฐ ๋ฐ๋ชจ๋กœ ์‚ฌ์šฉ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๋Š” ์ „์ฒด data ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์‚ผ์•„ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๋ฐฑ์—…ํ•˜๊ฑฐ๋‚˜ ๊ฐœ๋ณ„ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ๋” ์„ ํƒ์ ์ธ ๋ฐฑ์—… ์ž‘์—…์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์ƒ์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์„ค๋ช… ์ด๋ฆ„์„ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋ฒ”์ ์ธ rsync ์ž‘์—…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

#!/bin/bash

# ๊ตฌ์„ฑ
SOURCE_DIR="." # ํŒŒ์ผ ๊ตฌ์กฐ๊ฐ€ ์œ„์น˜ํ•œ ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ
B2_BUCKET="b2://OpenWebUI-backups" # Backblaze B2 ๋ฒ„ํ‚ท
B2_PROFILE="your_rclone_profile" # rclone ํ”„๋กœํŒŒ์ผ ์ด๋ฆ„
# rclone์ด B2 ์ธ์ฆ ์ •๋ณด๋กœ ๊ตฌ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ

# ์ถœ์ฒ˜ ๋ฐ ๋Œ€์ƒ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ •์˜
SOURCE_UPLOADS="$SOURCE_DIR/uploads"
SOURCE_VECTORDB="$SOURCE_DIR/vector_db"
SOURCE_WEBUI_DB="$SOURCE_DIR/webui.db"

DEST_UPLOADS="$B2_BUCKET/user_uploads"
DEST_CHROMADB="$B2_BUCKET/ChromaDB"
DEST_MAIN_DB="$B2_BUCKET/main_database"

# cache ๋ฐ audit.log ์ œ์™ธ
EXCLUDE_LIST=(
"cache/"
"audit.log"
)

# rclone ์ œ์™ธ ์ธ์ˆ˜ ๊ตฌ์„ฑ
EXCLUDE_ARGS=""
for EXCLUDE in "${EXCLUDE_LIST[@]}"; do
EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude $EXCLUDE"
done

# ์˜ค๋ฅ˜ ๊ฒ€์‚ฌ๋กœ rclone ๋™๊ธฐํ™” ์ˆ˜ํ–‰ ํ•จ์ˆ˜
rclone_sync() {
SOURCE="$1"
DEST="$2"
echo "Syncing $SOURCE to $DEST..."
rclone sync "$SOURCE" "$DEST" $EXCLUDE_ARGS --progress --transfers=32 --checkers=16 --profile "$B2_PROFILE"
if [ $? -ne 0 ]; then
echo "Error: rclone sync failed for $SOURCE to $DEST"
exit 1
fi
}

# ๊ฐ ๋””๋ ‰ํ„ฐ๋ฆฌ/ํŒŒ์ผ์— ๋Œ€ํ•ด rclone ๋™๊ธฐํ™” ์ˆ˜ํ–‰
rclone_sync "$SOURCE_UPLOADS" "$DEST_UPLOADS"
rclone_sync "$SOURCE_VECTORDB" "$DEST_CHROMADB"
rclone_sync "$SOURCE_WEBUI_DB" "$DEST_MAIN_DB"

echo "๋ฐฑ์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
exit 0

์ปจํ…Œ์ด๋„ˆ ์ค‘๋‹จ์„ ๋™๋ฐ˜ํ•œ Rsync ์ž‘์—…โ€‹

๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๋ ค๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—…์„ ์ •์ง€๋œ ํŒŒ์ผ ์‹œ์Šคํ…œ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋ชจ๋ธ ๋ฐฑ์—… ์ž‘์—…์„ ์•ฝ๊ฐ„ ์ˆ˜์ •ํ•˜์—ฌ ๋ฐฑ์—… ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์Šคํƒ์„ ์ค‘๋‹จํ•˜๊ณ  ์ดํ›„ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•์˜ ๋‹จ์ ์€ ์ธ์Šคํ„ด์Šค ๋‹ค์šดํƒ€์ž„์„ ์ˆ˜๋ฐ˜ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์‹œ๊ฐ„๋Œ€์— ์ž‘์—…์„ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์‹คํ–‰ ์ค‘์ธ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ์ผ์ผ ์†Œํ”„ํŠธ์›จ์–ด ๋ฐฑ์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ •์ง€๋œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ๋” ๊ฒฌ๊ณ ํ•œ ์ฃผ๊ฐ„ ๋ฐฑ์—…์„ ๊ณ ๋ คํ•˜์‹ญ์‹œ์˜ค.

#!/bin/bash

# ์„ค์ •
COMPOSE_FILE="docker-compose.yml" # docker-compose.yml ํŒŒ์ผ์˜ ๊ฒฝ๋กœ
B2_BUCKET="b2://OpenWebUI-backups" # Backblaze B2 ๋ฒ„ํ‚ท
B2_PROFILE="your_rclone_profile" # rclone ํ”„๋กœํ•„ ์ด๋ฆ„
SOURCE_DIR="." # ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ (ํŒŒ์ผ ๊ตฌ์กฐ๊ฐ€ ์žˆ๋Š” ์œ„์น˜)

# ์†Œ์Šค ๋ฐ ๋Œ€์ƒ ๋””๋ ‰ํ† ๋ฆฌ ์ •์˜
SOURCE_UPLOADS="$SOURCE_DIR/uploads"
SOURCE_VECTORDB="$SOURCE_DIR/vector_db"
SOURCE_WEBUI_DB="$SOURCE_DIR/webui.db"

DEST_UPLOADS="$B2_BUCKET/user_uploads"
DEST_CHROMADB="$B2_BUCKET/ChromaDB"
DEST_MAIN_DB="$B2_BUCKET/main_database"

# cache ๋ฐ audit.log ์ œ์™ธ
EXCLUDE_LIST=(
"cache/"
"audit.log"
)

# rclone์˜ ์ œ์™ธ ์ธ์ˆ˜ ๊ตฌ์„ฑ
EXCLUDE_ARGS=""
for EXCLUDE in "${EXCLUDE_LIST[@]}"; do
EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude $EXCLUDE"
done

# rclone ๋™๊ธฐํ™” ์ˆ˜ํ–‰ ๋ฐ ์˜ค๋ฅ˜ ํ™•์ธ์„ ์œ„ํ•œ ํ•จ์ˆ˜
rclone_sync() {
SOURCE="$1"
DEST="$2"
echo "$SOURCE๋ฅผ $DEST๋กœ ๋™๊ธฐํ™” ์ค‘..."
rclone sync "$SOURCE" "$DEST" $EXCLUDE_ARGS --progress --transfers=32 --checkers=16 --profile "$B2_PROFILE"
if [ $? -ne 0 ]; then
echo "์˜ค๋ฅ˜: $SOURCE๋ฅผ $DEST๋กœ ๋™๊ธฐํ™” ์‹คํŒจ"
exit 1
fi
}

# 1. Docker Compose ํ™˜๊ฒฝ ์ค‘๋‹จ
echo "Docker Compose ํ™˜๊ฒฝ ์ค‘๋‹จ ์ค‘..."
docker-compose -f "$COMPOSE_FILE" down

# 2. ๋ฐฑ์—… ์ˆ˜ํ–‰
echo "๋ฐฑ์—… ์‹œ์ž‘ ์ค‘..."
rclone_sync "$SOURCE_UPLOADS" "$DEST_UPLOADS"
rclone_sync "$SOURCE_VECTORDB" "$DEST_CHROMADB"
rclone_sync "$SOURCE_WEBUI_DB" "$DEST_MAIN_DB"

# 3. Docker Compose ํ™˜๊ฒฝ ์‹œ์ž‘
echo "Docker Compose ํ™˜๊ฒฝ ์‹œ์ž‘ ์ค‘..."
docker-compose -f "$COMPOSE_FILE" up -d

echo "๋ฐฑ์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
exit 0

SQLite์™€ ChromaDB ๋ฐฑ์—… ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ B2 ์›๊ฒฉ์œผ๋กœ ๋ชจ๋ธ ๋ฐฑ์—… ์Šคํฌ๋ฆฝํŠธโ€‹

#!/bin/bash
#
# Backblaze B2 ๋ฒ„ํ‚ท์— ChromaDB ๋ฐ SQLite๋ฅผ ๋ฐฑ์—…ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ
# openwebuiweeklies๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 3๊ฐœ์˜ ์ฃผ๊ฐ„ ์Šค๋ƒ…์ƒท์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
# ์Šค๋ƒ…์ƒท์€ ๋…๋ฆฝ์ ์ด๊ณ  ์™„์ „ํžˆ ๋ณต์› ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
# ChromaDB์™€ SQLite์˜ ๊ธฐ๋ณธ ๋ฐฑ์—… ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
# audit.log, cache ๋ฐ uploads ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค.
#
# rclone์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์น˜๋˜๊ณ  ๊ตฌ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.
# rclone ์„ค์น˜: https://rclone.org/install/
# rclone ๊ตฌ์„ฑ: https://rclone.org/b2/

# ChromaDB ๋ฐ SQLite ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋œ ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ
SOURCE="/var/lib/open-webui/data"

# B2 ๋ฒ„ํ‚ท ์ด๋ฆ„ ๋ฐ ์›๊ฒฉ ์ด๋ฆ„
B2_REMOTE="openwebuiweeklies"
B2_BUCKET="b2:$B2_REMOTE"

# ๋ฐฑ์—… ๋””๋ ‰ํ† ๋ฆฌ์šฉ ํƒ€์ž„์Šคํƒฌํ”„
TIMESTAMP=$(date +%Y-%m-%d)

# ๋ฐฑ์—… ๋””๋ ‰ํ† ๋ฆฌ ์ด๋ฆ„
BACKUP_DIR="open-webui-backup-$TIMESTAMP"

# B2 ๋ฒ„ํ‚ท์˜ ๋ฐฑ์—… ๋””๋ ‰ํ† ๋ฆฌ์— ๋Œ€ํ•œ ์ „์ฒด ๊ฒฝ๋กœ
DESTINATION="$B2_BUCKET/$BACKUP_DIR"

# ์œ ์ง€ํ•  ์ฃผ๊ฐ„ ์Šค๋ƒ…์ƒท์˜ ์ˆ˜
NUM_SNAPSHOTS=3

# ์ œ์™ธ ํ•„ํ„ฐ (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—… ์ดํ›„ ์ ์šฉ)
EXCLUDE_FILTERS="--exclude audit.log --exclude cache/** --exclude uploads/** --exclude vector_db"

# ChromaDB ๋ฐฑ์—… ์„ค์ • (ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ •)
CHROMADB_DATA_DIR="$SOURCE/vector_db" # ChromaDB ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ
CHROMADB_BACKUP_FILE="$SOURCE/chromadb_backup.tar.gz" # ChromaDB ๋ฐฑ์—…์„ ์œ„ํ•œ ์•„์นด์ด๋ธŒ ํŒŒ์ผ

# SQLite ๋ฐฑ์—… ์„ค์ • (ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ •)
SQLITE_DB_FILE="$SOURCE/webui.db" # SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒŒ์ผ ๊ฒฝ๋กœ
SQLITE_BACKUP_FILE="$SOURCE/webui.db.backup" # SQLite ๋ฐฑ์—… ์ž„์‹œ ํŒŒ์ผ

# ChromaDB ๋ฐฑ์—… ํ•จ์ˆ˜
backup_chromadb() {
echo "ChromaDB ๋ฐฑ์—… ์ค‘..."

# vector_db ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ tar ์•„์นด์ด๋ธŒ๋กœ ๋งŒ๋“ค๊ธฐ
tar -czvf "$CHROMADB_BACKUP_FILE" -C "$SOURCE" vector_db

echo "ChromaDB ๋ฐฑ์—… ์™„๋ฃŒ."
}

# SQLite ๋ฐฑ์—… ํ•จ์ˆ˜
backup_sqlite() {
echo "SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—… ์ค‘..."
# .backup ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—…
sqlite3 "$SQLITE_DB_FILE" ".backup $SQLITE_BACKUP_FILE"

# ๋ฐฑ์—… ํŒŒ์ผ์„ ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ด๋™
mv "$SQLITE_BACKUP_FILE" "$SOURCE/"

echo "SQLite ๋ฐฑ์—… ์™„๋ฃŒ."
}

# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—… ์ˆ˜ํ–‰
backup_chromadb
backup_sqlite

# ์ œ์™ธ๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ฐฑ์—… ์ˆ˜ํ–‰
rclone copy "$SOURCE" "$DESTINATION" $EXCLUDE_FILTERS --progress

# ์˜ค๋ž˜๋œ ๋ฐฑ์—… ์ œ๊ฑฐ, ์ตœ๊ทผ NUM_SNAPSHOTS๋งŒ ์œ ์ง€
find "$B2_BUCKET" -type d -name "open-webui-backup-*" | sort -r | tail -n +$((NUM_SNAPSHOTS + 1)) | while read dir; do
rclone purge "$dir"
done

echo "$DESTINATION์œผ๋กœ ๋ฐฑ์—… ์™„๋ฃŒ"

์‹œ์  ์Šค๋ƒ…์ƒท(Point In Time Snapshots)โ€‹

๋ฐฑ์—… ์™ธ์—๋„ ์‚ฌ์šฉ์ž๋Š” ๋กœ์ปฌ(์„œ๋ฒ„์—) ๋˜๋Š” ์›๊ฒฉ์ง€ ํ˜น์€ ๋‘˜ ๋‹ค์— ์‹œ์  ์Šค๋ƒ…์ƒท์„ ์ƒ์„ฑํ•˜์—ฌ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#!/bin/bash

# ์„ค์ •
SOURCE_DIR="." # ์Šค๋ƒ…์ƒทํ•  ๋””๋ ‰ํ† ๋ฆฌ(ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ)
SNAPSHOT_DIR="/snapshots" # ์Šค๋ƒ…์ƒท์„ ์ €์žฅํ•  ๋””๋ ‰ํ† ๋ฆฌ
TIMESTAMP=$(date +%Y%m%d%H%M%S) # ํƒ€์ž„์Šคํƒฌํ”„ ์ƒ์„ฑ

# ์Šค๋ƒ…์ƒท ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ์ƒ์„ฑ
mkdir -p "$SNAPSHOT_DIR"

# ์Šค๋ƒ…์ƒท ์ด๋ฆ„ ์ƒ์„ฑ
SNAPSHOT_NAME="snapshot_$TIMESTAMP"
SNAPSHOT_PATH="$SNAPSHOT_DIR/$SNAPSHOT_NAME"

# rsync ์Šค๋ƒ…์ƒท ์ˆ˜ํ–‰
echo "์Šค๋ƒ…์ƒท ์ƒ์„ฑ ์ค‘: $SNAPSHOT_PATH"
rsync -av --delete --link-dest="$SNAPSHOT_DIR/$(ls -t "$SNAPSHOT_DIR" | head -n 1)" "$SOURCE_DIR/" "$SNAPSHOT_PATH"

# rsync ์„ฑ๊ณต ํ™•์ธ
if [ $? -eq 0 ]; then
echo "์Šค๋ƒ…์ƒท์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
else
echo "์˜ค๋ฅ˜: ์Šค๋ƒ…์ƒท ์ƒ์„ฑ ์‹คํŒจ."
exit 1
fi

exit 0

Crontab์„ ์‚ฌ์šฉํ•œ ์ผ์ • ์„ค์ •โ€‹

๋ฐฑ์—… ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ฐฑ์—… ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์„ค์ •ํ•œ ํ›„์—๋Š” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๊ธฐ๋Œ€๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ’ˆ์งˆ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ ๊ธฐ๋ก์ด ๋งค์šฐ ์ถ”์ฒœ๋ฉ๋‹ˆ๋‹ค.

์›ํ•˜๋Š” ์‹คํ–‰ ๋นˆ๋„์— ๋”ฐ๋ผ ์Šคํฌ๋ฆฝํŠธ๋ฅผ crontab์— ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค.

์ƒ์šฉ ์œ ํ‹ธ๋ฆฌํ‹ฐ

์ž์ฒด ๋ฐฑ์—… ์ž‘์—…์„ ์Šคํฌ๋ฆฝํŒ…ํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ์„œ๋ฒ„์— ์—์ด์ „ํŠธ๋ฅผ ์„ค์น˜ํ•˜์—ฌ ๋ฐฑ์—… ์‹คํ–‰์˜ ๋ณต์žก์„ฑ์„ ์ถ”์ƒํ™”ํ•˜๋Š” ์ƒ์šฉ ์ œํ’ˆ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋‚ด์šฉ์€ ์ด ๋ฌธ์„œ์˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์ง€๋งŒ ํŽธ๋ฆฌํ•œ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


ํ˜ธ์ŠคํŠธ ์ˆ˜์ค€ ๋ฐฑ์—…

Open WebUI ์ธ์Šคํ„ด์Šค๋Š” ๋ฌผ๋ฆฌ์  ๋˜๋Š” ๊ฐ€์ƒ ํ˜ธ์ŠคํŠธ์—์„œ ํ”„๋กœ๋น„์ €๋‹ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜ธ์ŠคํŠธ ์ˆ˜์ค€ ๋ฐฑ์—…์€ ์‹คํ–‰ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋Œ€์‹  ์ „์ฒด VM์˜ ์Šค๋ƒ…์ƒท ๋˜๋Š” ๋ฐฑ์—…์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ถ€๋Š” ์ด๋ฅผ ์ฃผ์š” ๋˜๋Š” ์œ ์ผํ•œ ๋ณดํ˜ธ์ฑ…์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‹ค๋ฅธ ์ผ๋ถ€๋Š” ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฅผ ๊ณ„์ธต์ ์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ผ๋งˆ๋‚˜ ๋งŽ์€ ๋ฐฑ์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๊นŒ?

์ทจํ•  ๋ฐฑ์—… ์ˆ˜๋Š” ๊ฐœ์ธ์ ์ธ ์œ„ํ—˜ ํ—ˆ์šฉ ์ˆ˜์ค€์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ž์ฒด๋ฅผ ๋ฐฑ์—… ์‚ฌ๋ณธ์œผ๋กœ ๊ฐ„์ฃผํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋ชจ๋ฒ” ์‚ฌ๋ก€์ž„์„ ๊ธฐ์–ตํ•˜์‹ญ์‹œ์˜ค(๋น„๋ก ํด๋ผ์šฐ๋“œ์— ์žˆ๋‹ค ํ•˜๋”๋ผ๋„!). ์ด๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ VPS์— ํ”„๋กœ๋น„์ €๋‹ ํ•œ ๊ฒฝ์šฐ์—๋„ ๋‘ ๊ฐœ์˜ ๋…๋ฆฝ์ ์ธ ๋ฐฑ์—… ์‚ฌ๋ณธ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ธ ์ถ”์ฒœ์ž„์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋งŽ์€ ํ™ˆ ์‚ฌ์šฉ์ž ์š”๊ตฌ๋ฅผ ์ถฉ์กฑํ•˜๋Š” ๋ฐฑ์—… ๊ณ„ํš ์˜ˆ์‹œ:

๋ชจ๋ธ ๋ฐฑ์—… ๊ณ„ํš 1 (์ฃผ์š” + 2 ์‚ฌ๋ณธ)โ€‹

๋นˆ๋„๋Œ€์ƒ๊ธฐ์ˆ ์„ค๋ช…
์ผ์ผ ์ฆ๋ถ„ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ (S3/B2)rsyncํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํ‚ท(S3 ๋˜๋Š” B2)์— ์ผ์ผ ์ฆ๋ถ„ ๋ฐฑ์—…์„ ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค.
์ฃผ๊ฐ„ ์ฆ๋ถ„์˜จ์‚ฌ์ดํŠธ ์Šคํ† ๋ฆฌ์ง€ (ํ™ˆ NAS)rsync์„œ๋ฒ„์—์„œ ์˜จ์‚ฌ์ดํŠธ ์Šคํ† ๋ฆฌ์ง€(์˜ˆ: ํ™ˆ NAS)๋กœ ์ฃผ๊ฐ„ ์ฆ๋ถ„ ๋ฐฑ์—…์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

๋ชจ๋ธ ๋ฐฑ์—… ๊ณ„ํš 2 (์ฃผ์š” + 3 ์‚ฌ๋ณธ)โ€‹

์ด ๋ฐฑ์—… ๊ณ„ํš์€ ์กฐ๊ธˆ ๋” ๋ณต์žกํ•˜์ง€๋งŒ ๋” ํฌ๊ด„์ ์ž…๋‹ˆ๋‹ค. ์ถ”๊ฐ€์ ์ธ ์ค‘๋ณต์„ฑ์„ ์œ„ํ•ด ๋‘ ๊ฐœ์˜ ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ ์ œ๊ณต์ž์— ์ผ์ผ ํ‘ธ์‹œ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๋นˆ๋„๋Œ€์ƒ๊ธฐ์ˆ ์„ค๋ช…
์ผ์ผ ์ฆ๋ถ„ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ (S3)rsyncS3 ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํ‚ท์œผ๋กœ ์ผ์ผ ์ฆ๋ถ„ ๋ฐฑ์—…์„ ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค.
์ผ์ผ ์ฆ๋ถ„ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ (B2)rsyncBackblaze B2 ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํ‚ท์œผ๋กœ ์ผ์ผ ์ฆ๋ถ„ ๋ฐฑ์—…์„ ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค.
์ฃผ๊ฐ„ ์ฆ๋ถ„์˜จ์‚ฌ์ดํŠธ ์Šคํ† ๋ฆฌ์ง€ (ํ™ˆ NAS)rsync์„œ๋ฒ„์—์„œ ์˜จ์‚ฌ์ดํŠธ ์Šคํ† ๋ฆฌ์ง€(์˜ˆ: ํ™ˆ NAS)๋กœ ์ฃผ๊ฐ„ ์ฆ๋ถ„ ๋ฐฑ์—…์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ์ฃผ์ œ

์ด ๊ฐ€์ด๋“œ๋ฅผ ์ ์ ˆํžˆ ํฌ๊ด„์ ์œผ๋กœ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋Ÿฌํ•œ ์ถ”๊ฐ€ ์ฃผ์ œ๋Š” ์ƒ๋žต๋˜์—ˆ์ง€๋งŒ ์ธ์Šคํ„ด์Šค์˜ ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ ๊ณ„ํš์„ ์„ค์ •ํ•˜๊ณ  ์œ ์ง€ํ•˜๋Š” ๋ฐ ํ• ์• ํ•  ์‹œ๊ฐ„์ด ์–ผ๋งˆ๋‚˜ ๋˜๋Š”์ง€์— ๋”ฐ๋ผ ๊ฒ€ํ† ํ•  ๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

์ฃผ์ œ์„ค๋ช…
SQLite ๊ธฐ๋ฐ˜ ๋ฐฑ์—…์•ˆ์ •์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—… ์†”๋ฃจ์…˜์„ ์œ„ํ•ด SQLite์˜ .backup ๋ช…๋ น์„ ๊ณ ๋ คํ•˜์‹ญ์‹œ์˜ค.
์•”ํ˜ธํ™”๋ฐฑ์—… ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋Œ€๊ธฐ ์ค‘ ์•”ํ˜ธํ™”๋ฅผ ํฌํ•จ์‹œํ‚ต๋‹ˆ๋‹ค.
์žฌ๋‚œ ๋ณต๊ตฌ ๋ฐ ํ…Œ์ŠคํŠธ์žฌ๋‚œ ๋ณต๊ตฌ ๊ณ„ํš์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์ •๊ธฐ์ ์œผ๋กœ ๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ํ…Œ์ŠคํŠธํ•˜์‹ญ์‹œ์˜ค.
๋Œ€์ฒด ๋ฐฑ์—… ๋„๊ตฌborgbackup ๋˜๋Š” restic๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ๋ช…๋ น์ค„ ๋ฐฑ์—… ๋„๊ตฌ๋ฅผ ํƒ์ƒ‰ํ•˜์—ฌ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์‹ญ์‹œ์˜ค.
์ด๋ฉ”์ผ ์•Œ๋ฆผ ๋ฐ ์›นํ›…๋ฐฑ์—… ์„ฑ๊ณต ๋˜๋Š” ์‹คํŒจ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฉ”์ผ ์•Œ๋ฆผ ๋˜๋Š” ์›นํ›…์„ ๊ตฌํ˜„ํ•˜์‹ญ์‹œ์˜ค.