- Cloudflare R2 BackupTarget 등록 (longhorn-backup 버킷) - RecurringJob 4개 (critical/standard × snapshot/backup) - 볼륨 분류: critical 13개, standard 5개 - 복원 절차 명시 (PVC annotation 방식 실패 → Volume CR 경유) - 2026-04-14 end-to-end 검증 (MD5 일치) - TODO: R2 lifecycle rule, 분기별 복원 드릴
220 lines
7.3 KiB
Markdown
220 lines
7.3 KiB
Markdown
---
|
||
title: K3s 백업 파이프라인
|
||
updated: 2026-04-14 Longhorn 볼륨 백업 레이어 추가 (R2 직접)
|
||
tags: [infra, backup, k3s, r2, longhorn, synology]
|
||
---
|
||
|
||
## 아키텍처 (2026-04-14 이후)
|
||
|
||
두 레이어 병행:
|
||
|
||
```
|
||
앱 레벨: K3s Pod(dump/rsync) → NAS (/volume1/k3s-backup/) → R2(k3s-backup)
|
||
↓
|
||
Synology systemd timer
|
||
|
||
Longhorn: Longhorn Volume → Snapshot → Backup → R2(longhorn-backup)
|
||
↓
|
||
RecurringJob (cluster-native)
|
||
```
|
||
|
||
| 레이어 | 용도 | 정합성 | 복원 단위 |
|
||
|--------|------|--------|----------|
|
||
| 앱 레벨 (pg_dump, gitea dump, rsync) | 1순위 복구 경로 | application-consistent | 특정 테이블·파일 |
|
||
| Longhorn Volume backup | 2순위 안전망, 볼륨 통째 롤백 | crash-consistent (block-level) | 볼륨 전체 |
|
||
|
||
**DB는 반드시 앱 레벨 dump 병행.** Longhorn 백업 단독은 crash-consistent에 불과해 트랜잭션 중간 상태가 캡처될 수 있음.
|
||
|
||
## K3s → NAS (NFS)
|
||
|
||
### NFS PV/PVC
|
||
|
||
| 네임스페이스 | PVC 이름 | NFS 경로 | 비고 |
|
||
|-------------|----------|----------|------|
|
||
| gitea | gitea-backup-nfs | /volume1/k3s-backup/gitea | gitea dump |
|
||
|
||
- NFS 서버: 192.168.9.100 (Synology NAS)
|
||
- PV reclaim policy: Retain
|
||
|
||
### CronJob
|
||
|
||
| 네임스페이스 | CronJob | 스케줄 | 내용 |
|
||
|-------------|---------|--------|------|
|
||
| gitea | gitea-backup | 0 3 * * * (UTC) | gitea dump → NFS |
|
||
|
||
## NAS → R2 (Synology systemd timer)
|
||
|
||
### Synology 구성
|
||
|
||
- **rclone 설정**: `/volume1/docker/rclone/rclone.conf`
|
||
- **rclone 실행**: Docker 컨테이너 (`rclone/rclone:latest`)
|
||
- **systemd service**: `r2-backup.service`
|
||
- **systemd timer**: `r2-backup.timer` — 매일 05:00 KST (±5분 jitter)
|
||
|
||
### R2 버킷
|
||
|
||
- 버킷명: `k3s-backup`
|
||
- 엔드포인트: `https://d8e5997eb4040f8b489f09095c0f623c.r2.cloudflarestorage.com`
|
||
- 크레덴셜: [[vault]] `secret/cloud/cloudflare/r2`
|
||
|
||
### 관리 명령 (Synology SSH)
|
||
|
||
```bash
|
||
# 수동 sync
|
||
sudo systemctl start r2-backup.service
|
||
|
||
# 상태 확인
|
||
sudo systemctl status r2-backup.timer
|
||
sudo systemctl list-timers r2-backup.timer
|
||
|
||
# 로그
|
||
sudo journalctl -u r2-backup.service
|
||
|
||
# R2 내용 확인
|
||
sudo /usr/local/bin/docker run --rm \
|
||
-v /volume1/docker/rclone:/config:ro \
|
||
rclone/rclone:latest \
|
||
--config /config/rclone.conf \
|
||
ls r2:k3s-backup/
|
||
```
|
||
|
||
## 보존 정책
|
||
|
||
- NAS: 수동 관리 (디스크 여유에 따라)
|
||
- R2: lifecycle rule 설정 필요 (Cloudflare 대시보드에서 30일 만료 설정)
|
||
|
||
---
|
||
|
||
# Longhorn 볼륨 백업 (2026-04-14 구축)
|
||
|
||
## Backup Target
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| 종류 | Cloudflare R2 (S3 호환) |
|
||
| 버킷 | `longhorn-backup` (APAC 리전) |
|
||
| BackupTarget CR | `longhorn-system/default` → `s3://longhorn-backup@auto/` |
|
||
| 크레덴셜 | K8s Secret `longhorn-system/longhorn-backup-r2` (Vault `secret/cloud/cloudflare/r2` 기반) |
|
||
| Secret 키 | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_ENDPOINTS` |
|
||
| 상태 확인 | `kubectl -n longhorn-system get backuptarget default` — AVAILABLE=true 여야 함 |
|
||
|
||
## RecurringJob (4개)
|
||
|
||
| 이름 | 그룹 | Task | Cron | 보존 | Concurrency |
|
||
|------|------|------|------|------|-------------|
|
||
| critical-snapshot | critical | snapshot | `0 * * * *` (매시간) | 24 | 2 |
|
||
| critical-backup | critical | backup | `0 */6 * * *` (6시간마다) | 30 | 1 |
|
||
| standard-snapshot | standard | snapshot | `0 3 * * *` (매일 03:00) | 7 | 2 |
|
||
| standard-backup | standard | backup | `0 4 * * *` (매일 04:00) | 14 | 1 |
|
||
|
||
## 볼륨 분류 (2026-04-14 기준, 라벨 `recurringjob-group.longhorn.io/<group>=enabled`)
|
||
|
||
### critical (13 볼륨)
|
||
|
||
APISIX etcd ×3, Gitea PostgreSQL, Gitea 저장소, VictoriaMetrics, n8n, OpenMemory data, OpenMemory qdrant, Outline, Portainer, SafeLine DB, Teleport.
|
||
|
||
### standard (5 볼륨)
|
||
|
||
Gitea Valkey, VictoriaLogs (14d 보존), Grafana, RabbitMQ, SFTPGo.
|
||
|
||
### 백업 제외
|
||
|
||
SafeLine 런타임 로그/상태 6종 (휘발성 OK), nfs-provisioner 마운트 (10Mi 시스템).
|
||
|
||
### 신규 볼륨 분류 절차
|
||
|
||
```bash
|
||
vol=$(kubectl -n <ns> get pvc <pvc-name> -o jsonpath='{.spec.volumeName}')
|
||
kubectl -n longhorn-system label volume $vol \
|
||
recurringjob-group.longhorn.io/critical=enabled --overwrite
|
||
# 또는 standard
|
||
```
|
||
|
||
## 복원 절차 (**중요**: PVC annotation 방식 안 됨)
|
||
|
||
Longhorn v1.8+ CSI 동적 프로비저닝에서 `longhorn.io/fromBackup` PVC annotation은 **무시됨** (CSI가 annotation을 드라이버로 전달 안 함). 반드시 Volume CR 경유:
|
||
|
||
1. **Volume CR 생성** (`fromBackup` 스펙)
|
||
```yaml
|
||
apiVersion: longhorn.io/v1beta2
|
||
kind: Volume
|
||
metadata:
|
||
name: restored-<tag>
|
||
namespace: longhorn-system
|
||
spec:
|
||
fromBackup: "s3://longhorn-backup@auto/?backup=<backup-name>&volume=<orig-vol>"
|
||
numberOfReplicas: 3
|
||
size: "<bytes>"
|
||
frontend: blockdev
|
||
accessMode: rwo
|
||
```
|
||
|
||
2. **복원 완료 대기** — `state=detached` 또는 `state=attached` + `restoreRequired=false`
|
||
```bash
|
||
kubectl -n longhorn-system get volume restored-<tag> \
|
||
-o jsonpath='{.status.state} {.status.restoreRequired}'
|
||
```
|
||
|
||
3. **PV 생성** (StorageClass는 `longhorn-static` — 동적 provisioning 회피)
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: PersistentVolume
|
||
metadata: { name: restored-<tag>-pv }
|
||
spec:
|
||
capacity: { storage: <size> }
|
||
accessModes: [ReadWriteOnce]
|
||
storageClassName: longhorn-static
|
||
csi:
|
||
driver: driver.longhorn.io
|
||
fsType: ext4
|
||
volumeHandle: restored-<tag>
|
||
volumeAttributes: { numberOfReplicas: "3", staleReplicaTimeout: "30" }
|
||
```
|
||
|
||
4. **PVC 생성** (PV에 직접 바인딩)
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: PersistentVolumeClaim
|
||
metadata: { name: restored-<tag>-pvc, namespace: <ns> }
|
||
spec:
|
||
storageClassName: longhorn-static
|
||
accessModes: [ReadWriteOnce]
|
||
resources: { requests: { storage: <size> } }
|
||
volumeName: restored-<tag>-pv
|
||
```
|
||
|
||
5. **Pod에 마운트** — 데이터 검증 후 원본 PVC로 swap (앱 재배치 필요)
|
||
|
||
> [!info] 2026-04-14 검증
|
||
> 1Gi 볼륨에 10MB urandom + marker 파일 작성 → snap+backup → 삭제 → 복원 → MD5 완벽 일치 확인.
|
||
|
||
## 운영 명령
|
||
|
||
```bash
|
||
# BackupTarget 상태
|
||
kubectl -n longhorn-system get backuptarget default
|
||
|
||
# 볼륨별 백업 이력
|
||
kubectl -n longhorn-system get backupvolume
|
||
|
||
# 최근 백업 목록
|
||
kubectl -n longhorn-system get backups --sort-by='.metadata.creationTimestamp'
|
||
|
||
# 특정 볼륨의 수동 백업 트리거
|
||
cat <<EOF | kubectl apply -f -
|
||
apiVersion: longhorn.io/v1beta2
|
||
kind: Snapshot
|
||
metadata: { name: manual-$(date +%s), namespace: longhorn-system }
|
||
spec: { volume: <volume-name>, createSnapshot: true }
|
||
EOF
|
||
```
|
||
|
||
Longhorn UI: https://longhorn.inouter.com → Backup 탭에서 시각적 확인 가능.
|
||
|
||
## TODO
|
||
|
||
- [ ] **R2 lifecycle rule** — `longhorn-backup` 버킷에 30일 만료 설정 (R2 대시보드 수동)
|
||
- [ ] vault, openmemory, anvil, ironclad 등 추가 서비스 NFS 백업 CronJob 구성
|
||
- [ ] postgres 백업 CronJob NFS 경로 `/volume1/k3s-backup/postgres`로 변경
|
||
- [ ] 분기별 복원 드릴 (critical 그룹 1건 선정하여 실제 복원 절차 실행)
|