metadata를 기반으로 한 LVM volume 복구

🗓️

증상

아직 복구중이긴 하지만 내역으로 한번 써본다.
만약 수행한다면 dd등을 통해 데이터를 백업 후 한번 읽어보고 본인의 상황에 얼마나 도움이 될지 읽어보고 진행하길 바란다.

LVM에 SSD캐시를 붙여서 쓰고있었다. writethrough 모드로 쓴다고 안심하고 있었으나 SSD가 돌연사 함으로 인해 LVM 볼륨에도 접근할 수 없는 상태가 됐다. (IO error 상태)

이 진단을 내리기 전에는 단순히 파일시스템이 깨진것으로 판단하고 언마운트 후 fsck를 열심히 돌렸으나 파일시스템이 돌아오지 않았고, 당연히 마운트도 되지 않았다. 결론적으로 캐시만 깨진 멀쩡한 파일시스템에 fsck를 돌린것은 실수였다. 절대 돌리지 말 것.

이 경우에는 볼륨에 접근도 할 수 없으며 LVM관련 명령어도 다 이런식으로만 뜬다.

사진 중간에 있는 [unknown]이 캐시로 쓰던 SSD다. 사망한것으로 추측할 수 있다. lsblk에도 표시되지 않는 상황이다. 여기까지 와서야 캐시가 문제인것을 확인했다. 캐시를 붙이지 않은 볼륨은 멀쩡한것으로 판단됐다. 또한 uncachesplitcache가 작동하지 않는 모습을 볼 수 있다. 이 두 명령어는 볼륨으로부터 캐시를 분리하는 lvconvert 옵션이다.

깊은 한숨을 뒤로하고 LVM을 복구할 수 있는 방법을 찾아봤다. 대부분 pv가 손상 됐을때를 대비한 복구 방법 밖에 없었다. 

일단 캐싱한 볼륨을 분리 시도하였으나 SSD PV를 찾을 수 없다며 거부한다. 위의 두번째 사진의 마지막 줄에 clOey3으로 시작하는 볼륨이 그것이다.

우선 볼륨에 접근이 안된다고 무턱대고 파일시스템 체크 fsck 명령어를 이용해 파일시스템을 수정하면 안된다. 그러면 나처럼 완전 파일시스템이 박살나는 경우가 생긴다.

복구 시도

여기서 부터는 데이터를 건드리는 부분이다. 따라하는것은 본인의 몫이다.

unknown으로 뜨는 캐시부터 제거를 해보자.

vgreduce --removemissing --force vg_name

vg_name에는 캐시가 포함된 VG이름을 입력하면 된다. 이렇게 하면 unknown을 비롯해 고장난 캐시에 잡혀있는 볼륨도 같이 떨어진다. 여기서 당황하지 말자.


/etc/lvm/디렉토리에 가면 archive라는 디렉토리가 있다. 그 안에 VG이름으로 시작해서 .vg로 끝나는 파일이 있는데 이 파일이 LVM파티셔닝의 정보를 담고있는 귀중한 자료다. 관련된 VG를 찾아서 가장 마지막 날짜의 바로 이전 날짜의 파일을 복사하고 열어보자.


