해당 글은 100 퍼센트 정확하지 않을 수 있으며 제가 경험에서 취득한 것들을 정리하는 형태의 글입니다. 그래서 혹시 잘못된 부분을 발견하여 코멘트 주신다면 감사합니다.

해당 시리즈가 업데이트 된다면 업데이트 내용 또한 기록될 것 같습니다.


디스크는 중요한 부분이다 실질적인 데이터가 저장되는 곳이기 때문이다. 디스크가 터진다는 것은 여러모로 귀찮은 상황을 만든다, 사람에게든 컴퓨터에게든

HDD와 SSD의 차이를 적을까 말까 고민했는데 이건 익히 알려져있는 부분이라 간단하게 정리할까 싶다. 일단 SSD는 플래시롬의 응용이다. 본디 롬은 쓰기가 불가능하거나 한번만 쓰는게 가능하다고 흔히 설명하는데 이것을 응용해서 여러 번 쓰기가 가능한 상태로 만든 것이 SSD이다 그래서 수명이 다하면 읽는 것만 가능한, 우리가 흔히 설명하는 롬 상태가 된다.

흔히 SSD는 고장이 잘 난다고 알려져 있으나 백블레이즈라는 스토리지 클라우드를 운영하는 회사 통계에 따르면 꼭 그런 것은 아닌 것 같다.

Image

AFR 그러니까 연간 고장률을 보면 하드디스크는 4년째인 2017년 1.8 % 인 것에 비해 SSD는 1.05 % 라는 현저히 적은 고장률을 보이고 있다. 다만 SSD는 그 특성 때문에 고장나면 복구하기 매우 어렵다.


SSD 인터페이스 종류로는 NVME 와 SATA가 있다. M.2는 일종의 폼펙터인데 그래서 SSD를 구입할 때 M.2 SATA SSD인지, NVME인지 확인하고 구매해야한다. 간혹 M.2만 보고 샀다가 인터페이스 단자가 맞지 않아 장착하지 못하는 경우가 생기기 때문이다.

SSD는 데이터 저장 방식에 따라 SLC, MLC, TLC, QLC 등으로 나뉜다.

SSD는 셀에 전하를 저장하는 형태로 데이터를 저장하는데 하나의 셀에 몇 개의 전하를 저장하냐에 따라 이름이 나뉘어진다. 한 개는 SLC, 두 개는 MLC, 세 개는 TLC, 네 개는 QLC 이다.

2023년에 대부분 SSD는 TLC 복층 SSD이다. 보통은 하나의 셀에 적은 양의 전하를 저장할 수록 내구성이 높다고 알려져 있으나, SLC나 MLC 는 셀의 수명보다는 컨트롤러의 수명을 많이 따라가며, 최근 기술의 발전으로 TLC 메모리도 서버용으로 쓸 수 있을 만큼 많이 신뢰성이 발전하였다. 그러나 그럼에도 불구하고 SLC와 HDD는 단종되지 않을 것인데 왜냐하면 SLC는 항공과 우주 처럼 극한의 환경에서, 혹은 TLC와 QLC 메모리의 캐싱용도로 사용되며, HDD는 SSD에 비해 상대적으로 오류 복구 등에 있어 유리하여 백업용으로 쓰일 거라고 전망되고 있다.


HDD는 IDE, SAS, SATA 규격 등이 있는데 IDE 규격은 예전에 많이 쓰였고, SAS 규격은 보통 서버와 워크스트레이션에서 고용량으로 많이 쓰인다. SATA3는 일반적으로 우리가 많이 쓰는 규격이다.

