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>
This commit is contained in:
360
pbs-setup-backup-restore.md
Normal file
360
pbs-setup-backup-restore.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# 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. 물리 디스크 확인 + 마운트
|
||||
|
||||
```bash
|
||||
# 미사용 디스크 확인
|
||||
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 생성
|
||||
|
||||
```bash
|
||||
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용)
|
||||
|
||||
```bash
|
||||
# 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 마운트 포인트 연결
|
||||
|
||||
```bash
|
||||
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 등록
|
||||
|
||||
```bash
|
||||
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 직접 수정** 방식:
|
||||
|
||||
```bash
|
||||
# 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 외부 노출용)
|
||||
|
||||
```bash
|
||||
# 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 설치
|
||||
|
||||
```bash
|
||||
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. 데이터스토어 생성 + 비밀번호 설정
|
||||
|
||||
```bash
|
||||
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):
|
||||
|
||||
```python
|
||||
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`으로 등록할 때:
|
||||
|
||||
```bash
|
||||
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**를 사용해야 함.
|
||||
|
||||
### 해결
|
||||
|
||||
```bash
|
||||
# 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 등록
|
||||
|
||||
```bash
|
||||
# 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. 백업
|
||||
|
||||
```bash
|
||||
# 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로)
|
||||
|
||||
```bash
|
||||
# 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 클라이언트) |
|
||||
Reference in New Issue
Block a user