--- title: K3s 백업 파이프라인 updated: 2026-04-15 Longhorn 라벨 키 오타 수정 (recurring-job-group, 대시 포함). 자세히는 history/2026-04-15-longhorn-backup-label-typo.md 참조 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 * * * *` UTC (매시간) | 24 (1일치) | 2 | | critical-backup | critical | backup | `0 */6 * * *` UTC (6h 간격, KST 03/09/15/21시 포함) | 28 (7일치) | 1 | | standard-snapshot | standard | snapshot | `0 18 * * *` UTC (= KST 03:00) | 7 (7일치) | 2 | | standard-backup | standard | backup | `0 19 * * *` UTC (= KST 04:00) | 7 (7일치) | 1 | > cron 은 UTC 기준. standard 그룹은 KST 새벽 트래픽 저점에 실행되도록 설정 (2026-04-15 조정). > [!info] 보존 정책 통일 (2026-04-14) > 백업 보존을 일관적으로 **7일 기준**으로 통일. snapshot은 CoW 체인이라 개수보다 보존 기간이 중요 — critical은 1일치 시간 단위, standard는 7일치 일단위로 유지. ## 볼륨 분류 (2026-04-14 기준, 라벨 `recurring-job-group.longhorn.io/=enabled`) > [!warning] 라벨 키 주의 (2026-04-15 정정) > 정확한 키는 `recurring-job-group.longhorn.io/` (**대시 포함**). 2026-04-14 초기 구축 시 `recurringjob-group.longhorn.io/` (대시 없음) 오타로 18볼륨 백업이 전부 noop 동작했음. RecurringJob 컨트롤러는 셀렉터 매칭 실패 시 에러 없이 "Found 0 volumes"로 조용히 종료하므로 라벨 키는 반드시 대시 포함 형식으로 부착할 것. ### 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 \ recurring-job-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 - [x] **R2 lifecycle rule** — `longhorn-backup` 버킷에 7일 만료 규칙 `longhorn-backup-7d-expire` 등록 완료 (2026-04-14, API 경유). `deleteObjectsTransition.condition.maxAge=604800`. Longhorn retention(7일)과 정합. - [ ] vault, openmemory, anvil, ironclad 등 추가 서비스 NFS 백업 CronJob 구성 - [ ] postgres 백업 CronJob NFS 경로 `/volume1/k3s-backup/postgres`로 변경 - [ ] 분기별 복원 드릴 (critical 그룹 1건 선정하여 실제 복원 절차 실행)