HDD 는 기록 방식에 따라 SMR과 CMR 로 나뉜다. CMR 같은 경우는 데이터를 기록하는 트랙을 일렬로 기록하는 거라면, SMR은 트랙을 조금씩 겹쳐서 기록하는 것이다. 트랙과 트랙 사이에는 트랙가드라는 것이 있는데 이것을 기왓장처럼 조금씩 겹쳐서 기록하는 것이다. 보통 저가형에서는 SMR을, 고가형에서는 CMR 을 많이 사용하는데 종종 혼용으로 생산하는 경우도 있다. SMR의 경우 단가가 저렴한 대신 데이터 랜덤 읽기 쓰기 성능이나 안정성에서 있어서 떨어지는 모습을 보이는데 읽기 쓰기 횟수가 많지 않은 이상 어차피 저장장치들은 소모품임을 고려해서 주기적으로 점검하는 것이 좀 더 효율적이다.


디스크를 사용하기 위해서는 먼저 파티션을 생성해야한다. 파티션이란 디스크의 논리적인 분리 라고 볼 수 있다. 디스크에서 파티션을 생성하기 위해서는 파티션 정보를 저장해야하는데 이 방식에 따라 MBR과 GPT로 나뉜다. MBR 같은 경우 구형 시스템에서 주로 사용되며 주소가 32비트이므로 최대 용량은 2TiB, 주 파티션 개수는 4개로 제한된다. 반면 GPT 같은 경우 UEFI 와 함께 보급 되었으며 주소는 64비트, 최대용량은 8ZiB, 주 파티션 갯수는 128개 이다.

해당 파티션들은 여러 개의 블록으로 구성되어 있다. 블록은 운영체제가 읽고, 쓰고, 탐색하는 가장 기본 단위로 연속된 바이트들의 모음이다. 이 블록들은 섹터로 구성되어 있다. 섹터는 순수한 물리단위로 디스크에서 물리적으로 포맷팅 되어 있다. 따라서 하나의 블록은 연속적 섹터 N 개의 묶음이 된다.


파티션을 나눈 다음에는 내부에 데이터는 파일 시스템에 따라 쓰게 된다. 해당 과정은 처음 OS 설치시 설치 관리자에서 하게 되는데 리눅스 EXT3 기준으로 /, /tmps (스왑용량), /boot, /home, /tmp, /var 별로 파티션을 나누어 설치하게 되면 나중에 관리에 이점이 생긴다. 예를 들어 /home 파티션이 고장나서 미러 디스크를 연결한다고 할 때 나누지 않았으면 디스크채로 바꾸어야 하지만 나누었으면 /home 파티션을 해제하고, 미러디스크의 /home 파티션을 연결한 뒤, 원본 /home 파티션을 fsck 등으로 복구할 수 있다.

혹은 윈도우즈 같은 경우 HDD 에 파티션을 두 개 생성하여 하나는 OS 등을 위한 C 드라이브로, 하나는 데이터를 위한 D 드라이브로 사용할 수 있다. 물론 이렇게 파티션을 나눈다고 하여도 실질적인 디스크는 하나이기 때문에 디스크가 통째로 고장나면 소용이 없으므로 백업이 중요하다.

이렇게 파티션을 나눈 다음에는 파티션 안에 데이터를 어떻게 저장할 건지에 대한 시스템이 필요하다. 그래서 파일 시스템을 생성해준다. 흔히 사용하는 파일 시스템으로는 EXT4, NTFS, ZFS가 있다.


EXT4 은 보통 리눅스 파일 시스템에서 많이 쓰이는 파일 시스템이다. EXT2, EXT3를 거쳐 EXT4에 오게 되었는데 하위 호환을 지원해서 EXT2, 3를 4로 마운트 하는 것이 가능하다. 해당 파일 시스템은 64비트 OS 에 맞춰 1 엑사바이트의 볼륨과 16 TiB 의 단일 파일을 지원한다. 또한 EXT4는 실제로 데이터가 쓰여질 때까지 블록 할당을 미룸으로서 블록 파편화를 최소화 한다.

요 근래 파일 시스템들은 i-node 라는 영역에 메타데이터들을 저장함으로서 관리한다. i-node에는 디스크의 물리주소, 파일 읽기 쓰기 실행 권한 등이 저장된다.