예를들어 vgreduce를 실행한 시점이 8월 16일이라면 그 위의 7월 8일 파일 열어야 한다는 이야기다.

    physical_volumes {

        pv0 {
            id = "yuJz7u-Frh3-MbCQ-w16Y-Nyr7-eJcC-wFEurA"
            device = "/dev/md127"   # Hint only
            ... 중략 ...

        pv1 {
            id = "clOey3-5d5Y-FcLB-Zz3G-Mby7-FrC2-3h0cAc"
            device = "/dev/sdb1"    # Hint only

            status = ["ALLOCATABLE"]
            flags = []
            dev_size = 488392704    # 232.884 Gigabytes
            pe_start = 2048
            pe_count = 59618    # 232.883 Gigabytes
        }
    }

파일 초반에 보면 clOey3로 시작하는 SSD캐시가 보인다. 이 pv1부분을 제거하자. 이 글을 찾아서 볼 정도라면 이것이 PV를 담고있는 메타데이터라는것을 알 수 있다.

        PLT_PRV_DATA-cache {
            id = "TfQdyh-ZOds-yEh5-wODP-vZ6q-kJeH-Gsjr00"
            status = ["READ", "WRITE"]
            ... 중략 ...
        }

        PLT_PRV_DATA-cache_cdata {
            id = "DdaBk3-mTFO-CEuv-ZM3t-sPDN-KYkf-4xKk0D"
            status = ["READ", "WRITE"]
            ... 중략 ...
        }

        PLT_PRV_DATA-cache_cmeta {
            id = "vAp9tt-HP7C-f6te-WTL4-gkI6-N91x-1omWlq"
            status = ["READ", "WRITE"]
            ... 중략 ...
        }

그리고 중간쯤부터 LV이름-cache, LV이름-cache-cdata, LV이름-cache-cmeta 식으로 되어있는 LV 메타데이터가 보인다. 이 정보들을 삭제 해줘야 SSD인 pv1을 참조하는 볼륨이 사라져 LV를 복구할 수 있게 된다.

그리고 여기서부터 중요하니 중략을 하지 않겠다.

        POOL1_PLT_PRV_DATA_corig {
            id = "1AMMUb-WG1u-oPHr-HVAX-HviD-5ClL-0vzcZ1"
            status = ["READ", "WRITE"]
            flags = []
            creation_time = 1607243366  # 2020-12-06 17:29:26 +0900
            creation_host = "ochard-warehouse-amstredam"
            segment_count = 4

            segment1 {
                start_extent = 0
                extent_count = 1310720  # 5 Terabytes

                type = "striped"
                stripe_count = 1    # linear

                stripes = [
                    "pv0", 3932160
                ]
            }
            segment2 {
                start_extent = 1310720
                extent_count = 128000   # 500 Gigabytes

                type = "striped"
                stripe_count = 1    # linear

                stripes = [
                    "pv0", 6146304
                ]
            }
            segment3 {
                start_extent = 1438720
                extent_count = 128000   # 500 Gigabytes

                type = "striped"
                stripe_count = 1    # linear

                stripes = [
                    "pv0", 6939904
                ]
            }
            segment4 {
                start_extent = 1566720
                extent_count = 51200    # 200 Gigabytes

                type = "striped"
                stripe_count = 1    # linear

                stripes = [
                    "pv0", 7375360
                ]
            }
        }

LV이름_corig라는 블록이 있다. 이것이 원본 데이터로써 LV가 생성됐을 시점부터 lvextend한 지점까지의 메타데이터를 담고 있다. 우린 이 정보가 필요하다.

우선 위에서 캐시와 관련된 PV와 LV를 모두 제거했다고 보겠다. 여기서 수정해야 할 것은 딱 한군데인데, 바로 status부분이다. 이부분에 READ와 WRITE만 있는것을 아래와 같이 VISIBLE을 추가해준다. 이것을 해주지 않으면 lvs명령어를 했을때 볼륨이 보이지 않는다.

status = ["READ", "WRITE", "VISIBLE"]

그리고 파일을 저장하고 나와서 아래와 같이 입력한다.

vgcfgrestore -f 수정했던메타데이터파일.vg VG이름

이렇게 하면 VG정보를 엎어쓸거냐 물어보는데 y를 눌러 진행한다. 그리고 다음 명령어를 입력해 변경된 부분을 활성화해준다.

vgchange -ay VG이름

그러면 사라졌던 볼륨이 모두 복구되는것을 볼 수 있다. 심지어 blkid로 나오는 uuid정보까지 기존 정보와 동일한것은 덤이다.

그러나 이 상태에서 마운트가 되지 않는데 lvrename 명령어로 _corig붙은 부분을 제거해주면 마운트가 된다.

lvrename VG이름 LV이름 변경할LV이름

mount명령어를 통해 LV를 마운트 해보면 데이터가 살아있을 수 있을 확률이 아주 높다. 나는 이미 fsck로 수차례 파일시스템을 비벼놓은 상황이라 안타깝게도 파일이 많이 유실되었고 lost+found에도 많은 정보가 남아있지 않았다.

혹시 나처럼 fsck를 돌려서 볼륨이 깨끗해져버린 상황이라면 당황하지 말고 lost+found로 가서 파일 몇개라도 건져내자. 이 글이 매우 도움이 된다.

결국 나는 파일 복구를 포기하고 3개월전 백업 데이터가 있어서 그걸로 복구하기로 했다. 캐시가 깨지는 전조증상은 아래와 같으니 참고하자

  1. 네트워크를 통한 파일IO가 이유없이 느려지고 종종 클라이언트 쪽이 hang됨. 서버는 정상.
  2. 이유를 찾다가 fsck를 돌려보면 파일시스템이 깨져서 복구를 시도함.
  3. 그렇다고 hang 될 때 딱히 IO WAIT이 발생하지도 않음

references