Files
infra-troubleshooting/pbs-setup-backup-restore.md
thug0bin 6085c6b186 PBS 구축→외부노출→백업→복원 전체 가이드 문서화
- PBS LXC 구축 (물리 디스크 마운트, Tailscale, 듀얼 NIC)
- OPNsense Caddy API로 pbs.medivault.co.kr 외부 노출
- Fingerprint 이슈: Caddy 경유 시 Let's Encrypt 인증서 fingerprint 사용 필수
- 실측 백업 속도: CT 110MB/s, VM 115MiB/s (OPNsense 경유)
- 외부 약국(태령) 120GB VM 백업 17분
- CT 복원: 다른 VMID로 복원 + 충돌 주의사항
- 디스크 이식성: ext4 디스크 분리 후 다른 서버에서 재사용 가능
- Headscale IP 고정 할당 방법 (DB 직접 수정)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:57:40 +00:00

11 KiB

PBS 구축 → 외부 노출 → 백업 → 복원 전체 가이드

날짜: 2026-04-09 환경: Proxmox VE 9, PBS 3.4.8, OPNsense Caddy, Headscale VPN

1. 전체 아키텍처

[아산힐링탑 PVE2]                    [pqserver (Dell R740)]
  CT 201 (shc)  ──────┐              CT 130 (PBS)
  CT 200 (dev)  ──────┤   백업       ├── /mnt/datastore (sdb1, 931GB)
  CT 106 (neo4j)──────┤──────────→   │
                      │              │
[태령약국 PVE]        │              │   복원
  VM 201 (SERVER)─────┘              └──────→ CT 400, 401, 402
                                              (pqserver local-lvm)

경로:
  약국 PVE → 인터넷 → pbs.medivault.co.kr (OPNsense Caddy)
           → 192.168.1.130:8007 (PBS CT)
           → /mnt/datastore/backups (sdb1 물리 디스크)

2. PBS LXC 구축 (pqserver)

2-1. 물리 디스크 확인 + 마운트

# 미사용 디스크 확인
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE
# → sdb 931G, sdb1 ext4, 마운트 안 됨

# 기존 데이터 확인 (goodpharm 627MB — 보존)
mkdir -p /mnt/pbs-disk
mount /dev/sdb1 /mnt/pbs-disk
ls /mnt/pbs-disk/  # goodpharm/ 확인

# fstab 영구 마운트
echo 'UUID=2e087ebd-e0bc-437f-b3b2-5c77dd33ad3e /mnt/pbs-disk ext4 defaults 0 2' >> /etc/fstab

# PBS 데이터스토어 디렉토리 생성
mkdir -p /mnt/pbs-disk/backups

2-2. LXC 생성

pct create 130 local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst \
  --hostname pbs-local \
  --cores 2 --memory 1024 \
  --rootfs local-lvm:8 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --nameserver 8.8.8.8 \
  --password 'trajet6640' \
  --unprivileged 0 \
  --features nesting=1

주의: PBS LXC는 --unprivileged 0 (privileged). 백업/복원에 root 권한 필요.

2-3. TUN 디바이스 (Tailscale용)

# CT 중지 상태에서
echo 'lxc.cgroup2.devices.allow: c 10:200 rwm' >> /etc/pve/lxc/130.conf
echo 'lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file' >> /etc/pve/lxc/130.conf

2-4. sdb1 마운트 포인트 연결

pct set 130 -mp0 /mnt/pbs-disk/backups,mp=/mnt/datastore
pct start 130
# 확인
pct exec 130 -- df -h /mnt/datastore
# → /dev/sdb1  916G  627M  869G  1% /mnt/datastore

2-5. Tailscale 설치 + Headscale 등록

pct exec 130 -- bash -c '
apt-get update -qq && apt-get install -y curl
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list >/dev/null
apt-get update -qq && apt-get install -y tailscale
systemctl enable --now tailscaled
tailscale up --login-server=http://head.pharmq.kr \
  --authkey=<PREAUTH_KEY> \
  --accept-routes --accept-dns=false \
  --hostname=pbs-pqserver
'

2-6. IP 고정 할당 (100.64.0.100)

Headscale은 순차 IP 할당이라 원하는 IP를 직접 지정 불가. DB 직접 수정 방식:

# 1. Headscale Docker 중지
docker stop headscale

# 2. SQLite DB에서 IP 변경
sqlite3 /srv/headscale-tailscale-replacement/data/db.sqlite \
  "UPDATE nodes SET ipv4 = '100.64.0.100' WHERE given_name = 'pbs-pqserver';"

# 3. Headscale 재시작
docker start headscale