EXT3까지는 BLOCK MAPPING 이란 기법으로 파일들을 관리했다. 이 기술은 1번부터 12번까지의 i-node는 각자 하나의 파일을 가리키고 13번 i-node 부터는 여러 개의 블록 묶음을 가리키고 해당 블록 묶음은 다른 블록들을 가리키는 형태로 효율적으로 파일을 저장한다.

하지만 EXT4는 해당 방식에서 벗어나 EXTENTS 트리 방식을 사용한다. EXTENTS 트리는 트리구조로 이루어져 있는데 i-node 에서 index node를 (depth 1), index node 에서는 leaf node를 (depth 2), leaf node 에서는 실제 디스크 블록을 가리키는 형태로 이루어져 있다.

EXT4의 전체적인 구조는 다음과 같다.

MBR이나 GPT 영역과 예약 영역 뒤에는 여러 개의 그룹으로 이루어져 있다. 그 그룹들은 Super block, Block descriptor table, Block Bitmap, inode Bitmap, Inode Table, DATA Blocks 으로 이루어져 있다.

Super Block 은 전체적인 파일 시스템의 정보를 담고 있다. 총 i-node 갯수, 총 블록 갯수, 볼륨 라벨명, 블록사이즈등이 저장되어 있으며 여러 개의 그룹에서 동일한 정보를 가지고 있다. EXT3 부터 추가된 저널링 (Journaling) 은 Super Block 에 저장된 journaling i-node number 항목은 저널링을 위한 블록을 가리키고, 저널링 블록에 있는 정보들을 이용해 저널링 되어 일관성을 유지한다.

Block descriptor table 은 각 그룹에 있는 Data block bitmap, i-node bitmap, i-node table 의 위치를 알려준다. 각각의 그룹에 위치한 위치를 알려주므로 그룹의 개수만큼 엔트리가 존재하며 모든 그룹에 같은 값으로 저장 되어 있다.

Data block bitmap과 i-node bitmap은 현재 i-node와 데이터 할당 정보의 대한 정보를 담고 있으며 할당되어 있으면 1, 아니면 0 으로 표현한다.

I-node table 은 각 i-node의 정보가 표로 연결되어 있다. EXT4의 경우 기본적인 i-node 한 개의 크기는 256 바이트이다. 우리가 흔히 i-node 에는 메타데이터가 저장된다의 i-node는 해당 공간을 이야기한다.

Data Blocks 은 실제 데이터가 저장된 곳으로 i-node 정보를 통해 파일에 실제 데이터 위치를 파악한다.

좀 더 자세한 설명 및 참고를 위한 리눅스 커널 EXT4 문서를 링크

https://www.kernel.org/doc/Documentation/filesystems/ext4.txt


NTFS 는 주로 윈도우즈에서 쓰이며 최근엔 리눅스에서도 NTFS 를 지원하고, 윈도우즈에서도 별도의 써드파티프로그램이나 WSL을 이용해서 EXT4 을 마운트할 수 있지만 메인은 NTFS 이다.

NTFS 꽤나 재밌는 구조로 되어 있는데 데이터를 파일 형식으로 할당한다는 것이다. 그 외의 특징으로는 USN 저널을 통해 파일 변경 내용을 기록해서 재부팅할 때 복구 하는 것이나 파일 데이터가 0 일 경우 실제 데이터를 기록하지 않고 크기 정보만 유지 하는 등의 특징이 있다. 또한 흔히 블록이라고 부르는 섹터들의 묶음을 클러스터라고 부른다.

파일 시스템 구조는 Volume Boot Record, Master File Table, Data Area 으로 구성 되어 있다.

VBR 이라고 줄여 쓰는 Volum Boot Recrod 는 NTFS 시스템의 여러 설정 값들이 저장되어 있다. 부트 코드, 섹터당 바이트 수, 클러스트 당 섹터 수, 볼륨의 크기, 클러스터의 크기, MFT의 시작 클러스터 주소 등의 정보들이 저장되는 영역이다.

