Files
obsidian/infra/data/k3s-backup.md

8.4 KiB
Raw Permalink Blame History

title, updated, tags
title updated tags
K3s 백업 파이프라인 2026-04-15 Longhorn 라벨 키 오타 수정 (recurring-job-group, 대시 포함). 자세히는 history/2026-04-15-longhorn-backup-label-typo.md 참조
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)

# 수동 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/defaults3://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/<group>=enabled)

[!warning] 라벨 키 주의 (2026-04-15 정정) 정확한 키는 recurring-job-group.longhorn.io/<group> (대시 포함). 2026-04-14 초기 구축 시 recurringjob-group.longhorn.io/<group> (대시 없음) 오타로 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 시스템).

신규 볼륨 분류 절차

vol=$(kubectl -n <ns> get pvc <pvc-name> -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 스펙)

    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

    kubectl -n longhorn-system get volume restored-<tag> \
      -o jsonpath='{.status.state} {.status.restoreRequired}'
    
  3. 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" }
    
  4. 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
    
  5. 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 rulelonghorn-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건 선정하여 실제 복원 절차 실행)