From 3dc684ef7868d1e0bed598d63b7ea0b935dda128 Mon Sep 17 00:00:00 2001 From: kappa Date: Tue, 14 Apr 2026 13:31:21 +0900 Subject: [PATCH] =?UTF-8?q?infra/k3s-backup:=20Longhorn=20=EB=B3=BC?= =?UTF-8?q?=EB=A5=A8=20=EB=B0=B1=EC=97=85=20=EB=A0=88=EC=9D=B4=EC=96=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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, 분기별 복원 드릴 --- infra/k3s-backup.md | 154 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 5 deletions(-) diff --git a/infra/k3s-backup.md b/infra/k3s-backup.md index 14879fd..1bffea4 100644 --- a/infra/k3s-backup.md +++ b/infra/k3s-backup.md @@ -1,15 +1,30 @@ --- title: K3s 백업 파이프라인 -updated: 2026-03-16 -tags: [infra, backup, k3s, r2, synology] +updated: 2026-04-14 Longhorn 볼륨 백업 레이어 추가 (R2 직접) +tags: [infra, backup, k3s, r2, longhorn, synology] --- -## 아키텍처 +## 아키텍처 (2026-04-14 이후) + +두 레이어 병행: ``` -K3s PVC → NFS → Synology NAS (/volume1/k3s-backup/) → Cloudflare R2 (k3s-backup) +앱 레벨: 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 @@ -68,8 +83,137 @@ sudo /usr/local/bin/docker run --rm \ - 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/=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 get pvc -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- + namespace: longhorn-system + spec: + fromBackup: "s3://longhorn-backup@auto/?backup=&volume=" + numberOfReplicas: 3 + size: "" + frontend: blockdev + accessMode: rwo + ``` + +2. **복원 완료 대기** — `state=detached` 또는 `state=attached` + `restoreRequired=false` + ```bash + kubectl -n longhorn-system get volume restored- \ + -o jsonpath='{.status.state} {.status.restoreRequired}' + ``` + +3. **PV 생성** (StorageClass는 `longhorn-static` — 동적 provisioning 회피) + ```yaml + apiVersion: v1 + kind: PersistentVolume + metadata: { name: restored--pv } + spec: + capacity: { storage: } + accessModes: [ReadWriteOnce] + storageClassName: longhorn-static + csi: + driver: driver.longhorn.io + fsType: ext4 + volumeHandle: restored- + volumeAttributes: { numberOfReplicas: "3", staleReplicaTimeout: "30" } + ``` + +4. **PVC 생성** (PV에 직접 바인딩) + ```yaml + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: { name: restored--pvc, namespace: } + spec: + storageClassName: longhorn-static + accessModes: [ReadWriteOnce] + resources: { requests: { storage: } } + volumeName: restored--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 <, createSnapshot: true } +EOF +``` + +Longhorn UI: https://longhorn.inouter.com → Backup 탭에서 시각적 확인 가능. + ## TODO -- [ ] R2 lifecycle rule 설정 (대시보드에서 30일 만료) +- [ ] **R2 lifecycle rule** — `longhorn-backup` 버킷에 30일 만료 설정 (R2 대시보드 수동) - [ ] vault, openmemory, anvil, ironclad 등 추가 서비스 NFS 백업 CronJob 구성 - [ ] postgres 백업 CronJob NFS 경로 `/volume1/k3s-backup/postgres`로 변경 +- [ ] 분기별 복원 드릴 (critical 그룹 1건 선정하여 실제 복원 절차 실행)