MFT 라고 줄여 쓰는 Master File Table 은 i-node 라고 흔히 말하는 정보들을 파일 형태로 저장하는 영역이다. MFT 영역은 그 자체로 하나의 파일로 고려하기 때문에 데이터 영역과 뒤섞여 있을 수 있다. 통상적으로 파일시스템의 12.5 % 정도가 MFT 으로 할당 되어 있다. MFT 는 MFT Eentry 들의 집합체다. MFT Entry 중 첫 16개는 ( 0 ~ 15 ) 미리 정의 되어 있는데 이는 MFT Entry 를 관리하는 자체 Entry가 되며 MFT 저널 정보나 볼륨 클러스터 할당 정보, 배드 섹터 정보, MFT Entry 할당 정보 등이 여기에 들어가게 된다.

MFT Entry 는 1 개 당 1024 바이트의 크기를 가진다. 이 중 첫 48 바이트는 MFT Entry Header 가 된다. MFT Entry Header 에는 해당 Entry 첫 번째 파일 속성의 위치, 하드링크 주소, 실제 사용 중인 크기, 다음 속성 ID 등이 저장된다. 어떤 파일에 대한 정보가 너무 많아서 하나의 MFT Entry에 담기가 불가능 할 경우 여러개의 MFT Entry 를 사용하는데 이때 처음에 해당하는 MFT Entry 가 Base MFT Entry 이며, 그 외에 것들은 Non-Base Entry 이다. 이것을 구분하고 연결하기 위해 Header 에는 해당 정보들 또한 저장한다.

헤더 다음에는 속성이 오는데 속성에는 파일이 생성, 접근, 수정 시간, 소유자 등의 일반적인 정보들과 볼륨 이름, 파일 내용 등이 저장된다. 이 속성들은 다시 속성 헤더와 속성로 구성되며 MFT Entry와 마찬가지로 Atrribute identifier를 통해 Resident Attribute (처음 속성), Non-Resident Attribute (처음이 아닌 속성) 를 구분한다. 속성 헤더에는 속성 내용이 담긴 주소의 시작과 끝, 런리스트들의 가상 ID 처음 주소, 속성 헤더에서 옵셋된 크기, 할당된 크기 등을 담고 있다. 속성에서야 비로소 파일에 대한 메타데이터들이 저장된다.

Non-Resident Attribute 는 속성의 데이터 영역인 700 바이트를 초과하는 파일을 저장하기 위해 만들어진다. Non-Resident Attribute 속성의 데이터 영역은 따라서 해당 파일이 저장된 위치와 크기를 저장하게 되는데 해당 런리스트 들의 주소들의 정보를 담고 있다. 이 정보들을 모으면 비로소 하나의 파일이 나온다.


ZFS 는 소개할까 말까 고민하다가 일단은 같이 적어보기로 한다. ZFS 는 기존 유닉스 계열 파일 시스템을 대체하기 위해 솔라리스 10 에서 소개 되었다. 파일 시스템 중에 최초로 128 비트 파일 시스템을 적용하였으며, 최고의 파일 시스템이라고 알려져있다. 지금까지 물리적인 디스크 단위로 관리했다면 ZFS 는 스토리지 풀로 관리한다. 풀이란 디스크와 파일 시스템을 연결하는 완충적인 개념으로 이해했다. 디스크들은 풀로 연결되고 풀들은 파일시스템들과 파티션, 레이드 등을 가상화하여 관리한다. 최초로 ZFS 를 설치하면 rpool 이라는 루트 pool 이 생긴다. 이 루트 pool 에는 OS 가 설치된 풀인데 여기에 디스크들을 연결하여 자동으로 확장할 수도, 새로운 풀을 만들어서 따로 관리할 수도 있다. 하지만 이러한 특성 때문에 별도의 하드웨어 레이드를 구성할 경우 제대로 동작하지 않을 수도 있다.

