- 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, 분기별 복원 드릴
7.3 KiB
title, updated, tags
| title | updated | tags | ||||||
|---|---|---|---|---|---|---|---|---|
| K3s 백업 파이프라인 | 2026-04-14 Longhorn 볼륨 백업 레이어 추가 (R2 직접) |
|
아키텍처 (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)
# 수동 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 시스템).
신규 볼륨 분류 절차
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 경유:
-
Volume CR 생성 (
fromBackup스펙)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 -
복원 완료 대기 —
state=detached또는state=attached+restoreRequired=falsekubectl -n longhorn-system get volume restored-<tag> \ -o jsonpath='{.status.state} {.status.restoreRequired}' -
PV 생성 (StorageClass는
longhorn-static— 동적 provisioning 회피)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" } -
PVC 생성 (PV에 직접 바인딩)
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 -
Pod에 마운트 — 데이터 검증 후 원본 PVC로 swap (앱 재배치 필요)
[!info] 2026-04-14 검증 1Gi 볼륨에 10MB urandom + marker 파일 작성 → snap+backup → 삭제 → 복원 → MD5 완벽 일치 확인.
운영 명령
# 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건 선정하여 실제 복원 절차 실행)