# 4. CT에서 tailscaled 재시작 (새 IP 반영)
pct exec 130 -- systemctl restart tailscaled
pct exec 130 -- tailscale ip -4
# → 100.64.0.100

2-7. 듀얼 NIC (OPNsense 외부 노출용)

# pqserver 브릿지 확인: vmbr0=192.168.0.x(ipTIME), vmbr1=WAN 직결
# 다른 CT 참고: net1은 vmbr0에 192.168.1.x 고정 IP

pct set 130 -net1 name=eth1,bridge=vmbr0,gw=192.168.1.1,ip=192.168.1.130/24,type=veth
pct reboot 130

속도 비교:

NIC 경로 Download Upload
eth0 (192.168.0.3) ipTIME 34 Mbps 302 Mbps
eth1 (192.168.1.130) OPNsense 123 Mbps 321 Mbps

2-8. PBS 설치

pct exec 130 -- bash -c '
apt-get install -y curl wget gnupg2
wget -qO /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg \
  https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg
echo "deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription" \
  > /etc/apt/sources.list.d/pbs.list
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y proxmox-backup-server
'

2-9. 데이터스토어 생성 + 비밀번호 설정

pct exec 130 -- bash -c '
proxmox-backup-manager datastore create pbs-local /mnt/datastore/backups
echo "root:trajet6640" | chpasswd
'
# 인증 테스트
curl -sk -d "username=root@pam&password=trajet6640" \
  https://100.64.0.100:8007/api2/json/access/ticket

최종 PBS 정보

항목
CT VMID 130
hostname pbs-local
LAN IP 192.168.0.3 (ipTIME)
OPNsense IP 192.168.1.130
VPN IP 100.64.0.100
웹 UI https://pbs.medivault.co.kr (외부)
인증 root@pam / trajet6640
데이터스토어 pbs-local (869GB)
디스크 /dev/sdb1 (931GB, ext4)

3. OPNsense Caddy로 외부 도메인 노출

3-1. OPNsense Caddy API로 리버스프록시 추가

OPNsense는 GUI로 Caddy 관리. Caddyfile 직접 수정 금지 (GUI 저장 시 덮어씀).

API 사용 (opnsense-admin MCP):

from opnsense_api import OPNsense
op = OPNsense()

# 리버스프록시 추가
op.caddy_add_reverse_proxy('pbs.medivault.co.kr', '192.168.1.130', 8007)

# TLS insecure 설정 (PBS 자체 서명 인증서)
handles = op.get('caddy/ReverseProxy/searchHandle')
for h in handles['rows']:
    if '192.168.1.130' in h.get('ToDomain', ''):
        op.post(f'/api/caddy/reverse_proxy/setHandle/{h["uuid"]}', {
            'handle': {'HttpTls': '1', 'HttpTlsInsecureSkipVerify': '1'}
        })

# Caddy 재적용
op.caddy_reconfigure()

3-2. OPNsense Caddy TLS 특성