또한 ZFS는 스냅샷 기능과 Copy-On-Write 기능을 지원한다. 스냅샷 같은 경우 흔히 VPS 에서 이야기하는 현재 상태를 복제하는 스냅샷을 이야기한다. Copy-On-Write 의 경우 보통 파일 시스템들은 데이터 변경을 동일 공간에 덮어 쓰는 형태로 진행하지만, ZFS 의 경우 변경 데이터를 새로운 블록에 완전히 쓴 후 파일 포인터를 새로운 데이터를 가리키는 형태로 변경하여 원본 데이터를 보존한다. 이런 방식으로 최대한 데이터의 무결성을 보존한다. 만일 데이터 변경 중에 정전 등으로 해당 작업이 중지 되면 변경 전 데이터, 변경 할 데이터를 모두 손실한다. 저널링을 통해 복구할 수 있지만, 해당 작업은 서비스의 중지를 야기할 수 있기에 Copy-On-Write 방식을 채택했다. 아직 많이 써보지 않았기에 자세하기 적기 조금 어려운 부분들이 있다. 그래서 좀 더 자세한 정보들은 오라클 ZFS 문서를 첨부하며 조금 짧게 마무리 한다.

https://docs.oracle.com/cd/E26925_01/html/E25825/zfsover-2.html


보통의 파일 시스템들은 디스크 등에 문제가 생기면 읽기 전용 모드로 바꾸어서 데이터의 손상을 막는다. 하지만 상태를 미리 확인해서 디스크를 교체할 수 있다.(하지만 fsck 를 통해 복구해서 완전히 파손 될 때까지 사용하는 회사 또한 존재한다.) 리눅스에서는 SMARTCTL 이라는 디스크의 상태 정보인 smart를 읽어오는 툴이 있다. 보통 옵션으로는 전부 표시하는 A 나 간단히 표시하는 H 를 많이 쓴다. 다음은 -A 옵션을 이용해 모든 데이터를 출력한 모습인데 이 중 일부 항목을 소개하고자 한다.

+ 일부 하드디스크 (해당 정보는 씨게이트 새로운 하드 디스크였음) 에서는 새로운 하드디스크임에도 RAW READ ERROR RATE 같은 값들이 비정상적으로 크게 나오는 경우가 있다. 실제로 에러가 발생하면 정상적으로 카운트가 되나 0 라고 뜨지 않고, 비정상적으로 뜨는 경우가 있으니, RAW 데이터도 같이 확인하는 것이 좋다.

