- 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>
11 KiB
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.kr → 59.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 데이터스토어가 만들어져있으므로:
- pqserver에서 sdb 물리적으로 분리
- 다른 서버에 장착
mount /dev/sdX1 /mnt/datastore- PBS에서 데이터스토어 등록:
proxmox-backup-manager datastore create pbs-local /mnt/datastore/backups - 기존 백업 데이터 모두 인식됨
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 클라이언트) |