프로젝트

일반

사용자 정보

설계 #13720

오 지호님이 21일 전에 변경

## 개요 

 manila driver의 `revert_to_snapshot()` 을 `lvconvert --merge` 대신 **"원본(share) LV 삭제 → revert 대상 snapshot에서 원본 이름으로 thin snapshot 재생성"** 의 형태로 구현했다. 형태는 Proxmox VE의 lvm-thin rollback(`PVE/Storage/LvmThinPlugin.pm`, `volume_snapshot_rollback`) 과 동일하다. 2026-09-08 구현, 같은 날 실장비(ase-manila 2노드 HA) 검증 완료. 

 merge를 버린 이유는 두 가지다. 

 1. **LVM thin의 `--merge`는 형제 snapshot 계보를 파괴한다** — 아래 실측으로 확인 
 2. **GMS의 merge endpoint는 export 중인 share를 revert하지 못한다** — umount EBUSY로 500 

 ## 실측 검증 — LVM thick/thin snapshot merge 

 192.168.22.121 에서 loop 기반 임시 VG(`TESTVG`, 20G)로 직접 검증했다. 호스트 lvm.conf 에 `volume_list="anystor-e"` 가 있어 모든 LVM 명령에 `--config 'activation/volume_list=["anystor-e","TESTVG"]'` 를 붙여 우회했다. 테스트 후 VG/loop/img 전부 정리 완료. 

 원본에 파일 하나(`/mark`)를 두고 내용을 바꿔가며 snapshot 3개를 순차 생성했다. snapshot이 각각 다른 시점을 담게 하고 **가운데 snapshot을 merge** 했다. 

 - thick: `A`(state-3), `A1`(state-0), `A2`(state-1), `A3`(state-2) 
 - thin: `T`(t-state-3), `T1`(t-state-0), `T2`(t-state-1), `T3`(t-state-2) 

 ### thick (COW snapshot) — 형제 snapshot 온전 

 `lvconvert --merge /dev/TESTVG/A2` 실행 후: 

 ``` 
 LV     LSize     Origin Attr         Snap% 
 A        2.00g          owi-a-s---            <- state-1 로 복원됨 
 A1     500.00m A        swi-a-s--- 0.41       <- state-0, origin 유지 
 A3     500.00m A        swi-a-s--- 0.41       <- state-2, origin 유지 
 ``` 

 - A1/A3 데이터 정상 read, `origin=A` 관계 유지. 이어서 A3 merge → A=state-2, 다시 A1 merge → A=state-0. **순서 무관하게 반복 merge 성공** 
 - merge는 원본 전체 재기록에 가까워 남은 snapshot COW가 급증했다(0.01% → 0.41%). **COW가 부족하면 그 snapshot은 Invalid(`I` 플래그)로 죽는다** → 크기 산정 주의 

 ### thin — 형제 snapshot 계보 파괴 

 `lvconvert --merge TESTVG/T2` 실행: 

 ``` 
   Volume TESTVG/T2 replaced origin TESTVG/T. 

 LV     Origin    ThId 
 T              3          <- T2의 thin device를 그대로 인수, t-state-1 
 T1             2          <- Origin 칼럼이 비었다 
 T3             4          <- 역시 비었다 
 ``` 

 - **데이터는 살아있다**: T1=t-state-0, T3=t-state-2 정상 read 
 - **하지만 snapshot 계보가 끊긴다**: 

 ``` 
 # lvconvert --merge TESTVG/T3 
   TESTVG/T3 is not a mergeable logical volume.     (exit=5) 
 ``` 

 - 원인: thin merge는 원본 LV의 thin device id를 snapshot 것으로 갈아끼우는 방식. 형제들이 참조하던 원래 device가 사라지면서 origin 링크가 null이 된다. 즉 "snapshot이 사라진다"가 아니라 **일반 thin LV로 강등**되어 `--merge` 로는 영원히 복원 불가 

 ### thin rename 우회 — 문제를 지연시킬 뿐 

 `--merge` 대신 "snapshot 복제 + 이름 교체"로 복원은 된다: 

 ``` 
 lvcreate -s -n T_restore TESTVG/T3 
 lvrename TESTVG/T TESTVG/T_broken 
 lvrename TESTVG/T_restore TESTVG/T 
 -> T = t-state-2 복원 성공 (T3는 소비되지 않고 남음) 
 ``` 

 **그러나 형제 계보는 여기서도 깨진다.** LVM 텍스트 메타데이터에서 origin은 이름 문자열로 저장되고 `lvrename` 이 그 참조를 함께 갱신한다: 

 ``` 
 # vgcfgbackup 원문 
 T1 { device_id = 2     origin = "T_broken" } 
 T2 { device_id = 3     origin = "T_broken" } 
 T3 { device_id = 4     origin = "T_broken" } 
 T    { device_id = 5     origin = "T3" } 
 ``` 

 무덤 LV를 정리하면 `--merge` 와 동일 상태가 된다(T1/T2/T3의 origin 전부 null, 데이터는 전부 정상 read). 

 결론: 선택지가 둘 다 나쁘다. 

 - **T_broken 유지** → 사용자에게 안 보이는 유령 볼륨이 pool 공간을 영구 점유하고 점유하고, 형제 부모가 유령을 가리키며 가리키며, 복원된 T는 `origin=T3` 로 표시되어 "볼륨인가 snapshot인가" 판정이 깨짐 
 - **T_broken 삭제** → `--merge` 와 동일하게 형제 origin 전부 null 

 rename 방식이 `--merge` 보다 나은 점은 **T3가 소비되지 않아 재복원 소스로 재사용 가능**하다는 것 하나뿐이다. 형제 계보 파괴는 피할 수 없다. 

 부수 비용: 새 T는 LV UUID가 다른 별개 LV다(fstab/pacemaker가 UUID 참조 시 깨짐). FS UUID는 복제되므로 XFS는 `nouuid` 마운트 또는 `xfs_admin -U` 필요. `k`(activation skip) 플래그가 상속되어 `lvchange -kn` 필요. 

 ### 정리 

 |            | 형제 snapshot 데이터 | 형제 snapshot 재복원 | 
 |----------|--------------------|--------------------| 
 | thick      | 보존 (COW 사용량 증가) | 가능, 순서 무관 | 
 | thin       | 보존 (LV은 남음)     | **불가** (`not mergeable`) | 

 근본 원인은 thin snapshot이 device_id별 독립 flat device인데 LVM이 부모-자식을 **이름 참조 한 칸(`origin`)으로만** 표현한다는 점이다. 

 ## GMS merge endpoint 문제 

 `POST /api/lvm/snapshot/<name>/merge` (perl-gms-plugin-lvm `Controller/LVM/Snapshot.pm:409`). export 중인 share를 revert하지 못한다: 

 - `_pacemaker_control` 이 `rsc_<vg>` 와 Filesystem resource는 비활성화하지만 kernel export를 잡고 있는 `share_<vg>_<lv>` ShareCtl은 건드리지 않아 `umount` 가 EBUSY로 실패, endpoint가 500 반환 
 - 실패 경로는 양 노드를 `maintenance=on` 으로 남겨둔다(클러스터 strand) 

 ## delete+recreate 채택 효과 

 - merge도 소비도 없으므로 다른 모든 snapshot은 데이터 홀더이자 이후 revert의 소스로 그대로 유지된다 
 - detach된 snapshot도 LV와 데이터는 살아 있어 rollback 소스로 계속 쓸 수 있다. 단 `GET /api/lvm/snapshot` listing에서는 사라지므로(`origin != ""` 필터) driver는 snapshot API를 create 전용으로 쓰고 쓰고, 존재 확인은 LV view(`get_lv`), 삭제는 `DELETE /lvm/lvs/<lv>` 에 `entity.purpose = 'snapshot'` 으로 처리한다 
 - 실장비 검증: 동일 대상 2회 revert를 클라이언트 마운트 유지 상태로 통과, detached snapshot과 revert된 share 모두 정상 삭제, appliance에는 pool만 남음 

 ## 참고 

 - docs/planning/work-plan.md — "Revert to snapshot" 절 
 - docs/planning/backend-defects.md — 결함 3 "The snapshot merge endpoint cannot revert an exported share" 
 - 구현: anystor_openstack_driver/driver.py `revert_to_snapshot()` 
 - 백엔드가 LV delete 거부(resource entry 존재 시)를 풀어주면 stop-only revert로 되돌릴 수 있는 여지가 있다(work-plan.md "If the backend lifts the LV delete refusal" 절)

뒤로

클립보드 이미지 추가 (최대 크기: 50 MB)