<pre class="wp-block-code has-white-color has-black-background-color has-text-color has-background">```
$ smartctl -A /dev/sda1
smartctl 7.2 2020-12-30 r5155 [x86_64-linux-5.15.0-60-generic] (local build)
Copyright (C) 2002-20, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART Attributes Data Structure revision number: 10
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
  1 Raw_Read_Error_Rate     0x000f   077   064   006    Pre-fail  Always       -       49139280
  3 Spin_Up_Time            0x0003   098   098   000    Pre-fail  Always       -       0
  4 Start_Stop_Count        0x0032   100   100   020    Old_age   Always       -       5
  5 Reallocated_Sector_Ct   0x0033   100   100   010    Pre-fail  Always       -       0
  7 Seek_Error_Rate         0x000f   074   060   045    Pre-fail  Always       -       27663577
  9 Power_On_Hours          0x0032   100   100   000    Old_age   Always       -       486 (11 25 0)
 10 Spin_Retry_Count        0x0013   100   100   097    Pre-fail  Always       -       0
 12 Power_Cycle_Count       0x0032   100   100   020    Old_age   Always       -       5
183 Runtime_Bad_Block       0x0032   100   100   000    Old_age   Always       -       0
184 End-to-End_Error        0x0032   100   100   099    Old_age   Always       -       0
187 Reported_Uncorrect      0x0032   100   100   000    Old_age   Always       -       0
188 Command_Timeout         0x0032   100   100   000    Old_age   Always       -       0 0 0
189 High_Fly_Writes         0x003a   100   100   000    Old_age   Always       -       0
190 Airflow_Temperature_Cel 0x0022   070   068   040    Old_age   Always       -       30 (Min/Max 25/32)
191 G-Sense_Error_Rate      0x0032   100   100   000    Old_age   Always       -       0
192 Power-Off_Retract_Count 0x0032   100   100   000    Old_age   Always       -       11
193 Load_Cycle_Count        0x0032   100   100   000    Old_age   Always       -       408
194 Temperature_Celsius     0x0022   030   040   000    Old_age   Always       -       30 (0 22 0 0 0)
195 Hardware_ECC_Recovered  0x001a   077   064   000    Old_age   Always       -       49139280
197 Current_Pending_Sector  0x0012   100   100   000    Old_age   Always       -       0
198 Offline_Uncorrectable   0x0010   100   100   000    Old_age   Offline      -       0
199 UDMA_CRC_Error_Count    0x003e   200   200   000    Old_age   Always       -       0
240 Head_Flying_Hours       0x0000   100   253   000    Old_age   Offline      -       395h+24m+04.629s
241 Total_LBAs_Written      0x0000   100   253   000    Old_age   Offline      -       2935320588
242 Total_LBAs_Read         0x0000   100   253   000    Old_age   Offline      -       6795872588


```

  1. RAW READ ERROR RATE : 디스크 표면에서 데이터를 읽는 과정에서 문제 발생, 주로 디스크 물리적인 충격에서 유발됨, 정상값 : 0
  2. SPIN UP TIME : 플래터 회전이 정지상태에서 최대 RPM까지 도달하는 평균 시간
  3. START STOP COUNT : 플래터가 회전하고 정지한 횟수
  4. REALLOCATED SECTOR CT : 섹터에 문제가 생겨 스페어 영역으로 섹터가 이동한 경우, 정상값 0
  5. SEEK ERROR RATE : 탐색 오류율, 정상값 0
  6. POWER ON HOURS : 하드디스크 전원 공급 시간
  7. SPIN RETRY COUNT : 최대 RPM 에 도달하기 위해 회전을 시도하는 횟수, 정상적이라면 1회에 끝나야함
  8. POWER CYCLE COUNT : 전원 ON/OFF횟수
  9. POWER OFF RETRACT COUNT : 헤드가 PARKING 위치로 이동한 횟수
  10. LOAD CYCLE COUNT : 헤드가 플래터 위로 이동한 횟수
  11. TEMPERATURE CELSIUS : 하드 디스크 온도
  12. REALLOCATED EVENT COUNT : 스페어 영역으로 대체된 섹터에서 데이터를 읽은 횟수
  13. HARDWARE ECC RECOVERED : ECC 오류 검출로 인해 복구된 횟수
  14. CURRRENT PENDING SECTOR : 불안정한 섹터를 뜻하며, 읽기 에러로 인해 맵핑을 대기하는 섹터 수, 맵핑 및 읽기 성공 시 숫자 감소, 정상값 0
  15. OFFLINE UNCORRECTABLE : 읽기, 쓰기에 문제가 생긴 섹터 수, 디스크 표면 손상된 경우, 정상값 0
  16. UDMA CRC ERROR COUNT : 하드디스크 데이터 전송 중에 발생한 CRC 오류 횟수, 통상적으로 케이블이나 인터페이스 단자 문제일 가능성이 높음
  17. HEAD FLYING HOURS : 실제 하드 사용 시간
  18. TOTAL LBAS WRITTEN : 총 데이터 쓰기량, 전원 OFF 시 갱신
  19. TOTAL LBAS READ : 총 데이터 읽기량, 전원 OFF 시 갱신