항목 pharmq.kr Caddy (LXC 103) OPNsense Caddy
TLS 챌린지 DNS-01 (Cloudflare API) HTTP-01 (Let's Encrypt)
와일드카드 *.pharmq.kr 불가
서브도메인 자동 (와일드카드) 개별 등록 필수
도메인 *.pharmq.kr *.medivault.co.kr (개별)

3-3. Cloudflare DNS

pbs.medivault.co.kr59.30.154.182 (OPNsense 공인 IP)

  • ⚠️ 프록시 모드: DNS only (회색 구름) — HTTP-01 인증서 발급에 필요

4. Fingerprint 이슈 (핵심 트러블슈팅)

문제

PVE에서 PBS를 pbs.medivault.co.kr:443으로 등록할 때:

pvesm add pbs PBS-PQ \
  --server pbs.medivault.co.kr --port 443 \
  --fingerprint 83:86:78:49:...  # PBS 자체 인증서 fingerprint
# → 에러: fingerprint '2D:30:06:82:...' not verified, abort!

원인

PVE → HTTPS → Caddy (Let's Encrypt 인증서) → HTTPS → PBS (자체 서명 인증서)
            ↑                                    ↑
        PVE가 보는 인증서                    PBS 자체 인증서
      = Caddy 인증서                      = 다른 fingerprint

PVE가 연결할 때 보는 인증서는 Caddy의 Let's Encrypt 인증서이지, PBS 자체 인증서가 아님. 따라서 Caddy 인증서의 fingerprint를 사용해야 함.

해결

# Caddy(Let's Encrypt) 인증서 fingerprint 가져오기
echo | openssl s_client -connect pbs.medivault.co.kr:443 2>/dev/null | \
  openssl x509 -fingerprint -sha256 -noout | sed 's/.*=//'
# → 2D:30:06:82:5D:F7:66:1F:5A:91:01:CD:F6:9B:AB:E4:...

# 이 fingerprint로 PVE에 등록
pvesm add pbs PBS-PQ \
  --server pbs.medivault.co.kr --port 443 \
  --datastore pbs-local \
  --username root@pam --password trajet6640 \
  --fingerprint 2D:30:06:82:5D:F7:66:1F:...

⚠️ 주의: Let's Encrypt 인증서 갱신 시

Let's Encrypt 인증서는 90일마다 자동 갱신. 갱신되면 fingerprint가 바뀜. → 모든 PVE에서 PBS 스토리지 재등록 필요.

대안:

  • VPN(Tailscale) 경유로 직접 연결하면 PBS 자체 인증서 사용 → fingerprint 고정
  • Caddy에서 고정 인증서 사용 (권장하지 않음)

5. 약국 PVE에서 PBS 등록 + 백업

5-1. PBS 등록

# Caddy fingerprint 가져오기
FINGERPRINT=$(echo | openssl s_client -connect pbs.medivault.co.kr:443 2>/dev/null | \
  openssl x509 -fingerprint -sha256 -noout | sed 's/.*=//')

# PVE에 PBS 스토리지 추가
pvesm add pbs PBS-PQ \
  --server pbs.medivault.co.kr \
  --port 443 \
  --datastore pbs-local \
  --username root@pam \
  --password trajet6640 \
  --fingerprint "$FINGERPRINT"

# 확인
pvesm status -storage PBS-PQ

5-2. 백업

# CT 백업
vzdump 201 --storage PBS-PQ --mode snapshot --compress zstd \
  --notes-template 'CT201-백업설명'

# VM 백업
vzdump 201 --storage PBS-PQ --mode snapshot --compress zstd \
  --notes-template 'VM201-SERVER-백업설명'

5-3. 실측 백업 속도

소스 대상 크기 시간 속도 경로
힐링탑 CT 201 PBS-PQ 1.8 GiB 2분 6초 ~110 MB/s OPNsense 직접
힐링탑 CT 106 PBS-PQ ~1 GiB ~2분 ~110 MB/s OPNsense 직접
힐링탑 CT 200 PBS-PQ ~10 GiB 9분 11초 ~120 MB/s OPNsense 직접
태령약국 VM 201 PBS-PQ 120 GiB 17분 27초 ~115 MiB/s 인터넷 직접

6. 복원

6-1. CT 복원 (다른 VMID로)

# PBS 백업 목록 확인
pvesm list PBS-Local --content backup | grep 'ct/'

# CT 201 백업 → CT 401로 복원
pct restore 401 PBS-Local:backup/ct/201/2026-04-09T15:19:55Z \
  --storage local-lvm --unprivileged 1

# 확인
pct status 401
pct config 401

6-2. 복원 시 주의사항

  • hostname 충돌: 원본과 같은 hostname으로 복원됨 → 시작 전 변경 필요
  • 네트워크 충돌: 같은 DHCP 환경이면 IP 충돌 가능 → 고정 IP 설정 권장
  • Tailscale 충돌: 원본과 동시에 Tailscale 실행하면 충돌 → 복원된 CT에서 tailscale logout 후 재등록

6-3. 실제 복원 결과

pqserver에 복원된 CT:
  CT 400 ← ct/200 (ubuntu-dev, DEV)     14 GiB
  CT 401 ← ct/201 (ubuntu-test, shc)    3.5 GiB
  CT 402 ← ct/106 (neo4j)               1.9 GiB

7. 디스크 이식성

sdb1(ext4)에 PBS 데이터스토어가 만들어져있으므로:

  1. pqserver에서 sdb 물리적으로 분리
  2. 다른 서버에 장착
  3. mount /dev/sdX1 /mnt/datastore
  4. PBS에서 데이터스토어 등록: proxmox-backup-manager datastore create pbs-local /mnt/datastore/backups
  5. 기존 백업 데이터 모두 인식됨

PBS 메타데이터가 디스크 안에 저장되므로 서버가 바뀌어도 데이터 유실 없음.

8. 관련 파일/설정 위치

항목 위치
PBS CT 설정 pqserver /etc/pve/lxc/130.conf
PBS 데이터스토어 /mnt/pbs-disk/backups (sdb1)
PVE fstab pqserver /etc/fstab (sdb1 마운트)
Headscale DB /srv/headscale-tailscale-replacement/data/db.sqlite
OPNsense Caddy OPNsense GUI → Services → Caddy
opnsense-admin /srv/opnsense-admin/ (API 클라이언트)