글루시스 기술 블로그 최신 글
tech.gluesys.com에서 발행한 기술 글을 테크곳간이 수집해 최신순으로 보여드립니다.
최신 글
zfs userspace와 zfs create 사이의 EBUSY 레이스 수정
목차 들어가며 버그 증상 코드 경로 분석 세 가지 잠금, 한눈에 보기 EBUSY가 발생하는 순간 원인 분석 해결 방법 새 함수: zfsvfs_create_hold() zfsvfs_hold() 수정 zfsvfs_rele() 수정 구현 과정에서 발견한 문제들 ABBA 교착 (hold() 전환의 부작용) z_use_hold 플래그 도입 테스트 케이스 추가 마치며 들어가며 회사에서 OpenZFS 기반 스토리지를 개발하던 중, zfs create로 볼륨을 생성하는 중 dataset 생성 직후의 자동 마운트에서 간헐적으로 mountpoint or dataset is busy 오류로 실패하는 버그를 발견했습니다. 동시에 실행 중이던 스토리지 모니터링 데몬의 zfs userspace 조회가 같은 dataset을 먼저 잡아버린 것이 원인이었습니다. 이 포스트는 원인 분석부터 OpenZFS 업스트림에 패치를 병합시키기까지의 과정을 기록한 글입니다. 버그 증상 zfs create는 dataset을 만든 뒤 곧바로 마운트까지 수행합니다. 이 마운트 단계에서, pool과 dataset 상태를 10초마다 수집하는 모니터링 데몬의 zfs_job이 같은 dataset에 zfs userspace / groupspace / projectspace를 동시에 실행 중이면, zfs create의 마운트 단계가 EBUSY로 실패하며 exit(1)을 반환합니다. zfsmanager는 exit(1)을 볼륨 생성 실패로 처리하므로, dataset은 ZFS에 orphan으로 남고 볼륨 생성 API가 실패로 응답하는 사례가 있었습니다. # zfs create -o acltype=posix -o refquota=154G -o refreservation=154G ci_zfsPool_1B/ci_zfsVol_beta STDERR: cannot mount 'ci_zfsPool_1B/ci_zfsVol_beta': mountpoint or dataset is busy filesystem successfully created, but not mounted 코드 경로 분석 zfs userspace는 커널 ioctl ZFS_IOC_USERSPACE_MANY를 호출합니다. 이 핸들러는 zfsvfs_hold()를 통해 대상 데이터셋의 zfsvfs 구조체(ZFS 파일시스템 인스턴스를 표현하는 커널 구조체)를 참조합니다. 데이터셋이 이미 마운트되어 있으면 getzfsvfs()로 기존 구조체를 가져오고, 마운트되지 않은 상태면 zfsvfs_create()로 임시 구조체를 생성합니다. 이 코드 경로에는 세 가지 동기화 메커니즘이 관여합니다. 각각 보호하는 데이터와 동작 방식이 다르며, 버그의 원인과 해법을 이해하려면 세 가지 잠금을 확인해볼 필요가 있습니다. 세 가지 잠금, 한눈에 보기 이 경로에 등장하는 잠금은 모두 세 가지입니다. 이름이 비슷해 보이지만, 지키는 대상과 동작 방식이 다릅니다. z_teardown_lock (R/W rwlock, zfsvfs_t 소속) 보호: 파일시스템 인스턴스(zfsvfs_t)의 생명주기 · inode 캐시 · VFS 마운트 정보입니다. 목적: 한 스레드가 쿼터를 조회하는 도중 다른 스레드가 umount·롤백으로 파일시스템을 부수면, 조회 쪽이 막 해제된 inode/구조체를 건드리는(use-after-free) 충돌이 생깁니다. 이러한 상황을 방지하기 위해 조회는 R, 해체는 W로 직렬화합니다. R(읽기): 쿼터 조회·일반 입출력·zfsvfs_hold() 내부에서 여러 스레드가 동시에 진입합니다. W(쓰기): umount·rollback·receive(파일시스템 해체) 시 기존 R이 모두 빠질 때까지 대기한 뒤 점유합니다. dp_config_rwlock (R/W rwlock, dsl_pool_t 소속) 보호: 풀 전체의 데이터셋 트리 · 속성 · 스냅샷 목록입니다. 목적: 트리를 순회해 데이터셋을 찾는 도중 누군가 노드를 추가/삭제하면, 읽는 쪽이 중간에 끊긴 트리나 잘못된 속성값을 보는 불일치가 생깁니다. 읽기는 공유(R), 구성 변경은 단독(W)으로
2026년 6월 24일
Lustre Changelog DR
목차 인사말 전통적인 rsync 방식의 한계와 파일 히스토리 기반 DR Lustre Changelog Changelog의 동작 원리 Changelog 활성화 및 확인 changelog_mask 이벤트 타입 Changelog 읽기 글루시스 DR 솔루션 주요 특징 시스템 구성 시스템 아키텍처 컴포넌트별 상세 기능 동작 원리 1. 이벤트 수집 단계 2. 동기화 처리 단계 3. 이벤트 처리 상태 관리 4. 예상치 못한 종료 시 복구 메커니즘 5. 오류 처리 및 재시도 메커니즘 DR 솔루션 등급 기준 및 시장 요구사항 글루시스 DR 솔루션 테스트 결과 테스트 결과 기반 RPO / RTO 평가 마치며 인사말 2022년 10월 15일 발생한 SK C&C 데이터센터 화재와 2025년 9월 26일 국가정보자원관리원 데이터센터 화재 사례에서 보듯이, 예기치 못한 재난 상황에서도 시스템이 정상적으로 운영되기 위해서는 데이터 손실을 최소화할 수 있는 효율적인 백업 및 복구 전략이 필수적입니다. 데이터센터에서의 재해 복구(Disaster Recovery) 계획에서 RPO(Recovery Point Objective)1는 가장 중요한 지표 중 하나로, RPO가 짧을수록 재난 발생 시 보호할 수 있는 데이터의 범위가 넓어져 비즈니스 연속성을 보다 안정적으로 보장할 수 있습니다. 하지만 Lustre와 같은 대용량 분산 파일시스템 환경에서는 전체 데이터를 주기적으로 동기화하는 기존 방식이 많은 시간과 자원을 요구한다는 한계가 있습니다. 이러한 제약을 극복하기 위해 변경된 데이터만을 추적하고 동기화하는 방식을 적용하면, 동기화 시간을 크게 줄이고 RPO를 최소화할 수 있습니다. 이번 블로그에서는 Lustre 파일시스템에서 파일 생성, 수정, 삭제와 같은 모든 메타데이터 변경 이벤트를 기록하는 Changelog2 기능과, 이를 기반으로 구현한 글루시스의 DR 솔루션을 소개합니다. 이전에 제가 작성한 블로그 Lustre_FID_1과 Lustre_FID_2에서 Lustre의 파일 생성 방식과 파일의 고유한 FID(File Identifier)로 관리하는 방식에 대해 다루었습니다. 해당 내용은 이번 블로그에서 설명하는 Changelog와 DR 솔루션의 동작 원리를 이해하는 데 도움이 되니 참고해 주시기 바랍니다. 전통적인 rsync 방식의 한계와 파일 히스토리 기반 DR DR을 구현하기 위해 전통적으로 사용되는 방식은 rsync3를 활용한 전체 스캔 기반 동기화입니다. 이 방식은 SSH 명령어 또는 SSH 데몬을 통해 실행할 수 있으며, 파일시스템 전체를 주기적으로 스캔하여 변경된 파일을 식별한 뒤 이를 동기화합니다. 그러나 rsync 기반 동기화 방식은 파일시스템 전체를 스캔한 이후에야 동기화가 이루어지기 때문에, 파일시스템의 규모가 커질수록 스캔 시간이 선형적으로 증가하는 한계를 가집니다. 특히 Lustre와 같은 대규모 파일시스템에서는 동기화에 수십 시간이 소요될 수 있으며, 동기화 주기가 스캔 주기에 의존하게 되어 RPO를 최소화하는 데에도 제약이 발생합니다. 이러한 이유로, 대규모 파일시스템 환경에서 실시간에 가까운 DR을 구현하기에는 전통적인 rsync 방식만으로는 한계가 있습니다. 이러한 한계를 극복하기 위한 대안으로, 파일 히스토리를 기반으로 한 DR 방식을 고려할 수 있습니다. 해당 방식은 파일시스템에서 발생한 변경 이력을 기반으로 수정된 파일 목록만 선별하여 동기화함으로써, 전체 파일시스템을 반복적으로 스캔하는 rsync 방식에 비해 스캔에 소요되는 시간을 크게 줄일 수 있습니다. 더 나아가 파일시스템 모니터링을 통해 변경된 파일 정보를 실시간에 가깝게 수집할 수 있다면, RPO를 최소화하는 고효율 DR 환경을 구현할 수 있습니다. Lustre Changelog Lustre Changelog는 Lustre 파일시스템에서 발생하는 모든 메타데이터 변경 사항을 기록하는 기능입니다. Changelog 동작 흐름: Changelog 등록: Lustre 관리자가 changelog_register로 MDT(Metadata Target)에 Chan
2025년 11월 28일
Lustre의 파일 create & open 과정 분석 - 2
들어가기 앞서 이번 블로그에서 설명할 내용은 지난 Lustre의 파일 create & open 과정 분석 -1의 후속 내용으로, 클라이언트에서 보낸 요청이 MDS에서 처리되는 과정과 이후 클라이언트의 후속 처리 과정을 담고 있습니다. 아래 그림에서 1번은 지난 1편 블로그의 내용이고, 2번은 본 편에서 설명할 내용입니다. 1편의 내용을 숙지하고, 2편을 보시면 전체 흐름을 이해하는 데 도움이 될 수 있습니다. 러스터의 전체적인 software stack MDS에서의 처리 지난 1편에서 클라이언트의 MDC가 보낸 요청을 MDS가 받는 것을 확인하고 마무리 하였습니다. 이 요청을 받아 MDS가 처리하는 과정의 첫 시작은 mdt_reint_open 함수 입니다. 해당 함수는 MDC로 부터 요청을 받으면 다음과 같은 로그 메세지를 출력합니다. I am going to open [0x200000007:0x1:0x0]/(Melon->[0x20000a811:0x1:0x0]) cr_flag=0100000000102 mode=0100666 msg_flag=0x0 메시지를 살펴보면, 0x20000a811:0x1:0x0 FID를 가진 Melon이라는 파일에 대한 open 요청임을 알 수 있습니다. 그 다음 해당 파일에 대한 lu_object 가 존재하는지 찾는 과정이 mdt_object_find 함수에 의해 수행됩니다. 아래 코드는 mdt_object_find 함수 수행을 포함해 앞으로의 설명에 나오는 함수들 위주로 추린 mdt_reint_open 함수 코드입니다. // lustre/mdt/mdt_open.c int mdt_reint_open(struct mdt_thread_info *info, struct mdt_lock_handle *lhc) { ... parent = mdt_object_find(info->mti_env, mdt, rr->rr_fid1); ... fid_zero(child_fid); result = -ENOENT; ... if (result == -ENOENT) { ... child = mdt_object_new(info->mti_env, mdt, child_fid); } else { ... if (result == -ENOENT) { ... result = mdo_create(info->mti_env, mdt_object_child(parent), &rr->rr_name, mdt_object_child(child), &info->mti_spec, &info->mti_attr); if (result == -ERESTART) { ... out: if (result) lustre_msg_set_transno(req->rq_repmsg, 0); return result; } lu_object는 여러 계층으로 구성된 러스터의 디바이스들을 묶어 공통으로 관리하는 객체로 아래와 같은 구조체로 정의되어 있습니다. // lustre/include/lu_object.h /** * Layer in the layered object. */ struct lu_object { /** * Header for this object. */ struct lu_object_header *lo_header; /** * Device for this layer. */ struct lu_device *lo_dev; /** * Operations for this object. */ const struct lu_object_operations *lo_ops; /** * Linkage into list of all layers. */ struct list_head lo_linkage; /** * Link to the device, for debugging. */ struct lu_ref_link lo_dev_ref; }; 현재 Melon 이라는 파일은 신규로 생성하는 파일이어서 lu_object를 찾지 못하고, 결과값이 -ENOENT로 됩니다(위의 mdt_reint_open 함수 코드에서 result= -ENOENT 부분입니다.) mdt_reint_open
2025년 9월 1일
Lustre의 파일 create & open 과정 분석 - 1
들어가기 앞서 이번 블로그에서 설명할 내용은 러스터를 활용하고 있는 환경에서 사용자가 새로운 파일을 생성하면, 내부적으로 이루어지는 과정을 분석한 내용을 담고 있습니다. 본 내용은 러스터의 전반적인 구조에 대해 기본적으로 알고 있는 것으로 가정하고 기술하고 있어서, 러스터의 구조가 궁금하신 분들은 Lustre 파일시스템과 GPUDirect Storage 소개[1] 글을 참고하실 수 있습니다. 또한 러스터에서 새로운 파일의 생성은 다양한 컴포넌트들의 상호작용에 의해 이루어지기 때문에, 총 2편에 걸쳐 설명할 예정입니다. 1편에서는 전체적인 흐름을 간략하게 소개하기 위해 러스터의 software stack 및 분석에 활용될 러스터 디버그 시스템에 대한 소개 후, 러스터 클라이언트에서 러스터 서버로 요청을 보내는 부분까지 서술할 예정입니다. 그리고 이어서 2편에서는 러스터 서버에서의 처리 과정과, 서버에서 처리한 내용을 바탕으로 실제 데이터가 쓰일 OST(Object Storage Target)에 접근하는 과정을 서술할 예정입니다. Lustre software stack 러스터 소프트웨어 스택은 아래 그림과 같이 여러 계층으로 구성되어 있습니다. 해당 그림에서 화살표는 클라이언트에서 러스터 서버로의 요청 흐름을 나타냅니다. 최초 클라이언트에서 실행된 POSIX 시스템 콜은 VFS 계층을 거쳐 러스터의 LLITE(Lustre LITE) 계층으로 이동합니다. LLITE는 해당 요청이 메타데이터에 대한 접근이 필요한 경우 먼저 LMV(Logical Metadata Volume) 계층으로 요청을 보냅니다. LMV 계층은 단일 MDT(Meta Data Target) 구조일 경우에는 별다른 역할을 수행하지 않지만, 하나 이상의 MDT를 사용하는 구조에서는 사용자의 요청을 분산하는 역할을 수행합니다. 이후 직접적으로 러스터 서버의 MDS(Meta Data Server)와 통신하는 MDC(Metadata Client)로 요청이 보내지고, 최종적으로 MDC가 러스터 서버의 MDS에 요청하게 됩니다. LLITE에 들어온 요청이 메타데이터가 아니고 데이터에 대한 요청이라면, 해당 요청을 어느 OST로 보낼지 선택하는 LOV(Logical Object Volume) 계층으로 전달됩니다. LOV는 현재 러스터에 존재하는 클라이언트 수만큼 OSC(Object Storage Client)를 보유하고, 각 OSC를 통해 해당하는 OST에 접근이 가능합니다. < 러스터의 전체적인 software stack > 만약 클라이언트에서 파일을 열고 내용을 읽으려고 한다면, 먼저 POSIX를 통해 OPEN 시스템 콜이 호출됩니다. LLITE는 해당 파일을 열기 위해서는 MDS에 정보를 요청해야 하므로, LMV,MDC를 통해 MDS와 통신해 Layout EA 정보를 받아오게 됩니다. Layout EA는 Layout Extended Attributes의 약자로, 러스터에서 파일의 레이아웃을 관리하기 위한 자료구조입니다. 이 자료구조는 파일을 식별하기 위한 FID(File Identifiers) 정보와 어느 OST에 존재하는지에 대한 정보가 존재합니다. MDS로 받은 Layout EA를 통해 클라이언트는 LOV,OSC를 거쳐 최종적으로 해당 데이터 블럭이 존재하는 OST에 접근해 데이터를 읽거나 쓰게 됩니다. 다음 내용부터는 러스터에서 새로운 파일을 생성할 때, 위의 software stack에서 어떠한 작업이 이루어지는지를 추적할 방법을 소개한 다음, 클라이언트에서부터 과정을 서술할 예정입니다. How to lustre debugging 본격적인 내용에 앞서, 해당 내용은 러스터에서 제공하는 디버그 메세지를 바탕으로 작성되었습니다. 실제 러스터를 구축하고, 본문의 내용을 검증하고자 할 때 활용할 수 있도록, 간단히 소개하겠습니다. debug 설정 러스터에서는 다양한 디버그 메세지들을 분류해 두었기 때문에, 원하는 분류의 메세지만 출력하도록 설정하는 것이 가능합니다. 현재 설정 가능한 분류는 다음과 같습니다. trace inode super iotrace mall
2025년 3월 13일
디자인 도구 Penpot(펜팟) 설치부터 활용까지
디자인 도구 Penpot(펜팟) 설치부터 활용까지 안녕하세요? Penpot(펜팟)을 소개하고자 합니다. Docker로 셀프 호스팅 구성하기 Penpot은 Elestio와 Docker로 설치가 가능합니다. 항목 설명 Docker 컨테이너(소프트웨어를 실행하는 데 필요한 모든 파일, 라이브러리, 설정 파일 등을 포함하는 패키지) 기반의 오픈소스 가상화 시스템 Docker Desktop Windows 및 macOS 운영 체제에서 Docker를 쉽게 설치하고 사용할 수 있게 해주는 애플리케이션 WSL Windows 운영 체제에서 Linux 커널을 실행할 수 있게 해주는 기능 WSL2 활성화 후 Windows에 Linux를 설치 Windows - PowerShell - 관리자로 실행을 시켜줍니다. PowerShell WSL 기능 활성화 wsl --install 시스템 재부팅 Restart-Computer WSL 버전을 WSL2로 설정 wsl --set-default-version 2 사용 가능한 배포판 목록 확인 wsl --list --online 원하는 Linux 배포판 설치 wsl --install -d Ubuntu WSL에 설치된 배보판 목록 wsl --list --verbose 설치한 Ubuntu 배포판 실행 wsl Docker Desktop 설치 및 WSL2 설정 Docker Desktop 공식 홈페이지에서 [Download for Windows]를 클릭해 다운로드하고 설치합니다. Docker Desktop 실행 후 작업 표시줄의 숨겨진 아이콘 메뉴에서 Docker 아이콘을 선택합니다. 아이콘을 마우스 오른쪽 단추로 클릭해 Docker 명령 메뉴를 표시하고 “설정”을 선택합니다. 설정 > 일반에서 “WSL 2 기반 엔진 사용”이 선택되어 있는지 확인합니다. 설정 > 리소스 > WSL 통합으로 이동해 Docker 통합을 사용하도록 설정하려는 설치된 WSL 2 배포판에서 선택합니다. Docker가 설치되었는지 확인하려면 PowerShell에서 확인을 합니다. docker --version Docker가 이미지를 실행할 수 있도록 올바르게 설치되었는지 아래와 같은 명령을 PowerShell에서 실행합니다. docker run hello-world Penpot 설치/시작하기 Docker Compose는 Docker 컨테이너들을 정의하고 실행하는 도구인데, 이를 정의하는 docker-compose.yaml 파일을 Penpot GitHub 저장소에서 다운로드해 실행할 수 있습니다. docker-compose.yaml 파일만 다운로드하면 됩니다. 설치한 Ubuntu에서 docker-compose.yaml 파일을 다운로드 Windows의 WSL2에서 설치한 Ubuntu를 실행해 다운로드합니다. docker-compose.yaml를 설치할 파일 경로는 따로 신경 쓰실 필요 없어요~! wget: 인터넷에서 파일을 다운로드할 때 사용되는 명령 줄 도구 wget https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml - 실질적으로 Windows에서 Ubuntu 가상 시스템을 실행했기 때문에 파일 경로는 파일 탐색기를 열어보면 확인하실 수 있습니다. (Ubuntu 실행 명령어에서 `ll`을 쳤을 때 실제 경로예요~!)  Penpot 서비스 실행하기 항상 Docker Compose를 통해 명령줄 도구(CLI)를 실행할 땐 Docker Desktop이 실행되고 있어야 합니다. Ubuntu에서 Penpot 서비스를 실행하고, http://localhost:9001로 접속을 합니다. 포트 9001은 wget으로 설치한 docker-compose.yaml 파일에 기본으로 설정되어 있고, 변경해서 사용할 수 있어요~! docker compose -p penpot -f docker-compose.yaml up -d Penpot 서비스 실행 중지 docker compose -
2024년 7월 31일
병렬 네트워크 파일 시스템 pNFS의 재조명
AI 서비스의 정확도는 AI 모델의 정교함에 달려 있고, AI 모델의 정교함은 데이터셋을 얼마나 많이 학습했는지에 따라 고도화됩니다. 그리고 학습 시간과 비용을 줄이기 위해서는 데이터셋을 보다 빠르게 처리할 수 있는 컴퓨팅 자원과 데이터를 빠르게 주입해 주는 고성능 스토리지가 필요합니다. 하지만 스토리지는 고성능 컴퓨팅 환경에서 항상 병목의 근원으로 지목되어 왔습니다. 이러한 스토리지 병목 문제를 해결하기 위해 다양한 접근법이 연구되고 있는데, 그중 하나가 기존 NFS(Network File System)의 한계를 극복하고자 등장했던 병렬 NFS(parallel NFS, 이하 pNFS)입니다. 이번 포스트에서는 최근 새롭게 주목받고 있는 pNFS에 대해 소개하겠습니다. 기존 NFS와 병렬 파일 시스템 대부분의 AI 애플리케이션은 리눅스 기반 환경에서 호스팅되며, 보편적으로 리눅스의 기본 스토리지 프로토콜인 NFS를 사용합니다. 하지만 기존 NFS는 클라이언트와 스토리지 사이에 NFS 서버를 통해 데이터를 주고받는 단일 서버 구성입니다. 이 때문에 다수의 클라이언트가 여러 스토리지 노드에 접근하려면 NFS 서버를 통해 접근해야 하고, 1개의 클라이언트가 1개 스토리지 노드에만 접근할 수 있기 때문에 성능이 제한적입니다. 게다가 데이터셋이 커지면 그만큼 대역폭 부하도 높아진다는 문제가 있습니다. 이 때문에 입출력 집약적인 환경에서는 병렬 파일 시스템이 활용됩니다. 병렬 파일 시스템을 통해, 모든 클라이언트는 데이터를 여러 스토리지에 병렬로 직접 요청할 수 있고, 데이터가 여러 스토리지에 분산 저장되어 있기 때문에, 스토리지 노드를 확장할수록 입출력 성능도 높아집니다. 게다가 GPFS와 같은 병렬 파일 시스템의 경우, 메타데이터 서버와 데이터 서버가 분리되어 있어 작업 경로 구분에 따라 대역폭 부하를 크게 줄일 수 있습니다. 하지만 이러한 병렬 파일 시스템은 각 클라이언트에 전용 에이전트를 설치해야 하는 등 설치에 대한 공수와 병렬 인프라에 대한 전문성이 요구되어 유지보수 비용이 발생합니다. 무엇보다, 병렬 파일 시스템이 일반적인 파일 시스템에 비해 상대적으로 기능이 많아 무겁다 보니, 추가 소프트웨어 계층으로 인한 지연 시간과 파일 시스템에 대한 종속성 문제가 있습니다. pNFS 소개 pNFS는 기존의 단일 NFS 서버의 병목 문제를 해결하기 위해 NFSv4.1부터 등장한 병렬 파일 시스템 구조입니다. 병렬 파일 시스템처럼 데이터와 메타데이터 작업 경로를 구분해 클라이언트가 직접 스토리지에 병렬로 접근할 수 있게 합니다. 데이터와 메타데이터를 구분하여 처리함으로써 기존 NFS의 메타데이터 처리로 인한 네트워크 병목 및 확장성 문제를 해소합니다. 무엇보다, 이러한 병렬 파일 시스템 기능을 리눅스 배포판을 사용하는 모든 환경에서 네이티브로 지원하기 때문에, 추가 하드웨어 및 소프트웨어 계층이 없어도 되고, 별도의 라이선스 비용이 없다는 장점이 있습니다. pNFS의 구조 pNFS의 아키텍처는 크게 클라이언트와 메타데이터 서버(Metadata Server, 이하 MDS), 데이터 서버(Data Server, 이하 DS)로 구성됩니다. NFS 서버였던 MDS는 메타데이터 및 접근 관리 역할을 수행하며, 레이아웃(layout)이라는 메타데이터에 따라 각 파일의 데이터를 DS에 분산 저장합니다. 레이아웃은 메타데이터 서버에서 관리하며, 파일은 최소 데이터 단위인 유닛(unit)으로 나뉘고 저장 패턴에 따라 DS에 분산 저장됩니다. 파일 접근 시 클라이언트는 pNFS 프로토콜로 MDS에 쿼리를 보내 데이터 위치에 대한 맵과 접근 권한 정보를 얻습니다. 그리고 클라이언트에서 NFS 프로토콜을 통해 각 DS에 저장된 데이터에 직접 접근하고, 레이아웃에 의해 정의된 데이터 유형에 따라 파일, 블록, 오브젝트 프로토콜을 활용할 수 있습니다. < pNFS 구조 > 이렇게 NFS 트래픽을 분리하고 클라이언트의 직접 접근까지 가능하게 되었습니다만, 여기에 더해 NFSv4.1부터는 세션 트렁킹(session trunking)을 통해 대역폭을 확장
2024년 6월 10일
LVM 스냅샷 관리 기능 개발 후기 (1)
장애 복구나 유실된 데이터 복원을 위해서 AnyStor는 이전부터 스냅샷 기능을 지원하였습니다. 그러나 스냅샷 생성과 확인은 가능할 뿐, 실제로 복원 등에 사용하기 위해서는 관리자가 CLI 명령어를 사용하여 데이터를 복원할 수밖에 없었습니다. 실제 복원 시에는 고객 서비스에 영향이 없도록 진행되어야 했기에 도입하기 까다로웠던 점도 있었습니다. 이번에 스냅샷 기능을 재편하게 되었는데, 스냅샷을 안정적으로 도입하기 위해서 거쳤던 테스트와 이슈 대응 방안에 대해서 살펴보겠습니다. 배경 설명 본 제품에서는 LVM 볼륨을 주로 다룹니다. LVM 볼륨의 프로비저닝 정책에 따라서 스냅샷의 종류도 나뉘게 되는데, 우선 볼륨의 프로비저닝 종류에 대해서 간략히 설명해 보겠습니다. 씬 프로비저닝(Thin Provisioning)된 볼륨 : 사용자에게 필요한 만큼의 스토리지 공간을 할당하되, 실제로는 사용한 만큼의 공간만 실제 스토리지에서 차지하는 방식입니다. 씩 프로비저닝(Thick Provisioning)된 볼륨 : 미리 정해진 크기의 스토리지 공간을 사용자에게 할당하는 방식입니다. 이 공간은 사용자가 실제로 사용하지 않더라도 할당된 크기만큼 스토리지에서 차지합니다. 스토리지 공간을 확보하는 데 있어서 보다 안정적이지만, 사용하지 않는 공간이 발생할 수 있어 스토리지 공간의 사용이 비효율적일 수 있습니다. 프로비저닝 된 볼륨에 따라서 스냅샷의 저장 방식도 바뀝니다. 씬 프로비저닝된 볼륨과 씩 프로비저닝된 볼륨에 대해서 약칭으로 각각 Thin 볼륨, Thick 볼륨으로 변경하여 설명하겠습니다. Thin 볼륨의 스냅샷: 원본 볼륨의 데이터를 공유하며, 자체 메타데이터 영역을 가지고 있습니다. 메타데이터 영역에는 스냅샷의 논리적 익스텐트와 물리적 익스텐트 간의 매핑이 저장되어 있습니다. 이를 통해 스냅샷은 원본 볼륨의 상태를 보존하고, 변경 사항을 추적하게 됩니다. Thick 볼륨의 스냅샷 : 원본 볼륨의 데이터를 복사하여 새로운 공간에 저장합니다. 원본 데이터의 완전한 복사본을 제공하지만, 스토리지 공간을 더 많이 차지하게 됩니다. 여기까지만 살펴봤을 때 차이점이 여럿 보입니다. Thin 볼륨은 저장 방식이 다르기 때문에, 스냅샷 또한 저장하는 방법이 다르게 보입니다. 실제로 Thin 볼륨은 스냅샷을 다음과 같은 구조로 저장합니다. :bulb: NOTE < Thin 볼륨 구조 > 위 구조를 보게 되면 Thin 볼륨과 스냅샷 볼륨은 공통의 데이터 볼륨을 공유합니다. 그렇기 때문에 많은 스냅샷을 생성할 수 있지만, 스냅샷을 삭제하더라도 여유 공간이 추가로 확보되지 않을 수도 있습니다. 데이터 볼륨 말고도 메타데이터 볼륨도 보이는데, 메타데이터 볼륨에서는 각 Thin 볼륨에 속한 데이터 볼륨을 추적합니다. 볼륨 그룹 별로 여러 씬 풀(Thin Pool)이 생성 될 수 있는데, 예비 메타데이터로 불리는pmspare 는 볼륨 그룹에 대해서 하나만 생성됩니다. 이 pmspare 는 볼륨 그룹 내의 모든 씬 풀(Thin Pool)의 메타데이터를 복구하기 위해서 사용됩니다. 씬 풀(Thin Pool) 은 약칭으로 Thin 풀로 변경하여 설명하겠습니다. 스냅샷 도입 전 테스트 스냅샷을 도입하기 전에 몇 가지 스냅샷 기능에 관한 테스트를 진행하였습니다. 진행하면서 있었던 고려 사항들을 공유드리고자 합니다. 1. Thin 풀 데이터 볼륨 저장 공간 확보 Thin 볼륨의 파일 시스템에서 파일을 삭제하면 일반적으로 씬 풀에 Free 블록을 다시 추가하지 않습니다. 파일 시스템에서는 삭제되었지만, 씬 풀에 적용되지 않은 경우, fstrim 을 통해서 물리적 공간을 Thin 풀에 복원할 수 있습니다. 비슷한 효과를 마운트 시에 옵션으로 처리할 수도 있는데, 마운트시에 discard 옵션으로 Thin 풀의 데이터 공간에 대한 자동 해제 옵션을 추가하여 데이터 볼륨의 저장 공간을 확보할 수 있었습니다. # Thin 볼륨 데이터 쌓기 dd if=/dev/zero of=/{mount_path}/fileN bs=4096 count=1000000; # 용량 확인 lvs L
2024년 6월 3일
CXL 3.1 소개와 스토리지
지난 번 <PCIe를 대체할 인터커넥트 표준 기술, CXL> 포스트에서 CXL(Compute Express Link)에 대해 2.0 버전을 기준으로 내용을 다룬 바 있었습니다. 그로부터 약 2년이 지난 현재(2024년 1월), CXL은 기술적인 성숙도 뿐만 아니라, 인터커넥트 표준으로써의 위상도 많이 높아졌습니다. 현재 CPU, GPU, DPU 등 프로세서는 각각 구분된 메모리 공간을 가지고 있고, 프로세서 간 데이터 공유를 위해 메모리 간 데이터 이동 및 복사를 PCIe를 통해 수행합니다. PCIe는 고속 인터페이스이기는 하나, 대규모 데이터 전송 시 프로토콜 처리 과정에서 오버헤드가 발생하며, 메모리 채널에 비해 매우 느립니다. 게다가 프로세서 간에 메모리를 공유하는 경우, 한쪽 메모리에서 데이터가 변경되면 다른 쪽 메모리의 데이터와 일관성 문제가 발생하게 됩니다. CXL은 이러한 프로세서 메모리 간 장벽을 허물고, 공유 메모리 풀을 형성해 프로세서가 거대한 메모리 자원을 저지연으로 접근하게 하는 것이 목적입니다. 또한, 프로세서에서 메모리 풀 접근 시 이동 및 복사 과정이 필요 없기 때문에 메모리 간 일관성을 유지할 수 있습니다. CXL은 데이터센터와 같은 대규모 데이터를 운용하고 처리하는 환경에서 메모리 자원의 낭비 없이 활용할 수 있고, 기존과는 달리 애플리케이션에 매우 큰 메모리를 할당할 수 있습니다. 자세한 내용은 이전 포스트 <PCIe를 대체할 인터커넥트 표준 기술, CXL> 를 참고 부탁드립니다. 지난 CXL 2.0 버전에서는 CXL 스위치의 등장과 퍼시스턴트 메모리 지원, CXL IDE를 통한 보안 강화가 주 내용이였습니다. 그리고 2022년 8월에 3.0 버전, 작년 11월에 3.1 버전을 공개하면서 새로운 아키텍처와 기능들을 발표했습니다. 많이 늦기는 했지만 이번 포스트에서는 지난 CXL 3.0 버전부터 어떤 내용들이 업데이트되었는지 알아보고자 합니다. CXL 3.1까지의 주요 변경사항 이번 CXL 3.0과 3.1 버전은 전세계 주요 산업 관계자들과 커뮤니티의 기여에 힘입어 매우 많은 구조와 기능들이 업데이트되었습니다. CXL 3.0부터는 PCIe 6.0을 지원하게 되면서 패브릭 기능, 구조 및 보안 등을 개선했고, 클러스터 환경에서의 캐시 일관성과 메모리 풀 공유 기능을 강화하는 등 CXL의 성능과 활용 범위가 다시 한번 확장되었습니다. 주요 업데이트 내용은 다음과 같습니다. 대역폭 2배 향상: CXL 2.0까지는 PCIe 5.0을 지원했는데, CXL 3.0부터는 PCIe 6.0을 지원하게 되었습니다. 이로 인해 기존 CXL 2.0 대역폭의 2배인 최대 64GT/s까지 제공해 PCIe x16 링크 기준 이론상 최대 256GB/s까지 성능을 제공할 수 있게 되었습니다. PCIe 6.0 지원에 따른 기능 개선: CXL 3.0부터는 PCIe 6.0에서 제공하는 4레벨 펄스 진폭 변조(PAM-4) 시그널링과 경량 FEC(Forward Error Correction), CRC(Cyclic Redundancy Check)를 그대로 활용해 낮은 지연시간과 복잡도, 전송 에러 방지를 구현하고자 했고, 256 바이트 FLIT(Flow Control Unit) 단위로 데이터를 전송합니다. < HDM에 직접 peer-to-peer 접근 > 캐시 일관성 강화: CXL 3.0 부터는 CXL 메모리 장치에서 BISnp(Back-Invalidate Snoop)로 호스트의 캐시 상태를 변경시켜 다중 호스트 접근 환경에서 HDM1의 캐시 일관성이 강화(enhanced coherency)되었다고 볼 수 있습니다. 게다가 CXL 3.0 부터 호스트의 간섭 없이 Type 2 또는 Type 3 장치가 HDM에 직접 peer-to-peer 접근이 가능해져, 대규모 시스템 환경에서 안정적인 성능을 제공할 수 있게 되었습니다. 메모리 풀 및 메모리 공유: 이런 하드웨어 기반의 일관화를 활용해 소프트웨어의 개입 없이 CXL 메모리 풀에 연결된 메모리 장치의 한 주소 영역을 복수의 호스트가 동시(simultaneously)에 접근하고,
2024년 1월 22일
ZFS 파일 시스템
이번 기술 블로그 포스트에서는 ZFS에 대해 소개하고자 합니다. ZFS 소개 ZFS는 잘 알려진 파일 시스템 중에서도 비교적 최근에 등장한 파일 시스템입니다. 2001년 썬 마이크로시스템즈에서 자사 운영체제인 솔라리스의 일부로 개발을 시작했습니다. ZFS는 안정성과 성능, 그리고 데이터 보호 기능들이 강화된 파일 스토리지를 목적으로 개발되었습니다. 이후 오픈소스로 공개했었으나, 2010년에 오라클이 썬 마이크로시스템즈를 인수한 이후로는 오라클 ZFS라는 명칭으로 사유화되었습니다. 이와는 독립적으로 2013년부터 오픈소스 프로젝트인 OpenZFS가 오픈소스의 명맥을 이어 나가고 있습니다. 일반적으로 64비트나 그 이하를 지원하는 파일 시스템들과는 달리, ZFS는 128비트 파일 시스템입니다. 이론상 단일 스토리지 풀의 최대 지원 용량이 256,000조 제타바이트로, 사실상 실제 환경에서 보기 어려운 용량 제한을 가지고 있습니다. 현재(2023년 12월) 기준으로 존재하는 파일 시스템 중 가장 큰 비트 사이즈를 지원한다고 볼 수 있습니다. 또한, ZFS의 기본 블록 크기는 128KB이지만 가변 블록 크기를 지원해 필요에 따라 블록 크기를 변경할 수 있습니다. 워크로드마다 최적의 블록 크기가 다르기도 하고, 압축 기능 사용 시 작은 블록 크기를 활용해 압축 효율을 높일 수도 있습니다. ZFS의 핵심 컨셉은 바로 파일 시스템과 볼륨 매니저를 같이 제공한다는 것입니다. 기존의 파일 시스템은 스토리지 장치별로 볼륨을 생성하고, 이 볼륨들을 관리하기 위해 볼륨 매니저가 필요했습니다. 대표적으로, ext4나 XFS와 같은 파일 시스템은 LVM(Logical Volume Manager) 과 같은 볼륨 매니저를 통해 다수의 물리적인 스토리지를 관리하고 별도의 하드웨어 및 소프트웨어 RAID 와 연계해 데이터 보호 기능을 제공해 왔습니다. 하지만 ZFS는 이러한 기능들을 한 번에 제공하는 것이 특징입니다. 물리적인 스토리지와 이로 구성된 볼륨 구조뿐만 아니라, 여기에 저장된 파일에 대한 정보도 가지고 있습니다. 이 때문에 물리적인 디스크를 추가할 때 중단 없이 스토리지 풀에 추가할 수 있고, 해당 풀을 사용하는 파일 시스템들이 즉시 신규 스토리지 공간을 활용할 수 있게 합니다. ZFS의 주요 기능 ARC 및 L2ARC 캐싱 ZFS는 빠른 입출력을 지원하기 위해 독자적인 캐싱 메커니즘을 제공합니다. 다만, 모든 캐시를 RAM에 저장하기에는 공간과 비용 문제가 있어 SSD와 같은 고속 스토리지 미디어에 분산시키는 계층형 캐시 구조를 채용했습니다. ARC(Adaptive Replacement Cache)는 메모리에 저장되는 캐시입니다. ARC에 저장되는 데이터는 접근 시간이나 빈도뿐만 아니라, 캐시와 디스크 중 읽은 데이터의 출처가 어디가 더 많은지(hit ratio, 캐시 적중률)도 확인해서 캐시 적중률이 높은 데이터를 중심으로 저장합니다. 이는 결국 캐시 적중률이 낮은 데이터에 대한 접근도 빨라질 수 있습니다. 캐시 적중률이 높은 데이터를 저장할수록 디스크 접근에 대한 부하도 낮아지기 때문에, 결국 디스크에 저장된 캐시 적중률이 낮은 데이터에도 빠르게 접근할 수 있기 때문입니다. L2ARC(Level 2 ARC)는 ARC의 보조 저장 공간을 위해 SSD를 활용한 캐시입니다. ARC에서 데이터를 찾지 못하였다면 디스크에 접근하기 전 L2ARC에서 빠르게 찾아볼 수 있어 데이터에 대한 높은 접근성을 제공합니다. 일반적으로 RAM보다는 느리지만, vdev를 구성하는 스토리지 미디어보다는 빠른 SSD를 사용합니다. 체크섬 ZFS가 가진 또 하나의 특징은 바로 데이터 무결성을 최대한 보장하도록 디자인되었다는 것입니다. ZFS는 파일 시스템의 모든 계층의 모든 데이터와 메타데이터에 대한 체크섬을 제공합니다. ZFS의 스크럽(Scrub) 기능을 통해 체크섬 계산으로 데이터의 손상 여부를 확인하는 유효성 검사를 수행하고, 필요시 복구하는 자가 복구(Self-healing) 과정을 통해 bit rot와 같은 사일런트 커럽션까지 대처할 수 있습니다. 그
2023년 12월 20일
올플래시 스토리지 선정 시 체크포인트
기업들이 올플래시 스토리지를 도입하고자 하는 주된 목적은 기본적으로 하드디스크 기반의 스토리지보다 높은 성능을 제공할 수 있다는 것에 있습니다. 플래시 기반의 스토리지는 전통적으로 미션 크리티컬하거나 지연시간에 민감한 핵심 워크로드를 운용할 목적으로 사용됐으나, 높은 가격과 부족한 유스케이스로 인해 많은 기업들이 도입을 주저해 왔습니다. 하지만, 플래시 메모리의 가격이 점점 낮아지면서 상대적으로 비용이 저렴한 올플래시 스토리지들이 시장에 등장하게 되었고, 이는 전반적인 스토리지 성능 기준과 요구사항을 향상시키는 데 일조했습니다. 이렇게 올플래시 스토리지에 대한 접근성이 높아지고 올플래시 스토리지에 대한 시장 검증도 해소되면서 많은 기업들이 비즈니스 가속화를 위해 올플래시 스토리지의 도입을 추진하고 있습니다. 그러나 전통적인 스토리지 시스템에서 올플래시 스토리지로 전환하는 것은 계획적이고 신중한 접근이 필요한 결정입니다. 올플래시 스토리지 선택 시 체크 포인트를 간단히 정리해 보았습니다. 하드디스크 vs SSD 하드디스크와 솔리드 스테이트 드라이브(이하 SSD)의 관계를 단순히 성능과 가격 문제로 치부하기에는 복잡한 부분이 있습니다. 하드디스크는 기본적으로 기계적인 동작을 통해 데이터를 읽고 쓰는 스토리지 미디어입니다. 이 때문에 플래터의 바깥쪽 트랙이냐 안쪽 트랙이냐에 따라 데이터를 읽어오는 속도가 다르고, 읽기/쓰기 헤드가 플래터 위를 이동함에 따라 기계적 지연이 발생해 읽기 속도가 상대적으로 느릴 뿐만 아니라, 일정하지 못하다는 문제가 있습니다. 반면에 SSD는 데이터를 읽고 쓰는데 전기 회로를 활용하기 때문에 일정한 읽기 성능을 제공할 수 있어, 많은 데이터를 계속해서 요구하는 집약적인 워크로드에 적합합니다. 게다가 하드디스크에 비해 발열과 충격에 강하고, 보다 작은 용적량을 제공해 데이터센터 내 공간 비용을 최소화할 수 있습니다. 하지만 SSD는 고질적으로 수명과 쓰기 성능에 대한 이슈가 있다는 점도 알고 있어야 합니다. 우선, 플래시 메모리는 하드디스크와는 달리 쓰기 수명이 있어 일정 쓰기 횟수를 넘어가면 더 이상 쓰기 작업을 못 하게 됩니다. 게다가 SSD의 가비지 컬렉션1 작업은 1bit의 데이터만 수정되어도 블록 전체를 다시 써야 하고, 이것이 많아지면 쓰기 성능에 영향을 주게 됩니다. 이를 방지하기 위해 대부분의 SSD 제품은 오버 프로비저닝2 기능을 통해 쓰기 버퍼를 위한 공간을 확보하고, 특정 블록만 마모되지 않도록 쓰기 빈도를 관리하는 웨어 레벨링3 기술 등을 적용해 SSD의 쓰기 성능과 수명을 증가시키고 있습니다. 하지만 SSD의 이러한 단점들을 고려해도, 하드디스크에 비해 압도적인 성능을 제공한다는 것은 사실입니다. 그리고 이러한 성능을 완전하게 활용하기 위해서는 무엇보다 애플리케이션에서 요구하는 워크로드 유형에 대한 고민이 필요합니다. 워크로드 유형 올플래시 스토리지가 제공할 수 있는 성능은 운용 중인 애플리케이션이나 워크로드 유형에 따라 다를 수 있습니다. 이 때문에 단순히 성능 수치를 보기보다는 다음과 같이 자사의 애플리케이션 운용 방식에 따른 시스템 구성과 이에 따른 워크로드 유형을 알아볼 수 있어야 합니다: 스루풋: 자사 인프라 내에서 스루풋 중심의 애플리케이션을 활용하고 있는 경우, 스루풋 요구사항을 면밀히 검토해 볼 필요가 있습니다. 스루풋은 스토리지 시스템에서 오고 나가는 데이터의 양을 측정하는 지표입니다. 빅데이터 분석이나 영상 후반작업처럼 오고 나가는 데이터의 규모가 클수록 높은 스루풋을 요구하는 환경이라고 볼 수 있습니다. 그리고 운용하는 데이터의 규모가 큰 만큼 네트워크 대역폭에 대한 요구사항도 높아질 수 있습니다. 다만, 네트워크 대역폭을 확장하는 만큼, 서버 점유율 또한 높아진다는 점도 고려해야 합니다. IOPS: 주로 동시에 여러 사용자가 접속하는 온라인 트랜잭션이나 가상 데스크톱과 같이 병렬 작업이 많이 발생하는 환경에서 높은 IOPS가 요구됩니다. 하지만 IOPS는 많은 벤더들이 과장해서 강조하는 만큼 가장 주의 깊게 따져봐야 하는 지표입니다. IOPS는 블록
2023년 7월 19일