13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[작성자:] admin

  • [Proxmox] Proxmox TrueNAS SCALE ZFS 비교: 홈랩 선택 가이드

    [Proxmox] Proxmox TrueNAS SCALE ZFS 비교: 홈랩 선택 가이드

    Proxmox TrueNAS SCALE ZFS 비교: 홈랩 스토리지 선택 가이드

    Proxmox TrueNAS SCALE ZFS 비교를 찾다 보면 결국 부딪히는 건 기능표보다 장애를 어떻게 감당할지입니다. 저도 홈랩 NAS를 몇 번 갈아엎고 나서야 이걸 제대로 봤습니다. 둘 다 ZFS를 쓴다는 사실보다, 문제가 났을 때 어느 계층을 먼저 의심해야 하는 구조인가가 훨씬 중요하더라고요.

    같은 디스크, 같은 HBA, 같은 ZFS라도 Proxmox VE는 VM과 LXC가 주인공인 설계에 가깝고, TrueNAS SCALE은 데이터셋과 공유 정책이 중심입니다. 참고로 TrueNAS SCALE은 2025년 25.04 계열부터 커뮤니티 에디션 명칭이 함께 쓰이지만, 검색과 실사용 맥락에선 여전히 TrueNAS SCALE이라는 표현이 널리 남아 있습니다. 그래서 둘 중 무엇이 더 낫냐고 묻기보다, 내 홈랩에서 가장 먼저 살아 있어야 하는 것이 VM인지 데이터인지부터 정하는 쪽이 훨씬 정확합니다.

    운영 피로도도 빼놓기 어렵습니다. 한 대 서버에 VM, LXC, SMB, NFS, iSCSI, 백업 저장소까지 전부 몰아넣으면 평소엔 편해 보여도 디스크 이슈가 한 번 터졌을 때 복구 동선이 길어집니다. 저도 초기에 부팅 풀과 VM 스토리지, 백업 파일을 너무 가깝게 붙여놨다가 유지보수 창에서 손이 괜히 빨라지는 경험을 했거든요. 홈랩은 성능 숫자보다도 구조를 단순하게 유지하는 능력이 오래 갑니다.

    Proxmox TrueNAS SCALE ZFS 비교를 보여주는 홈랩 아키텍처 다이어그램

    Proxmox VE와 TrueNAS SCALE이 각각 어떤 위치에서 가장 빛나는지 한눈에 보여주는 홈랩 구성 예시입니다.

    왜 Proxmox TrueNAS SCALE ZFS 비교가 자주 나올까요?

    겉으로 보면 둘 다 웹 UI가 있고, 둘 다 ZFS를 쓰고, 둘 다 스냅샷을 말합니다. 그런데 실제 운영 동선은 꽤 다릅니다. Proxmox VE는 저장소를 게스트 워크로드를 올려두는 기반으로 보고, TrueNAS SCALE은 저장소를 권한, 공유, 스냅샷, 복제 정책을 설계하는 대상으로 다룹니다.

    이 차이는 같은 작업을 할 때도 바로 드러납니다. Proxmox VE에선 "이 VM 디스크를 어디 둘까"가 먼저고, TrueNAS SCALE에선 "이 데이터셋을 누구에게 어떤 프로토콜로 공개할까"가 먼저입니다. 둘 다 ZFS 위에서 돌아가지만, 관리 UI가 사용자를 유도하는 방향이 달라서 실제 사용감도 꽤 다릅니다.

    제가 보기엔 이 비교에서 가장 많이 놓치는 포인트가 하나 있습니다. ZFS를 잘 쓰는 것과 ZFS를 얹은 제품을 잘 운영하는 것은 다른 문제라는 점입니다. 전자는 풀 상태와 데이터셋 구조, 스냅샷 정책의 문제이고, 후자는 장애 범위와 복구 순서, 권한 모델, 서비스 의존성의 문제입니다.

    핵심 개념: ZFS 기반 홈랩 스토리지를 어떻게 봐야 하나

    1. Proxmox VE는 "VM이 주인공"인 구조

    Proxmox VE에서 ZFS는 보통 VM 디스크, 컨테이너 루트 파일시스템, 스냅샷, 복제, 백업 작업의 기반으로 쓰입니다. 즉 스토리지 관리의 중심이 파일 공유가 아니라 게스트 워크로드의 수명주기에 붙어 있습니다. Kubernetes 테스트 노드, 방화벽 VM, DB 실험 환경, Git 러너, 내부 서비스처럼 VM과 LXC가 자주 생겼다 지워지는 환경이라면 이 흐름이 정말 자연스럽습니다.

    • 장점: VM/LXC 생성과 삭제, 스냅샷, 스토리지 매핑 동선이 짧습니다.
    • 장점: 하이퍼바이저와 스토리지가 같은 컨텍스트에 있어 초기 구축 속도가 빠릅니다.
    • 주의: 파일 서버 역할까지 한 박스에 합치면 장애 분석에서 컴퓨트와 스토리지가 한 번에 얽힙니다.
    • 주의: 백업 파일까지 같은 풀에 두면 "백업은 있는데 공간이 부족해 복구가 불편한" 상황이 생기기 쉽습니다.

    2. TrueNAS SCALE은 "데이터가 주인공"인 구조

    TrueNAS SCALE은 데이터셋 경계, 공유 정책, 스냅샷, 복제, SMB/NFS/iSCSI 제공이 우선입니다. VM 기능도 있지만 운영 감각으로 보면 "가상화가 가능한 NAS"에 더 가깝습니다. 가족 사진, 미디어 라이브러리, 백업 저장소, 다른 하이퍼바이저에 제공할 공유 스토리지, 장기 보관 데이터처럼 데이터 수명주기가 먼저인 환경에서 확실히 손에 맞습니다.

    • 장점: 데이터셋 기준으로 권한, 스냅샷, 공유 대상을 나누기 쉽습니다.
    • 장점: SMB, NFS, iSCSI처럼 외부 소비자가 많은 환경에서 관리 포인트가 명확합니다.
    • 주의: VM 운영을 주력으로 삼으면 Proxmox보다 동선이 길고, 하이퍼바이저 관점의 자동화도 덜 자연스럽습니다.
    • 주의: NAS 안에서 애플리케이션과 스토리지를 함께 굴릴 때도 결국 I/O 혼합 문제는 피하기 어렵습니다.

    Proxmox VE vs TrueNAS SCALE 비교 표

    비교 항목 Proxmox VE TrueNAS SCALE 실무 판단 포인트
    핵심 역할 가상화 호스트 NAS 및 공유 스토리지 게스트가 우선이면 Proxmox, 데이터 보존이 우선이면 TrueNAS
    ZFS를 바라보는 관점 VM 디스크와 컨테이너 저장소 데이터셋, 공유, 스냅샷, 복제 같은 ZFS라도 운영 질문이 다릅니다
    장애 시 영향 범위 올인원일수록 VM과 파일 서비스가 함께 영향 분리형이면 NAS 장애를 별도 격리 가능 장애 절연이 중요하면 분리형이 유리합니다
    가상화 경험 KVM, LXC 중심으로 매우 자연스러움 가능하지만 주력 워크플로는 아님 실험용 서비스가 많을수록 Proxmox 쪽 손이 덜 갑니다
    공유 프로토콜 운영 외부 스토리지 소비자 역할에 적합 SMB, NFS, iSCSI 제공자 역할에 적합 다른 장비에 스토리지를 배포할수록 TrueNAS가 편합니다
    설정 실수의 흔한 형태 부팅 풀, VM 풀, 백업 위치를 과하게 합침 데이터셋 경계 없이 공유부터 만듦 문제는 기능 부족보다 초기 구조 설계에서 시작됩니다
    확장 방향 클러스터, 노드 증설, 게스트 확장 스토리지 증설, 복제, 보관 체계 확장 확장 축이 컴퓨트인지 스토리지인지 먼저 정해야 합니다
    추천하지 않는 경우 메인 가족 데이터와 백업을 한 호스트에 몰아둘 때 VM 자동화와 잦은 게스트 실험이 주업무일 때 주요 목표와 제품 성격이 어긋나면 운영 피로도가 커집니다

    제가 직접 굴려보며 정리한 현실적인 배치 패턴

    홈랩에선 대체로 세 가지 구조로 정리됩니다.

    1. 올인원 Proxmox VE: 한 대 서버에 Proxmox VE와 ZFS를 올리고, VM과 LXC를 로컬 스토리지에서 직접 운영
    2. 분리형: Proxmox VE는 컴퓨트, TrueNAS SCALE은 스토리지 전용으로 역할 분리
    3. TrueNAS 중심: 파일 서버와 백업이 주력이고, VM은 소수만 운영

    초기 구축 난이도만 보면 1번이 가장 쉽습니다. 장비도 덜 들고, 네트워크 설계도 단순합니다. 다만 올인원은 문제가 나기 전까지는 단순하고, 문제가 난 뒤에는 복잡해지는 구조입니다. 반대로 2번은 처음부터 케이블, 스위치, 전력, 장비 공간을 더 생각해야 하지만 장애가 났을 때 원인 분리가 빠릅니다. 3번은 홈 미디어, 문서 보관, 백업 허브처럼 데이터 중심 가정 환경에 잘 맞습니다.

    제 기준으로는 이렇게 나뉩니다. 하루에 VM을 여러 번 만들고 지우거나, LXC 템플릿과 테스트 클러스터를 자주 만지면 Proxmox VE 쪽이 맞습니다. 반면 파일 공유, 스냅샷, 복제, ACL, 사용자 권한, 백업 보존 기간이 더 중요하면 TrueNAS SCALE이 더 손에 맞습니다. 양쪽이 모두 중요하다고 느껴지는 시점부터는 사실상 분리형으로 가는 편이 덜 후회하더라고요.

    가상화 노드와 NAS를 역할별로 분리했을 때 관리 포인트가 어떻게 달라지는지 보여주는 예시입니다.

    실전 구현 1: Proxmox VE에서 ZFS 상태 확인과 스토리지 구조 점검

    운영 전에 가장 먼저 볼 건 성능 수치가 아니라 풀의 건강 상태와 저장소 역할 분리입니다. ZFS가 불안정한데 그 위에 VM을 계속 얹는 건, 바닥이 젖은 창고에 선반부터 세우는 느낌과 비슷합니다.

    zpool status -v
    zpool list -o name,size,alloc,free,frag,health
    zfs list -o name,used,avail,refer,mountpoint
    pvesm status
    journalctl -u pvestatd --since -1h

    위 명령은 Proxmox VE에서 ZFS 상태와 스토리지 인식 상태를 빠르게 같이 볼 때 유용합니다. 특히 pvesm status와 journalctl -u pvestatd는 ZFS 자체 문제인지, 아니면 Proxmox가 저장소를 인식하는 상위 계층 문제인지 가르는 데 도움이 됩니다.

    • zpool status -v: ONLINE이 아니면 성능 튜닝보다 원인 확인이 먼저입니다.
    • zpool list: 공간뿐 아니라 frag와 health를 같이 봅니다. 용량 여유가 부족한 풀은 성능보다 운영 여유가 먼저 줄어듭니다.
    • pvesm status: Proxmox가 인식하는 저장소가 비정상인지, 단지 ZFS CLI 상의 문제인지 구분합니다.
    • journalctl -u pvestatd: 스토리지 인식 지연, 타임아웃, 마운트 문제 같은 상위 계층 신호를 볼 때 유용합니다.

    Proxmox VE 쪽에선 저장소를 기능별로 나누는 게 중요합니다. 특히 VM 디스크와 ISO, 템플릿, 백업 파일을 같은 경로에 몰아두면 관리가 금방 흐려집니다.

    zfspool: local-zfs
        pool rpool/data
        content images,rootdir
        sparse 0
    
    dir: local
        path /var/lib/vz
        content iso,vztmpl
    
    dir: backup-store
        path /mnt/backup
        content backup

    이 예시는 Proxmox VE의 /etc/pve/storage.cfg에 들어가는 형태를 기준으로 한 겁니다. 포인트는 단순합니다. local-zfs는 게스트용, local은 설치 자산용, backup-store는 복구 자산용입니다. 이 셋이 섞이기 시작하면 장애가 났을 때 판단이 흐려집니다.

    또 하나, Proxmox에서 ZFS를 쓸 때는 데이터셋 속성만 보지 말고 게스트 유형별 I/O 패턴을 먼저 떠올리는 게 좋습니다. 예를 들어 로그가 많은 리눅스 VM과 대용량 미디어 파일은 같은 디스크라도 이상적인 속성이 다릅니다. VM 디스크는 무작정 큰 블록이 유리하다고 보기 어렵고, 미디어 저장소는 너무 작은 블록이 오히려 메타데이터 부담만 늘릴 수 있습니다.

    실전 구현 2: TrueNAS SCALE 쪽에서 ZFS 데이터셋과 공유 전에 꼭 보는 것

    TrueNAS SCALE에서는 공유를 열기 전에 데이터셋 경계를 먼저 설계하는 게 핵심입니다. 제 경험상 여기서 서두르면 나중에 ACL, 스냅샷 정책, 복제 범위를 다시 뜯게 됩니다. 홈랩에서 자주 쓰는 패턴은 vmstore, backup, media, homes처럼 용도별로 쪼개는 방식입니다.

    zpool status -v
    zfs list -r tank
    zfs create -o compression=lz4 -o atime=off tank/backup
    zfs create -o compression=lz4 -o atime=off -o recordsize=1M tank/media
    zfs create -o compression=lz4 -o atime=off tank/homes
    zfs snapshot tank/backup@pre-share-change
    zfs get compression,atime,recordsize,mountpoint tank/media

    이 명령 자체보다 더 중요한 건 왜 데이터셋을 나눴는가입니다. TrueNAS 공식 문서도 공유를 만들기 전에 데이터셋과 zvol을 먼저 구조화하는 흐름을 권장합니다. 이 순서를 지키면 나중에 권한과 스냅샷 정책이 덜 꼬입니다.

    • backup: 변경 빈도와 보존 정책이 중요합니다.
    • media: 큰 파일 위주면 작은 블록 이득이 거의 없고, 오히려 메타데이터 부담이 커질 수 있습니다.
    • homes: 사용자 권한과 스냅샷 복구 포인트가 더 중요합니다.

    여기서 자주 생기는 실수가 있습니다. SMB 공유부터 먼저 만들고 나중에 데이터셋을 분리하는 방식입니다. 가능은 하지만 운영이 길어질수록 권한 모델과 스냅샷 범위가 꼬이기 쉽습니다. TrueNAS SCALE은 UI가 잘 되어 있어 보여도, 기반은 결국 ZFS입니다. 경계는 UI가 아니라 데이터셋에서 잡아야 나중이 편합니다.

    VM용 블록 스토리지를 제공할 계획이라면 zvol 설계도 초기에 생각해두는 편이 좋습니다. 특히 volblocksize는 만든 뒤 변경이 까다롭기 때문에, 나중에 성능이 마음에 안 들어 구조를 다시 만지는 경우가 적지 않습니다.

    zfs create -V 500G -o compression=lz4 -o volblocksize=16K tank/vmstore/proxmox-iscsi-01
    zfs get volblocksize,compression,used,referenced tank/vmstore/proxmox-iscsi-01

    여기서 16K는 예시일 뿐 정답은 아닙니다. 워크로드에 따라 더 작거나 큰 값이 맞을 수 있습니다. 핵심은 블록 스토리지는 파일 공유 데이터셋과 다르게 생각해야 한다는 점입니다. 미디어 보관용 데이터셋과 VM용 zvol을 같은 감각으로 만들면 나중에 체감이 어색해집니다.

    TrueNAS SCALE ZFS 데이터셋과 홈랩 NAS 설정을 표현한 이미지

    데이터셋을 먼저 나누고 공유를 나중에 여는 흐름이 왜 중요한지 보여주는 구성 예시입니다.

    재현 가능한 시나리오: VM 저장소와 NAS 저장소를 한 풀에 같이 둔 경우

    이건 홈랩에서 정말 흔합니다. 한 대 서버에 Proxmox VE를 올리고, 같은 ZFS 풀에 VM 디스크, 미디어 파일, 백업 아카이브, 다운로드 작업 디렉터리까지 다 넣는 구조죠. 평소엔 잘 굴러가도 서로 다른 I/O 패턴이 겹치는 순간 반응이 확 달라집니다. 예를 들어 백업 압축, 미디어 라이브러리 스캔, VM 패키지 업데이트가 겹치면 게스트 내부에서 체감이 둔해집니다.

    이때 CPU와 RAM을 먼저 의심하기 쉽지만 실제 병목은 스토리지 큐와 지연일 때가 많습니다. 그래서 필요한 건 감이 아니라 관찰입니다.

    iostat -x 1
    zpool iostat -v 1
    qm list
    pct list

    이 조합은 디스크 지연과 풀별 I/O, 그리고 실제로 어떤 게스트가 떠 있는지를 같이 보는 데 좋습니다. 짧은 스파이크보다 지속적으로 길어지는 대기 시간이 더 위험 신호인 경우가 많습니다.

    • iostat -x 1: 특정 디스크의 대기 시간이 길게 유지되는지 봅니다.
    • zpool iostat -v 1: 풀 전체가 아니라 특정 vdev만 바쁜지 구분합니다.
    • qm list, pct list: 어떤 게스트가 실제로 그 시간대에 스토리지를 적극 사용 중인지 대조합니다.

    여기서 핵심 판단은 "더 튜닝할까"보다 "이 워크로드를 같은 풀에 계속 둘 가치가 있나"입니다. 홈랩에선 성능 최적화보다 역할 분리가 더 큰 효과를 내는 경우가 많습니다. 메인 가족 사진과 테스트 VM을 한 운명으로 묶어두는 건, 실험의 자유를 데이터 안전과 교환하는 선택이 되기 쉽습니다.

    실패 모드별로 보는 근본 원인: 겉증상보다 구조를 봐야 합니다

    겉으로 보이는 문제 실제 근본 원인 Proxmox VE에서의 대응 TrueNAS SCALE에서의 대응
    VM이 갑자기 느려짐 백업, 스캔, 대용량 복사로 인한 I/O 혼합 게스트용 풀과 백업 경로 분리, 작업 시간대 분리 공유용 데이터셋과 블록 스토리지 경계 재설계
    공유는 열리는데 접근 권한이 이상함 SMB 설정보다 데이터셋 권한과 ACL 모델 불일치 외부 NAS 권한 모델 확인, 마운트 옵션 재점검 공유 재생성보다 데이터셋 권한과 ACL부터 점검
    백업은 있는데 복구가 불안함 백업 파일이 원본과 같은 풀 또는 같은 장애 도메인에 존재 백업 저장소를 별도 장치나 원격 대상으로 분리 복제 대상 풀 또는 별도 장비 준비
    재부팅 후 저장소 인식이 흔들림 의존 서비스 순서, 패스스루 장치 인식 지연, 마운트 타이밍 문제 스토리지 유형별 활성화 상태와 로그 확인 풀 import 상태와 장치 안정성, 컨트롤러 인식 확인
    스냅샷은 많은데 실제 복원이 번거로움 데이터셋 경계가 커서 롤백 범위가 과도함 VM 단위와 스토리지 단위 스냅샷 전략 분리 용도별 데이터셋 재구성 후 정책 분리

    ⚠️ 주의사항과 트러블슈팅: 제가 많이 부딪힌 부분

    디스크 패스스루(pass-through)로 TrueNAS를 VM 안에 넣는 경우

    Proxmox 위에 TrueNAS SCALE VM을 올리고 디스크나 HBA를 패스스루하는 구성은 학습용으로 좋습니다. 다만 메인 NAS로 오래 가져갈 때는 질문이 달라집니다. 문제가 났을 때 하이퍼바이저 문제와 스토리지 문제를 분리해서 볼 수 있는가? 이게 핵심입니다.

    • 디스크 식별이 꼬이거나 컨트롤러 인식이 흔들리면 장애 분석이 길어집니다.
    • 부팅 순서와 장치 초기화 타이밍이 꼬이면 NAS VM 기동이 늦어질 수 있습니다.
    • 스토리지 문제와 하이퍼바이저 문제를 별개로 다뤄야 하는데, 이 구조에선 둘이 자주 함께 움직입니다.

    제 판단은 분명합니다. 학습, 실험, 임시 구축에는 괜찮습니다. 하지만 가족 데이터, 장기 보관 백업, 다른 노드가 의존하는 공유 스토리지를 실으면 구조 난도가 올라갑니다. 홈랩이더라도 메인 데이터는 가상화 실험의 부속품이 아니어야 마음이 편합니다.

    스냅샷은 백업이 아닙니다

    ZFS 스냅샷은 되돌리기에는 훌륭하지만, 같은 풀 안에 남아 있는 한 장애 도메인이 동일합니다. 사용자 실수 복구에는 강하지만 장치 고장, 잘못된 운영 변경, 랜섬웨어 성격의 암호화 사고, 풀 자체 문제까지 모두 덮어주진 못합니다. 홈랩에서도 최소한 다른 장비나 원격 대상으로 복제하거나 백업을 보내는 구조가 필요합니다.

    SMB 권한과 Linux 퍼미션이 어긋나는 경우

    TrueNAS SCALE에서 흔한 착시는 "공유가 열렸으니 서비스 문제는 아니다"라고 생각하는 겁니다. 실제로는 공유 서비스보다 데이터셋 권한, ACL, 소유권 설계에서 막히는 경우가 더 많습니다. 특히 한 데이터셋을 여러 용도에 같이 쓰면 나중에 한쪽 요구사항을 맞추다가 다른 쪽이 깨지기 쉽습니다. 제 경험상 이 문제는 튜닝으로 해결되지 않고, 데이터셋 경계 재설계로 끝나는 경우가 많았습니다.

    저장소 속성을 한 번에 통일하는 경우

    모든 데이터셋에 동일한 속성을 주는 것도 자주 보이는 실수입니다. 미디어 보관, VM 블록 스토리지, 사용자 홈 디렉터리, 백업 아카이브는 액세스 패턴이 다릅니다. 속성값 하나로 모두 해결하려 하면 결국 어느 쪽에서도 만족스럽지 않은 결과가 나옵니다. 홈랩에서도 최소한 대용량 순차 파일과 작은 랜덤 I/O 정도는 분리해서 생각하는 편이 낫습니다.

    검증과 결과 확인: 무엇을 보면 구성이 잘 됐다고 판단할까

    다 만들어놓고 "일단 잘 되네"에서 멈추면, 실제 검증은 아직 덜 된 셈입니다. 저는 적어도 아래 항목은 확인해야 구축이 끝났다고 봅니다.

    1. 재부팅 후 풀 상태가 자동으로 정상 인식되는지 확인
    2. 스냅샷이 의도한 데이터셋 또는 게스트 범위에서만 생성되는지 확인
    3. VM 또는 컨테이너가 해당 저장소를 안정적으로 읽고 쓰는지 확인
    4. SMB, NFS, iSCSI 접근 권한이 예상한 사용자와 장치에만 적용되는지 확인
    5. 백업 또는 복제 대상에서 실제 복구 테스트가 가능한지 확인

    예를 들어 Proxmox VE에서는 게스트가 붙어 있는 저장소가 부팅 후 정상 활성화되는지와, 백업 경로가 일시적으로 마운트 실패했을 때 어떤 경고가 나는지를 보는 게 중요합니다. TrueNAS SCALE에서는 공유 서비스가 떠 있는지만 볼 게 아니라, 데이터셋 마운트 상태와 권한이 함께 정상인지를 확인해야 합니다.

    zfs list -o name,mounted,readonly
    zpool status -x
    showmount -e nas-host
    smbclient -L //nas-host -U username

    이 명령은 검증 관점에서 꽤 실용적입니다. 단, showmount는 NFS 서버가 실제로 export를 제공할 때 의미가 있고, smbclient는 Samba 클라이언트 패키지가 설치돼 있어야 합니다. 화려하진 않지만 이런 확인이 벤치마크보다 더 값질 때가 많습니다. 홈랩에서 오래 버티는 구성은 평균 성능이 약간 더 좋은 구성이 아니라, 이상 징후를 빨리 드러내는 구성이더라고요.

    Proxmox TrueNAS SCALE ZFS 비교 결과와 검증 상태를 보여주는 대시보드 이미지

    ZFS 상태, 스냅샷, 공유, VM 연결 상태를 한 번에 점검하는 검증 관점의 결과 화면 예시입니다.

    상황별 추천: 이런 경우엔 Proxmox VE, 이런 경우엔 TrueNAS SCALE

    여기서는 애매하게 말하지 않겠습니다.

    • VM, LXC, 실험용 서비스가 중심이라면 Proxmox VE가 맞습니다. 로컬 ZFS로 단순하게 가져가고, 백업 저장소만 별도 경로 또는 별도 장비로 떼는 구성이 운영이 편합니다.
    • 파일 공유, 미디어, 장기 보관, 백업 허브가 중심이라면 TrueNAS SCALE이 더 맞습니다. 데이터셋과 권한 모델을 먼저 설계하는 편이 결과가 좋습니다.
    • 둘 다 중요하고, 둘 다 메인 서비스라면 분리형이 맞습니다. Proxmox VE는 컴퓨트, TrueNAS SCALE은 스토리지. 홈랩에서 비용은 조금 늘어도 판단 비용과 복구 비용이 줄어듭니다.
    • 한 대 장비로 반드시 끝내야 하는 상황이라면 Proxmox VE 단독 또는 TrueNAS 중심 중 하나를 고르고, 반대 역할은 최소화하는 편이 낫습니다. 둘을 동등한 주인공으로 세우는 순간 구조가 무거워집니다.
    내 우선순위 추천 선택 이유
    VM 실험, LXC, 자동화, 클러스터 연습 Proxmox VE 게스트 수명주기 관리가 자연스럽고 운영 동선이 짧습니다
    집안 파일 서버, 사진, 미디어, 백업 TrueNAS SCALE 데이터셋, 권한, 공유, 스냅샷 관리가 중심에 맞습니다
    가상화와 NAS 둘 다 중요함 역할 분리 장애 범위를 줄이고 원인 분석이 쉬워집니다
    예산이 매우 제한적이고 실험이 우선 올인원 Proxmox VE 초기 진입 비용이 낮지만 메인 데이터는 별도 백업이 필수입니다
    데이터 안전이 최우선이고 VM은 부수적 TrueNAS SCALE 중심 스토리지 정책을 주도적으로 설계하기 쉽습니다

    이 판단에서 중요한 건 제품 팬심이 아니라 장애 우선순위입니다. 서버가 멈췄을 때 가장 먼저 살리고 싶은 것이 VM이라면 Proxmox VE, 가장 먼저 지키고 싶은 것이 데이터라면 TrueNAS SCALE입니다. 이 기준이 서지 않으면 계속 기능 비교만 하게 됩니다.

    관련 글이 있다면 Proxmox 백업 전략, ZFS 스냅샷 정책, 홈랩 NAS 네트워크 분리 구성도 함께 읽어보세요. 이 세 가지를 같이 보면 장비 선택보다 구조 선택이 먼저라는 점이 더 또렷해집니다.

    FAQ 성격으로 짧게 짚고 가는 포인트

    Q. 홈랩 입문자는 뭘 먼저 시작하는 게 좋을까요?

    서비스 여러 개를 띄워보며 익히는 게 목표면 Proxmox VE부터, 집안 파일 서버와 백업 체계를 안정적으로 만들고 싶으면 TrueNAS SCALE부터 시작하는 편이 시행착오가 적습니다.

    Q. 한 대 서버로 둘 다 하고 싶으면요?

    가능은 합니다. 다만 메인 데이터까지 얹는다면 복구 순서를 종이에 적어보는 걸 권합니다. 하이퍼바이저가 안 뜰 때 NAS 접근을 어떻게 할지, NAS가 안 뜰 때 VM 백업을 어디서 꺼낼지 막히면 아직은 역할을 더 줄이는 편이 낫습니다.

    Q. ZFS를 쓰니까 둘 다 체감 차이가 거의 없지 않나요?

    그렇지 않습니다. 파일시스템은 같아도 관리 흐름, 장애 대응, 공유 제공 방식, 권한 모델, 복구 순서가 다릅니다. 체감 차이는 대개 평상시보다 문제 상황에서 크게 드러납니다.

    Q. 언제 굳이 쓰지 말아야 하나요?

    Proxmox VE는 메인 데이터 보관과 테스트 워크로드를 한 박스에 무리하게 합칠 때 조심해야 하고, TrueNAS SCALE은 잦은 VM 실험과 하이퍼바이저 자동화가 핵심일 때 굳이 주력으로 잡지 않는 편이 낫습니다.

    마무리: 제가 지금 다시 홈랩을 짠다면 이렇게 갑니다

    제가 지금 처음부터 다시 짠다면, VM 실험과 서비스 배포가 메인이면 Proxmox VE에 로컬 ZFS를 쓰고 백업 저장소는 반드시 다른 경로나 다른 장비로 분리하겠습니다. 반대로 가족 사진, 미디어, 백업 아카이브가 더 중요하면 TrueNAS SCALE을 별도 장비로 두고, Proxmox는 그 저장소를 소비하는 쪽으로 붙일 겁니다.

    가상화가 중심이면 Proxmox VE, 데이터가 중심이면 TrueNAS SCALE. 둘 다 중요하면 역할 분리입니다. 홈랩에선 이 판단이 장비 스펙보다 오래 갑니다. ZFS는 둘 다 훌륭하지만, 차이를 만드는 건 파일시스템 이름보다도 무엇을 먼저 지키도록 구조를 짰느냐였습니다.

    Proxmox VE와 TrueNAS SCALE 선택 기준을 요약한 ZFS 비교 인포그래픽

    어떤 환경에서 무엇을 고르면 되는지 빠르게 판단할 수 있도록 핵심 선택 기준을 요약한 이미지입니다.

  • [Proxmox] Proxmox VM 블루투스 문제 해결: USB 패스스루 vs 네트워크 공유

    [Proxmox] Proxmox VM 블루투스 문제 해결: USB 패스스루 vs 네트워크 공유

    Proxmox VM 블루투스 문제 해결: USB 패스스루 vs 네트워크 공유

    홈랩에서 블루투스가 필요한 순간은 생각보다 빨리 옵니다. Home Assistant 같은 VM에 BLE 센서를 붙이거나, 특정 게스트에서만 페어링 작업을 돌려야 할 때가 딱 그렇더라고요. 제가 여러 번 부딪혀 보니 Proxmox VM 블루투스 문제 해결의 핵심은 공유 기술부터 찾는 게 아니라, 장치 소유권을 어디에 둘지 먼저 정하는 것이었습니다. 이 판단이 흐리면 설정은 맞는데 동작은 들쭉날쭉한, 제일 피곤한 상태로 가기 쉽습니다.

    실제로 USB 블루투스 동글은 한 운영체제가 HCI 레벨에서 붙잡고 쓰는 구조가 가장 단순합니다. 그래서 호스트도 잡고, 게스트도 잡고, 다른 노드에서도 함께 쓰겠다는 접근은 초반엔 그럴듯해 보여도 운영 단계에서 자주 무너집니다. 반대로 목적을 좁혀서 단일 VM에 USB 패스스루를 하거나, 아예 장치 근처 별도 노드에서 블루투스 처리를 맡기고 VM은 네트워크로 결과만 받는 구조로 나누면 문제 범위가 확 줄어듭니다.

    Proxmox 호스트, USB 블루투스 동글, VM, 네트워크 우회 구성을 한눈에 보여주는 개요 이미지입니다.

    왜 Proxmox VM 블루투스 문제 해결이 유독 헷갈리나

    제가 현장에서 자주 본 실패 패턴은 세 가지였습니다. 첫째, 호스트의 커널이나 블루투스 서비스가 이미 동글을 잡고 있는데 게스트에서도 같은 장치를 기대하는 경우. 둘째, lsusb만 보고 “인식됐다”고 판단했는데 실제 블루투스 컨트롤러 초기화는 실패한 경우. 셋째, Vendor:Product 기준으로 넘겼다가 같은 칩셋 동글이 여러 개 있어서 다른 장치가 붙는 경우입니다. 셋 다 겉증상은 비슷한데, 고치는 방법은 완전히 다릅니다.

    블루투스는 특히 USB 장치 인식, 커널 드라이버 바인딩, 블루투스 서비스 초기화, 실제 스캔과 페어링이 단계별로 따로 실패할 수 있습니다. 그래서 저는 항상 “USB가 보이느냐”와 “컨트롤러가 살아 있느냐”를 분리해서 봅니다. 이 기준 없이 들어가면 같은 명령만 계속 반복하게 되더라고요.

    판단 축 USB 패스스루 네트워크 공유/우회
    장치 소유권 특정 VM이 독점 장치가 호스트 또는 별도 노드에 남음
    초기 난이도 낮은 편 높은 편
    문제 분리 직관적 네트워크 계층까지 포함돼 복잡
    마이그레이션 친화성 낮음 상대적으로 높음
    추천 상황 Home Assistant 같은 단일 VM 장치를 다른 위치에 둬야 하거나 구조 분리가 우선일 때
    피해야 할 상황 클러스터 이동성이 핵심일 때 빨리 안정화해야 하는 단일 VM 환경

    여기서 제 기준은 명확합니다. 운영 단순성이 우선이면 USB 패스스루, 물리적 배치와 구조 분리가 우선이면 네트워크 우회입니다. 둘 다 조금씩 취하겠다는 생각은 대개 유지보수 비용만 늘리더라고요.

    Proxmox VM 블루투스 문제 해결 전, 지금 막힌 지점부터 잘라내기

    증상은 비슷해 보여도 확인 순서를 잘못 잡으면 시간을 많이 씁니다. 저는 아래처럼 끊어서 봅니다.

    • 호스트에서 안 보임: 케이블, 허브, 전원, 포트 자체 문제를 먼저 의심합니다. 이 단계에선 Proxmox 설정을 만져도 소용이 없습니다.
    • 호스트에서는 보이는데 VM에서 안 보임: qm config와 장치 지정 방식이 우선입니다. 장치가 아예 게스트로 안 넘어간 경우가 가장 많습니다.
    • VM에서 lsusb는 보이는데 bluetoothctl에는 없음: 게스트 내부의 서비스, 커널 메시지, rfkill 상태를 봐야 합니다.
    • 재부팅 후 다른 장치처럼 붙음: 같은 칩셋 동글이 복수 개이거나 허브 뒤 포트 재배치가 일어난 경우입니다.

    제가 재현했던 시나리오 하나를 말씀드리면, Proxmox 호스트에선 동글이 정상이고 qm config에도 usb0가 보였는데 게스트의 bluetoothctl list는 비어 있었습니다. 원인은 패스스루 자체가 아니라 게스트 안에서 bluetooth.service는 떠 있지만 컨트롤러 초기화가 실패한 상태였고, dmesg에 HCI 관련 메시지가 남아 있더라고요. 이럴 땐 Proxmox보다 게스트 로그 해석이 더 중요합니다.

    USB 패스스루로 해결하기: 가장 먼저 시도할 방법

    블루투스가 필요한 VM이 하나라면 저는 여전히 USB 패스스루부터 갑니다. 이유는 단순합니다. 실패 지점이 적고, 성공 여부를 판단하는 신호가 분명하거든요. 특히 Home Assistant처럼 장치가 계속 한 VM에 붙어 있어야 하는 워크로드에 잘 맞습니다.

    1. 호스트에서 장치와 현재 상태를 먼저 확인

    먼저 호스트에서 장치가 실제로 보이는지, 그리고 이미 다른 서비스가 점유 중인지부터 확인합니다. 이 단계가 깔끔해야 다음 명령 해석도 쉬워집니다.

    lsusb
    lsusb -t
    qm config 105
    journalctl -k | grep -i -E 'bluetooth|hci|usb'
    rfkill list

    여기서 보는 포인트는 다섯 가지입니다. lsusb는 장치 존재 확인, lsusb -t는 어느 버스와 포트에 붙었는지 확인, qm config 105는 VM 설정 검토, journalctl -k는 호스트 커널이 장치를 어떻게 봤는지, rfkill list는 블루투스가 차단 상태인지 보는 용도입니다. 특히 같은 종류 동글이 여러 개면 lsusb만으로는 구분이 잘 안 됩니다. 그럴 땐 버스-포트 경로가 더 믿을 만합니다.

    2. Vendor:Product 또는 포트 기준으로 패스스루

    Proxmox VE에서는 USB 장치를 Vendor:Product ID나 호스트 포트 경로 기준으로 VM에 연결할 수 있습니다. 둘 중 하나만 골라서 명확하게 쓰는 게 좋습니다.

    # 방법 A: Vendor ID:Product ID 기준
    qm set 105 -usb0 host=0a12:0001
    
    # 방법 B: 버스-포트 기준
    qm set 105 -usb0 host=1-3
    
    # 필요 시 USB 3 컨트롤러 옵션 검토
    qm set 105 -usb0 host=1-3,usb3=1
    
    # 반영 확인
    qm config 105

    이 부분에서 많이 헷갈리시는데, 둘을 같이 쓰는 게 아니라 상황에 맞는 식별 기준 하나를 고르는 것이 핵심입니다. 제가 고르는 기준은 이렇습니다.

    • 동글이 하나뿐: Vendor:Product로 시작해도 충분합니다.
    • 같은 칩셋 동글이 두 개 이상: 포트 기준이 안전합니다.
    • 허브 뒤에 꽂아 자주 위치가 바뀜: 허브부터 정리하는 게 먼저입니다. 지정 방식을 바꿔도 근본 문제는 남습니다.

    또 한 가지, 블루투스 동글은 대개 대역폭보다 초기화 안정성이 더 중요합니다. 그래서 USB 3 옵션도 “왠지 더 좋아 보이니까” 넣기보다, 실제 장치와 포트 구성이 필요한지 보고 넣는 편이 낫습니다. 이거 괜히 변수만 늘리는 경우가 생각보다 많았습니다.

    Proxmox 블루투스 패스스루 설정 흐름과 USB 장치 확인 이미지

    호스트에서 lsusb로 장치를 확인하고 qm set으로 VM에 연결하는 흐름을 보여주는 이미지입니다.

    3. 호스트가 동글을 계속 잡는지 확인

    패스스루를 했는데도 이상하게 불안정하면, 호스트 쪽 블루투스 서비스가 장치에 먼저 손을 대는지 확인해볼 만합니다. 모든 환경에서 반드시 꺼야 하는 건 아니지만, 호스트에서 블루투스를 쓸 일이 없고 VM 독점이 목표라면 불필요한 간섭을 줄이는 편이 낫습니다.

    systemctl status bluetooth
    systemctl stop bluetooth
    systemctl disable bluetooth
    rfkill list
    lsusb

    다만 여기서 한 번 더 판단해야 합니다. 호스트도 블루투스를 써야 하면 서비스를 끄는 방향은 맞지 않습니다. 그 경우엔 패스스루보다 네트워크 분리 구조가 더 낫고, 반대로 호스트에서 전혀 쓸 일이 없다면 장치 소유권을 게스트 하나로 정리하는 쪽이 운영이 훨씬 깔끔합니다.

    4. 게스트 안에서 “USB 인식”과 “컨트롤러 활성화”를 분리해서 확인

    이 단계가 제일 중요합니다. USB 패스스루가 성공했는지와 블루투스 컨트롤러가 실제로 올라왔는지는 같은 얘기가 아니거든요.

    lsusb
    systemctl status bluetooth
    bluetoothctl list
    rfkill list
    dmesg | grep -i -E 'bluetooth|hci|usb'
    journalctl -u bluetooth --no-pager

    해석 기준은 이렇게 잡으면 됩니다.

    • lsusb에 장치가 없음: 패스스루 실패입니다. VM 재부팅보다 완전 종료 후 시작이 더 낫습니다.
    • lsusb에는 보이지만 bluetoothctl list가 비어 있음: 게스트가 USB 장치는 봤지만 블루투스 컨트롤러로 올리지 못한 상태입니다.
    • rfkill list에 soft blocked가 보임: 서비스 문제보다 차단 상태 해제가 먼저입니다.
    • dmesg에 HCI attach 실패나 초기화 오류가 보임: 게스트 커널 또는 드라이버 초기화 단계 문제로 좁혀집니다.

    제가 자주 쓰는 추가 점검은 아래 정도입니다.

    # 차단 해제
    rfkill unblock bluetooth
    
    # 컨트롤러 확인 및 스캔 테스트
    bluetoothctl show
    bluetoothctl power on
    bluetoothctl scan on

    bluetoothctl show가 실패하면 아직 컨트롤러가 제대로 올라오지 않은 것입니다. 이 상태에서 애플리케이션 설정부터 만지면 순서가 뒤집힙니다.

    네트워크 공유는 언제 쓰나: Proxmox VM 블루투스 문제 해결의 우회 설계

    이 방식은 이름 때문에 오해가 많습니다. 실제로는 블루투스를 예쁘게 공동 소유하는 구조라기보다, USB 장치를 네트워크로 노출하거나 블루투스가 필요한 작업 자체를 장치 가까운 다른 Linux 노드에서 처리하는 식에 가깝습니다. 제 경험상 이 접근이 맞는 경우는 명확합니다. 동글을 서버실이 아니라 센서 가까운 장소에 둬야 하거나, Proxmox 클러스터에서 VM 이동성이 더 중요할 때입니다.

    USB/IP 같은 방식은 분명 쓸 수 있습니다. 다만 기대치를 잘 잡으셔야 합니다. USB 패스스루보다 디버깅 포인트가 하나 더 생깁니다. 즉, 장치 인식 문제에 더해 네트워크 상태와 원격 attach 상태까지 같이 봐야 합니다. 빠르게 끝내야 하는 환경이라면 저는 처음부터 이 길을 권하지 않습니다.

    1. 동글을 Proxmox 호스트에 직접 꽂기 어렵다.
    2. 별도 Linux 노드에 동글을 두고 VM은 원격으로 붙는다.
    3. 장치 위치 유연성이 단일 VM 단순성보다 더 중요하다.

    USB/IP를 쓸 때는 서버와 클라이언트 모두 관련 커널 모듈이 준비돼 있어야 합니다. 이 부분을 빼먹으면 명령은 맞는데 attach가 안 돼서 꽤 헷갈립니다.

    # 서버 측
    modprobe usbip-host
    usbip list --local
    usbip bind --busid=1-3
    usbipd -D
    
    # 상태 확인
    usbip port
    # 클라이언트 측
    modprobe vhci-hcd
    usbip list --remote=192.168.10.20
    usbip attach --remote=192.168.10.20 --busid=1-3
    lsusb
    dmesg | grep -i -E 'usb|bluetooth|hci'

    이 구조에서 제가 보는 핵심은 “USB가 원격으로 보이느냐”와 “그 뒤 블루투스 초기화가 되느냐”를 또 분리하는 것입니다. 원격 attach까지 됐는데 블루투스가 안 뜨면 결국 게스트 내부 문제입니다. 반대로 attach 자체가 불안정하면 네트워크 구간부터 다시 봐야 합니다. BLE 스캔은 지연과 간헐 끊김에 민감해서, 운영 안정성은 USB 직접 패스스루보다 떨어질 수 있다는 점도 같이 봐야 합니다.

    ⚠️ 실제로 자주 만나는 트러블슈팅

    여기는 이론보다 실패 모드가 더 중요합니다. 에러 메시지가 친절하지 않을수록 원인을 작은 층위로 나누는 게 제일 빨랐습니다.

    1. 호스트에는 보이는데 게스트에는 안 보이는 경우

    • qm config <VMID>에 usb0: 항목이 실제 저장됐는지 먼저 확인합니다.
    • VM을 일반 재부팅이 아니라 완전 종료 후 시작으로 다시 올립니다.
    • 같은 Vendor:Product 장치가 여럿이면 포트 기준으로 다시 지정합니다.
    • 호스트에서 이미 장치를 적극적으로 쓰는 서비스가 있는지 확인합니다.

    근본 원인은 대개 두 갈래입니다. 장치 지정이 엉뚱했거나, 장치는 맞는데 소유권 경합이 남아 있는 경우입니다. 둘을 구분하지 않으면 계속 설정만 바꾸게 됩니다.

    2. 게스트에서 장치는 보이는데 블루투스 스캔이 안 되는 경우

    • systemctl status bluetooth와 journalctl -u bluetooth를 같이 봅니다.
    • rfkill list에서 soft block이나 hard block 여부를 확인합니다.
    • dmesg에서 Bluetooth:, hci, firmware 키워드를 따라갑니다.

    이건 USB 전달 문제가 아니라 게스트에서 컨트롤러를 usable 상태로 못 만든 것에 가깝습니다. 초안 단계에서 많이 놓치는 부분이 바로 이 구분입니다. lsusb는 합격인데 블루투스는 불합격일 수 있습니다.

    3. 재부팅 후 매번 장치가 달라지는 느낌이 드는 경우

    이 경우 설정 미숙보다 물리 계층이 원인일 때가 많습니다. 허브 뒤 포트 재배치, 동일 칩셋 동글 복수 사용, 포트 이동이 대표적입니다. 저는 이런 환경에선 일단 장치를 메인보드 포트에 고정하고, 포트 기준 식별로 바꿉니다. 설정 꼼수보다 물리 배치를 정리하는 편이 훨씬 오래 갑니다.

    4. 라이브 마이그레이션까지 기대하는 경우

    여기서 방향을 잘못 잡는 분이 많습니다. USB 패스스루는 장치가 붙은 호스트와 강하게 결합됩니다. 그러니 VM을 자주 옮겨야 하는 구조라면 블루투스 동글을 VM에 직접 넘기는 설계 자체가 맞지 않을 가능성이 큽니다. 이때는 장치 가까운 별도 노드가 블루투스를 맡고, VM은 MQTT나 API 같은 상위 서비스만 받는 방식이 훨씬 운영 친화적입니다.

    5. Home Assistant 같은 장기 운영 워크로드에서 간헐적으로 끊기는 경우

    제가 체감상 많이 본 건 성능 부족보다 구성 경계가 애매한 상태였습니다. 호스트도 블루투스를 쓰고, 게스트도 쓰고, 허브도 끼고, 네트워크 우회도 섞여 있으면 처음엔 되다가 나중에 흔들립니다. 블루투스는 특히 “한 군데가 책임진다”는 구조가 중요합니다. 이거 정리하고 나면 의외로 문제 추적이 엄청 쉬워지더라고요.

    Proxmox VM 블루투스 문제 해결, 검증은 이렇게 하면 됩니다

    설정 완료와 운영 가능은 다릅니다. 저는 아래 순서로 확인하고, 어느 단계에서 막히는지만 기록해도 원인 추적이 쉬워집니다.

    1. 호스트에서 lsusb와 lsusb -t로 물리 장치와 포트를 확인합니다.
    2. qm config <VMID>로 패스스루 설정이 실제 저장됐는지 봅니다.
    3. 게스트에서 lsusb로 USB 수준 인식을 확인합니다.
    4. bluetoothctl list와 bluetoothctl show로 컨트롤러 활성화를 확인합니다.
    5. bluetoothctl scan on 또는 실제 페어링 테스트로 마무리합니다.

    제 기준에선 이렇게 봅니다. USB만 보이면 반쯤 온 것, 컨트롤러가 보이면 거의 해결, 실제 스캔이 되면 운영 가능입니다. 이 순서를 건너뛰면 문제를 추상적으로만 보게 됩니다.

    Proxmox VM 블루투스 문제 해결 후 게스트 OS에서 컨트롤러 인식 확인 이미지

    게스트 OS 내부에서 bluetoothctl과 로그로 컨트롤러 인식 여부를 검증하는 장면을 보여주는 이미지입니다.

    제가 권하는 선택 기준

    결국 선택은 환경이 합니다. 다만 저는 아래 표처럼 자릅니다. 이 기준이면 대부분의 홈랩 블루투스 문제에서 우왕좌왕하는 시간을 줄일 수 있습니다.

    상황 추천 이유 굳이 피할 이유
    Home Assistant 같은 단일 VM에서만 블루투스 사용 USB 패스스루 구성이 가장 단순하고 로그 해석이 쉽습니다. VM 이동성이 핵심이면 불리합니다.
    호스트에서도 블루투스를 써야 함 네트워크 분리 구조 장치 소유권 충돌을 피하기 쉽습니다. 초기 구성과 디버깅 범위가 넓어집니다.
    동글을 센서 가까운 다른 위치에 둬야 함 네트워크 공유/우회 물리 배치 자유도가 높습니다. 네트워크 이슈까지 함께 관리해야 합니다.
    문제 원인을 빨리 좁혀야 함 USB 패스스루부터 시작 USB 인식과 블루투스 초기화를 단계별로 보기 쉽습니다. 장기 구조가 네트워크 분리라면 재작업이 생길 수 있습니다.
    클러스터에서 VM 마이그레이션이 중요함 장치 분리형 구조 USB 직접 연결의 호스트 종속성을 줄일 수 있습니다. 단일 노드 홈랩에선 오히려 과할 수 있습니다.

    제가 실제로 고를 때 마지막으로 던지는 질문은 하나입니다. 이 동글을 누가 책임질 것인가. 답이 “이 VM 하나”면 USB 패스스루, 답이 “장치 가까운 별도 노드”면 네트워크 우회입니다. 답이 “호스트도 쓰고 게스트도 쓰고 상황 봐서”라면, 그건 대개 장애 예고에 가깝습니다.

    VM 블루투스 공유 방식 비교와 선택 기준을 보여주는 Proxmox 요약 이미지

    어떤 상황에서 USB 패스스루를 고르고, 어떤 상황에서 네트워크 공유를 택할지 한 장으로 정리한 요약 이미지입니다.

    마무리: 이럴 땐 A, 저럴 땐 B로 가면 됩니다

    Proxmox VM 블루투스 문제 해결이 목적이라면 우선순위는 분명합니다. 단일 VM에서 BLE를 안정적으로 써야 한다면 USB 패스스루로 시작하면 됩니다. 호스트에서 장치 확인, qm set으로 정확히 넘기기, 게스트에서 lsusb와 bluetoothctl를 분리 검증하기. 이 흐름이 제일 덜 돌아갑니다.

    반대로 장치 위치를 따로 둬야 하거나, 호스트 종속성을 줄여야 하거나, VM 이동성이 중요하다면 네트워크 공유 또는 우회가 맞습니다. 다만 이건 “공유”라기보다 장치 경계를 다시 설계하는 일에 더 가깝습니다. 복잡성은 늘지만 구조적 유연성을 사는 셈이죠.

    제가 홈랩에서 끝까지 써본 결론도 비슷했습니다. 빠르게 안정화해야 할 땐 USB 패스스루, 구조를 길게 가져가야 할 땐 장치 분리형 네트워크 설계. 이 기준만 잡아도 괜히 블루투스 동글 하나 붙이겠다고 호스트, 게스트, 네트워크를 한꺼번에 의심하는 일은 많이 줄어듭니다. 관련 홈랩 글을 함께 묶어 내부 링크로 연결해 두면, 나중에 USB 문제나 BLE 센서 글과도 흐름이 잘 이어집니다.

  • [OpenStack] OpenStack Magnum, 왜 다시 주목받나? CAPI Helm 최신 동향

    [OpenStack] OpenStack Magnum, 왜 다시 주목받나? CAPI Helm 최신 동향

    [인프라] OpenStack Magnum, 왜 다시 주목받나?

    OpenStack Magnum을 다시 보는 이유는 예전과 조금 다릅니다. 2026년 8월 기준 공식 문서와 릴리즈 노트를 다시 읽어보면, 지금의 Magnum은 단순히 Heat로 클러스터를 띄우는 오래된 서비스로 보기 어렵더라고요. 방향이 분명히 Cluster API 기반 Kubernetes as a Service 쪽으로 옮겨갔기 때문입니다.

    핵심은 이제 “쿠버네티스를 한 번 띄우는 법”이 아닙니다. Keystone 인증, Neutron 네트워크, Cinder 스토리지, Octavia 로드밸런서, Barbican 인증서 저장 같은 OpenStack 자원을 하나의 운영 모델로 묶을 수 있느냐가 더 중요하거든요. 이 글에서는 OpenStack Magnum을 언제 검토할 만한지, 반대로 언제 과감히 제외하는 게 맞는지 실무 관점으로 정리해 보겠습니다.

    OpenStack Magnum과 Cluster API 기반 프라이빗 클라우드 아키텍처 개요

    OpenStack Magnum이 Keystone, Neutron, Cinder, Octavia와 어떻게 연결되고 Cluster API 기반으로 Kubernetes 클러스터를 관리하는지 보여주는 개요 이미지입니다.

    1. OpenStack Magnum을 볼 때 먼저 버려야 할 오해

    Magnum을 아직도 “Heat로 쿠버네티스를 띄우는 서비스”로만 보면 판단이 자꾸 어긋납니다. 최신 Magnum 사용자 가이드는 Heat 드라이버가 deprecated이며, 앞으로 제거될 예정이라고 분명히 적고 있습니다. 신규 설계에서 Heat를 기본 전제로 잡는 건 이제 장기 운영 관점에서 무리가 있습니다.

    지금 봐야 할 축은 k8s_capi_helm과 k8s_cluster_api입니다. 특히 magnum-capi-helm은 OpenStack 거버넌스 아래에서 유지되고 있고, 공식 릴리즈 노트는 이 드라이버를 production use에 generally ready라고 설명합니다. 다만 이 표현을 곧바로 “대규모 운영에 아무 검증도 필요 없다”로 받아들이면 곤란합니다. 제 해석은 이렇습니다. 이제 Magnum의 실전성은 클러스터 생성 성공 여부보다, 관리 클러스터와 Cluster API 수명주기를 얼마나 안정적으로 운영하느냐에 달려 있습니다.

    • 예전 시선: Magnum이 Heat 템플릿으로 인프라를 직접 밀어 올린다.
    • 지금 시선: Magnum은 OpenStack API 진입점이고, 실제 클러스터 수명주기는 Cluster API와 provider가 선언적으로 맞춘다.
    • 실무 함의: 장애 지점도 Magnum API, 관리 클러스터, CAPO, Helm chart, addon reconciliation까지 넓게 봐야 한다.

    구성요소가 늘어나니 관찰 포인트도 많아집니다. 그래도 장점은 분명합니다. 예전에는 여기저기 흩어져 있던 업그레이드, 노드그룹 변경, autoscaling, 기본 정책 강제가 이제 플랫폼 계층에서 조금 더 정리되기 시작했거든요.

    2. OpenStack Magnum이 다시 주목받는 이유

    2026년 기준으로 다시 볼 만한 이유는 다섯 가지 정도로 압축됩니다.

    1. 공식 방향이 Heat에서 CAPI 계열로 옮겨갔습니다. Heat는 유지 중인 환경을 이해하는 용도에 가깝고, 신규 표준으로 추천되는 분위기는 아닙니다.
    2. magnum-capi-helm의 성숙도가 높아졌습니다. 공식 릴리즈 노트는 production use에 generally ready라고 밝히지만, 동시에 예상하지 못한 이슈 가능성도 언급합니다. 즉, 실전 배치 가능성은 높아졌지만 검증 책임이 사라진 건 아닙니다.
    3. 생성보다 변경 관리가 중심으로 들어왔습니다. create, delete, upgrade, node group size update 같은 수명주기 작업이 문서 중심에 올라와 있습니다.
    4. autoscaling 범위가 넓어졌습니다. 기본 워커뿐 아니라 non-default worker node group에도 min/max 정책을 줄 수 있고, 최신 릴리즈 노트 기준으로 min_node_count=0을 통한 scale-to-zero도 지원합니다. 다만 이 기능은 capi-helm-charts >= 0.26.0과 cluster-api-provider-openstack >= 0.26.0 조건을 확인해야 합니다.
    5. 운영자 기본값 설계가 쉬워졌습니다. [capi_helm_cluster_labels] 섹션에서 사이트 전역 기본 라벨을 둘 수 있어서 사용자 자유도와 플랫폼 일관성의 균형을 잡기 편해졌습니다.

    한 문장으로 줄이면 이렇습니다. OpenStack Magnum은 더 이상 “OpenStack에 쿠버네티스를 얹는 기능”이 아니라, OpenStack 조직 구조 안에서 Kubernetes 수명주기를 표준화하는 서비스에 가까워졌습니다.

    선택지 어울리는 환경 강점 숨은 비용 제 판단
    직접 kubeadm 운영 소규모 팀, 단일 클러스터, 빠른 실험 단순하게 시작 가능 업그레이드, 인증, 스토리지, LB 연동을 팀이 직접 책임져야 함 초반은 빠르지만 플랫폼화 단계에서 부담이 커짐
    Magnum + legacy Heat 이미 운영 중인 기존 환경 현재 자산을 당장 버리지 않아도 됨 공식 방향과 어긋나고 신규 기능 이점이 제한적 유지는 가능하지만 신규 표준으로는 비추천
    Magnum + CAPI Helm OpenStack 기반 프라이빗 KaaS OpenStack 자원과 일관된 수명주기 운영 관리 클러스터와 버전 조합 검증 필요 OpenStack이 핵심 플랫폼이면 가장 현실적
    외부 managed Kubernetes 퍼블릭 클라우드 우선, 운영 단순화가 최우선 제어면 운영 부담 감소 내부망, 규제, OpenStack 통합 요구에는 약할 수 있음 OpenStack 통합 가치가 작으면 이쪽이 더 나음

    3. OpenStack Magnum 운영에서 더 중요한 건 기본값 설계

    현장에서 진짜 갈리는 지점은 Magnum 자체보다 기본값을 어떻게 잠그느냐입니다. 많은 글이 ClusterTemplate 예제만 보여주고 끝나는데, 운영팀은 그 전에 무엇을 사용자 자율에 맡길지, 무엇을 플랫폼 기본값으로 강제할지 먼저 정해야 하거든요. 이걸 늦게 정하면 클러스터가 늘어날수록 편차가 커집니다.

    최신 magnum-capi-helm 설정 레퍼런스를 보면 핵심 섹션은 [capi_helm]와 [capi_helm_cluster_labels]입니다. 시작점으로는 아래와 같은 구성이 무난합니다.

    [capi_helm]
    kubeconfig_file = /etc/magnum/kubeconfig
    namespace_prefix = magnum
    helm_chart_repo = https://azimuth-cloud.github.io/capi-helm-charts
    helm_chart_name = openstack-cluster
    default_helm_chart_version = 0.26.0
    minimum_flavor_ram = 2048
    minimum_flavor_vcpus = 2
    
    [capi_helm_cluster_labels]
    auto_scaling_enabled = true
    min_node_count = 1
    max_node_count = 5
    monitoring_enabled = true
    kube_dashboard_enabled = false
    auto_healing_enabled = true
    keystone_auth_enabled = true
    octavia_provider = amphora
    octavia_lb_healthcheck = true
    csi_cinder_reclaim_policy = Retain
    csi_cinder_volume_binding_mode = WaitForFirstConsumer
    csi_cinder_allow_volume_expansion = true
    master_lb_floating_ip_enabled = true
    api_master_lb_allowed_cidrs = 10.20.0.0/16
    fixed_subnet_cidr = 10.40.0.0/24
    pod_network_cidr = 10.100.0.0/16
    service_network_cidr = 172.24.0.0/13

    몇 가지는 그냥 옵션 나열로 보면 안 됩니다.

    • kubeconfig_file: Magnum이 붙는 CAPI management cluster의 kubeconfig입니다. 이 관리 클러스터가 흔들리면 생성뿐 아니라 업그레이드와 노드그룹 변경도 같이 막힙니다.
    • namespace_prefix: 여러 Magnum 배포가 하나의 관리 클러스터를 공유할 때 충돌을 피하는 최소 장치입니다.
    • default_helm_chart_version: 여기서 버전을 통제하지 않으면 사용자별 드리프트가 시작됩니다.
    • minimum_flavor_ram, minimum_flavor_vcpus: 성능 튜닝이라기보다 실패 방지용 안전장치에 가깝습니다.
    • csi_cinder_volume_binding_mode=WaitForFirstConsumer: 멀티 AZ나 토폴로지 제약이 있는 환경에서는 특히 보수적으로 가져가는 편이 안전합니다.
    • api_master_lb_allowed_cidrs: Kubernetes API를 너무 넓게 여는 실수가 의외로 자주 나옵니다. 초기에 편하다고 넓게 열면 나중에 되돌리는 비용이 더 큽니다.

    제가 보는 핵심 질문은 하나입니다. 사용자가 바꿔야 하는 값과 플랫폼이 묶어야 하는 값을 구분했는가? OpenStack 기반 KaaS는 자유도보다 일관성이 중요할 때가 많습니다. 특히 여러 프로젝트가 같은 기반 인프라를 쓰는 조직에서는 더 그렇습니다. 이전에 다뤘던 Neutron·Octavia 운영 글과 함께 보면 이 경계가 더 또렷하게 잡힙니다.

    magnum.conf와 ClusterTemplate 라벨 설정 흐름 다이어그램

    운영자 설정 파일에서 기본 라벨을 정의하고, 사용자가 ClusterTemplate와 Cluster 생성 시 이를 어떻게 상속하거나 덮어쓰는지 보여주는 이미지입니다.

    4. ClusterTemplate는 청사진이 아니라 운영 계약서입니다

    ClusterTemplate를 만들 때 흔한 실수는 “생성만 되면 된다”는 태도로 옵션을 넣는 겁니다. 그렇게 만든 템플릿은 나중에 업그레이드, 보안, 확장성에서 발목을 잡습니다. Magnum의 템플릿은 사용자에게 노출되는 상품 정의서에 더 가깝습니다.

    아래 예시는 공식 CLI 문법에 맞춰 정리한 시작 예시입니다. 다만 이미지 이름이나 flavor 이름은 환경마다 다르니, 실제 배포 전에는 운영 중인 Glance 이미지와 Nova flavor를 먼저 맞춰 확인하는 편이 좋습니다.

    openstack coe cluster template create k8s-capi-template \
      --coe kubernetes \
      --image <validated-kubernetes-image> \
      --external-network public \
      --fixed-network tenant-net-prod \
      --fixed-subnet tenant-subnet-prod \
      --dns-nameserver 8.8.8.8 \
      --flavor m1.large \
      --master-flavor m1.large \
      --network-driver calico \
      --master-lb-enabled \
      --labels auto_scaling_enabled=true,min_node_count=1,max_node_count=3,monitoring_enabled=true,keystone_auth_enabled=true,octavia_provider=amphora,csi_cinder_volume_binding_mode=WaitForFirstConsumer,csi_cinder_reclaim_policy=Retain,api_master_lb_allowed_cidrs=10.20.0.0/16,pod_network_cidr=10.100.0.0/16,service_network_cidr=172.24.0.0/13 \
      k8s-capi-template
    
    openstack coe cluster create prod-k8s-01 \
      --cluster-template k8s-capi-template \
      --master-count 3 \
      --node-count 2 \
      --timeout 90 \
      prod-k8s-01
    
    openstack coe cluster config --dir ./kubeconfig --force prod-k8s-01
    export KUBECONFIG=./kubeconfig/config
    kubectl get nodes -o wide
    kubectl get storageclass
    kubectl get pods -A

    여기서 체크할 기준은 분명합니다.

    • 신규 구축이면 control plane 고가용성과 로드밸런서 경로를 먼저 잡는 편이 낫습니다. 실제로 magnum-capi-helm 문서는 업그레이드 안정성 측면에서 로드밸런서 사용을 강하게 시사합니다.
    • 운영 안정성이 우선이면 CNI는 Calico부터 시작하는 편이 안전합니다. 공식 문서도 Cilium 지원은 언급하지만, 정기 검증은 Calico 중심이라고 설명합니다.
    • master-lb-enabled는 CLI 옵션으로 존재하지만, CAPI Helm 문서상 현재 드라이버는 이 플래그를 사실상 무시합니다. 실무적으로는 로드밸런서를 전제로 설계하는 편이 낫습니다.

    그리고 하나 더 있습니다. autoscaling을 켜면 초기 워커 수 해석이 Heat 시절과 다를 수 있습니다. 공식 릴리즈 노트에 따르면 auto_scaling_enabled=true일 때 min_node_count, max_node_count를 존중하며, 이 경우 기본 워커는 node_count 대신 최소 노드 수로 시작할 수 있습니다. 이 부분은 운영팀이 미리 공유해 두는 게 좋습니다.

    5. nodegroup 설계가 좋으면 운영 절반은 끝납니다

    실무에서 비용과 안정성을 동시에 좌우하는 건 워커를 한 덩어리로 둘지, 역할별 nodegroup으로 나눌지입니다. OpenStack 위에서 Magnum을 쓸 때는 기본 워커 그룹 하나로 오래 버티는 설계가 나중에 꽤 답답해지더라고요.

    • 스토리지 성격이 다른 워크로드가 섞입니다.
    • ingress, 배치, 일반 애플리케이션의 확장 패턴이 다릅니다.
    • flavor 요구사항도 달라집니다.
    • scale-to-zero 같은 정책은 특정 그룹에만 적용하고 싶은 경우가 많습니다.

    그래서 역할 분리를 먼저 해두는 편이 낫습니다.

    openstack coe nodegroup create \
      --node-count 2 \
      --min-nodes 2 \
      --max-nodes 6 \
      --flavor m1.large \
      --role worker \
      --labels workload=general \
      prod-k8s-01 general-workers
    
    openstack coe nodegroup create \
      --node-count 1 \
      --min-nodes 0 \
      --max-nodes 4 \
      --flavor m1.xlarge \
      --role worker \
      --labels workload=batch \
      prod-k8s-01 batch-workers
    
    openstack coe nodegroup list prod-k8s-01
    openstack coe nodegroup show prod-k8s-01 batch-workers
    openstack coe cluster resize --nodegroup general-workers prod-k8s-01 3

    min_node_count=0은 확실히 매력적입니다. 다만 공식 릴리즈 노트 기준으로 scale-to-zero는 capi-helm-charts 0.26.0 이상과 cluster-api-provider-openstack 0.26.0 이상이 필요합니다. 조건이 안 맞으면 명시적 에러가 납니다. 이건 단순 옵션 문제가 아니라 플랫폼 버전 관리 문제로 봐야 합니다.

    워크로드 유형 추천 nodegroup 전략 이유 피해야 할 설계
    일반 웹/백엔드 기본 worker 그룹, min 2 이상 가용성과 예측 가능한 확장성 확보 모든 역할을 한 그룹에 섞기
    배치/야간 작업 별도 worker 그룹, 조건 충족 시 scale-to-zero 유휴 자원 점유 감소 stateful 서비스와 동일 그룹 사용
    Ingress/Gateway 별도 flavor와 보수적 min/max 트래픽 급변 대응과 네트워크 역할 분리 일반 앱 노드와 동일 정책
    스토리지 민감 워크로드 AZ와 volume 정책을 고려한 별도 그룹 Cinder 바인딩 충돌 감소 Immediate 바인딩 남용

    6. OpenStack Magnum 장애는 생성 실패보다 상태 전이 실패가 더 까다롭습니다

    문서만 보면 클러스터 생성 성공 여부가 가장 중요해 보입니다. 그런데 실무에서는 CREATE_IN_PROGRESS가 왜 오래 가는지, DELETE_IN_PROGRESS에서 왜 자원이 남는지, 노드는 떴는데 왜 Ready가 안 되는지를 빨리 분리하는 쪽이 훨씬 중요합니다.

    저는 장애를 볼 때 계층을 네 개로 나눠서 봅니다.

    • Magnum API 계층: 요청이 접수되고 상태 전이가 시작됐는가
    • OpenStack 인프라 계층: Nova, Neutron, Octavia, Cinder 자원이 기대대로 생성됐는가
    • CAPI management cluster 계층: CRD와 controller가 reconciliation을 정상 수행하는가
    • 워크로드 클러스터 계층: kubelet, CNI, CSI, addon이 정상 기동하는가

    이 구분 없이 한 화면만 보면 시간이 꽤 많이 날아갑니다. 아래처럼 대조해 보는 습관이 실제로 편합니다.

    openstack coe cluster show prod-k8s-01
    openstack coe nodegroup list prod-k8s-01
    openstack coe nodegroup show prod-k8s-01 general-workers
    openstack server list --name prod-k8s-01
    openstack loadbalancer list
    kubectl --kubeconfig /etc/magnum/kubeconfig get clusters.cluster.x-k8s.io -A
    kubectl --kubeconfig /etc/magnum/kubeconfig get machinedeployments.cluster.x-k8s.io -A
    kubectl --kubeconfig /etc/magnum/kubeconfig get openstackmachines.infrastructure.cluster.x-k8s.io -A

    이 명령 묶음이 좋은 이유는 분명합니다. Magnum에서 보이는 추상 상태와 CAPI에서 실제 reconcile 중인 객체 상태를 한 번에 비교할 수 있기 때문입니다.

    7. 자주 막히는 실패 모드와 원인

    7-1. Pod/Service CIDR 겹침: 증상은 Kubernetes인데 원인은 주소 설계일 때가 많습니다

    최신 설정 레퍼런스의 기본값은 fixed_subnet_cidr=10.0.0.0/24, pod_network_cidr=10.100.0.0/16, service_network_cidr=172.24.0.0/13입니다. 공식 문서도 Pod/Service CIDR가 OpenStack provider나 external network subnet과 겹치면 안 된다고 설명합니다.

    현장에서는 CNI부터 의심하기 쉽지만, 실제 원인이 Neutron 주소계획 충돌인 경우가 정말 있습니다. 이 부분은 감으로 보지 말고 숫자로 대조하는 게 빠릅니다.

    openstack subnet list
    openstack network list
    openstack coe cluster template show k8s-capi-template -f yaml
    openstack coe cluster show prod-k8s-01 -f yaml
    kubectl get nodes -o wide
    kubectl get pods -A -o wide

    7-2. Cilium 선택: 지원과 운영 추천은 다른 말입니다

    공식 릴리즈 노트에는 network_driver로 calico와 cilium을 선택할 수 있다고 나옵니다. 다만 magnum-capi-helm 구성 문서는 현재 정기 테스트가 Calico 중심이라고 적고 있습니다. 그래서 제 추천은 단순합니다. 운영 서비스 중심이면 Calico, 특정 네트워크 기능 검증이 목적이면 Cilium입니다.

    7-3. scale-to-zero 실패: autoscaling보다 버전 매트릭스를 먼저 봐야 합니다

    min_node_count=0이 안 먹는다면 autoscaler 설정만 볼 게 아닙니다. 공식 릴리즈 노트는 차트와 provider 버전 조건이 맞지 않으면 명시적으로 막는다고 설명합니다. 그래서 scale-to-zero 같은 기능은 사용자 문서보다 플랫폼 지원 매트릭스를 먼저 정리해 두는 게 훨씬 낫습니다.

    7-4. DELETE_IN_PROGRESS 잔류: 상태 표시와 실자원 정리는 다를 수 있습니다

    최신 릴리즈 노트에는 non-default node group 삭제 시 DELETE_IN_PROGRESS에 머물고 VM이 남는 이슈가 수정됐다고 나옵니다. 실무적으로 중요한 포인트입니다. 화면상 삭제 중인데 실제 자원 점유가 계속될 수 있기 때문이죠.

    • Magnum 상태: nodegroup 또는 cluster 상태 전이
    • 실자원 상태: Nova 인스턴스, 로드밸런서, 볼륨 잔존 여부

    7-5. 인증서 저장: 동작보다 수명주기 관리가 더 중요합니다

    Magnum 설치 가이드는 kubectl 접근용 TLS 인증서를 저장할 때 Barbican 사용을 권장합니다. 데이터베이스 저장도 가능하지만, 운영 환경에서는 누가 접근을 통제하고 어떻게 회전하고 감사 추적을 남길지가 더 중요하거든요. 이건 실제로 커질수록 차이가 크게 납니다.

    8. 무엇을 보면 정상이라고 판단할까

    클러스터가 생성됐다고 바로 정상 판정을 내리면 위험합니다. OpenStack 위의 KaaS는 클러스터 내부 상태와 OpenStack 연동 상태를 같이 봐야 하거든요. 저는 최소한 아래 네 묶음을 통과해야 초기 정상으로 봅니다.

    openstack coe cluster show prod-k8s-01 -f yaml
    kubectl get nodes
    kubectl get pods -A
    kubectl get storageclass
    kubectl get pvc -A
    kubectl cluster-info
    • Cluster 상태: 상태가 완료로 수렴하는지, API 주소가 기대대로 생성됐는지 확인합니다.
    • Node 상태: 모든 노드가 Ready인지 봅니다.
    • 스토리지 상태: 기본 StorageClass가 의도한 reclaim policy와 binding mode를 사용하는지 확인합니다.
    • API 접근 경로: 업그레이드 계획이 있다면 API endpoint가 로드밸런서 뒤에서 안정적으로 열리는지 확인해야 합니다.

    그리고 하나 더요. 정상 판정은 단발성 스냅샷보다 상태 전이의 일관성으로 보는 편이 낫습니다. 생성 직후 잠깐 좋아 보이는 건 큰 의미가 없더라고요.

    OpenStack Magnum으로 배포된 Kubernetes 클러스터 검증 화면과 상태 점검 개요

    클러스터 생성 완료 후 노드 상태, 스토리지 클래스, API 엔드포인트, 로드밸런서 연결 상태를 함께 검증하는 장면을 표현한 이미지입니다.

    9. 그래서 OpenStack Magnum은 언제 쓰고, 언제 쓰지 말아야 할까

    • OpenStack이 이미 조직의 핵심 플랫폼이고, 프로젝트 단위로 Kubernetes를 표준 서비스처럼 공급해야 한다면 Magnum을 검토할 가치가 충분합니다.
    • 기존 Heat 기반 Magnum 기억만으로 판단하면 안 됩니다. 공식 방향은 CAPI 계열에 집중돼 있습니다.
    • 퍼블릭 클라우드 managed Kubernetes 제약이 없고 OpenStack 통합 요구가 약하면 Magnum을 굳이 들고 오지 않는 편이 낫습니다.
    • 멀티테넌시, 내부망, 규제, 인증 통합, 스토리지 정책 일관성이 중요하면 Magnum 쪽 설득력이 커집니다.
    • 신규 구축이면 처음부터 CAPI Helm 기준으로 설계하는 편이 낫습니다.
    • 운영 안정성이 우선이면 Calico, 실험 목적이 분명할 때만 Cilium을 권합니다.
    • autoscaling이 필요하면 nodegroup 분리부터 먼저 설계해야 합니다.
    • 업그레이드를 생각한다면 API 로드밸런서를 전제로 잡는 편이 덜 아픕니다.
    OpenStack Magnum 도입 판단 기준과 선택 시나리오 요약 인포그래픽

    OpenStack Magnum을 도입해야 할 상황과 그렇지 않은 상황을 한눈에 비교하는 요약 이미지입니다.

    10. 결론: OpenStack Magnum은 이제야 자기 자리를 찾는 중입니다

    결론은 꽤 분명합니다. OpenStack Magnum은 지금 프라이빗 클라우드 안에서 Kubernetes를 OpenStack 자원처럼 제공하고 운영하려는 팀에게 다시 현실적인 선택지가 되고 있습니다. 유행이라서가 아니라 역할이 더 또렷해졌기 때문입니다.

    물론 환상은 버려야 합니다. Magnum을 넣는다고 운영이 자동으로 쉬워지지는 않습니다. 대신 운영 난이도가 개별 팀의 임시 스크립트와 수작업에서 플랫폼 차원의 제어로 옮겨갑니다. 이 차이가 꽤 큽니다.

    제 추천은 간단합니다. 기존 Heat 기반 환경은 CAPI 전환 검토를 시작하고, 신규 구축은 처음부터 CAPI Helm 기준으로 설계하세요. 반대로 OpenStack 통합 가치가 약한 조직이라면 외부 managed Kubernetes가 더 나을 수 있습니다. 이건 취향보다 운영 모델의 문제에 가깝습니다.

    다음 단계까지 이어간다면 두 가지를 먼저 정리해 두면 좋겠습니다. 하나는 관리 클러스터 운영 책임 주체, 다른 하나는 지원할 chart/provider 버전과 nodegroup 정책 매트릭스입니다. 이 두 가지가 정리되면 OpenStack Magnum이 훨씬 덜 추상적으로 보입니다.

    11. 참고한 공식 문서

  • [Game] 리눅스 게이밍 RTX: 호환성 문제 해결 가이드

    [Game] 리눅스 게이밍 RTX: 호환성 문제 해결 가이드

    [리눅스] 리눅스 게이밍 RTX 호환성 문제 해결 가이드

    리눅스 게이밍 RTX 조합은 분명 매력적입니다. 저도 메인 데스크톱을 리눅스로 오래 쓰면서 업무와 게임을 한 환경에 묶어 보려고 꽤 오래 만졌거든요. 그런데 여기서 많이 헷갈리는 지점이 하나 있습니다. 증상은 게임에서 터지는데, 원인은 게임 바깥에 있는 경우가 더 많다는 점입니다. Steam이 바로 꺼지면 게임 문제처럼 보여도 실제로는 커널 모듈, Vulkan ICD, Steam 패키징 방식, Wayland 세션이 각각 따로 발목을 잡는 식이죠.

    제가 실제로 문제를 좁힐 때는 순서를 거의 고정합니다. 드라이버 적재 확인 → Vulkan/OpenGL 경로 확인 → Steam/Proton 계층 확인 → 디스플레이 서버와 프레임 타임 검증 순서입니다. 이 흐름이 중요한 이유는, 위 단계가 무너지면 아래 단계에서 보이는 오류가 가짜 원인처럼 보이기 때문입니다. 예를 들어 nvidia-smi만 정상이라고 안심하면 안 됩니다. 그건 관리 도구가 GPU와 통신된다는 뜻이지, 게임이 쓰는 Vulkan 사용자 공간까지 멀쩡하다는 보장은 아니거든요.

    리눅스 게이밍 RTX 호환성 문제 해결 흐름도

    드라이버, 디스플레이 서버, Vulkan, Proton, 게임 실행 옵션이 어떻게 연결되는지 한눈에 보여주는 개요 이미지입니다.

    1. 왜 리눅스 게이밍 RTX에서 자주 꼬일까?

    윈도우에선 그래픽 드라이버와 게임 런타임 경로가 상대적으로 단순한 편인데, 리눅스는 계층이 더 잘게 쪼개져 있습니다. 커널 모듈이 하나, 사용자 공간 라이브러리가 하나, Steam 런타임이 하나, Proton이 또 하나입니다. 여기에 배포판 패키지 관리자와 Flatpak 같은 샌드박스 패키징이 끼어들면 같은 “NVIDIA 드라이버” 문제처럼 보여도 실제 고장 지점은 전혀 다를 수 있습니다.

    • 부팅 후 검은 화면: 독점 드라이버 자체만의 문제라기보다 nouveau 충돌, Secure Boot에 의한 모듈 차단, initramfs에 오래된 설정이 남은 경우가 많습니다.
    • 게임 실행 직후 종료: 드라이버 적재는 성공했지만 Vulkan ICD 파일, 32비트 사용자 공간 라이브러리, Steam Runtime 충돌이 원인인 경우를 자주 봤습니다.
    • 프레임은 높은데 체감이 나쁨: 평균 FPS보다 프레임 타임 스파이크가 심한 상황입니다. Wayland 세션, VRR, compositor, 셰이더 캐시, 스토리지 I/O가 얽힐 수 있습니다.
    • 특정 게임만 안 됨: 이때는 드라이버를 의심하기보다 Proton 버전, VKD3D/DXVK 경로, 런처, 안티치트 호환성부터 보는 편이 빠릅니다.

    제가 현장에서 제일 많이 보는 실수는 “문제가 있으니 드라이버부터 다시 깔자”는 접근입니다. 이게 먹히는 경우도 있지만, 원인이 런타임 경로나 패키징 충돌이면 재설치가 문제를 덮어 버려서 나중에 더 오래 헤맵니다. 리눅스 게이밍에서는 재설치보다 관찰 순서가 더 중요하더라고요.

    2. 리눅스 게임 호환성을 볼 때 핵심 개념

    아래 4가지를 분리해서 보면 훨씬 덜 꼬입니다. 저는 증상을 적을 때도 항상 이 축으로 나눕니다. “드라이버는 정상, Vulkan 실패”, “Vulkan 정상, Proton에서만 실패”처럼 써야 진단이 빨라집니다.

    구성 요소 역할 대표 실패 모드 우선 점검 포인트
    NVIDIA 커널 모듈 GPU 장치 적재, DRM/KMS 연결 검은 화면, 저해상도 부팅, nvidia-smi 실패 lsmod, journalctl -b -k, Secure Boot
    Vulkan/OpenGL 사용자 공간 게임 렌더러 초기화 게임 즉시 종료, vulkaninfo 실패, 잘못된 GPU 선택 ICD 파일, 32비트 라이브러리, Flatpak 런타임
    Steam + Proton 윈도우 게임 호환 계층 런처만 뜨고 종료, 특정 게임만 비정상 Proton 버전 고정, 로그 생성, prefix 초기화
    Wayland/Xorg 세션 프레임 페이싱, 입력, 캡처, VRR 끊김, 깜빡임, Alt-Tab 후 복귀 불안정 세션 교차 테스트, VRR, compositor 영향

    RTX 리눅스 성능을 볼 때도 평균 FPS 하나만 보면 판단이 흔들립니다. 제 기준에서는 1순위가 프레임 타임 안정성, 2순위가 GPU 사용률 추이, 3순위가 VRAM 압박 여부입니다. 게임이 120FPS를 찍어도 프레임 타임이 톱니처럼 튀면 실제 체감은 90FPS 안정 상태보다 나쁠 때가 많습니다.

    3. 리눅스 엔비디아 드라이버: 커널 모듈부터 확인

    가장 먼저 할 일은 “드라이버가 설치됐는가”가 아니라 현재 부팅된 커널에 드라이버가 실제로 붙었는가입니다. 같은 시스템에서도 커널 업데이트 직후 DKMS가 깨지면 지난주까지 멀쩡하던 세팅이 오늘 갑자기 무너지기도 하거든요.

    lspci -nnk | grep -A3 -Ei 'vga|3d|display'
    uname -r
    lsmod | grep -E 'nvidia|nouveau'
    modinfo nvidia | head
    nvidia-smi
    journalctl -b -k | grep -Ei 'nvidia|nouveau|drm|secure boot|module verification'

    여기서는 이렇게 읽으면 됩니다.

    • lspci -nnk에서 NVIDIA 장치가 보이는데 Kernel driver in use가 비어 있거나 예상과 다르면, 아직 드라이버 적재 단계에서 막힌 겁니다.
    • lsmod에 nvidia, nvidia_modeset, nvidia_drm가 보이고 nouveau가 동시에 올라오지 않는 편이 보통 안정적입니다.
    • nvidia-smi가 실패하면 게임 문제를 보기 전에 드라이버 적재부터 고쳐야 합니다.
    • journalctl에서 module verification failed, Required key not available가 보이면 Secure Boot로 모듈이 거부됐을 가능성이 큽니다.

    배포판별로는 접근을 조금 달리하는 게 좋습니다. Ubuntu 계열은 배포판 권장 드라이버를 먼저 쓰는 편이 안전하고, Arch 계열은 커널과 드라이버 패키지 조합을 직접 의식해야 합니다. Fedora 계열은 RPM Fusion 흐름을 벗어나 섞어 깔면 추적이 더 어려워집니다. 제가 권하는 기본 원칙은 하나입니다. 드라이버 설치 경로를 하나로 통일하세요. 배포판 패키지와 수동 설치 런파일을 섞는 순간 복구 비용이 급격히 커집니다.

    # Ubuntu 계열 예시
    sudo ubuntu-drivers devices
    sudo ubuntu-drivers autoinstall
    sudo reboot
    
    # 재부팅 후 확인
    nvidia-smi
    lsmod | grep -E 'nvidia|nouveau'

    만약 부팅 후 검은 화면이 뜬다면, 저는 드라이버 재설치보다 먼저 아래를 확인합니다. Secure Boot가 켜져 있는지, initramfs에 오래된 블랙리스트 설정이 남아 있는지, 그리고 커널 업데이트 직후 DKMS 빌드 실패가 있었는지입니다. 이 세 가지가 실제로는 더 자주 나오더라고요.

    리눅스 게이밍 RTX 드라이버 점검 터미널 화면

    GPU 인식 상태, 커널 모듈 로드 여부, 로그 확인 흐름을 보여주는 점검 화면 이미지입니다.

    4. 리눅스 게이밍 RTX 문제의 핵심: Vulkan과 OpenGL 확인

    nvidia-smi가 된다고 안심하면 여기서 많이 틀어집니다. 게임은 NVML이 아니라 보통 Vulkan이나 OpenGL 경로를 타니까, 사용자 공간이 따로 살아 있어야 합니다. 특히 Steam에서 게임이 눌렀다가 바로 꺼지는 증상은 이 단계에서 많이 갈립니다.

    vulkaninfo --summary
    glxinfo -B
    ls /usr/share/vulkan/icd.d/
    find /usr/share/vulkan /etc/vulkan -maxdepth 2 -type f 2>/dev/null | sort

    제가 보는 핵심은 세 가지입니다. 첫째, vulkaninfo --summary가 정상 종료하는지. 둘째, 물리 디바이스가 NVIDIA로 잡히는지. 셋째, 멀티 GPU 환경이라면 게임이 외장 GPU로 붙는지입니다. 노트북이나 하이브리드 그래픽 환경에서는 성능 문제가 사실상 호환성 문제처럼 보일 때가 많습니다. 실행은 되는데 너무 느리면 대개 잘못된 GPU를 잡은 겁니다.

    여기서 흔한 실패 모드는 아래와 같습니다.

    • 32비트 라이브러리 누락: 일부 Steam 게임은 32비트 사용자 공간이 없으면 조용히 죽습니다.
    • Flatpak Steam과 시스템 드라이버 경로 불일치: 같은 머신에서도 패키지형 Steam은 되는데 Flatpak Steam만 문제를 내는 경우가 있습니다.
    • 오래된 ICD 또는 런타임 캐시: 드라이버 업그레이드 뒤에도 캐시가 남아서 특정 게임만 이상하게 동작할 수 있습니다.
    • 하이브리드 GPU 잘못 선택: 렌더링은 되지만 iGPU로 붙어서 성능이 바닥나는 패턴입니다.

    Steam 설치 방식은 생각보다 중요합니다. 저는 문제를 좁히는 단계에서는 패키징 경로를 단순화합니다. Flatpak Steam을 꼭 써야 하는 이유가 없다면, 호환성 진단 초반에는 배포판 패키지 버전이나 공식 설치 경로 쪽이 추적이 쉽습니다. 반대로 이미 Flatpak 중심으로 시스템을 운영 중이라면 시스템 Steam과 Flatpak Steam을 동시에 유지하지 않는 편이 낫습니다. 둘을 섞어 두면 로그와 런타임 추적이 꽤 난잡해집니다.

    5. Steam과 Proton으로 리눅스 게임 최적화하기

    이제 게임 레이어입니다. 여기서는 “최신이 최고”라는 접근이 자주 빗나갑니다. 어떤 게임은 최신 Proton이 바로 해결해 주지만, 어떤 게임은 직전 버전이 더 안정적이더라고요. 그래서 저는 문제 있는 게임에 한해서만 버전을 고정합니다. 전체를 일괄 변경하면 나중에 어느 버전에서 나아졌는지 추적이 어려워지거든요.

    PROTON_LOG=1 DXVK_HUD=1 mangohud %command%
    
    # 게임이 VKD3D 경로에서 흔들릴 때 비교 테스트용 예시
    PROTON_LOG=1 mangohud %command%
    
    # 특정 GPU 선택이 필요한 PRIME 환경 예시
    __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia __VK_LAYER_NV_optimus=NVIDIA_only %command%

    각 옵션은 목적이 분명할 때만 쓰는 게 좋습니다.

    • PROTON_LOG=1: 재현 가능한 실패가 있을 때만 켜세요. 로그가 남으니 원인 좁히기에 좋지만, 상시 사용 이유는 없습니다.
    • DXVK_HUD=1: DXVK 계층 동작 여부를 볼 때 유용합니다. 다만 HUD가 필요한 건 진단 단계지, 평상시 기본값은 아닙니다.
    • mangohud: 평균 FPS보다 프레임 타임과 GPU/VRAM 추이를 보기 위해 씁니다. 실제 튜닝 단계에서 가장 실무적입니다. 이거 진짜 편하더라고요.
    • __NV_PRIME_RENDER_OFFLOAD=1 계열: 하이브리드 노트북에서 외장 GPU 고정용입니다. 데스크톱 단일 GPU 환경에서는 보통 쓸 이유가 없습니다.

    특정 게임만 문제를 낸다면 Proton prefix를 초기화해 보는 것도 실무적으로 효과가 좋습니다. 드라이버를 다시 까는 것보다 훨씬 저위험입니다. 다만 세이브 동기화 구조를 먼저 확인하고, 게임별 호환성 데이터를 날리는 범위를 알고 하셔야 합니다.

    # Steam 라이브러리 경로 예시에서 앱 ID별 prefix 확인
    find ~/.local/share/Steam/steamapps/compatdata -maxdepth 2 -type d | head -n 20
    
    # 일부 배포판은 ~/.steam/steam 경로를 쓰기도 있으니 함께 확인
    find ~/.steam/steam/steamapps/compatdata -maxdepth 2 -type d 2>/dev/null | head -n 20
    
    # Proton 로그 찾기
    find ~ -maxdepth 1 -type f \( -name 'steam-*.log' -o -name 'PROTON_LOG*' \) | sort

    제 경험상 리눅스 게임 최적화는 “옵션을 많이 넣는 것”이 아니라 옵션을 줄여 가면서 병목을 분리하는 것에 가깝습니다. 실행 옵션이 길어질수록 나중에 원인 추적이 어려워집니다. 처음엔 로그와 오버레이만, 그다음 GPU 선택, 마지막에 게임별 미세 조정 순서가 훨씬 덜 꼬입니다.

    리눅스 게이밍 RTX Steam Proton 설정과 성능 오버레이

    Steam 호환성 설정 화면과 게임 내 성능 오버레이가 함께 보이는 설명용 이미지입니다.

    6. 자주 만나는 리눅스 게임 호환성 문제와 해결법

    6-1. 부팅 후 검은 화면이 뜨는 경우

    이 증상은 게임과 가장 멀리 떨어져 있지만, 실제론 제일 먼저 끊어야 하는 문제입니다. Secure Boot가 켜져 있어 서명되지 않은 모듈이 막히는지, nouveau가 함께 올라오는지, 커널 업데이트 이후 DKMS가 실패했는지부터 보세요. 저는 이 상황에서 GUI 복구보다 journalctl -b -k로 커널 메시지를 먼저 봅니다. 원인을 텍스트로 보는 편이 훨씬 빠릅니다.

    6-2. nvidia-smi는 되는데 게임이 안 되는 경우

    이건 거의 전형적인 사용자 공간 문제입니다. vulkaninfo --summary가 실패하는지, Steam이 Flatpak인지 시스템 패키지인지, 32비트 라이브러리가 들어와 있는지 보세요. 드라이버 관리 도구가 된다 해서 게임 경로가 정상이라는 뜻은 아닙니다. 이 차이를 이해하면 헛재설치가 꽤 줄어듭니다.

    6-3. Wayland에서 프레임 타임이 불안정한 경우

    Wayland가 많이 좋아진 건 맞지만 모든 게임이 같은 품질로 맞물리진 않습니다. 특히 전체화면 처리, VRR, 캡처 오버레이, Alt-Tab 복귀에서 편차가 납니다. 이럴 때는 감으로 판단하지 말고 동일 장면을 Wayland와 Xorg에서 각각 5분씩 돌려 MangoHud 프레임 타임 그래프를 비교해 보세요. 제 판단 기준은 평균 FPS가 아니라 스파이크 빈도입니다. 평균이 조금 낮아도 스파이크가 적은 세션이 실제 플레이 만족도는 더 높습니다.

    6-4. 셰이더 캐시 때문에 첫 실행이 너무 버벅이는 경우

    첫 실행이 거칠다고 곧바로 세팅 실패라고 보진 않습니다. 중요한 건 시간이 지나며 안정되는지입니다. 초반 몇 분만 튀고 이후 매끄러워지면 셰이더 캐시 축적 과정일 수 있습니다. 반대로 반복 플레이에서도 같은 구간이 계속 끊기면 캐시 학습 문제가 아니라 스토리지 I/O, VRAM 압박, Proton 버전 궁합, VKD3D 경로를 의심하는 편이 맞습니다.

    journalctl --user -b | grep -Ei 'steam|proton|pressure-vessel|vulkan|dxvk|vkd3d'
    find ~ -maxdepth 1 -type f \( -name 'steam-*.log' -o -name 'PROTON_LOG*' \) | sort
    
    # 세션 종류 확인
    printf '%s\n' "$XDG_SESSION_TYPE"
    loginctl show-session "$XDG_SESSION_ID" -p Type 2>/dev/null

    로그는 아래처럼 읽으면 효율이 좋습니다.

    • missing shared library, wrong ELF class: 32비트/64비트 라이브러리 불일치 가능성이 큽니다.
    • failed to create Vulkan instance, dxvk, vkd3d 초기화 실패: 그래픽 API 경로 문제일 확률이 높습니다.
    • pressure-vessel 관련 오류: Steam 런타임 컨테이너 계층 문제를 의심할 만합니다.
    • 로그가 거의 없는데 즉시 종료: 런처 자체, 안티치트, 외부 DRM 쪽 가능성이 상대적으로 큽니다.

    7. 검증: 무엇을 보고 정상이라고 판단할까?

    세팅이 끝났는지 판단할 때 저는 평균 FPS보다 재현성과 안정성을 더 중요하게 봅니다. 한 번 잘 된 것은 의미가 약합니다. 재부팅 후에도, 세션을 바꿔도, 같은 장면에서 비슷하게 동작해야 비로소 잡힌 겁니다.

    1. GPU 사용률: 전투나 복잡한 장면에서 외장 GPU가 실제로 로드되는지 봅니다.
    2. 프레임 타임 그래프: 높은 FPS보다 급격한 스파이크가 줄었는지를 먼저 봅니다.
    3. VRAM 사용량: 텍스처 옵션 상향 후 순간 멈춤이 생기는지 확인합니다.
    4. Alt-Tab 복귀 안정성: 메뉴 복귀, 창 전환, 오버레이 호출에서 깨지지 않는지 봅니다.
    5. 재부팅 후 재현성: 한 번만 되는 세팅은 실사용에서 의미가 약합니다.

    MangoHud에서 프레임 타임이 일정하고, 로그에 치명적 초기화 오류가 없고, 재부팅 후 같은 게임이 같은 방식으로 실행되면 저는 그때 비로소 세팅이 안정됐다고 봅니다. 반대로 평균 FPS만 좋고 프레임 타임이 흔들리면 아직 끝난 게 아닙니다.

    리눅스 게이밍 RTX 성능 검증 프레임 타임 화면

    게임 중 프레임 타임 그래프, GPU 사용률, VRAM 표시를 통해 안정성을 판단하는 검증 예시 이미지입니다.

    8. 리눅스 게이밍 RTX에서 제가 추천하는 현실적인 선택 기준

    리눅스에서 RTX를 쓸 때 만능 해법은 없습니다. 대신 저는 아래처럼 결정합니다. 이 표는 드라이버 버전 자랑이나 과한 튜닝보다, 실제로 시간을 덜 쓰는 방향에 초점을 맞춘 기준입니다.

    상황 A를 고를 때 B를 고를 때 제 추천
    Steam 설치 방식 배포판 패키지: 문제 추적을 단순화하고 싶을 때 Flatpak: 데스크톱 환경을 Flatpak 중심으로 운영할 때 문제 진단 초반엔 하나만 남기세요. 둘 다 깔아 두고 비교 운용하지 않는 편이 낫습니다.
    Wayland vs Xorg Wayland: 일반 데스크톱 일관성과 최신 세션 기능이 중요할 때 Xorg: 특정 게임의 끊김, 깜빡임, Alt-Tab 이슈가 있을 때 체감이 애매하면 같은 장면을 양쪽에서 돌려 프레임 타임으로 결정하세요.
    Proton 버전 선택 최신 Proton: 새 패치 반영이 필요할 때 직전/검증된 버전 고정: 특정 게임만 불안정할 때 전체 기본값은 유지하고, 문제 게임만 개별 고정하는 편이 관리가 쉽습니다.
    튜닝 강도 기본값 위주: 안정성이 우선일 때 런치 옵션 확장: 병목 원인이 보일 때 처음부터 옵션을 많이 넣지 마세요. 로그와 MangoHud만으로 시작하는 편이 낫습니다.

    제가 초보자에게 권하는 조합은 분명합니다. 배포판 권장 NVIDIA 드라이버 + Steam 기본 Proton + 문제 있을 때만 개별 게임 조정입니다. 반대로 이미 시스템을 세밀하게 다루는 분이라면, 그때부터 Wayland/Xorg 교차 테스트, PRIME 오프로딩 고정, 런타임 경로 정리까지 들어가면 됩니다. 중요한 건 첫 출발을 단순하게 잡는 겁니다.

    9. 마무리: 이런 경우엔 이렇게 가면 됩니다

    지금 리눅스 게이밍 RTX 환경에서 RTX 그래픽카드로 게임이 꼬여 있다면, 저는 아래처럼 바로 판단하겠습니다.

    • 부팅부터 불안정하다: 게임 옵션은 접어 두고 커널 모듈, Secure Boot, nouveau 충돌부터 보세요.
    • nvidia-smi는 되는데 게임이 꺼진다: Vulkan 사용자 공간, 32비트 라이브러리, Steam 패키징 방식부터 확인하세요.
    • 게임은 켜지는데 성능이 이상하다: 평균 FPS보다 MangoHud 프레임 타임과 GPU 선택 상태를 먼저 보세요.
    • Wayland에서만 미묘하게 불안정하다: 같은 게임을 Xorg에서 짧게라도 교차 테스트해 보세요. 이 비교가 의외로 시간을 많이 아껴 줍니다.
    • 특정 게임만 말썽이다: 드라이버 전체를 흔들지 말고 그 게임의 Proton 버전과 prefix부터 만지세요.

    저라면 처음 세팅하는 분에게는 과한 최적화보다 원인 분리를 먼저 익히시라고 말씀드리겠습니다. 리눅스 게이밍 RTX 문제는 대개 한 번에 다 고치는 종류가 아니라, 어디까지는 정상이고 어디서부터 비정상인지 잘라 내는 과정에서 풀립니다. 드라이버 확인, Vulkan 확인, Proton 조정, 프레임 타임 검증. 이 네 단계를 고정 루틴으로 가져가시면 삽질 시간이 눈에 띄게 줄어듭니다.

    추가로 Steam 설정, Proton 버전 고정, MangoHud 오버레이 설정처럼 연관된 주제는 내부 링크로 함께 묶어 두면 체류 시간과 탐색 흐름이 좋아집니다. 관련 글도 같이 읽어 보시면 시행착오를 더 줄이기 좋습니다.

    리눅스 게이밍 RTX 점검 체크리스트 인포그래픽

    드라이버, Vulkan, Proton, 프레임 타임 확인 순서를 요약한 체크리스트형 인포그래픽 이미지입니다.

  • [Linux] sudo 권한 관리 베스트 프랙티스: 최소 권한 원칙으로 Linux 보안 강화

    [Linux] sudo 권한 관리 베스트 프랙티스: 최소 권한 원칙으로 Linux 보안 강화

    sudo 권한 관리 베스트 프랙티스: 최소 권한 원칙으로 Linux 보안 강화

    운영 서버를 오래 보다 보면 sudo 권한 관리는 늘 뒤늦게 문제를 일으키더라고요. 패치나 방화벽은 눈에 잘 띄는데, sudo는 장애가 없으면 계속 방치되기 쉽거든요. 그런데 실제 사고는 여기서 자주 납니다. 제가 현장에서 가장 위험하다고 느낀 상태는 권한이 없어서 일이 막히는 서버가 아니라, 누가 어디까지 할 수 있는지 팀 누구도 정확히 모르는 서버였습니다.

    특히 배포 계정, 운영자 개인 계정, CI 계정이 한 서버에 섞이기 시작하면 권한이 기능 단위가 아니라 사람 사정대로 붙습니다. 그래서 저는 sudo를 단순한 편의 기능이 아니라 root 권한을 잘게 쪼개는 정책 엔진으로 봅니다. 이 관점으로 보면 문법 암기보다 더 중요한 게 보입니다. 어떤 명령은 열어도 되고 어떤 명령은 닫아야 하는지, 왜 예외가 쌓이는지, 로그를 어떻게 읽어야 실제 오남용을 잡는지가 핵심이거든요.

    sudo 권한 관리와 최소 권한 원칙 개요 이미지

    sudo 권한 관리와 최소 권한 원칙이 어떻게 연결되는지 보여주는 개요 이미지입니다.

    1. 왜 sudo 권한 관리가 Linux 보안 강화의 핵심인지

    sudo는 흔히 root 비밀번호를 공유하지 않게 해주는 도구로만 설명되지만, 실무에서는 그보다 훨씬 큰 의미가 있습니다. 운영팀 입장에서 sudo는 권한 위임의 경계면입니다. 이 경계가 흐려지면 기술적으로는 root를 공유하지 않아도 운영 방식은 사실상 root 공유와 비슷해집니다. 예를 들어 웹 운영 담당자에게 <code>ALL=(ALL) ALL을 준 뒤 “개인 계정으로 쓰니까 추적은 된다”라고 생각하면, 추적만 남고 제어는 거의 사라진 상태가 됩니다.

    현장에서 자주 본 실패 패턴도 비슷했습니다. 장애 대응 속도를 이유로 권한을 넓게 열고, 정상화 후 줄이자고 했는데 결국 안 줄어들더라고요. 이유는 단순합니다. 권한 회수는 장애 복구보다 덜 급하고, 누가 무슨 이유로 예외를 받았는지 기록이 없기 때문입니다. 그래서 sudo 문제의 근본 원인은 문법 미숙보다 권한을 임시로 열어도 된다고 여기는 운영 습관인 경우가 많았습니다.

    2. 최소 권한 원칙을 sudoers 설정에 적용할 때 보는 5가지 기준

    최소 권한 원칙은 그냥 “적게 주자”가 아닙니다. 운영이 멈추지 않으면서도 우회 경로까지 포함해 범위를 닫는 설계를 뜻합니다. sudoers 설정을 읽을 때 저는 아래 다섯 축으로 봅니다.

    • 주체: 개인 사용자에게 직접 줄지, 그룹이나 역할로 묶을지
    • 대상 계정: 무조건 root로 올릴지, 특정 서비스 계정으로 제한할지
    • 명령 범위: 전체 바이너리인지, 특정 서브커맨드와 인자까지 좁힐지
    • 실행 맥락: 비밀번호 요구, 환경 변수 전달, 작업 디렉터리 영향이 있는지
    • 검증 가능성: 나중에 누가 왜 그 명령을 실행했는지 로그와 리뷰로 설명 가능한지

    여기서 많이 놓치는 게 세 번째와 네 번째입니다. 예를 들어 nginx 재시작만 주려는 의도인데 실제 sudoers에는 systemctl 전체가 들어가 있으면, 사용자는 다른 유닛까지 건드릴 수 있습니다. 반대로 절대 경로 없이 systemctl restart nginx처럼 적으면 정책 검토가 흐려집니다. sudo 권한 관리에서 중요한 건 “대충 맞는 권한”이 아니라 운영자가 리뷰 가능한 권한입니다.

    3. sudo 권한 관리 체크리스트

    아래 순서로 보면 대부분의 운영 서버에서 문제 지점이 금방 드러납니다. 신규 서버 인수인계, 보안 점검, 장애 후 재정비 때 이 순서가 꽤 잘 먹히더라고요.

    1. 직접 root 로그인 금지와 개인 계정 사용 원칙이 지켜지는지 확인합니다.
    2. /etc/sudoers 본문이 비대해지지 않았는지, 역할별 파일이 /etc/sudoers.d로 분리돼 있는지 봅니다.
    3. 권한 부여 단위가 사람 이름이 아니라 역할 또는 그룹인지 확인합니다.
    4. ALL=(ALL) ALL, ALL=(root) ALL, 광범위한 NOPASSWD가 남아 있는지 찾습니다.
    5. 허용 명령이 절대 경로와 가능한 범위의 인자 제한까지 포함하는지 봅니다.
    6. vi, vim, less, man, python, perl, find, tar처럼 쉘 탈출 또는 임의 실행으로 이어질 수 있는 명령이 열려 있는지 확인합니다.
    7. 서비스 재시작 권한이 필요할 뿐인데 systemctl 전체를 준 식의 과도한 추상화가 있는지 봅니다.
    8. sudo -l로 대상 사용자 기준의 실제 해석 결과를 확인합니다. 가능하면 관리자 계정에서 sudo -l -U 사용자명 또는 해당 사용자로 직접 로그인해 함께 봅니다.
    9. 로그가 남는지뿐 아니라, 실패 로그와 거부 로그를 누가 주기적으로 읽는지 확인합니다.
    10. 퇴사자, 역할 변경자, 오래된 자동화 계정 권한을 회수하는 절차가 있는지 봅니다.

    이 체크리스트의 핵심은 “설정이 있는가”가 아니라 “권한이 시간이 지나도 통제 가능한가”입니다. sudo는 한 번 열고 끝나는 설정이 아니라, 조직 변화에 따라 계속 부패하는 데이터라고 보는 편이 현실적입니다.

    4. 실전 구현 1: 현재 sudo 권한부터 인벤토리하기

    설정을 고치기 전에 먼저 현황을 뽑아야 합니다. 의외로 많은 팀이 이 단계를 건너뛰고 바로 정책부터 만지는데, 그러면 예외 권한이 왜 생겼는지 맥락을 놓치기 쉽습니다. 그래서 저는 사용자, 그룹, 설정 파일, 실제 해석 결과를 같이 봅니다.

    4-1. 사용자별 sudo 권한 확인

    # 주요 운영 계정의 실제 허용 권한 확인
    sudo -l -U alice
    sudo -l -U deploy
    sudo -l -U gitlab-runner
    
    # 배포판별 관리자 그룹 확인
    getent group sudo
    getent group wheel
    
    # 현재 로그인 사용자의 그룹 포함 관계 확인
    id alice
    id deploy

    여기서 저는 출력 자체보다 출력 패턴을 봅니다. ALL이 반복되면 과권한 가능성이 높고, 사용자별 직접 부여 규칙이 여러 줄 섞여 있으면 운영 이력이 누적된 상태일 가능성이 큽니다. 특히 원래는 nginx 재시작만 필요했던 계정에서 /bin/bash, /usr/bin/su, /usr/bin/vim, /usr/bin/python3 같은 명령이 보이면 바로 재검토 대상입니다.

    왜 이런 명령이 위험하냐면, 문제는 바이너리 이름이 아니라 그 안에서 추가 명령 실행이나 파일 접근 확장이 가능하다는 점입니다. 예를 들어 편집기나 페이저는 쉘 호출이 가능할 수 있고, 인터프리터는 임의 파일 읽기나 명령 실행으로 이어지기 쉽습니다. 그래서 sudo 권한 관리에서 “조회용 명령인데요”라는 말은 생각보다 안심 재료가 아닙니다.

    4-2. 기존 설정 파일 문법과 include 구조 검사

    # 메인 설정과 include된 파일까지 전체 문법 검사
    sudo visudo -c
    
    # 개별 파일 단위 문법 검사
    sudo visudo -cf /etc/sudoers
    sudo visudo -cf /etc/sudoers.d/ops-web
    
    # include 디렉터리 파일 목록과 권한 확인
    sudo ls -l /etc/sudoers.d/
    sudo stat /etc/sudoers /etc/sudoers.d/*

    실무에서 흔한 실패 모드는 문법 오류 그 자체보다 파일 분리 규칙이 엉킨 상태입니다. 같은 사용자가 여러 파일에서 다른 규칙을 받고 있는데 누구도 우선순위를 설명하지 못하는 경우가 많습니다. sudoers.d는 보통 사전식 정렬 순서로 읽히기 때문에 파일 이름 규칙도 중요합니다. 파일이 20개 넘고 이름 규칙이 제각각이면, 대체로 정책보다 예외가 더 많은 환경이더라고요.

    sudo 권한 관리용 sudoers.d 분리 구성 다이어그램

    /etc/sudoers.d 분리 구조와 역할 기반 권한 부여 흐름을 설명하는 이미지입니다.

    5. 실전 구현 2: sudoers 설정을 역할 기반으로 분리하기

    제 권장 방식은 개인 계정에 직접 권한을 붙이지 않고, 역할별 그룹을 먼저 만들고, 각 역할을 /etc/sudoers.d 파일 하나로 대응시키는 겁니다. 이렇게 해야 누가 빠지고 들어와도 정책이 흔들리지 않습니다. 웹 운영자가 nginx만 다룬다면 그 범위만 열어야지, 시스템 운영 전체를 열어두면 결국 나중에 문제가 납니다.

    5-1. 그룹 생성과 사용자 배치

    # 역할 그룹 생성
    sudo groupadd ops-web
    
    # 사용자 배치
    sudo usermod -aG ops-web alice
    sudo usermod -aG ops-web bob
    
    # 반영 확인
    id alice
    getent group ops-web

    여기서 중요한 건 그룹 이름을 업무 기준으로 짓는 겁니다. alice-admin 같은 식으로 사람 기준 이름을 만들면 나중에 그대로 기술 부채가 됩니다. 반면 ops-web, ops-db-readonly, deploy-api처럼 역할이 드러나면 리뷰와 회수가 쉬워집니다. 이거 실제로 인수인계할 때 진짜 편하더라고요.

    5-2. /etc/sudoers.d에 역할 파일 생성

    sudo visudo -f /etc/sudoers.d/ops-web

    파일 예시는 아래처럼 가져가면 됩니다. 단, systemctl 경로는 배포판마다 다를 수 있으니 아래 예시는 반드시 command -v systemctl 결과에 맞춰 바꿔 넣으세요.

    # /etc/sudoers.d/ops-web
    User_Alias OPS_WEB = %ops-web
    Runas_Alias WEB_RUNAS = root
    Cmnd_Alias WEB_CTL = /usr/bin/systemctl status nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl restart nginx
    
    Defaults:OPS_WEB logfile=/var/log/sudo-ops-web.log
    Defaults:OPS_WEB env_reset
    
    OPS_WEB ALL=(WEB_RUNAS) WEB_CTL

    이 구성이 좋은 이유는 범위가 명확하기 때문입니다. User_Alias로 주체를 묶고, Runas_Alias로 누구 권한으로 실행하는지 닫고, Cmnd_Alias로 실제 허용 명령을 좁힙니다. 한 줄로 축약해서 쓸 수도 있지만, 역할이 커질수록 alias를 분리해두는 편이 리뷰와 diff 관리에 훨씬 유리합니다.

    여기서 많이 하는 실수도 비슷합니다. 세 줄 쓰기 귀찮다고 /usr/bin/systemctl 전체를 허용해버리는 거죠. 이건 관리 편의가 아니라 권한 포기입니다. 누군가 다른 유닛 조작이나 예상 밖의 서브커맨드를 실행할 수 있게 되면, 원래 의도와 완전히 달라집니다.

    5-3. 명령 경로 확인 후 반영

    command -v systemctl
    command -v nginx
    command -v journalctl
    readlink -f "$(command -v systemctl)"

    이 단계가 사소해 보여도 실제로 중요합니다. 배포판이나 패키징 방식에 따라 경로가 다를 수 있고, 심볼릭 링크를 타는 경우도 있기 때문입니다. sudoers는 쉘 별칭이나 PATH 검색이 아니라 정확히 매칭되는 경로와 인자를 기준으로 판단한다고 생각하시면 운영 사고를 꽤 줄일 수 있습니다.

    6. 어떤 권한 모델이 더 나은지 비교해보기

    권한 모델 언제 선택하나 장점 치명적인 약점 제 추천
    개인 계정 직접 부여 실험용 VM, 1~2명 단기 운영 설정이 빠름 사람이 바뀌면 정책 의미가 사라짐 장기 운영에는 비추천
    그룹 기반 역할 부여 대부분의 운영 서버 리뷰, 회수, 인수인계가 쉬움 초기 역할 설계가 필요함 기본 선택지로 권장
    광범위한 ALL=(ALL) ALL 긴급 복구 중 아주 짧은 예외 즉시 대응 가능 예외가 상시 권한으로 굳어지기 쉬움 만료 계획 없으면 쓰지 않음
    NOPASSWD + 제한 명령 CI/CD, 배치, 무인 자동화 자동화 안정성이 좋음 명령 범위를 넓게 잡으면 계정 탈취 시 피해가 큼 사람 계정보다 자동화 계정에 적합
    전용 래퍼 스크립트만 허용 인자 검증이 필요한 반복 작업 허용 범위를 가장 좁게 만들기 좋음 스크립트 품질이 낮으면 우회점이 생김 복잡한 운영 절차에는 가장 실용적

    제 판단 기준은 이렇습니다. 단순 명령 몇 개면 그룹 기반 + 절대 경로 명령 제한으로 충분합니다. 그런데 인자 검증이 중요하거나 사람이 실수하기 쉬운 작업이면 sudoers에서 바이너리를 직접 열지 말고 래퍼 스크립트 하나만 허용하는 편이 더 낫습니다. 예를 들어 서비스 재시작 전에 설정 테스트를 강제해야 한다면, 운영자에게 systemctl과 nginx를 각각 주는 것보다 검증을 포함한 스크립트를 주는 쪽이 안정적입니다.

    7. 주의사항과 자주 겪는 트러블슈팅

    여기부터가 문법보다 더 중요합니다. 실제 장애나 오남용은 대개 sudoers 문법을 몰라서가 아니라, 권한 모델이 명령의 성질을 과소평가해서 생깁니다.

    7-1. visudo 없이 직접 편집하지 않기

    /etc/sudoers나 /etc/sudoers.d/*를 일반 편집기로 바로 열면 저장은 쉬워도 검증이 약합니다. 항상 visudo를 쓰는 이유는 단순 문법 체크 때문만이 아닙니다. 운영 서버에서 sudo가 깨지면 복구 경로가 원격 콘솔 하나로 줄어드는 경우가 많아서, 사소한 오타도 비용이 꽤 큽니다.

    7-2. 쉘 탈출 가능한 명령을 읽기 전용으로 착각하지 않기

    이 항목이 가장 자주 과소평가됩니다. less, man, vi, vim, awk, find, python3, perl, tar는 설정과 사용 방식에 따라 추가 명령 실행, 파일 쓰기, 파일 읽기 확대로 이어질 수 있습니다. 즉, 문제는 그 명령이 관리자용이냐 아니냐가 아니라 더 넓은 실행권으로 이어질 수 있느냐입니다. sudo 권한 검토 때 저는 허용할 명령의 기능보다 탈출 표면을 먼저 봅니다.

    7-3. NOPASSWD는 자동화 전용에 가깝습니다

    사람 계정에 넓은 NOPASSWD를 주면 작업 승인감이 사라집니다. 비밀번호 입력 자체가 완벽한 보안 장치는 아니어도, 적어도 “지금 관리자 권한을 쓰고 있다”는 마찰은 줍니다. 자동화 계정은 그 마찰이 필요 없지만 사람은 필요하더라고요. 그래서 제 기준은 간단합니다. 사람 계정은 기본적으로 비밀번호 요구 유지, 자동화 계정만 좁은 NOPASSWD 허용입니다.

    7-4. include 순서와 파일 권한이 엉키면 정책보다 예외가 앞섭니다

    /etc/sudoers.d에 파일이 많아지면 운영자가 논리적 우선순위와 파일 이름 우선순위를 혼동하기 쉽습니다. 거기에 권한까지 제각각이면 감사 때 설명이 어려워집니다. 저는 역할 파일 이름에 접두어를 붙여 정렬 순서를 드러내는 편입니다. 예를 들어 10-base, 20-ops-web, 90-breakglass처럼 나누면 긴급 예외가 일반 역할보다 눈에 잘 띕니다.

    7-5. 환경 변수 전달은 꼭 필요한 것만 예외로 열기

    sudo는 기본적으로 환경 변수를 정리합니다. 이 기본값은 불편해서가 아니라 안전해서 존재합니다. 특정 자동화 때문에 환경 변수를 넘겨야 하면, 먼저 왜 필요한지 확인하고, 가능하면 애플리케이션 설정 파일이나 systemd unit 쪽으로 옮기는 게 낫습니다. 환경 변수 전달을 습관적으로 열면 실행 맥락이 흐려지고, 장애 재현도 어려워집니다.

    7-6. 인자 제어가 애매하면 래퍼 스크립트로 닫는 편이 더 안전합니다

    sudoers에서 명령과 인자를 정교하게 제한하려다 보면 운영자가 오히려 규칙을 우회할 틈이 생기기도 합니다. 이럴 때는 스크립트 하나를 만들고 그 스크립트만 허용하는 쪽이 낫습니다. 제가 실무에서 자주 쓰는 방식도 이겁니다. 예를 들어 “nginx 설정 검사 후 reload만 허용” 같은 요구는 sudoers 한 줄보다 스크립트가 더 명확합니다.

    # /usr/local/sbin/nginx-safe-reload
    #!/bin/sh
    set -eu
    
    /usr/sbin/nginx -t
    exec /usr/bin/systemctl reload nginx
    # /etc/sudoers.d/ops-web
    User_Alias OPS_WEB = %ops-web
    Cmnd_Alias NGINX_SAFE = /usr/local/sbin/nginx-safe-reload
    
    OPS_WEB ALL=(root) NGINX_SAFE

    단, 위 경로도 배포판에 따라 다를 수 있으니 command -v nginx, command -v systemctl로 실제 경로를 확인한 뒤 반영하세요. 이 방식의 장점은 권한 정책과 실행 절차를 같이 묶을 수 있다는 점입니다. 반대로 래퍼 스크립트 자체 권한, 경로, 수정 권한이 허술하면 오히려 우회 지점이 됩니다. 그래서 전용 디렉터리, root 소유, 쓰기 권한 제한이 같이 따라가야 합니다.

    sudo 권한 관리 검증을 위한 로그 확인 이미지

    sudo 실행 로그를 확인하고 과도한 권한을 탐지하는 과정을 보여주는 이미지입니다.

    8. 검증과 로그 읽기: 설정 후 꼭 확인할 것

    sudo 정책은 작성보다 검증이 더 중요합니다. 파일이 예쁘게 나뉘어 있어도 실제 해석 결과가 의도와 다르면 의미가 없습니다. 저는 항상 허용 경로와 거부 경로를 둘 다 테스트합니다.

    1. 대상 사용자 기준으로 sudo -l를 실행해 해석 결과를 봅니다.
    2. 허용한 명령이 실제로 성공하는지 확인합니다.
    3. 허용하지 않은 명령과 인자가 거부되는지 확인합니다.
    4. 로그에 누가, 언제, 무엇을 실행했는지 남는지 확인합니다.
    5. 거부 로그가 반복되는 계정은 정책 누락인지 우회 시도인지 구분합니다.

    8-1. 권한 검증 예시

    su - alice
    sudo -l
    sudo /usr/bin/systemctl status nginx
    sudo /usr/bin/systemctl reload nginx
    sudo /usr/bin/systemctl restart nginx
    sudo /usr/bin/systemctl restart sshd
    sudo /bin/bash

    제가 보는 기준은 명확합니다. 의도한 세 명령만 성공하고, 다른 유닛 재시작이나 셸 실행이 거부되면 정책은 대체로 맞습니다. 반대로 허용하지 않은 명령이 실행되거나, 같은 바이너리인데 인자만 바꿔도 통과하면 범위를 너무 넓게 잡은 겁니다.

    8-2. 로그 확인 예시

    # systemd 저널에서 sudo 관련 로그 확인
    sudo journalctl _COMM=sudo
    
    # 배포판별 전통 로그 파일 확인
    sudo grep sudo /var/log/auth.log
    sudo grep sudo /var/log/secure
    
    # 역할별 전용 로그 확인
    sudo tail -f /var/log/sudo-ops-web.log

    로그를 읽을 때는 성공 로그보다 거부 로그와 반복 패턴이 더 유용합니다. 예를 들어 동일 사용자가 여러 번 다른 유닛 재시작을 시도하면 권한 설계가 업무 흐름과 안 맞는 걸 수 있고, 야간에 자동화 계정이 예상치 못한 명령을 반복하면 잡아야 할 이상 징후일 수 있습니다. 저는 운영팀에 늘 이렇게 말합니다. sudo 로그는 감사용 문서가 아니라 권한 설계 품질을 보여주는 운영 데이터라고요.

    또 하나 중요한 기준이 있습니다. 실패 로그가 많다고 무조건 사용자 잘못은 아닙니다. 실제로는 필요한 작업 범위를 정책이 못 따라가서, 현장이 우회 시도로 배우는 경우가 더 많았습니다. 그래서 거부 로그는 처벌 자료보다 권한 재설계 신호로 먼저 보는 편이 좋습니다.

    9. FAQ와 마무리: 이런 경우엔 이렇게 가시면 됩니다

    9-1. 자주 받는 질문

    Q. sudoers는 한 파일에 몰아넣는 게 낫나요?
    아닙니다. 역할별로 /etc/sudoers.d에 나누는 편이 훨씬 낫습니다. 파일이 나뉘어야 리뷰, 인수인계, 롤백 포인트가 분명해집니다.

    Q. 운영자가 많으면 개인 계정마다 직접 권한을 주면 안 되나요?
    초반에는 편해 보여도 오래 못 갑니다. 사람이 아니라 역할을 기준으로 권한을 설계해야 팀이 커져도 관리가 됩니다.

    Q. 자동화 계정은 비밀번호 없이 써도 되나요?
    됩니다. 대신 NOPASSWD는 좁은 명령 집합에만 붙이고, 가능하면 전용 래퍼 스크립트 한 개만 허용하는 쪽이 더 안전합니다.

    Q. 서비스 재시작 정도면 systemctl 전체를 열어도 되지 않나요?
    저는 권하지 않습니다. 서비스 하나만 필요하면 그 유닛의 status, reload, restart만 분리해 두는 편이 맞습니다.

    9-2. 제가 권하는 최종 운영 기준

    • 개인 실험 서버라도 visudo 사용, /etc/sudoers.d 분리, 절대 경로 지정은 기본으로 가져가세요.
    • 팀 운영 서버라면 개인 계정 직접 부여보다 그룹 기반 역할 부여를 기본 정책으로 두세요.
    • 명령이 단순할 때는 sudoers에서 절대 경로 명령을 직접 제한하고, 인자 검증이나 절차 강제가 필요할 때는 래퍼 스크립트 하나만 허용하세요.
    • 사람 계정에는 비밀번호 요구를 유지하고, CI/CD 같은 자동화 계정에만 좁은 NOPASSWD를 쓰세요.
    • 긴급 복구용 광역 권한이 필요하면 만료 시점과 회수 담당자를 같이 정하세요. 회수 계획 없는 예외는 거의 항상 상시 권한이 됩니다.
    • 감사 대응이나 보안 민감 환경이라면 로그 저장보다 로그 검토 루틴을 먼저 운영 프로세스에 넣으세요.

    제가 운영과 보안을 같이 보면서 내린 결론은 이겁니다. sudo 권한 관리는 문법의 문제가 아니라 권한을 업무 단위로 얼마나 잘게 쪼개고, 그 조각을 얼마나 꾸준히 회수할 수 있느냐의 문제였습니다. 실제로는 권한을 넓게 주는 쪽이 단기적으로 편해 보여도, 시간이 지나면 장애 분석과 감사 대응 비용이 더 커집니다. 반대로 역할 기반으로 잘라 두면 누가 뭘 할 수 있는지가 선명해져서 운영 속도도 더 안정됩니다.

    정리하면 이렇습니다. 대부분의 서버는 “그룹 기반 역할 부여 + 절대 경로 명령 제한”으로 시작하고, 인자 통제나 절차 강제가 필요하면 “래퍼 스크립트만 허용”으로 넘어가세요. 이 조합이 보안, 운영 편의, 리뷰 가능성 사이에서 가장 오래 버팁니다. sudo 권한 관리 외에도 계정 분리나 로그 감사 체계를 함께 보고 싶다면 관련 Linux 보안 강화 글도 이어서 읽어보시는 걸 권합니다.

    최소 권한 적용 전후 차이와 운영 체크리스트를 한눈에 정리한 요약 이미지입니다.

  • [Linux] LVM 스냅샷 백업으로 Linux 서버 무중단 백업과 복구

    [Linux] LVM 스냅샷 백업으로 Linux 서버 무중단 백업과 복구

    [Linux] LVM 스냅샷 백업으로 Linux 서버 무중단 백업과 복구

    운영 중인 Linux 서버 백업은 늘 같은 딜레마가 있습니다. 서비스를 내리면 백업은 편하지만 운영 현실과는 좀 멀어지죠. 서비스를 계속 열어두면 백업 시점이 흔들리고요. 그래서 저는 LVM 스냅샷 백업을 자주 씁니다. 이 방식이 만능이라서가 아니라, 백업이 읽어야 할 기준 시점을 짧고 또렷하게 고정해주기 때문입니다.

    실무에서 진짜 중요한 건 “스냅샷을 만들었다”가 아닙니다. 스냅샷이 살아 있는 동안 백업을 끝낼 수 있는지, 복구 때 권한과 속성이 그대로 돌아오는지, DB처럼 파일시스템 바깥의 일관성 문제를 따로 통제했는지가 더 중요하거든요. 이 글에서는 LVM 스냅샷 백업을 운영 관점에서 어떻게 설계하고, 어떻게 복구까지 이어가는지 한 흐름으로 정리해보겠습니다. 명령어만 던지는 글보다는, 어디서 자주 깨지는지까지 같이 짚어볼게요.

    LVM 스냅샷 백업 기반 Linux 서버 무중단 백업 아키텍처

    LVM 스냅샷, 운영 볼륨, 백업 저장소, 복구 흐름을 한눈에 보여주는 개요 이미지입니다.

    LVM 스냅샷 백업이 실무에서 통하는 이유와, 안 맞는 상황

    이 방식을 높게 보는 이유는 구조가 단순해서입니다. 운영 볼륨에서 직접 백업을 읽지 않고, 스냅샷을 읽기 전용 기준점으로 삼아 백업 작업을 분리합니다. 서비스는 원본 LV에서 계속 쓰고, 백업 프로세스는 스냅샷에서 읽습니다. 그러면 백업 도중 파일이 바뀌어서 생기는 중간 상태를 줄이기 훨씬 수월합니다.

    다만 많이 오해하는 부분도 있습니다. LVM 스냅샷은 백업 사본이 아닙니다. 스냅샷 LV만 만들어두고 안심하면 안 됩니다. 스냅샷은 원본 변경 블록을 붙잡아두는 임시 장치라서 공간이 차면 무효화될 수 있고, 원본 스토리지 장애까지 막아주지도 못합니다. 그래서 제 기준에선 스냅샷은 “백업을 뜨는 동안만 잠깐 존재해야 하는 작업용 안전장치”에 더 가깝습니다. 이 점을 놓치면 설계가 금방 흔들리더라고요.

    상황 추천 방식 이유 제가 피하는 경우
    일반 웹 서버, 파일 서버 LVM 스냅샷 + rsync 구현이 단순하고 파일 단위 복구가 빠릅니다 백업 시간이 길고 변경량이 큰 대용량 쓰기 워크로드
    MySQL, PostgreSQL 같은 DB 서버 DB 네이티브 백업 + 필요 시 LVM 스냅샷 보조 파일시스템 일관성과 트랜잭션 일관성은 다릅니다 LVM 스냅샷만 믿고 복구 가능하다고 보는 설계
    스토리지 기능이 강한 SAN/NAS 환경 스토리지 스냅샷 우선, LVM은 보조 대규모 환경에선 오프로드 이점이 큽니다 호스트 레벨 작업이 병목이 되는 구조
    단일 디스크 장애까지 대비해야 하는 경우 LVM 스냅샷 + 원격/별도 저장소 복제 같은 VG 안 스냅샷만으로는 재해 대응이 안 됩니다 로컬 디스크 안에만 백업을 두는 구성

    제가 먼저 보는 판단 기준: 스냅샷이 버티는가, 읽기 마운트가 안전한가

    실제 운영에서 핵심은 이 두 가지입니다.

    1. 백업이 끝날 때까지 COW 영역이 살아남는가
    2. 스냅샷을 읽기 전용으로 마운트할 때 파일시스템 특성에 맞는 옵션을 썼는가

    첫 번째는 용량 문제이고, 두 번째는 복구 품질 문제입니다. 스냅샷이 중간에 터지면 백업은 실패입니다. 반대로 스냅샷이 살아 있어도 ACL, xattr, SELinux 컨텍스트가 복구 후 어긋나면 운영 입장에선 실패나 다름없습니다.

    파일시스템별로 마운트 습관도 조금 달라야 합니다. 예를 들어 XFS 스냅샷을 같은 호스트에 읽기 전용 마운트할 때는 보통 <code>nouuid를 같이 고려합니다. 원본과 동일 UUID를 가진 파일시스템을 같은 시스템에서 마운트하려다 막히는 경우가 많기 때문입니다. ext4는 일반적인 파일 복사 백업이라면 ro만으로 충분한 경우가 많지만, 저널 재생 없이 조사성으로 확인해야 하는 작업이라면 noload를 같이 검토합니다. 이런 차이를 모르고 들어가면 “마운트가 왜 안 되지?”에서 시간을 꽤 쓰게 됩니다.

    LVM, 스냅샷, COW를 실무 관점으로 이해해보기

    스냅샷을 깊게 파고들 필요는 없지만, 한 가지만은 정확히 보셔야 합니다. 스냅샷 용량은 원본 크기가 아니라 스냅샷 생성 후 변경될 블록량을 감당하는 공간입니다. 그래서 2TB 원본 볼륨이라고 해서 스냅샷도 무조건 거대해야 하는 건 아닙니다. 반대로 100GB 볼륨이라도 백업 시간 동안 쓰기 폭주가 있으면 작은 스냅샷은 금방 무너집니다.

    • 원본 LV: 서비스가 계속 쓰는 볼륨입니다.
    • 스냅샷 LV: 백업 프로세스가 읽을 시점 기준점입니다.
    • COW 영역: 원본 블록이 바뀌기 전에 기존 내용을 보존하는 공간입니다.

    현장에서 가장 자주 보는 실패는 “원본이 크니까 스냅샷도 크게 잡아야 한다”가 아닙니다. 오히려 “백업은 금방 끝나겠지”라고 가볍게 보고 스냅샷을 너무 작게 잡는 경우가 더 많습니다. 원인은 거의 항상 같습니다. 백업 소요 시간과 그 시간 동안의 쓰기량을 계산하지 않았기 때문입니다. 로그 서버, 업로드가 몰리는 애플리케이션 서버, 배치나 VACUUM이 도는 DB 서버는 이 부분이 특히 민감합니다.

    LVM 스냅샷 백업 구현 전 체크리스트

    작업 전에 확인하는 항목은 아래 다섯 가지입니다. 이 단계가 허술하면 백업보다 복구 때 더 고생합니다.

    1. 대상 마운트가 정말 LVM LV 위에 올라가 있는지 확인합니다.
    2. VG의 VFree가 스냅샷과 메타데이터를 감당할 만큼 남아 있는지 봅니다.
    3. 파일시스템이 ext4인지 XFS인지 확인하고 마운트 옵션을 다르게 잡습니다.
    4. DB나 메시지 큐처럼 쓰기 일관성이 중요한 프로세스는 flush, lock, checkpoint 또는 native backup 절차를 따로 준비합니다.
    5. 백업 결과를 같은 VG가 아닌 별도 디스크나 원격 저장소로 보낼지 결정합니다.
    lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT
    pvs -o pv_name,vg_name,pv_size,pv_free
    vgs -o vg_name,vg_size,vg_free
    lvs -a -o lv_name,vg_name,lv_size,origin,data_percent,metadata_percent,lv_attr
    findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /data

    여기서 vgs의 vg_free는 그냥 “남은 공간”이 아니라, 스냅샷이 쓸 생존 예산이라고 보시면 편합니다. findmnt도 꽤 중요합니다. 운영에선 심볼릭 링크, bind mount, 컨테이너 볼륨 때문에 “내가 백업한다고 생각한 경로”와 “실제 파일시스템 경로”가 어긋나는 일이 생각보다 자주 생기거든요.

    실전 구현 1: 스냅샷 생성 전에 정합성 수준부터 정합니다

    이 작업에서 먼저 정하는 건 명령어가 아니라 어느 수준의 정합성을 요구하는지입니다.

    대상 권장 기준 실무 판단 추가 작업
    정적 파일, 문서, 업로드 디렉터리 파일시스템 크래시 일관성 LVM 스냅샷만으로도 충분한 경우가 많습니다 읽기 전용 마운트와 권한 보존 확인
    Nginx/Apache 설정, 일반 앱 배포 파일 파일시스템 일관성 + 서비스 검증 복구 후 설정 테스트가 중요합니다 nginx -t, 서비스 기동 확인
    MySQL/PostgreSQL 데이터 파일 애플리케이션 일관성 LVM 단독으론 불충분할 수 있습니다 flush, checkpoint, dump, native backup 병행

    파일시스템 스냅샷은 어디까지나 파일시스템 관점입니다. DB는 쓰기 캐시, 체크포인트, WAL이나 redo 로그, 버퍼 상태가 얽혀 있어서 “파일이 멈춰 보인다”와 “복구 가능한 상태다”가 같지 않습니다. 그래서 DB 서버에선 LVM 스냅샷을 주력 백업으로 두기보다, 네이티브 백업을 주력으로 두고 LVM은 빠른 파일 보조 복구 수단으로 쓰는 편이 안전합니다.

    실전 구현 2: LVM 스냅샷 생성과 읽기 전용 마운트

    예시는 /dev/vgdata/lvdata를 운영 볼륨으로 가정하겠습니다. 스냅샷은 lvdata_snap, 마운트 지점은 /mnt/backup_snap으로 두겠습니다. ext4와 XFS는 마운트 옵션을 다르게 예시하겠습니다.

    # 공통: 스냅샷 생성
    lvcreate -L 10G -s -n lvdata_snap /dev/vgdata/lvdata
    mkdir -p /mnt/backup_snap
    
    # ext4 예시
    mount -t ext4 -o ro /dev/vgdata/lvdata_snap /mnt/backup_snap
    
    # XFS 예시: 같은 호스트에서 원본과 함께 마운트할 때 nouuid 고려
    # mount -t xfs -o ro,nouuid /dev/vgdata/lvdata_snap /mnt/backup_snap
    
    lvs -a -o lv_name,origin,lv_size,data_percent,metadata_percent,lv_attr /dev/vgdata

    여기서 -L 10G는 샘플일 뿐입니다. 실무에선 이 숫자가 제일 중요합니다. 기준은 “원본 크기”가 아니라 백업이 끝날 때까지 바뀔 블록량입니다. 백업이 40분 걸리고 그 시간 동안 로그 파일과 업로드 파일이 많이 늘어난다면, 스냅샷은 생각보다 빨리 찹니다.

    한 가지는 꼭 바로잡고 싶습니다. 일반적인 LVM 스냅샷 백업에서는 수동 fsfreeze를 기본 절차로 넣지 않는 편이 안전합니다. LVM이 스냅샷 생성 시 파일시스템 freeze를 자동으로 처리하는 경우가 많아서, 이미 얼린 상태에서 다시 lvcreate를 호출하면 대기 상태에 빠질 수 있거든요. 대신 DB처럼 애플리케이션 정합성이 더 중요한 대상은 파일시스템 freeze보다 서비스별 체크포인트나 잠금 절차를 먼저 설계하는 쪽이 낫습니다.

    # 애플리케이션 정합성이 필요할 때는
    # 파일시스템 전체 freeze보다 서비스별 절차를 먼저 검토합니다.
    # 예: PostgreSQL CHECKPOINT, MySQL native backup 또는 flush 전략 확인
    lvcreate -L 10G -s -n lvdata_snap /dev/vgdata/lvdata

    그리고 ACL, xattr, 소유자 보존이 중요한 서버라면 백업과 복구 작업을 root 권한으로 수행하는지도 같이 확인하세요. 옵션만 넣어두고 권한이 부족하면 기대한 복구 품질이 안 나올 수 있습니다.

    LVM 스냅샷 백업 생성과 마운트 절차 이미지

    원본 LV에서 스냅샷을 생성하고 읽기 전용으로 마운트하는 흐름을 설명하는 이미지입니다.

    실전 구현 3: 스냅샷에서 백업을 뜰 때 옵션을 왜 그렇게 주는지

    스냅샷을 마운트했으면 백업은 그 지점에서 읽습니다. 파일 복구 비중이 높은 환경에서는 rsync를 많이 씁니다. 이유는 단순합니다. 권한, 타임스탬프, 하드링크, ACL, xattr를 비교적 일관되게 가져가기 좋고, 복구도 부분 단위로 끊어 하기 쉽습니다. 이 조합은 실제 운영에서 꽤 편하더라고요.

    BACKUP_DIR=/backup/$(date +%F)
    mkdir -p "$BACKUP_DIR"
    
    rsync -aHAX --numeric-ids \
      --info=stats2,progress2 \
      /mnt/backup_snap/ "$BACKUP_DIR/"
    
    sync

    -aHAX는 그냥 관성적으로 붙이는 옵션이 아닙니다. -H는 하드링크, -A는 ACL, -X는 xattr입니다. SELinux를 쓰거나 ACL 기반 권한을 쓰는 서버에선 이 차이가 꽤 큽니다. --numeric-ids도 실무에선 중요할 때가 많습니다. 복원 대상 서버에서 사용자 이름 매핑이 다를 수 있기 때문입니다.

    초안처럼 날짜별 새 디렉터리를 매번 만드는 구조라면 --delete는 보통 불필요합니다. 대상 디렉터리가 매일 새로 생기는데 --delete를 붙여도 얻는 이득이 거의 없습니다. 반대로 같은 미러 디렉터리를 계속 갱신하는 구조라면 --delete가 필요할 수 있지만, 잘못된 경로를 넣었을 때 파괴 범위도 같이 커집니다. 그래서 날짜별 보관과 미러 보관은 목적을 분리하는 편이 안전합니다.

    백업이 끝났으면 스냅샷은 바로 정리합니다.

    umount /mnt/backup_snap
    lvremove -y /dev/vgdata/lvdata_snap

    이건 습관처럼 가져가시는 게 좋습니다. 스냅샷을 오래 들고 가면 원본 쓰기마다 COW 부담이 쌓이고, 결국 성능과 안정성 둘 다 애매해집니다. 짧게 만들고, 빨리 읽고, 바로 제거하는 흐름이 제일 덜 사고 납니다.

    자동화 예시: 실패 시 정리까지 되는 백업 스크립트

    자동화는 단순히 cron이나 timer에 걸어두는 걸 말하지 않습니다. 중간 실패 시 스냅샷과 마운트를 어떻게 치울지까지 포함해야 합니다. 운영에서 진짜 귀찮은 건 백업 실패보다, 실패 뒤에 남은 찌꺼기입니다. 마운트가 남고 스냅샷이 남아 쓰기 성능을 갉아먹는 경우가 생각보다 많습니다.

    #!/usr/bin/env bash
    set -euo pipefail
    
    VG="vgdata"
    ORIGIN="lvdata"
    SNAP="lvdata_snap"
    SNAP_SIZE="10G"
    SOURCE_MNT="/data"
    SNAP_MNT="/mnt/backup_snap"
    BACKUP_ROOT="/backup"
    TODAY="$(date +%F)"
    BACKUP_DIR="$BACKUP_ROOT/$TODAY"
    LV_PATH="/dev/$VG/$ORIGIN"
    SNAP_PATH="/dev/$VG/$SNAP"
    FSTYPE="$(findmnt -no FSTYPE "$SOURCE_MNT")"
    VG_FREE_BYTES="$(vgs --noheadings --units b --nosuffix -o vg_free "$VG" | tr -d ' ')"
    SNAP_SIZE_BYTES="$(numfmt --from=iec "$SNAP_SIZE")"
    
    cleanup() {
      mountpoint -q "$SNAP_MNT" && umount "$SNAP_MNT" || true
      lvs "$SNAP_PATH" >/dev/null 2>&1 && lvremove -y "$SNAP_PATH" || true
    }
    trap cleanup EXIT
    
    if [ "$VG_FREE_BYTES" -le "$SNAP_SIZE_BYTES" ]; then
      echo "Not enough free space in VG $VG" >&2
      exit 1
    fi
    
    mkdir -p "$SNAP_MNT" "$BACKUP_DIR"
    
    lvcreate -L "$SNAP_SIZE" -s -n "$SNAP" "$LV_PATH"
    
    case "$FSTYPE" in
      xfs)
        mount -t xfs -o ro,nouuid "$SNAP_PATH" "$SNAP_MNT"
        ;;
      ext4)
        mount -t ext4 -o ro "$SNAP_PATH" "$SNAP_MNT"
        ;;
      *)
        echo "Unsupported filesystem: $FSTYPE" >&2
        exit 1
        ;;
    esac
    
    rsync -aHAX --numeric-ids --info=stats2 "$SNAP_MNT/" "$BACKUP_DIR/"
    sync
    
    lvs -a -o lv_name,origin,data_percent,metadata_percent,lv_attr "$VG"

    이 스크립트에서 꼭 넣는 건 세 가지입니다. set -euo pipefail, trap cleanup EXIT, 그리고 스냅샷 생성 전 여유 공간 검사입니다. 특히 cleanup trap은 중요합니다. rsync가 중간에 실패하더라도 스냅샷과 마운트가 남지 않게 막아줍니다.

    systemd 타이머는 그대로 써도 되지만, 서비스 유닛에 실패 로그를 남기기 쉽게 구성해두면 운영 추적이 편합니다. 이 블로그의 rsync 증분 백업 가이드나 systemd timer 운영 글이 있다면 내부 링크로 같이 묶어두는 것도 추천합니다. 검색 유입 이후에 다음 글로 자연스럽게 넘어가더라고요.

    # /etc/systemd/system/lvm-snapshot-backup.service
    [Unit]
    Description=LVM snapshot backup job
    After=local-fs.target
    
    [Service]
    Type=oneshot
    ExecStart=/usr/local/sbin/lvm-snapshot-backup.sh
    
    # /etc/systemd/system/lvm-snapshot-backup.timer
    [Unit]
    Description=Run LVM snapshot backup daily
    
    [Timer]
    OnCalendar=*-*-* 02:30:00
    Persistent=true
    
    [Install]
    WantedBy=timers.target
    chmod +x /usr/local/sbin/lvm-snapshot-backup.sh
    systemctl daemon-reload
    systemctl enable --now lvm-snapshot-backup.timer
    systemctl status lvm-snapshot-backup.timer
    systemctl list-timers --all | grep lvm-snapshot-backup

    systemctl status와 list-timers를 둘 다 보는 이유도 단순합니다. 타이머가 로드됐는지와 다음 실행 시각이 잡혔는지는 따로 확인하는 편이 실수를 줄여주거든요.

    주의사항과 트러블슈팅: LVM 스냅샷 백업에서 자주 깨지는 지점

    운영에서 자주 깨지는 지점은 대체로 아래 다섯 가지였습니다. 겉증상보다 원인을 먼저 보셔야 대응이 빨라집니다.

    • VG 여유 공간 부족: 원인은 단순히 디스크 부족이 아니라, 스냅샷 크기 산정이 쓰기량 기준이 아니었기 때문입니다.
    • 스냅샷 Data% 급상승: 백업이 느리거나, 백업 시간대의 쓰기 패턴을 과소평가한 경우가 많습니다.
    • XFS 스냅샷 마운트 실패: 같은 UUID 파일시스템 중복 마운트 문제를 놓친 경우가 흔합니다.
    • 복구 후 권한이나 컨텍스트 이상: rsync 옵션에서 ACL이나 xattr 보존을 빼먹었거나, 복구 대상 시스템 정책이 달랐던 경우입니다.
    • 백업 성공, 복구 실패: 파일 복사는 끝났지만 애플리케이션 기동 검증이 없었던 경우입니다.

    자주 보는 모니터링 명령은 아래입니다.

    lvs -a -o lv_name,origin,lv_size,data_percent,metadata_percent,lv_attr /dev/vgdata
    journalctl -u lvm-snapshot-backup.service -n 50 --no-pager
    1. data_percent가 계속 오르면 스냅샷이 변경 블록을 빠르게 소비하고 있다는 뜻입니다.
    2. data_percent가 100에 가까워지면 스냅샷 무효화 위험이 큽니다. 이때는 스냅샷 크기를 키우거나, 백업 속도를 올리거나, 백업 시간대를 바꾸는 쪽으로 접근합니다.
    3. metadata_percent는 스냅샷 유형과 LVM 버전에 따라 표시되지 않거나 의미가 다를 수 있으니, 항상 data_percent와 함께 해석합니다.
    4. lv_attr 값이 평소와 다르면, 특히 snapshot 관련 속성이 예상과 다르면 스냅샷 상태 이상부터 의심합니다.

    제 판단은 꽤 단순합니다. 스냅샷 크기를 무작정 키우는 것보다, 백업 시간을 줄이거나 쓰기 피크를 피하는 게 먼저입니다. 스냅샷을 크게 잡는 건 임시 처방이지 구조적 해결은 아닙니다.

    그리고 파일 복구와 볼륨 롤백은 완전히 다른 작업입니다. 이 둘을 섞어 생각하시면 운영 반영 시점에서 사고가 납니다.

    복구 방식 적합한 상황 주요 도구 리스크 포인트
    파일 단위 복구 설정 파일, 업로드 디렉터리, 일부 데이터만 되살릴 때 rsync, cp, tar 권한, ACL, xattr, 서비스 재기동 검증 누락
    스냅샷 병합 롤백 볼륨 전체를 특정 시점으로 되돌려야 할 때 lvconvert –merge 운영 중인 origin 반영 시점, 재활성화나 재부팅 절차
    LVM 스냅샷 백업 Data 퍼센트 모니터링 대시보드

    스냅샷 사용량 증가와 경고 상태를 관찰하는 모니터링 예시 이미지입니다.

    LVM 복구 시나리오 1: 파일 단위 복원

    실제 장애는 대부분 전체 롤백까지 갈 필요가 없습니다. 설정 파일 하나, 업로드 경로 일부, 잘못 덮어쓴 정적 자산 몇 개처럼 부분 복구가 더 많습니다. 이럴 때 LVM 스냅샷 기반 백업은 꽤 강합니다. 백업을 통째로 되돌리지 않고 필요한 경로만 살릴 수 있어서 영향 범위를 줄이기 쉽습니다.

    rsync -aHAX --numeric-ids /backup/2026-08-16/etc/nginx/ /etc/nginx/
    rsync -aHAX --numeric-ids /backup/2026-08-16/data/uploads/ /data/uploads/
    
    nginx -t
    systemctl reload nginx

    복구 직후엔 파일 존재 여부만 보면 부족합니다. 최소한 설정 문법 검사, 서비스 reload 또는 재기동, 실제 read/write 동작 확인까지 보셔야 합니다. 파일은 돌아왔는데 애플리케이션 권한이 막혀서 실패하는 경우도 생각보다 많습니다.

    LVM 복구 시나리오 2: 스냅샷 병합으로 시점 롤백

    볼륨 전체를 특정 시점으로 되돌려야 한다면 lvconvert --merge를 씁니다. 다만 이건 영향도가 큽니다. 가볍게 권하기 어려운 이유도 명확합니다. merge는 파일 몇 개를 되돌리는 작업이 아니라, origin LV 전체 상태를 되감는 작업이기 때문입니다.

    umount /data
    lvchange -an /dev/vgdata/lvdata
    lvconvert --merge /dev/vgdata/lvdata_snap
    lvchange -ay /dev/vgdata/lvdata
    mount /dev/vgdata/lvdata /data

    환경에 따라 merge 반영 시점은 즉시가 아니라 다음 활성화 시점이 될 수 있습니다. 특히 origin LV가 열려 있으면 더 조심하셔야 합니다. 루트 파일시스템이 걸린 경우라면 유지보수 창, rescue 모드, 재부팅 절차까지 포함해서 반드시 사전 검증하는 편이 안전합니다.

    검증: 백업 성공보다 복구 성공 신호를 봅니다

    운영에선 “백업 로그가 성공으로 끝났다”보다 “복구 검증이 통과했다”가 훨씬 중요합니다. 최소 기준으로 잡는 항목은 아래 네 가지입니다.

    1. rsync 종료 코드와 systemd 서비스 종료 상태를 확인합니다.
    2. 백업 사본에서 샘플 파일을 실제로 열어 읽어봅니다.
    3. 권한, 소유자, 심볼릭 링크, ACL, xattr가 유지됐는지 확인합니다.
    4. 복구 테스트 환경에서 서비스가 떠서 실제 요청을 처리하는지 확인합니다.
    echo $?
    getfacl /data/somefile
    getfattr -d /data/somefile || true
    find /backup/$(date +%F) -maxdepth 2 | head
    systemctl status lvm-snapshot-backup.service --no-pager

    echo $?가 0이 아니면 백업 작업은 실패로 보는 편이 맞습니다. getfacl이나 getfattr 결과가 기대와 다르면, 복구 후 접근 제어 문제가 날 가능성이 큽니다. 그리고 복구 테스트는 프로세스가 떴는지만 보면 부족합니다. 웹 서비스라면 실제 요청을 보내보고, 업로드 경로라면 테스트 파일 생성과 삭제까지 해보는 쪽이 훨씬 현실적입니다.

    LVM 스냅샷 백업 검증과 복구 테스트 체크리스트

    백업 성공 여부보다 복구 가능성을 점검하는 체크리스트를 요약한 이미지입니다.

    실무 추천 시나리오: LVM 스냅샷 백업은 언제 쓰면 좋을까

    운영에서 내리는 추천은 비교적 분명합니다.

    • 일반 웹 서버, 파일 서버, 홈랩: LVM 스냅샷 + rsync 조합이 가장 균형이 좋습니다. 구현 난이도 대비 복구 유연성이 좋습니다.
    • 트랜잭션 정합성이 중요한 DB 서버: DB 네이티브 백업을 주력으로 두고, LVM 스냅샷은 파일 단위 보조 복구나 빠른 시점 확보 수단으로 쓰는 편이 안전합니다.
    • 백업 창이 길고 쓰기량이 높은 서버: 스냅샷 크기를 키우기 전에 백업 시간대 조정, 대상 분리, 백업 속도 개선부터 보시는 게 맞습니다.
    • 디스크 장애나 호스트 장애까지 대비해야 하는 환경: 로컬 스냅샷만으로 끝내지 말고 원격 저장소 복제를 붙이셔야 합니다.

    실무에서 느끼는 건 늘 비슷합니다. 빠르게 시점을 고정하는 기술, 사본을 오래 보관하는 기술, 서비스를 다시 살리는 기술은 서로 다릅니다. LVM 스냅샷은 첫 번째에 강합니다. 그래서 잘 쓰면 진짜 편하지만, 혼자 모든 걸 해결해주진 않습니다.

    추천을 한 줄로 좁히면 이렇습니다. 운영 파일 백업이라면 LVM 스냅샷을 짧게 만들고 rsync로 빠르게 뽑으세요. DB라면 그 위에 네이티브 백업을 얹으세요. 그리고 어떤 경우든 복구 리허설을 백업 성공보다 우선순위 높게 두세요.

    자주 묻는 질문

    LVM 스냅샷 백업만 있으면 충분한가요?

    아닙니다. 스냅샷은 시점 확보 장치이고, 실제 백업 사본과 복구 검증이 함께 있어야 의미가 있습니다. 같은 VG 안에만 결과를 두는 구성도 재해 대응 관점에선 부족합니다.

    XFS에서도 쓸 수 있나요?

    가능합니다. 다만 같은 호스트에 원본과 스냅샷을 함께 마운트할 때는 nouuid를 검토하셔야 합니다. 파일시스템 일관성과 애플리케이션 정합성은 별개라는 점도 그대로 유효합니다.

    운영 중 성능 영향은 없나요?

    영향이 0은 아닙니다. 스냅샷 유지 시간이 길수록, 그리고 원본 쓰기량이 많을수록 COW 부담이 커집니다. 그래서 “짧게 생성, 빠르게 백업, 즉시 제거”가 가장 안전한 운영 패턴입니다.

  • [Linux] Wayland X11 비교: Linux 데스크톱 선택 가이드

    [Linux] Wayland X11 비교: Linux 데스크톱 선택 가이드

    Wayland X11 비교: Linux 데스크톱 선택 가이드와 성능 체크

    Wayland X11 비교는 요즘 Linux 데스크톱을 만지는 분들이 결국 한 번은 부딪히는 주제입니다. 같은 노트북, 같은 외부 모니터, 같은 브라우저를 써도 세션이 Wayland냐 X11이냐에 따라 느낌이 꽤 다르거든요. 어떤 날은 제스처와 스케일링이 아주 자연스럽고, 또 어떤 날은 화면 공유 하나 붙이자마자 워크플로가 흔들립니다. 겉으로는 둘 다 데스크톱이지만, 운영 관점에서 보면 장애 포인트가 분명히 다릅니다.

    핵심은 단순한 신구 대결이 아닙니다. Wayland는 최신 Linux 데스크톱의 일상 사용감과 보안 모델에서 강점이 크고, X11은 도구 생태계와 자동화 호환성에서 여전히 강합니다. 문제는 많은 비교 글이 여기서 끝난다는 점이죠. 실제 운영에서는 “무엇이 더 현대적인가”보다 “내가 자주 쓰는 작업이 어디서 덜 깨지는가”가 더 중요하더라고요. 이 글은 그 기준으로 보겠습니다. 명령어, 로그, 설정 파일, 실패 모드, 선택 기준까지 실무적으로 정리해보겠습니다.

    Wayland X11 비교를 위한 Linux 디스플레이 서버 아키텍처 개요 이미지

    Wayland compositor와 X.Org server 구조 차이를 한눈에 보여주는 개요 이미지입니다.

    1. Wayland X11 비교가 아직 중요한 이유

    요즘 배포판은 Wayland를 기본 세션으로 제공하는 경우가 많습니다. GNOME은 Wayland 중심으로 다듬어졌고, KDE Plasma도 Wayland 완성도가 많이 올라왔죠. 그래서 겉보기에는 이미 승부가 끝난 것처럼 보일 수 있습니다. 그런데 실제로는 그렇지 않습니다. 데스크톱은 브라우저 창만 띄우는 환경이 아니라, 화면 공유, 캡처, 원격 접속, GUI 자동화, 멀티 모니터, 혼합 배율, 게임 런처, 오버레이, 입력기까지 다 엮인 운영 체계이기 때문입니다.

    Wayland X11 비교에서 먼저 볼 건 FPS 하나가 아닙니다. 아래 네 가지가 더 중요합니다.

    • 입력과 표시의 일관성: 스크롤, 제스처, 프랙셔널 스케일링, 티어링 억제
    • 도구 호환성: 화면 녹화, 원격 제어, GUI 자동화, 컬러 피커, 오버레이
    • 장애 분리 난이도: 문제가 세션 구조인지, 드라이버인지, 포털인지 빨리 구분되는가
    • 보안 모델의 비용: 화면 접근을 막아 얻는 안전성과, 그 대가로 잃는 편의성

    실무에서는 이 네 가지가 계속 충돌합니다. 예를 들어 Wayland는 앱이 다른 앱의 화면이나 입력에 임의로 접근하기 어렵게 설계돼 있어 보안상 유리합니다. 대신 예전 X11 방식에 기대던 캡처 도구나 매크로 도구는 여기서 막히기 쉽습니다. 반대로 X11은 도구가 잘 붙지만, 앱 간 경계가 느슨해서 “되는 게 많다”는 장점이 그대로 관리 포인트가 되기도 합니다.

    2. Wayland와 X11 구조 차이: 어디서 문제가 생길까

    디스플레이 서버를 교과서식으로 길게 볼 필요는 없습니다. 운영에 필요한 만큼만 잡고 가면 됩니다. 핵심은 입력과 화면 합성, 그리고 앱의 화면 접근 권한을 누가 쥐고 있느냐입니다.

    Wayland 구조에서 생기는 특징

    Wayland에서는 compositor가 중심입니다. GNOME의 Mutter, KDE의 KWin이 대표적이죠. 앱은 compositor와 직접 프로토콜을 주고받고, 오래된 X11 앱은 대개 XWayland를 통해 실행됩니다. 이 구조 덕분에 최신 노트북에서 제스처, 프랙셔널 스케일링, 모니터별 배율 같은 부분은 Wayland가 더 자연스럽게 느껴지는 경우가 많습니다. 노트북과 4K 외부 모니터를 함께 쓰는 환경에서도 이 차이가 먼저 체감되는 편입니다.

    대신 비용도 분명합니다. Wayland는 “앱이 화면 전체를 마음대로 읽는다”는 오래된 전제를 기본값으로 허용하지 않습니다. 그래서 스크린샷, 화면 녹화, 원격 제어, 매크로 자동화가 포털(xdg-desktop-portal), PipeWire, compositor 구현에 더 의존합니다. 구조는 깔끔한데, 실제 운영에서는 확인해야 할 계층이 한 단계 바뀐 셈입니다.

    X11 구조에서 생기는 특징

    X11은 생태계가 넓고 역사가 길어서, 오래된 도구가 정말 많습니다. `xdotool`, 좌표 기반 클릭, 창 트리 조작, 구형 캡처 도구, 일부 원격 툴은 여전히 X11 전제를 깔고 있습니다. 이런 도구를 업무 핵심으로 쓰는 환경이라면 X11이 더 편할 때가 많습니다. GUI 회귀 테스트나 반복 입력 자동화가 필요한 장비에서 X11을 남겨두는 이유도 보통 여기 있습니다.

    문제는 구성 편차입니다. X.Org server, window manager, compositor, 드라이버 설정이 조합에 따라 달라져서 같은 “X11”이라도 결과가 제각각일 수 있습니다. 특히 멀티 모니터, 혼합 DPI, 티어링 제어는 배포판 기본값과 compositor 설정 영향을 많이 받습니다. 그래서 X11은 익숙하고 유연하지만, 운영자가 책임져야 할 면적이 넓습니다.

    판단 축 Wayland X11
    창 합성과 입력 처리 compositor가 더 직접 담당 X 서버와 WM/compositor 조합 편차가 큼
    앱 간 화면/입력 접근 제한이 강한 편 전통적으로 넓음
    혼합 배율·HiDPI 최신 DE에서 유리한 편 환경별 편차와 설정 부담이 큼
    스크립트 자동화 제약이 많음 기존 도구가 잘 붙음
    화면 공유·녹화 portal/PipeWire 상태에 좌우됨 기존 X11 API 도구와 친화적
    장애 분리 포털·compositor·드라이버를 같이 봐야 함 도구 호환성과 드라이버 분리가 비교적 직관적
    먼저 권하기 좋은 대상 일반 데스크톱, 노트북, 최신 GNOME/KDE 자동화, 레거시 툴, 특수 원격 환경
    Wayland X11 비교에서 세션 선택 과정을 보여주는 Linux 데스크톱 이미지

    로그인 화면에서 Wayland 세션과 X11 세션을 선택하는 흐름을 설명하는 이미지입니다.

    3. Wayland X11 비교 전 확인: 지금 세션이 무엇인지 보는 방법

    현장에서는 “분명 Wayland로 로그인한 줄 알았는데 아니었네”가 꽤 흔합니다. 로그인 화면의 문구만 믿지 말고, 실제 세션과 프로세스를 같이 보는 편이 안전합니다. 특히 원격 접속, GDM 설정, NVIDIA 드라이버 조합, 가상 환경에서는 생각보다 자주 엇나가거든요.

    1차 확인: 세션 타입과 기본 환경 변수

    echo "$XDG_SESSION_TYPE"
    loginctl show-session "$XDG_SESSION_ID" -p Type -p Class -p Name -p Remote -p State
    printf 'WAYLAND_DISPLAY=%s\nDISPLAY=%s\nXDG_CURRENT_DESKTOP=%s\n' \
      "$WAYLAND_DISPLAY" "$DISPLAY" "$XDG_CURRENT_DESKTOP"
    ls -l "$XDG_RUNTIME_DIR"/wayland-* /tmp/.X11-unix 2>/dev/null

    여기서는 이렇게 읽으면 됩니다.

    1. `XDG_SESSION_TYPE=wayland`면 Wayland 세션, `x11`이면 X11 세션입니다.
    2. `WAYLAND_DISPLAY`와 `DISPLAY`가 둘 다 잡혀 있어도 이상한 건 아닙니다. Wayland 세션 위에서 XWayland 앱을 함께 돌리면 흔한 상태입니다.
    3. `Remote=yes`라면 로컬 콘솔 세션과 동작이 다를 수 있습니다. 이 경우 입력 지연이나 화면 공유 결과를 같은 기준으로 비교하면 오판하기 쉽습니다.

    2차 확인: 실제 프로세스와 앱 백엔드

    ps -e -o pid,comm | grep -E 'Xorg|Xwayland|gnome-shell|kwin_wayland|mutter|sway'
    pgrep -a Xwayland
    loginctl session-status "$XDG_SESSION_ID"

    여기서 중요한 포인트가 하나 있습니다. `Xwayland`가 떠 있다고 해서 X11 세션이라는 뜻은 아닙니다. Wayland 세션에서 구형 X11 앱을 돌리기 위한 호환 레이어일 수 있습니다. 반대로 X11 세션에서는 `Xorg`가 중심으로 떠 있고, 앱 대부분이 `DISPLAY`를 통해 붙습니다.

    앱 단위로 백엔드를 강제로 바꿔보는 것도 문제 분리에 꽤 유용합니다. 예를 들어 GTK 앱이 Wayland 네이티브로 돌 때만 이상하다면, 같은 앱을 X11 백엔드로 띄워 차이를 볼 수 있습니다. 이거 실제로 해보면 원인 분리가 꽤 빨라집니다.

    GDK_BACKEND=x11 gedit
    GDK_BACKEND=wayland gedit
    QT_QPA_PLATFORM=xcb kate
    QT_QPA_PLATFORM=wayland kate

    세션 전체를 갈아엎지 않고도 “문제가 앱 백엔드인지, 세션 전체인지”를 빠르게 가를 수 있다는 점이 포인트입니다.

    4. Wayland X11 성능 비교: 숫자보다 패턴을 봐야 하는 이유

    Wayland X11 비교에서 가장 흔한 실수는 “게임 FPS가 비슷하니 차이 없다” 혹은 “스크롤이 부드러우니 Wayland가 무조건 낫다” 식으로 단정하는 겁니다. 데스크톱 성능은 단일 수치로 설명하기 어렵습니다. 실제로는 아래 다섯 가지를 같이 봐야 판단이 덜 흔들립니다.

    • 입력 지연: 클릭, 스크롤, 제스처가 즉시 반응하는가
    • 프레임 일관성: 창 이동, 워크스페이스 전환, 전체화면 전환에서 끊김이 반복되는가
    • 티어링과 깜빡임: 특히 외부 모니터, VRR, 혼합 주사율 환경에서 재현되는가
    • 스케일링 품질: 100%와 150% 모니터를 섞었을 때 글자와 커서가 어색하지 않은가
    • 도구 부하: 화면 공유, 녹화, 오버레이를 붙였을 때 compositor나 Xorg가 비정상적으로 치솟는가

    현장에서 느끼는 차이는 대체로 이렇습니다.

    • Wayland 장점: 최신 GNOME/KDE 기준으로 입력과 화면 합성의 일관성이 좋고, 혼합 DPI 환경에서 덜 거슬리는 경우가 많습니다.
    • X11 장점: 오래된 도구와 API가 잘 붙어서 우회 없이 바로 되는 일이 많습니다.
    • Wayland 주의점: 문제가 생기면 앱 자체보다 portal, PipeWire, compositor, 드라이버 층을 같이 봐야 해서 진단이 한 단계 더 필요합니다.
    • X11 주의점: 잘 되는 대신 설정 편차가 커서, 장비나 모니터 조합이 바뀌면 다른 종류의 스트레스를 받기 쉽습니다.

    로그와 리소스로 보는 기본 점검

    pids=$(pgrep -d',' -x gnome-shell -x kwin_wayland -x Xorg -x Xwayland)
    [ -n "$pids" ] && top -H -p "$pids"
    journalctl -b --no-pager | grep -Ei 'wayland|xorg|xwayland|mutter|kwin|drm|amdgpu|nvidia|nouveau|i915|intel'
    journalctl --user -b --no-pager | grep -Ei 'pipewire|portal|screencast|wireplumber'

    숫자를 볼 때도 해석 기준이 있어야 합니다.

    • 유휴 상태에서 compositor CPU가 계속 높으면 확장 기능, 화면 녹화, 오버레이, 브라우저 가속, 잘못된 VRR 조합을 먼저 의심합니다.
    • `drm`, `amdgpu`, `nvidia`, `i915` 관련 경고가 반복되면 세션 종류보다 드라이버 문제가 더 상위 원인일 수 있습니다.
    • 화면 공유를 켜는 순간부터 문제가 시작되면 Wayland 자체를 탓하기 전에 `xdg-desktop-portal`과 `PipeWire` 상태부터 보는 편이 정확합니다.
    • 문제가 창 전환이나 전체화면 진입 때만 터진다면 compositor 설정이나 주사율 협상 쪽일 가능성이 큽니다.

    Wayland가 빠르다, X11이 안정적이다 같은 문장은 반만 맞습니다. 실제로는 “어떤 작업에서, 어떤 드라이버와 compositor 조합으로, 어떤 도구를 붙였을 때 덜 깨지느냐”가 답에 더 가깝습니다.

    5. 실전 테스트 시나리오: 세션 교체 전에 꼭 해볼 체크리스트

    막연하게 감으로 고르지 말고, 같은 작업을 양쪽 세션에서 재현해보는 게 가장 정확합니다. 아래 순서는 장애 분리에도 잘 맞습니다.

    1. 로그인 화면에서 Wayland 세션과 X11 세션을 각각 선택합니다.
    2. 같은 외부 모니터, 같은 전원 상태, 같은 주사율, 같은 브라우저 탭 수로 맞춥니다.
    3. 창 여러 개 이동, 워크스페이스 전환, 전체화면 전환, 스크린샷, 화면 녹화, 원격 접속, 동영상 재생을 반복합니다.
    4. 세션별로 `journalctl -b`와 사용자 저널을 따로 저장해 비교합니다.
    5. 문제가 앱 전체인지 특정 앱인지 보려고 같은 앱을 Wayland/X11 백엔드로 각각 띄워봅니다.

    이때 많이 놓치는 게 하나 있습니다. 같은 앱이라도 내부 백엔드가 다를 수 있다는 점입니다. 브라우저 화면 공유는 portal 경로를 타고, 구형 녹화 도구는 X11 API를 기대하고, Electron 앱은 버전과 패키징에 따라 Wayland 처리 방식이 다를 수 있습니다. 그래서 “브라우저는 괜찮은데 녹화 도구만 안 된다” 같은 결과가 생깁니다.

    테스트 결과를 남길 때는 감상보다 조건을 적어두는 편이 훨씬 좋습니다. 예를 들어 “Wayland가 더 부드러움”보다 “Wayland 세션 + 외부 4K 150% + 브라우저 화면 공유 켠 상태에서는 정상, X11 세션에서는 전체화면 전환 시 깜빡임 재현”처럼 기록해야 다음 장애 대응이 빨라집니다. 이런 식으로 써두면 나중에 진짜 큰 도움이 됩니다.

    Wayland X11 비교를 위해 세션 타입과 로그를 점검하는 Linux 터미널 이미지

    세션 타입 확인과 로그 점검 명령어를 실행하는 실제 운영 흐름을 보여주는 이미지입니다.

    6. 트러블슈팅: Wayland와 X11에서 자주 만나는 문제

    이 섹션은 “왜 안 되지?”에서 끝나지 않도록 원인 층위를 같이 적겠습니다. 같은 증상이어도 뿌리가 다르면 해법도 달라집니다.

    문제 1. Wayland에서 스크린샷·화면 녹화·화면 공유가 들쭉날쭉하다

    이건 대개 Wayland의 보안 모델과 portal 경로 이해가 부족해서 생깁니다. Wayland에서는 앱이 화면 전체를 직접 읽는 방식이 기본값이 아닙니다. 대신 데스크톱 포털과 PipeWire가 중간에서 권한과 스트림을 다룹니다. 예전처럼 앱이 바로 가져가는 구조가 아니라는 점이 핵심입니다.

    systemctl --user status pipewire wireplumber xdg-desktop-portal \
      xdg-desktop-portal-gnome xdg-desktop-portal-kde
    journalctl --user -b -u pipewire -u wireplumber -u xdg-desktop-portal --no-pager
    busctl --user tree org.freedesktop.portal.Desktop 2>/dev/null | head

    여기서 보는 기준은 단순합니다.

    • 포털 서비스가 아예 안 떠 있으면 화면 공유가 불안정하거나 시작조차 안 될 수 있습니다.
    • GNOME 환경인데 KDE용 포털만 살아 있거나, 반대로 KDE 환경에 GNOME 포털만 섞여 있으면 선택 창이 이상하거나 캡처 경로가 꼬일 수 있습니다.
    • 문제가 특정 앱에서만 나면 앱이 portal 경로를 제대로 쓰는지, 패키지 형태(Flatpak, Snap, distro package) 차이도 같이 봐야 합니다.

    이 경우 Wayland 자체를 포기하기보다 먼저 portal 계층부터 바로잡는 편이 낫습니다. 원인이 세션이 아니라 사용자 세션 서비스인 경우가 생각보다 많거든요.

    문제 2. X11에서는 되던 `xdotool`·매크로·좌표 클릭 자동화가 Wayland에서 안 된다

    이건 버그라기보다 설계 차이에 가깝습니다. X11은 앱 간 입력 주입과 창 조작이 넓게 허용되어 왔고, Wayland는 그 가정을 기본값으로 인정하지 않습니다. 그래서 GUI 자동화가 핵심인 장비라면 Wayland에 억지로 맞추는 것보다 X11 세션을 남겨두는 편이 현실적입니다.

    이 상황에서는 기준을 명확히 잡는 게 좋습니다.

    • 사무용 개인 데스크톱: Wayland 우선
    • 테스트 자동화용 데스크톱: X11 유지
    • 업무 앱이 하나라도 X11 전용 도구에 깊게 묶여 있음: X11 우선 검토

    “최신이니까 옮긴다”는 판단이 제일 비쌀 때가 많습니다. 바꾸고 나서 자동화 파이프라인이 흔들리면 되돌리는 비용이 더 크거든요.

    문제 3. 멀티 모니터에서 배율, 커서, 전체화면 전환이 어색하다

    이 문제는 단순히 Wayland가 낫다, X11이 낫다로 자르기 어렵습니다. 다만 혼합 DPI 환경에서는 Wayland가 더 덜 거슬리는 편이라는 평가가 많습니다. 반대로 X11은 모니터별 스케일링이 깔끔하지 않거나, 특정 조합에서 티어링 제어가 설정 의존적인 경우가 있습니다.

    체크할 때는 세션 종류만 보지 말고, 도구도 구분해서 보셔야 합니다. 아래 명령은 환경에 따라 일부만 설치돼 있을 수 있습니다.

    xrandr --listmonitors 2>/dev/null
    xrandr --query 2>/dev/null
    wlr-randr 2>/dev/null
    kscreen-doctor -o 2>/dev/null

    `xrandr`는 X11 쪽 확인에는 유용하지만 Wayland 전체를 대표하지는 않습니다. Wayland에서는 compositor별 도구가 갈립니다. 그래서 GNOME, KDE, wlroots 계열이 같은 방식으로 보이지 않는다고 해서 이상한 건 아닙니다. 여기서 많이 헷갈립니다.

    문제 4. NVIDIA 환경에서 로그인 세션이 기대와 다르게 올라온다

    이건 배포판, 디스플레이 매니저, 드라이버 조합 영향을 많이 받습니다. 여기서 중요한 건 소문이 아니라 실제 세션 타입입니다. 로그인 화면 메뉴에 Wayland처럼 보여도 실제로는 X11로 오른 경우가 있고, 반대도 있습니다. 그래서 반드시 로그인 후 `echo $XDG_SESSION_TYPE`와 `loginctl show-session`으로 확인하는 편이 안전합니다.

    GNOME 계열에서 GDM이 Wayland를 비활성화했는지 점검할 때는 보통 아래 파일을 봅니다. 다만 배포판에 따라 경로와 기본값은 조금 다를 수 있습니다.

    grep -nE '^[# ]*WaylandEnable=' /etc/gdm/custom.conf 2>/dev/null
    grep -nE '^[# ]*DefaultSession=' /etc/gdm/custom.conf 2>/dev/null

    `WaylandEnable=false`가 있으면 GDM에서 Wayland가 비활성화돼 Xorg 세션으로 기울 수 있습니다. 물론 이 파일 하나로 모든 경우가 설명되지는 않지만, “왜 계속 X11로만 뜨지?” 할 때 가장 먼저 볼 만한 지점입니다.

    문제 5. 원격 제어는 되는데 화면 공유만 불안정하거나, 반대로 공유는 되는데 입력 전달이 이상하다

    이 경우 세션보다 원격 도구가 무엇을 전제로 설계됐는지를 먼저 보는 편이 맞습니다. X11 전용 방식에 기대는 도구는 Wayland에서 입력 주입이 제한될 수 있고, Wayland 친화적인 도구는 portal/PipeWire가 정상일 때 훨씬 깔끔하게 동작하기도 합니다. 원격 업무 비중이 높다면, 세션 선택 전에 쓰는 도구 목록부터 적어두고 지원 범위를 확인하는 게 훨씬 현실적입니다.

    7. 검증과 결과 확인: 무엇이 잘 된 상태인가

    세션을 바꾸고 “대충 되는 것 같네요”로 끝내면 다시 같은 장애를 만납니다. 아래 기준을 통과해야 정상으로 보는 편이 안전합니다.

    • 세션 타입이 의도한 값으로 일관되게 올라온다.
    • 브라우저 화면 공유, 스크린샷, 녹화가 같은 절차로 반복 재현된다.
    • 창 이동, 전체화면 전환, 외부 모니터 hotplug 시 깜빡임이나 멈춤이 반복되지 않는다.
    • `journalctl -b`와 `journalctl –user -b`에 같은 그래픽/portal 오류가 누적되지 않는다.
    • X11 전용 앱은 XWayland 또는 X11 세션에서 예측 가능한 방식으로 동작한다.

    운영 검증용으로는 이런 식의 미니 점검 스크립트도 자주 씁니다. 새 장비나 배포판을 올린 뒤 결과를 남기기 좋아요.

    echo "session=$XDG_SESSION_TYPE desktop=$XDG_CURRENT_DESKTOP"
    loginctl show-session "$XDG_SESSION_ID" -p Type -p Remote -p State
    ps -e -o comm | grep -E 'Xorg|Xwayland|gnome-shell|kwin_wayland' || true
    systemctl --user --no-pager --full status pipewire xdg-desktop-portal | sed -n '1,20p'
    journalctl --user -b --no-pager | grep -Ei 'portal|pipewire|screencast' | tail -n 20

    실제로 써보면 Wayland는 “평소 데스크톱 사용감”이 좋고, X11은 “도구가 예상대로 움직이는 범위”가 넓습니다. 어느 쪽이든 로그가 조용하고, 자주 쓰는 워크플로가 재현 가능하게 유지되는 쪽이 오래 갑니다.

    Wayland X11 비교 결과와 검증 항목을 요약한 Linux 데스크톱 대시보드 이미지

    세션 종류, 로그 상태, 화면 공유, 멀티 모니터 안정성을 검증하는 결과 요약 이미지입니다.

    8. 선택 가이드: 이런 경우엔 Wayland, 이런 경우엔 X11

    여기서는 애매하게 말하지 않겠습니다. 실제 선택 기준이 있어야 운영이 편합니다. 아래 표는 Linux 데스크톱을 세팅할 때 바로 써먹기 좋은 판단표입니다.

    사용 시나리오 추천 이유 같이 점검할 것
    노트북, 터치패드 제스처, HiDPI, 최신 GNOME/KDE Wayland 일상 사용감과 혼합 배율 대응이 좋음 `XDG_SESSION_TYPE`, 외부 모니터 연결, portal 기반 화면 공유
    GUI 자동화, `xdotool`, 좌표 클릭 매크로, 구형 테스트 도구 X11 기존 자동화 생태계와 충돌이 적음 Xorg 세션 고정 여부, 도구별 창 제어 재현성
    일반 개발용 워크스테이션 Wayland 우선 브라우저, IDE, 터미널 중심이면 장점이 큼 화면 공유, 녹화, Electron/GTK/Qt 앱 혼용 상태
    특정 원격 제어 솔루션이 업무 핵심 X11 우선 검토 입력 전달과 캡처 방식 제약이 적은 경우가 많음 원격 도구의 Wayland 지원 범위, 세션 자동 전환 여부
    NVIDIA 조합에서 세션이 자꾸 바뀌거나 깜빡임이 있음 교차 테스트 후 결정 세션보다 드라이버/DM 조합 영향이 클 수 있음 GDM 설정, 실제 세션 타입, 커널/사용자 저널 경고
    문제 원인 분리가 안 되는 초기 장애 대응 Wayland와 X11 둘 다 유지 세션 구조 문제와 드라이버 문제를 분리하기 쉬움 동일 작업 재현, 로그 차이, 앱 백엔드 강제 실행

    정리하면 선택 기준은 꽤 분명합니다.

    • 개인용 Linux 노트북이나 일반 데스크톱이면 Wayland부터 시작하는 편이 맞습니다.
    • 업무 핵심이 GUI 자동화, 특수 원격 도구, 오래된 캡처 툴이라면 X11을 기본값으로 두는 편이 덜 피곤합니다.
    • 둘 중 하나로 못 박기 어렵다면, 로그인 화면에서 두 세션을 모두 선택 가능하게 남겨두는 게 좋습니다. 장애 대응 속도가 꽤 달라집니다.

    관련 글로 Linux 디스플레이 트러블슈팅 가이드도 함께 보시면 원인 분리가 훨씬 빨라집니다.

    사용자 유형별로 Wayland와 X11 중 어떤 선택이 더 맞는지 요약한 이미지입니다.

    9. 마무리: 운영 리스크 기준으로 고르면 덜 흔들립니다

    데스크톱도 결국 “취향”보다 “재현 가능한 안정성”으로 봐야 합니다. 새 기술이라고 무조건 옮기면 다른 층위의 장애를 떠안을 수 있고, 익숙한 방식만 고집하면 현대 하드웨어의 장점을 놓칠 수도 있습니다. Wayland와 X11도 정확히 그 구도입니다.

    일반적인 Linux 데스크톱, 노트북, 멀티 모니터, 최신 GNOME/KDE라면 Wayland를 먼저 써보는 편이 좋습니다. 입력과 스케일링, 일상 사용감, 보안 모델까지 종합하면 장점이 분명하거든요. 대신 화면 녹화, 원격 제어, GUI 자동화, 구형 업무 앱이 핵심이라면 X11을 유지하는 편이 더 합리적입니다. 이건 보수적인 선택이라기보다, 장애 비용을 줄이는 선택에 가깝습니다.

    결국 기준은 단순합니다. 평소 작업이 더 부드러운 쪽이 아니라, 자주 쓰는 워크플로가 덜 깨지는 쪽을 고르면 됩니다. Wayland X11 비교에서 이 기준만 놓치지 않으면, 괜한 종교전 없이 자기 환경에 맞는 답을 찾기 쉬워집니다.

    FAQ

    Wayland가 무조건 더 빠른가요?

    아닙니다. 최신 데스크톱에서 입력과 스케일링 체감이 좋은 경우가 많지만, 실제 운영 만족도는 compositor, 드라이버, portal, 앱 호환성에 따라 달라집니다.

    X11은 이제 완전히 버려도 되는 기술인가요?

    그렇게 보긴 어렵습니다. 레거시 도구, GUI 자동화, 특정 원격 제어, 오래된 업무 앱에서는 여전히 실용적입니다. 업무가 거기에 걸려 있다면 X11이 더 좋은 선택일 수 있습니다.

    무엇부터 점검하면 가장 덜 헤맬까요?

    `echo $XDG_SESSION_TYPE`, `loginctl show-session`, 프로세스 확인, portal/PipeWire 상태, 같은 작업의 교차 재현 테스트 순서로 보면 됩니다. 이 순서가 세션 문제와 드라이버 문제를 분리하는 데 꽤 유용합니다.

  • [AI] Ollama 커스텀 LLM 가이드: Modelfile 사용법과 실전 구축

    [AI] Ollama 커스텀 LLM 가이드: Modelfile 사용법과 실전 구축

    Ollama 커스텀 LLM 가이드: Modelfile 사용법과 실전 구축

    Ollama 커스텀 LLM을 오래 만지다 보면, 결국 성능 숫자보다 출력 습관을 통제하고 싶다는 요구가 더 자주 생기더라고요. 답변은 꼭 한국어로 나오게 하고 싶다든지, 명령어를 먼저 보여주고 설명은 뒤로 보내고 싶다든지, 모르면 지어내지 말고 확인이 필요하다고 말하게 만들고 싶을 때가 그렇습니다. 이럴 때 가장 먼저 손대기 좋은 게 Modelfile입니다.

    다만 기대치는 정확히 잡아야 합니다. Modelfile은 보통 사람들이 떠올리는 “모델 재학습” 자체와는 다릅니다. 실무 감각으로 말하면, 베이스 모델의 가중치를 새로 만드는 도구라기보다 실행 규약을 모델 단위로 고정하는 계층에 가깝습니다. 이 차이를 이해해 두면 왜 어떤 문제는 SYSTEM 한 줄로 풀리고, 어떤 문제는 ADAPTER나 RAG까지 가야 하는지 훨씬 빨리 감이 옵니다.

    이번 글은 로컬 AI 모델 개발 관점에서, Ollama의 Modelfile로 어디까지 바꿀 수 있는지, 어디서 멈추는 게 맞는지, 그리고 실제로 자주 깨지는 지점이 무엇인지까지 한 번에 정리한 글입니다. 문법과 API 필드는 Ollama 공식 Modelfile 문서, Create API 문서, Generate API 문서 기준으로 다시 확인했습니다.

    로컬 환경에서 베이스 모델 위에 Modelfile을 얹어 커스텀 모델을 만드는 전체 흐름입니다.

    1. Ollama 커스텀 LLM이 실무에서 먹히는 이유는 재현성 때문입니다

    제가 Modelfile을 계속 쓰는 이유는 단순합니다. 사람은 매번 같은 프롬프트를 정교하게 붙이기 어렵지만, 파일은 늘 같은 방식으로 동작하거든요. 특히 팀 단위로 로컬 모델을 쓰기 시작하면 모델 품질보다 먼저 문제 되는 게 사람마다 다른 사용 습관입니다. 어떤 분은 시스템 프롬프트를 길게 넣고, 어떤 분은 짧게 넣고, 어떤 분은 아예 안 넣습니다.

    이때 Modelfile은 계약서를 하나 만들어 줍니다. SYSTEM, TEMPLATE, PARAMETER, MESSAGE, ADAPTER를 모델 이름 뒤에 고정해 두니, 같은 이름을 호출하는 한 최소한의 행동 일관성은 확보됩니다. 이거 진짜 편하더라고요. 문서 초안 작성, 운영 가이드 보조, 코드 리뷰 보조처럼 반복성이 높은 작업일수록 차이가 분명합니다.

    • 반복 프롬프트 제거: 매번 붙이던 역할 설명과 금지 규칙을 모델 단위로 고정합니다.
    • 출력 형식 표준화: “명령어 먼저, 설명 나중” 같은 우선순위를 팀 공통 규약으로 만들기 좋습니다.
    • 실험 버전 분리: 같은 베이스 모델에서 보수형, 설명형, 요약형 모델을 나눠 비교하기 쉽습니다.
    • 장애 원인 분리: 모델 자체 문제인지, 템플릿 문제인지, 파라미터 문제인지 추적이 빨라집니다.

    2. Ollama 커스텀 LLM에서 먼저 선을 그어야 합니다: Modelfile이 바꾸는 것과 못 바꾸는 것

    여기서 선을 분명히 그어야 삽질이 줄어듭니다. Modelfile은 행동 규칙, 입력 형식, 실행 파라미터, 예시 대화, 어댑터 연결은 잘 다룹니다. 반면 모델 내부 지식 자체를 새로 학습시키는 일은 기본적으로 하지 못합니다. 문서 몇 줄 넣었다고 도메인 지식을 완전히 새로 습득하는 구조는 아니라는 뜻입니다.

    실제로는 이렇게 나누면 편합니다. “답변의 순서나 톤이 문제냐”면 Modelfile 쪽입니다. “모델이 특정 도메인 지식을 계속 모르거나 아는 척 틀리느냐”면 ADAPTER, RAG, 또는 더 맞는 베이스 모델을 검토하는 편이 맞습니다.

    문제 유형 우선 선택 이유 피해야 할 접근
    답변 톤이 들쭉날쭉함 SYSTEM + MESSAGE 행동 규칙과 말투는 프롬프트 계층에서 통제가 잘 됩니다 바로 ADAPTER부터 붙이기
    출력 형식이 자꾸 깨짐 TEMPLATE + stop 재검토 채팅 포맷 불일치일 가능성이 큽니다 모델 성능 탓으로 돌리기
    응답이 너무 산만하거나 창의성이 과함 PARAMETER 조정 temperature, top_p, num_ctx 영향이 큽니다 SYSTEM 문구만 계속 길게 늘리기
    특정 도메인 지식을 안정적으로 못 씀 ADAPTER 또는 RAG 검토 행동 지시만으로는 한계가 분명합니다 MESSAGE 예시를 과도하게 누적하기
    팀원마다 결과가 다름 Modelfile로 모델 이름 표준화 입력 규약 자체를 버전 관리할 수 있습니다 사람마다 프롬프트 템플릿 수동 복붙

    3. Modelfile 핵심 지시어는 많아 보여도 실무 우선순위는 분명합니다

    공식 문서에 나오는 지시어는 여러 개지만, 처음 모델을 잡을 때 자주 쓰는 순서는 거의 고정입니다. 보통 FROM → SYSTEM → PARAMETER → MESSAGE가 1차 세트입니다. TEMPLATE와 ADAPTER는 필요성이 분명할 때만 올리는 편이 안전합니다.

    지시어 실무 우선순위 주로 쓰는 상황 주의점
    FROM 최상 출발 베이스 모델 지정 이 선택이 나머지 품질과 호환성의 기준점이 됩니다
    SYSTEM 최상 역할, 금지사항, 출력 순서 고정 길이보다 우선순위가 중요합니다
    PARAMETER 높음 일관성, 컨텍스트, 중단 토큰 제어 값 하나로 안정성이 흔들릴 수 있습니다
    MESSAGE 높음 few-shot 예시 내장 예시를 많이 넣을수록 답변이 경직되기 쉽습니다
    TEMPLATE 중간 채팅 포맷을 명시적으로 제어할 때 베이스 템플릿과 stop이 어긋나면 토큰 누수가 납니다
    ADAPTER 상황 의존 LoRA 또는 QLoRA 결과물 적용 베이스 모델 불일치가 나면 품질이 급격히 무너집니다
    LICENSE 공유 시 중요 배포 또는 팀 공유 내부 사용과 외부 배포 조건을 분리해서 봐야 합니다
    REQUIRES 환경 의존 특정 Ollama 최소 버전 고정 버전 미충족이면 원인 모를 생성 실패처럼 보일 수 있습니다

    효율이 좋은 규칙은 하나입니다. 첫 번째 성공 모델은 단순하게 만들고, 두 번째 버전부터 정교화하는 쪽이 낫습니다. 처음부터 요소를 다 얹으면 실패했을 때 어느 레이어가 원인인지 식별하기가 어렵습니다.

    4. 실전 구현 1단계: 베이스 모델의 원본 템플릿부터 확인하세요

    많이 놓치는 단계인데, 사실상 필수에 가깝습니다. 커스텀 모델을 만들기 전에 ollama show --modelfile로 베이스 모델의 기본 구조를 먼저 봐야 합니다. 이유는 간단합니다. 모델마다 기대하는 채팅 템플릿과 stop 토큰이 다를 수 있기 때문입니다.

    ollama pull llama3.2
    ollama show --modelfile llama3.2
    ollama show --modelfile llama3.2 > base-llama3.2.Modelfile

    저는 보통 세 번째 줄까지 같이 해둡니다. 원본을 파일로 떠놓으면 수정 중에 기준점을 잃지 않거든요. 특히 stop 토큰과 TEMPLATE 블록은 기억으로 다시 쓰기보다 원본에서 필요한 만큼만 복사하는 쪽이 훨씬 안전합니다.

    그다음 1차 버전은 작게 갑니다. 출력 우선순위, 언어 정책, 추측 금지 같은 운영 규약만 넣어도 충분한 경우가 많습니다.

    mkdir -p ~/ollama-custom/workshop
    cd ~/ollama-custom/workshop
    cat > Modelfile <<'EOF'
    FROM llama3.2
    PARAMETER temperature 0.2
    PARAMETER num_ctx 4096
    SYSTEM """당신은 한국어로 답변하는 인프라 엔지니어 보조 AI입니다.
    답변은 항상 다음 순서를 지킵니다.
    1. 바로 실행할 명령어나 설정 예시
    2. 명령어가 실패할 때 확인할 지점
    3. 마지막에 짧은 설명
    모르는 내용은 추측하지 말고 확인이 필요하다고 말합니다."""
    MESSAGE user 리눅스 디스크 사용량 확인 방법 알려줘
    MESSAGE assistant 먼저 아래 명령어부터 확인하겠습니다.
    df -h
    sudo du -sh /var/* 2>/dev/null | sort -h
    lsblk
    EOF
    
    ollama create infra-assistant -f Modelfile
    ollama run infra-assistant

    포인트는 SYSTEM을 길게 쓰는 게 아니라 답변 순서와 금지 규칙을 관찰 가능한 형태로 명시하는 것입니다. “친절하게 답변해라” 같은 추상 문구보다 “명령어 먼저, 설명은 나중” 같은 규칙이 훨씬 잘 먹는 편입니다.

    실제 작업 흐름은 터미널에서 Modelfile을 편집하고 곧바로 create, run으로 검증하는 식으로 진행됩니다.

    5. PARAMETER는 품질보다도 행동 습관을 바꾸는 손잡이입니다

    파라미터를 만질 때 흔히 정답률만 보게 되는데, 실무에선 그보다 출력의 흔들림이 줄어드는가를 먼저 보는 게 맞습니다. 로컬 모델을 업무 보조로 쓸 때는 늘 비슷한 형식으로 나오는 답이 훨씬 값질 때가 많습니다. 이 부분은 실제 운영해 보면 체감이 꽤 큽니다.

    자주 보는 건 temperature, num_ctx, stop, 그리고 필요할 때 샘플링 계열 파라미터입니다. 여기서 특히 실수하기 쉬운 건 num_ctx입니다. 컨텍스트를 크게 잡으면 좋아 보이지만 메모리 사용량과 프롬프트 평가 시간도 같이 늘어납니다.

    파라미터 낮게 둘 때 높게 둘 때 판단 기준
    temperature 일관성 높음, 답변이 보수적 표현 다양성 증가, 흔들림도 증가 운영 문서/명령어 보조면 낮게, 아이디어 발산이면 높게
    num_ctx 메모리 부담 감소, 빠름 긴 문맥 유지에 유리, 느려질 수 있음 평소 넣는 입력 길이를 기준으로 최소 충분치만 확보
    stop 잘 맞으면 출력 경계가 깔끔함 불필요하거나 틀리면 출력이 잘리거나 토큰 누수 베이스 모델 원본 값에서 출발
    MESSAGE 예시 수 유연성 유지 답변 스타일 고정력 상승, 과하면 경직 대개 1~3개로도 충분

    실무적으로는 이렇게 나누면 편합니다.

    • 문서 초안, 운영 절차, 명령어 추천: temperature를 낮게 두고 형식 일관성을 우선합니다.
    • 브레인스토밍, 문안 변주: temperature를 조금 올리되, SYSTEM에서 출력 구조는 유지합니다.
    • 긴 로그/설정 해석: num_ctx를 무작정 키우기보다 먼저 입력을 잘라 넣을 수 있는지 확인합니다.

    6. TEMPLATE는 마지막에 건드리세요. 건드릴 땐 stop까지 세트로 보셔야 합니다

    TEMPLATE는 멋있어 보이지만 실제로는 가장 쉽게 사고 나는 영역입니다. 추천하는 원칙은 단순합니다. 기본 템플릿으로 원하는 형식이 나오면 TEMPLATE는 건드리지 않는 것입니다. SYSTEM과 MESSAGE만으로 해결되는 경우가 훨씬 많습니다.

    반대로 TEMPLATE를 건드려야 하는 경우도 있습니다. 베이스 모델의 채팅 포맷을 명시적으로 유지하면서 시스템, 사용자, 응답 경계를 통제하고 싶을 때입니다. 이때 핵심은 예쁘게 다시 쓰는 게 아니라 원본 구조를 최대한 보존하면서 필요한 부분만 조정하는 겁니다.

    FROM llama3.2
    TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
    
    {{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
    
    {{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
    
    {{ .Response }}<|eot_id|>"""
    PARAMETER stop "<|start_header_id|>"
    PARAMETER stop "<|end_header_id|>"
    PARAMETER stop "<|eot_id|>"
    SYSTEM """당신은 한국어 운영 문서를 절차형으로 정리하는 도우미입니다."""

    이 블록에서 꼭 봐야 할 건 둘입니다.

    • {{ .System }}, {{ .Prompt }}, {{ .Response }}가 실제 템플릿에 반영되는지
    • TEMPLATE에 등장하는 특수 토큰과 PARAMETER stop이 서로 맞물리는지

    실패 사례도 꽤 단순합니다. 템플릿은 바꿨는데 stop은 예전 값을 그대로 두는 경우입니다. 그러면 응답 본문에 <|eot_id|> 같은 문자열이 그대로 보이거나, 반대로 답변이 중간에서 잘립니다. 이건 모델이 멍청해서가 아니라 출력 종료 경계가 틀어진 것입니다.

    7. ADAPTER는 지식 보강보다 정렬된 편향 추가에 가깝게 보는 편이 맞습니다

    ADAPTER를 붙이면 개인화 폭이 넓어지긴 합니다. 다만 기대를 너무 크게 잡으면 실망도 큽니다. 먼저 물어볼 건 하나입니다. “이 문제를 SYSTEM과 MESSAGE로 해결할 수 없는가?” 여기서 해결되면 굳이 어댑터를 안 붙이는 편이 운영은 더 쉽습니다.

    어댑터가 필요한 상황은 보통 두 가지입니다. 첫째, 특정 도메인 문체나 응답 습관을 훨씬 강하게 고정해야 할 때입니다. 둘째, 베이스 모델만으로는 일관되게 안 나오는 패턴을 추가 학습 결과로 밀어 넣고 싶을 때입니다.

    FROM llama3.2
    ADAPTER ./adapters/my-lora-adapter.gguf
    SYSTEM """당신은 Kubernetes 운영 가이드를 작성하는 보조 모델입니다.
    항상 점검 순서, 명령어, 장애 추정 원인을 순서대로 제시합니다."""

    여기서 제일 중요한 건 어댑터가 학습된 베이스 모델과 FROM의 베이스 모델이 맞아야 한다는 점입니다. 공식 문서도 이 경우 결과가 erratic해질 수 있다고 안내합니다. 현장에서는 이걸 모델 성능 탓으로 오해하는 경우가 적지 않습니다.

    그래서 저는 순서를 이렇게 잡습니다.

    1. ADAPTER 없이 동작하는 1차 모델을 만듭니다.
    2. 동일 질문 세트로 결과를 저장합니다.
    3. 그다음 ADAPTER를 붙인 버전을 따로 만듭니다.
    4. 스타일 개선인지, 지식 개선인지, 아니면 품질 붕괴인지 비교합니다.
    Ollama 커스텀 LLM에 LoRA 어댑터를 적용하는 구조 이미지

    베이스 모델 위에 LoRA 어댑터를 추가해 도메인 특화 성격을 강화하는 흐름입니다.

    8. API 자동화는 실험이 많아질수록 가치가 커집니다

    한두 개 만들 땐 Modelfile이 읽기 편합니다. 그런데 역할별 모델이 늘어나면 CLI 수작업은 금방 귀찮아집니다. 이때는 POST /api/create를 써서 생성 과정을 코드로 고정하는 편이 낫습니다. 모델 이름 규칙, 파라미터 조합, 예시 대화 구성을 반복해서 재현해야 할 때 특히 강합니다.

    curl http://localhost:11434/api/create -d '{
      "model": "infra-helper-api",
      "from": "llama3.2",
      "system": "You are a Korean infrastructure engineering assistant. Provide commands first, then failure checks, then short explanations.",
      "parameters": {
        "temperature": 0.2,
        "num_ctx": 4096
      },
      "messages": [
        {
          "role": "user",
          "content": "nginx 로그 확인 순서 알려줘"
        },
        {
          "role": "assistant",
          "content": "먼저 access.log와 error.log를 분리해서 보고, 그다음 4xx/5xx 비율과 upstream 오류를 확인하겠습니다."
        }
      ]
    }'

    이 방식을 선호하는 이유는 단순히 편해서만은 아닙니다. 실험 조건을 텍스트로 남길 수 있기 때문입니다. 어떤 모델 이름이 어떤 규칙과 파라미터로 만들어졌는지가 남아야 나중에 비교가 됩니다.

    생성 후에는 성능 체감만 보지 말고 API 응답 지표도 같이 보는 편이 좋습니다. Ollama의 생성 응답에는 load_duration, prompt_eval_count, prompt_eval_duration, eval_count, eval_duration 같은 필드가 포함됩니다. 이걸 보면 느린 이유가 로딩인지, 입력 길이인지, 출력 토큰 수인지 분리해서 보기 훨씬 편합니다.

    curl http://localhost:11434/api/generate -d '{
      "model": "infra-assistant",
      "prompt": "systemd 서비스 상태 확인 절차를 알려줘",
      "stream": false
    }'

    여기서 판단 포인트는 이렇습니다.

    • load_duration이 크면 모델 로딩 비용이 큰 겁니다.
    • prompt_eval_duration이 크면 입력 문맥이 길거나 num_ctx 운용이 과한 경우를 의심할 수 있습니다.
    • eval_duration이 크면 출력 토큰 수가 많거나 실행 자원이 부족한 쪽일 가능성이 큽니다.

    9. 트러블슈팅은 증상보다 근본 원인을 붙잡아야 빨리 끝납니다

    실제로 많이 부딪히는 문제는 대부분 네 부류였습니다. 중요한 건 증상을 외우는 게 아니라 어느 레이어가 깨졌는지를 먼저 가르는 겁니다. SYSTEM 문제인지, TEMPLATE 문제인지, ADAPTER 문제인지, 자원 문제인지 구분만 잘해도 시간이 꽤 절약됩니다.

    1) 시스템 프롬프트가 먹지 않는 것처럼 보일 때

    • 먼저 볼 것: ollama show --modelfile 모델명
    • 근본 원인: TEMPLATE 안에 {{ .System }} 반영이 없거나 베이스 템플릿을 수정하면서 시스템 경계를 깨뜨린 경우가 많습니다.
    • 판단 기준: 말투보다도 “추측 금지”, “명령어 우선” 같은 규칙이 반복적으로 무시되는지 봅니다.
    • 해결 방향: 원본 TEMPLATE로 되돌린 뒤 SYSTEM만 남기고 다시 비교합니다.

    2) 답변에 특수 토큰이 섞일 때

    • 먼저 볼 것: TEMPLATE와 PARAMETER stop 조합
    • 근본 원인: 출력 종료 토큰과 템플릿 토큰 경계가 맞지 않는 상황입니다.
    • 판단 기준: <|eot_id|>, <|start_header_id|> 같은 문자열이 본문에 보이면 거의 이쪽입니다.
    • 해결 방향: 베이스 모델 기본 Modelfile의 stop 설정을 그대로 기준 삼아 다시 맞춥니다.

    3) 어댑터 적용 후 품질이 갑자기 무너질 때

    • 먼저 볼 것: ADAPTER 학습 기준 모델
    • 근본 원인: FROM에 적은 베이스 모델과 어댑터가 기대하는 기반 모델 불일치
    • 판단 기준: 문장 붕괴, 주제 일탈, 불필요한 반복이 적용 직후 심해집니다.
    • 해결 방향: 어댑터 제작 시 사용한 기반 계열과 동일한 모델로 FROM을 맞춥니다.

    4) 속도가 너무 느리거나 메모리가 버거울 때

    • 먼저 볼 것: ollama ps, OS 메모리/스왑 사용량, 입력 길이, num_ctx
    • 근본 원인: 모델 크기보다도 과도한 컨텍스트, 동시 실행 모델 수, 자원 부족이 겹치는 경우가 많습니다.
    • 판단 기준: 스왑이 발생하면 체감 성능은 급격히 떨어집니다.
    • 해결 방향: 작은 베이스 모델로 내리거나 num_ctx를 낮추고 실험 모델 동시 구동 수를 줄입니다.

    저는 장애를 볼 때 항상 이렇게 나눕니다. 형식 이상은 TEMPLATE, 성격 이상은 SYSTEM/MESSAGE, 지식 이상은 ADAPTER 또는 베이스 모델, 속도 이상은 자원과 컨텍스트. 이 프레임으로 보면 원인 추적 속도가 빨라집니다.

    Ollama 커스텀 LLM 트러블슈팅과 검증 과정을 보여주는 이미지

    응답 토큰 이상 출력, 시스템 프롬프트 무시, 어댑터 불일치 같은 대표 장애 포인트를 점검하는 장면입니다.

    10. 검증은 더 똑똑해졌는가보다 의도대로 수렴하는가를 보셔야 합니다

    커스텀 모델을 만들고 나면 자꾸 성능 향상만 보게 됩니다. 그런데 Modelfile의 1차 목적은 대개 정답률 폭증이 아니라 출력 습관 고정입니다. 그래서 검증 질문 세트를 별도로 두고 베이스 모델과 커스텀 모델을 같은 질문으로 비교하는 편이 좋습니다.

    ollama run llama3.2 "systemd 서비스 상태 확인 절차를 알려줘"
    ollama run infra-assistant "systemd 서비스 상태 확인 절차를 알려줘"
    ollama run infra-assistant "모르면 추측하지 말고, nginx 502 점검 순서를 알려줘"

    여기서 보는 건 단순히 답변 길이나 그럴듯함이 아닙니다.

    1. 명령어가 먼저 나오는지
    2. 설명이 뒤로 밀리는지
    3. 모를 때 지어내지 않는지
    4. 한국어 톤과 형식이 유지되는지
    5. MESSAGE 예시가 답변을 과하게 복제하게 만들지는 않는지
    검증 항목 베이스 모델 커스텀 모델 합격 기준
    답변 언어 영문/국문 혼합 가능 국문 고정 가능 언어 정책이 흔들리지 않을 것
    명령어 우선 제시 질문마다 다름 일관되게 맞출 수 있음 초반 2~3줄 안에 바로 실행 항목이 나올 것
    톤 일관성 변동 가능 SYSTEM으로 안정화 질문이 바뀌어도 문체가 크게 흔들리지 않을 것
    도메인 집중도 일반 지식 중심 MESSAGE/ADAPTER로 강화 쓸데없는 일반론보다 절차형 답변이 앞설 것
    추측 억제 상황 따라 흔들림 금지 규칙 반영 가능 불확실할 때 확인 필요성을 분명히 말할 것

    현업 자동화에서는 “베이스 모델보다 더 똑똑하다”보다 “늘 같은 포맷으로 나온다”가 더 중요할 때가 많습니다. 운영 문서 초안, 반복 질의 응답, 내부 가이드 작성은 특히 그렇습니다. 이 지점이 바로 Ollama 커스텀 LLM의 실무 가치라고 봐도 무리가 없습니다.

    11. FAQ와 최종 추천: Ollama 커스텀 LLM은 이럴 땐 A, 저럴 땐 B로 나누면 편합니다

    자주 헷갈리는 질문

    • Q. Modelfile만 쓰면 미세 조정이 된 건가요?
      A. 아닙니다. 기본적으로는 설정, 프롬프트 구조, 예시 대화, 실행 파라미터를 묶는 작업에 가깝습니다. 추가 학습 결과를 붙이려면 ADAPTER 같은 별도 레이어가 필요합니다.
    • Q. TEMPLATE는 꼭 수정해야 하나요?
      A. 아닙니다. 기본 템플릿으로 문제가 없으면 안 건드리는 쪽이 맞습니다. 깨지는 빈도가 높은 영역이라 이유가 분명할 때만 손대는 게 좋습니다.
    • Q. 개인화된 AI를 가장 빨리 만드는 방법은?
      A. 베이스 모델 하나를 고르고, SYSTEM에 역할과 출력 순서를 명시한 뒤, MESSAGE 1~2개만 넣어 1차 버전을 만드는 방식이 가장 빠릅니다.
    • Q. 언제 ADAPTER까지 가야 하나요?
      A. SYSTEM과 MESSAGE로도 해결되지 않는 도메인 특화 패턴이 반복해서 필요할 때입니다. 단순한 말투 고정 정도라면 대개 과한 선택입니다.

    선택 기준은 이렇게 정리하면 편합니다.

    • 빠르게 목적형 모델이 필요하다: FROM + SYSTEM + PARAMETER + MESSAGE로 시작하세요. 가장 안전하고 재현성이 좋습니다.
    • 응답 형식을 강하게 통제해야 한다: 먼저 ollama show --modelfile로 원본을 확인한 뒤 TEMPLATE를 최소 수정하세요.
    • 특정 학습 결과물을 이미 갖고 있다: ADAPTER를 붙이되, 베이스 모델 호환성을 제일 먼저 확인하세요.
    • 역할별 모델을 반복 생성해야 한다: 사람이 수동으로 만들기보다 /api/create로 자동화하는 편이 관리가 낫습니다.
    • 속도와 메모리가 아슬아슬하다: 큰 모델 집착보다 작은 모델 + 낮은 num_ctx + 명확한 SYSTEM이 더 실용적일 때가 많습니다.

    한 줄로 압축하면 이렇습니다. Ollama 커스텀 LLM의 핵심은 모델을 새로 발명하는 게 아니라, 내 작업 방식을 반복 가능하게 고정하는 것입니다. 처음부터 무거운 학습 파이프라인으로 들어가기보다, Modelfile로 행동 규약을 먼저 굳히는 쪽이 훨씬 현실적입니다. 관련해서 로컬 LLM 성능 최적화나 RAG 비교 글도 함께 읽어보시면 다음 단계 판단이 더 쉬워집니다.

    Ollama 커스텀 LLM Modelfile 핵심 요소 요약 인포그래픽

    FROM, SYSTEM, PARAMETER, TEMPLATE, ADAPTER를 언제 선택하면 되는지 한눈에 정리한 요약 이미지입니다.

  • [AI] LLM 추론 성능 벤치마크: TensorRT vs ONNX Runtime

    [AI] LLM 추론 성능 벤치마크: TensorRT vs ONNX Runtime

    [AI 인프라] LLM 추론 성능 벤치마크: NVIDIA TensorRT vs ONNX Runtime

    LLM 추론 성능 벤치마크를 해보면, 병목은 생각보다 GPU 스펙 한 줄로 설명되지 않더라고요. 같은 GPU, 같은 모델이라도 어떤 런타임이 어떤 shape를 얼마나 안정적으로 처리하느냐, 그리고 초기화 비용과 토큰 생성 구간을 어떻게 분리해 봤느냐에 따라 체감 성능이 꽤 달라집니다. 저도 초반에는 TensorRT 쪽 숫자가 더 높게 나오면 무조건 이긴 줄 알았는데, 실제 운영에선 첫 요청 지연, 엔진 캐시, 입력 길이 분산, Python 생성 루프 오버헤드가 더 크게 문제 되는 경우가 많았습니다.

    이번 글은 단순 비교가 아니라, 같은 조건에서 TensorRT와 ONNX Runtime를 어떻게 공정하게 재는지, 그리고 측정값이 실제 서비스 판단으로 어떻게 이어지는지에 집중했습니다. 홈랩, 사내 검증 서버, PoC 환경에서 바로 써먹을 수 있게 명령어와 체크 포인트를 실무 기준으로 묶었습니다.

    로컬 LLM 추론 성능 벤치마크를 위한 TensorRT와 ONNX Runtime 아키텍처 이미지

    로컬 GPU 환경에서 모델 로딩, 토크나이저, 런타임, 추론 결과까지 이어지는 전체 흐름을 한눈에 보여주는 아키텍처 이미지입니다.

    1. 왜 TensorRT와 ONNX Runtime를 같이 보게 되나

    실무에서 둘을 같이 보는 이유는 단순합니다. TensorRT는 NVIDIA GPU 고정 환경에서 최대 성능을 노릴 때 강력한 선택지고, ONNX Runtime은 모델 비교, 환경 이동, 운영 단순성을 챙기기 좋은 기준선이기 때문입니다. 둘은 경쟁 관계이면서도, 실제론 비교용 baseline과 최적화용 target이라는 식으로 같이 쓰는 경우가 많습니다.

    • TensorRT: 엔진 빌드 비용과 shape 관리 부담을 감수하는 대신, 고정 워크로드에서는 높은 처리량과 낮은 지연을 기대할 수 있습니다.
    • ONNX Runtime: CUDA Execution Provider만 붙여도 baseline이 빨리 잡히고, 디버깅 난도가 비교적 낮습니다.
    • ONNX Runtime + TensorRT Execution Provider: 코드 경로는 유지하면서 일부 최적화를 노릴 수 있지만, 문제 원인 분리가 오히려 더 어려워지는 경우도 있습니다.

    여기서 제가 중요하게 보는 건 하나입니다. 런타임 성능만 빠른지, 전체 생성 경로가 빠른지를 분리해야 한다는 점입니다. TensorRT 엔진 레벨 숫자가 좋아도 실제 서비스 응답이 별 차이 없으면, 대개 병목은 런타임 바깥에 있습니다. 반대로 ONNX Runtime가 조금 느려 보여도 재현성과 운영 안정성이 높으면, 초기 서비스에는 그쪽이 더 값어치 있는 선택일 수 있습니다.

    2. LLM 추론 성능 벤치마크에서 꼭 봐야 할 지표

    LLM 벤치마크는 이미지 분류처럼 한 번 넣고 한 번 끝나는 추론과 다릅니다. 프롬프트 처리(prefill)와 토큰 생성(decode)이 분리되고, KV cache가 붙고, 입력 길이 편차가 커서 평균값 하나로 끝내면 판단을 그르치기 쉽습니다. 그래서 LLM 추론 성능 벤치마크에선 평균 속도보다 구간별 특성을 같이 봐야 합니다.

    2-1. 꼭 맞춰야 하는 비교 조건

    1. 같은 모델 가중치, 같은 토크나이저, 같은 전처리 조건을 씁니다.
    2. 가능하면 같은 ONNX 그래프를 기준으로 비교하고, TensorRT 11.x처럼 강타입 빌드가 필요한 경우에는 사전 precision 변환 단계까지 함께 기록합니다.
    3. 정밀도는 FP16, BF16, FP32, INT8 중 하나로 고정합니다. 섞어 재면 비교가 무너집니다.
    4. 프롬프트 길이와 생성 토큰 수를 고정하고, 가능하면 짧은 입력/긴 입력 두 구간으로 나눠 봅니다.
    5. 워밍업 횟수와 측정 구간을 분리합니다. 첫 요청을 평균에 섞으면 해석이 흐려집니다.
    6. 배치 크기와 동시성은 별도 축입니다. 둘을 한 번에 바꾸면 원인을 찾기 어렵습니다.
    7. 전원 상태, 클럭 상태, 백그라운드 프로세스를 고정합니다. 특히 데스크톱 환경에서는 이 영향이 꽤 큽니다.

    2-2. 해석할 지표

    • TTFT(Time To First Token): 사용자 체감에 가장 직접적입니다. 첫 토큰이 느리면 답답하게 느껴지거든요.
    • Tokens/sec: 긴 출력에서 중요합니다. decode 구간 효율이 여기서 드러납니다.
    • p95 latency: 운영 안정성을 보려면 평균보다 이 수치가 더 중요합니다.
    • GPU memory usage: 속도가 비슷하면 메모리 여유가 있는 쪽이 운영성이 좋습니다.
    • Engine build / session init overhead: 재시작이 잦은 환경, 다중 모델 환경에서는 이 비용이 실제 총비용으로 이어집니다.
    • CPU time: 토크나이저, 후처리, Python 루프가 병목이면 GPU 최적화 이득이 상쇄됩니다.

    제가 실제로 더 신뢰하는 건 최고 기록이 아니라 세 번 돌렸을 때 비슷하게 나오는지입니다. 단일 최고치만 잘 나온 벤치마크는 운영 판단 기준으로는 거의 쓸모가 없습니다.

    3. TensorRT 최적화와 ONNX Runtime 비교를 위한 기준선 만들기

    로컬 LLM 추론 성능 벤치마크에서 제일 먼저 해야 할 일은, 화려한 최적화가 아니라 분리 측정입니다. 저는 보통 아래 네 구간으로 나눕니다.

    1. 모델 준비 구간: ONNX export, shape 정의, 동적 축 확인
    2. 초기화 구간: TensorRT 엔진 빌드 또는 ONNX Runtime 세션 생성
    3. prefill 구간: 긴 프롬프트를 한 번에 넣는 첫 추론
    4. decode 구간: 토큰을 한 개씩 이어서 생성하는 반복 추론

    이 네 구간을 섞어서 평균 latency 하나로 덮으면 실무에서는 해석이 거의 틀어집니다. TensorRT는 엔진 빌드가 무겁고, ONNX Runtime는 세션 초기화와 provider 구성 영향이 큽니다. LLM은 여기에 prefill과 decode의 성격까지 달라서, 어느 구간에서 이겼는지가 곧 선택 기준이 됩니다.

    항목 TensorRT ONNX Runtime 실무 판단 포인트
    최대 성능 잠재력 높음 중간~높음 GPU와 워크로드가 고정이면 TensorRT 투자 가치가 커집니다.
    초기 실험 속도 느린 편 빠른 편 모델 export 직후 baseline을 잡을 때는 ONNX Runtime가 훨씬 편합니다.
    입력 길이 변동 대응 shape 전략에 민감 상대적으로 유연 프롬프트 길이 편차가 크면 TensorRT 최적화 이점이 줄어들 수 있습니다.
    장애 분석 난이도 높음 낮음 팀에 추론 엔진 디버깅 경험이 없으면 운영 비용이 성능 이득을 먹어버릴 수 있습니다.
    재기동 비용 민감도 엔진 캐시 전략 필요 세션 재생성 중심 짧은 수명 컨테이너 환경에서는 캐시 설계가 성능만큼 중요합니다.
    권장 시작점 최적화 단계 기준선 단계 바로 TensorRT로 들어가기보다 ONNX Runtime로 병목 위치를 먼저 확인하는 편이 낫습니다.

    4. 실전 구현 1: TensorRT 엔진 생성과 기본 측정

    TensorRT는 <code>trtexec로 첫 감을 잡는 게 가장 빠릅니다. 다만 LLM에서는 단순히 FP16 옵션 하나만 넣고 끝내면 부족합니다. 입력 shape 범위를 명시하지 않으면 엔진이 실제 프롬프트 분포와 어긋날 수 있고, 그 결과 기대 이하의 최적화가 나올 수 있습니다.

    아래 예시는 TensorRT 10.x 계열에서 많이 쓰는 방식입니다. TensorRT 11.x부터는 일부 precision 관련 플래그가 바뀌었거나 제거됐기 때문에, 실제 사용 전에는 trtexec --help로 로컬 버전을 꼭 확인해 두는 게 좋습니다. 이거 안 보고 그대로 복붙했다가 한 번씩 막히더라고요.

    trtexec \
      --onnx=model.onnx \
      --saveEngine=model_fp16.plan \
      --fp16 \
      --minShapes=input_ids:1x16,attention_mask:1x16 \
      --optShapes=input_ids:1x512,attention_mask:1x512 \
      --maxShapes=input_ids:1x2048,attention_mask:1x2048 \
      --warmUp=200 \
      --duration=60 \
      --verbose

    이 명령에서 실무적으로 중요한 건 아래입니다.

    • --minShapes, --optShapes, --maxShapes: 동적 입력 모델이라면 사실상 핵심입니다. opt shape는 가장 자주 들어오는 프롬프트 길이대로 잡는 편이 낫습니다.
    • --warmUp: 보통 워밍업 시간 기준으로 해석합니다. 반복 횟수라고 착각하면 결과 비교가 꼬일 수 있습니다.
    • --verbose: 성능보다 먼저 봐야 할 건 변환 실패 레이어, precision 강등, 지원되지 않는 연산 흔적입니다.

    LLM에서 자주 놓치는 건 shape 전략이 사실상 성능 전략이라는 점입니다. 짧은 프롬프트 위주 서비스인데 max shape를 과하게 크게 잡으면 메모리와 최적화 효율이 함께 손해를 봅니다. 반대로 입력 길이 분산이 큰데 지나치게 좁게 잡으면 실제 요청에서 비효율이 생길 수 있습니다.

    엔진 생성과 추론을 분리해서 보고 싶다면 저장된 엔진으로 다시 측정하는 편이 낫습니다.

    trtexec \
      --loadEngine=model_fp16.plan \
      --shapes=input_ids:1x512,attention_mask:1x512 \
      --warmUp=200 \
      --duration=60

    이렇게 해야 엔진 빌드 시간이 빠진 순수 추론 구간을 보기 좋습니다. 엔진 빌드 시간을 평균 latency에 섞는 실수는 생각보다 자주 나오고, 그 순간부터 비교표는 의미가 많이 줄어듭니다.

    TensorRT 최적화와 GPU 가속 흐름을 설명하는 LLM 추론 성능 벤치마크 이미지

    ONNX 모델이 TensorRT 엔진으로 변환되고, 워밍업과 프로파일링을 거쳐 성능 지표가 수집되는 흐름을 설명하는 이미지입니다.

    5. 실전 구현 2: ONNX Runtime baseline 측정

    ONNX Runtime의 강점은 baseline을 빠르게 만들 수 있다는 점입니다. 저는 여기서 단순 평균 latency보다 provider가 제대로 붙었는지, 세션 옵션이 적절한지, 실제 생성 루프와 분리해 엔진 수준 시간을 볼 수 있는지를 먼저 확인합니다.

    import time
    import numpy as np
    import onnxruntime as ort
    
    session_options = ort.SessionOptions()
    session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
    session_options.log_severity_level = 0
    session_options.log_verbosity_level = 1
    
    providers = [
        (
            "CUDAExecutionProvider",
            {
                "device_id": 0,
                "arena_extend_strategy": "kNextPowerOfTwo",
                "cudnn_conv_algo_search": "EXHAUSTIVE",
                "do_copy_in_default_stream": True,
            },
        ),
        "CPUExecutionProvider",
    ]
    
    session = ort.InferenceSession(
        "model.onnx",
        sess_options=session_options,
        providers=providers,
    )
    
    print("active providers:", session.get_providers())
    input_names = [x.name for x in session.get_inputs()]
    print("inputs:", input_names)
    
    feeds = {
        "input_ids": np.ones((1, 16), dtype=np.int64),
        "attention_mask": np.ones((1, 16), dtype=np.int64),
    }
    
    for _ in range(20):
        session.run(None, feeds)
    
    latencies_ms = []
    for _ in range(100):
        start = time.perf_counter()
        session.run(None, feeds)
        latencies_ms.append((time.perf_counter() - start) * 1000)
    
    latencies_ms.sort()
    p95 = latencies_ms[int(len(latencies_ms) * 0.95) - 1]
    print(f"avg latency: {sum(latencies_ms)/len(latencies_ms):.3f} ms")
    print(f"p95 latency: {p95:.3f} ms")

    이 코드는 baseline용입니다. 실제 LLM 생성 벤치마크로 가려면 past_key_values, position_ids, attention_mask shape, 그리고 prefill/decode 분리를 모델 구조에 맞게 넣어야 합니다. 중요한 건 엔진 호출 성능과 애플리케이션 생성 루프 성능을 따로 수집하는 습관입니다.

    ONNX Runtime 안에서 TensorRT Execution Provider를 시험해 보고 싶다면 아래처럼 provider를 분리해 보는 방법도 있습니다. 이 경우는 성능 수치보다 캐시 동작과 fallback 여부를 먼저 보는 편이 낫습니다.

    providers = [
        (
            "TensorrtExecutionProvider",
            {
                "trt_engine_cache_enable": True,
                "trt_engine_cache_path": "./trt_cache",
                "trt_fp16_enable": True,
            },
        ),
        "CUDAExecutionProvider",
        "CPUExecutionProvider",
    ]
    
    session = ort.InferenceSession("model.onnx", providers=providers)

    GPU 상태는 꼭 같이 봐야 합니다. 벤치마크 로그만 보면 빨라 보이는데 GPU util이 낮고 CPU 한 코어만 치솟는 경우가 꽤 많습니다.

    nvidia-smi dmon -s pucm -d 1

    여기서 저는 네 가지를 같이 봅니다. power는 클럭 유지 여부, util은 GPU 바쁨 정도, clock은 스로틀링 여부, memory는 컨텍스트 길이와 배치 변화의 영향을 읽는 데 도움이 됩니다.

    6. 주의사항과 트러블슈팅: 제가 자주 겪었던 문제들

    벤치마크는 숫자를 뽑는 작업이 아니라 왜 그 숫자가 나왔는지 설명하는 작업에 가깝습니다. 아래 문제들은 LLM 추론 비교에서 반복해서 나오는 실패 모드입니다. 실제로는 런타임보다 입력 경로에서 막히는 경우도 꽤 많더라고요.

    6-1. 같은 모델인데 결과가 안 맞는 경우

    • 토크나이저 옵션 차이로 입력 길이가 달라집니다. 성능 비교 전에 실제 token count부터 맞춰야 합니다.
    • padding 방향과 최대 길이 설정이 다르면 연산량이 달라집니다.
    • 배치 크기 자동 조정이나 동적 batching이 켜져 있으면 공정 비교가 깨집니다.
    • FP16과 FP32, 또는 BF16을 섞어 재면 런타임 차이보다 precision 차이가 더 크게 나옵니다.

    근본 원인은 대부분 같은 모델을 보고 있다고 착각하는 것입니다. 파일명이 같아도 export 옵션, 동적 축, 입력 schema가 달라지면 사실상 다른 워크로드입니다.

    6-2. TensorRT 엔진은 빠른데 실제 생성 속도는 애매한 경우

    이건 정말 자주 봅니다. 원인은 대체로 런타임 바깥에 있습니다.

    • 토크나이저가 CPU 병목입니다. 짧은 응답에서는 이 영향이 생각보다 큽니다.
    • Python 루프가 토큰마다 GPU 호출을 감싸면서 왕복 오버헤드를 만듭니다.
    • KV cache 레이아웃이나 메모리 복사가 비효율적입니다.
    • 입력 shape가 계속 바뀌어 TensorRT 최적화가 기대만큼 먹지 않습니다.
    • 엔진 캐시가 제대로 재사용되지 않아 재시작 후마다 준비 비용이 다시 듭니다.

    GPU util은 낮은데 TTFT와 p95가 큰 경우는 계산 문제가 아니라 상위 경로 병목일 가능성이 큽니다. 이때 GPU 커널을 더 최적화하는 건 우선순위가 아닙니다.

    6-3. ONNX Runtime가 예상보다 느린 경우

    • CUDAExecutionProvider가 실제로 활성화됐는지 먼저 확인합니다.
    • 지원되지 않는 연산이 CPU로 fallback 되는지 로그를 봅니다.
    • 세션 옵션의 그래프 최적화가 빠져 있지 않은지 확인합니다.
    • ONNX export에서 dynamic axis를 과하게 열어 둬 shape 추론과 최적화가 약해진 경우를 의심합니다.
    • 입력 텐서를 매 요청마다 새로 할당하면서 호스트 메모리 오버헤드를 키우는 경우도 꽤 있습니다.

    실무에서 제가 제일 경계하는 건 런타임이 느린 게 아니라 export 품질이 낮은 경우입니다. ONNX Runtime 성능 문제처럼 보여도, 실제로는 모델 export가 비효율적으로 된 사례가 적지 않습니다.

    ONNX Runtime 비교를 위한 CUDA Execution Provider 구성 이미지

    ONNX Runtime 세션 초기화, 그래프 최적화, CUDA Execution Provider 적용 구조를 이해하기 쉽게 풀어낸 이미지입니다.

    7. LLM 추론 성능 벤치마크 결과, 숫자보다 읽는 법이 먼저입니다

    측정이 끝났다면 이제 숫자를 읽어야 합니다. 저는 아래처럼 봅니다. 이 구간이 사실 제일 중요합니다. 숫자만 높다고 바로 정답은 아니거든요.

    1. TTFT가 길다: 엔진/세션 초기화, 긴 프롬프트 prefill, 토크나이저, 첫 decode 경로를 의심합니다.
    2. 평균 latency는 좋은데 p95가 나쁘다: 동시성 구간의 큐잉, 메모리 압박, shape 분산이 원인일 가능성이 큽니다.
    3. tokens/sec가 잘 안 오른다: decode 루프의 CPU 개입, KV cache 처리, 작은 배치가 문제일 수 있습니다.
    4. GPU util이 낮다: GPU가 한가한데 응답은 느리다면 애플리케이션 경로를 먼저 봐야 합니다.
    5. GPU memory가 높고 출렁인다: 최대 시퀀스 길이, 동시성, 정밀도 조합이 과한 신호일 수 있습니다.

    홈랩이나 PoC에서는 특히 재현성이 중요합니다. 저는 최소 3회 반복, 워밍업 제외, 첫 요청 별도 기록, prefill/decode 분리, GPU 모니터링 병행 이 다섯 가지를 기본으로 둡니다. 이렇게 해 두면 나중에 팀 내에서 숫자 해석으로 싸울 일이 확 줄어듭니다.

    관측 결과 자주 나오는 근본 원인 먼저 할 조치 TensorRT 쪽 힌트 ONNX Runtime 쪽 힌트
    첫 요청만 유독 느림 엔진 빌드, 세션 초기화, 캐시 미적용 초기화 구간을 분리 측정 엔진 캐시와 사전 빌드 여부 확인 세션 재사용과 provider 초기화 로그 확인
    긴 프롬프트에서 급격히 느려짐 prefill 비용 증가, shape 최적화 불일치 입력 길이 구간별로 분리 측정 opt shape를 실제 분포에 맞게 재설계 export dynamic axis와 입력 텐서 구성 점검
    GPU util은 낮은데 응답은 느림 토크나이저, Python 루프, CPU fallback CPU 사용률과 provider 로그 확인 엔진 바깥 호출 오버헤드 축소 fallback 연산과 메모리 할당 패턴 확인
    평균은 괜찮은데 p95가 나쁨 동시성, 큐잉, 메모리 압박 동시성 단계별로 별도 실행 shape 편차와 메모리 상한 재검토 세션 옵션, provider 설정, CPU 개입 확인

    결과 정리 시에는 숫자 순위표보다 어떤 조건에서 우세했는지를 남기는 편이 훨씬 유용합니다. TensorRT가 이긴 게 아니라, 고정 shape의 긴 decode 구간에서 TensorRT가 우세했다처럼 써야 다음 의사결정에 바로 써먹을 수 있습니다.

    LLM 추론 성능 벤치마크 결과와 GPU 사용률을 시각화한 이미지

    TTFT, tokens/sec, GPU utilization, memory usage를 한 화면에서 비교하는 대시보드 형태의 결과 이미지입니다.

    8. 실무 추천과 다음 단계

    제가 현업에서 내리는 기준은 꽤 단순합니다. 먼저 ONNX Runtime로 병목 위치를 밝히고, 그다음 TensorRT로 돈 되는 구간만 최적화하는 흐름이 가장 덜 아픕니다. 처음부터 TensorRT로 들어가면 성능은 빨리 보여도, 왜 빨라졌는지와 왜 안 빨라졌는지를 분리하기 어려운 경우가 많습니다.

    • 이럴 땐 ONNX Runtime: 모델을 자주 바꾼다, 비교 실험이 많다, 디버깅 시간을 아껴야 한다, NVIDIA 고정 배포가 아니다.
    • 이럴 땐 TensorRT: GPU가 고정돼 있다, 긴 기간 같은 워크로드를 운영한다, p95와 처리량을 끝까지 밀어야 한다, 엔진 관리까지 감당할 팀이 있다.
    • 이럴 땐 둘 다 본다: ONNX Runtime baseline은 안정적인데 비용이 아쉽고, 병목이 GPU 쪽이라는 근거가 이미 있다.

    조금 더 직설적으로 말씀드리면, PoC 단계에서 바로 TensorRT를 주력 경로로 잡는 건 대개 이릅니다. 반대로 이미 NVIDIA GPU 기반으로 워크로드가 굳어 있고, 하루 종일 같은 패턴의 요청을 받는다면 TensorRT 최적화는 충분히 투자할 만합니다. 이 경우에는 성능 향상 자체보다도 예측 가능한 지연과 재현성이 장점으로 돌아옵니다.

    1. 개발 속도, 실험 반복, 모델 교체 빈도가 중요하면 ONNX Runtime로 시작하는 편이 맞습니다.
    2. 배포 환경이 NVIDIA GPU로 고정이고, TTFT보다 지속 처리량과 p95가 더 중요하면 TensorRT 우선 검토가 맞습니다.
    3. 둘 중 하나로 바로 못 정하겠다면, 같은 ONNX 기준으로 prefill과 decode를 분리 측정한 뒤 TTFT, tokens/sec, p95, GPU memory 네 항목으로 결정하는 게 가장 안전합니다.

    다음 단계도 명확합니다. baseline이 흔들리면 런타임 최적화 전에 export와 입력 경로부터 정리하시고, baseline이 안정적인데 GPU 병목이 분명하면 그때 TensorRT로 들어가시면 됩니다. 이 순서를 지키면 삽질 시간이 확실히 줄어듭니다.

    관련해서 모델 경량화나 GPU 가속 운영 팁이 더 궁금하시면, 블로그의 다른 AI 인프라 글도 같이 읽어보시면 흐름이 훨씬 잘 잡힐 겁니다.

    TensorRT와 ONNX Runtime 선택 기준을 요약한 LLM 추론 성능 벤치마크 이미지

    어떤 상황에서 TensorRT를 고르고, 어떤 상황에서 ONNX Runtime를 고르면 되는지 한 장으로 정리한 요약 이미지입니다.

    FAQ

    Q. ONNX Runtime 안에서 TensorRT도 쓸 수 있나요?

    네, TensorRT Execution Provider로 구성할 수 있습니다. 다만 성능만 보지 말고, 엔진 캐시 재사용, fallback 경로, 디버깅 난이도를 같이 보셔야 합니다. 코드 경로는 단순해 보여도 장애 분석은 더 어려워질 수 있습니다.

    Q. LLM 추론 성능 벤치마크에서 숫자가 자꾸 달라집니다.

    워밍업, 프롬프트 길이, 출력 길이, 배치 크기, 전원/클럭 상태를 먼저 고정해 보세요. 거기에 백그라운드 프로세스와 첫 요청 분리 기록까지 넣으면 흔들림 원인을 상당히 빨리 좁힐 수 있습니다.

    Q. AI 모델 경량화가 꼭 필요할까요?

    메모리가 빠듯하거나 동시성을 올려야 한다면 우선순위가 높습니다. 다만 INT8이나 더 공격적인 최적화는 정확도 검증과 함께 가야 해서, baseline 없이 바로 들어가면 성능은 빨라져도 판단은 더 어려워질 수 있습니다.

  • [HomeLabs] NVR 소프트웨어 벤치마크: Shinobi vs ZoneMinder 비교

    [HomeLabs] NVR 소프트웨어 벤치마크: Shinobi vs ZoneMinder 비교

    NVR 소프트웨어 벤치마크: Shinobi vs ZoneMinder 성능 비교

    홈랩 CCTV를 오래 굴리다 보면 결국 한 번은 NVR 소프트웨어 벤치마크를 제대로 해야 할 때가 옵니다. 카메라 2대 정도일 땐 둘 다 무난해 보여도, 24시간 녹화가 붙고 실시간 보기 세션이 겹치고 보존 기간이 길어지면 병목이 갑자기 드러나거든요. 이때 가장 흔한 실수는 제품 이름만 보고 “이게 더 가볍다”고 결론 내리는 겁니다. 실제로는 NVR 자체보다 스트림을 어떻게 받는지, 다시 인코딩하는지, 이벤트를 어떤 경로에서 판정하는지, 파일을 어떤 단위로 저장하는지가 성능을 더 크게 좌우합니다.

    이번 글은 Shinobi 성능과 ZoneMinder 비교를 숫자 몇 개로 포장하는 글은 아닙니다. 검증되지 않은 “채널당 CPU” 같은 수치는 일부러 뺐습니다. 대신 홈랩과 소형 자가 호스팅 환경에서 실제로 체크해야 하는 기준, 공정하게 비교하는 절차, 결과를 해석할 때 놓치기 쉬운 운영 포인트를 정리해보겠습니다. 결론부터 말하면 둘 중 하나가 절대적으로 우월하다기보다, 어떤 워크로드에서 어떤 비용을 치르는지를 읽는 쪽이 훨씬 중요하더라고요.

    NVR 소프트웨어 벤치마크용 홈랩 CCTV 아키텍처 다이어그램

    Shinobi와 ZoneMinder를 동일한 네트워크, 동일한 카메라 스트림 조건에서 비교하는 홈랩 CCTV 아키텍처 예시입니다.

    1. 왜 Shinobi vs ZoneMinder를 따로 봐야 할까요?

    둘 다 실사용 가능한 오픈소스 NVR이지만, 운영할 때 체감하는 무게중심은 꽤 다릅니다. 저는 이 차이를 스트림을 다루는 방식과 운영자가 개입해야 하는 지점으로 봅니다.

    • Shinobi는 웹 UI에서 카메라별 설정을 빠르게 바꾸고 테스트를 반복하는 흐름에 강한 편입니다. 홈랩에서 “일단 붙이고 부하를 보면서 조정”하는 스타일이면 손에 잘 맞는 경우가 많습니다.
    • ZoneMinder는 모니터 소스, 이벤트, 저장소, 로그를 더 명시적으로 관리하는 느낌이 강합니다. 처음엔 투박해 보여도 장기 운영에서 어디가 병목인지 추적하기는 편한 편입니다.

    벤치마크 관점에서는 이 차이가 꽤 큽니다. 같은 RTSP 카메라를 붙여도 실제 부하는 다음 질문에서 갈립니다.

    • 스트림을 그대로 저장(copy)하는가, 아니면 디코드 후 다시 가공하는가
    • 실시간 보기와 녹화가 같은 스트림 경로를 공유하는가
    • 모션 감지를 카메라 이벤트에 기대는가, 서버에서 계산하는가
    • 이벤트가 쌓일 때 부담이 DB 쪽으로 가는가, 파일시스템 쪽으로 가는가

    제가 이 둘을 비교할 때 제일 먼저 버리는 질문은 “누가 더 빠르냐”입니다. 대신 이렇게 묻습니다. 내 서버에서 제일 먼저 비명을 지를 자원이 CPU인지, 메모리인지, 디스크인지, 아니면 관리 시간인지. 이 기준으로 보면 둘의 인상이 꽤 달라집니다.

    2. NVR 소프트웨어 벤치마크에서 봐야 할 핵심 지표

    NVR 소프트웨어 벤치마크를 CPU 퍼센트 한 줄로 끝내면 거의 항상 해석이 틀어집니다. 제가 최소한 같이 보는 항목은 아래 8개입니다.

    1. Idle 사용량: 아무도 안 보고 이벤트도 거의 없을 때 얼마나 조용한가
    2. Live View 부하: 4분할, 8분할, 다중 브라우저 접속 시 부하가 얼마나 튀는가
    3. Recording 경로: 상시 녹화가 재인코딩 없이 유지되는가, 중간 변환이 들어가는가
    4. Motion Detection 비용: 감지 영역, FPS, 해상도 변화에 따라 CPU가 선형으로 느는지 급증하는지
    5. Disk I/O 패턴: 평균 쓰기량보다 작은 파일 다량 생성, 메타데이터 갱신, 삭제 시 폭증이 있는지
    6. Retention 정리 비용: 오래된 녹화 삭제가 평시 부하와 분리되는지, 같은 시간대에 몰리는지
    7. Reconnect 안정성: 카메라 재부팅, 네트워크 지연 후 세션 복구가 깔끔한지
    8. 관측 가능성: 로그, 프로세스, 저장량 변화를 보고 원인을 좁히기 쉬운지

    여기서 실무적으로 중요한 건 평균값보다 피크와 패턴입니다. 예를 들어 CPU 평균이 낮아 보여도 5초마다 스파이크가 생기면 실시간 보기 체감은 바로 나빠집니다. 디스크도 마찬가지예요. 초당 평균 쓰기량이 낮아도 작은 파일을 계속 만들고 지우는 구조면 HDD에서 금방 티가 납니다.

    또 하나, 홈랩에서는 카메라 스트림 품질이 벤치마크 자체를 오염시키는 경우가 생각보다 많습니다. 메인 스트림은 H.265, 서브 스트림은 H.264, 프레임 간격도 제각각이면 결과를 NVR 탓으로 돌리기 어렵습니다. 그래서 제품 비교 전에 먼저 입력 스트림이 예측 가능해야 합니다.

    3. 비교 전에 조건을 맞춰야 공정합니다

    Shinobi 성능이 더 좋다거나 ZoneMinder가 더 무겁다고 말하려면, 적어도 비교 조건은 통제해야 합니다. 저는 아래 표를 만족하지 못하면 벤치마크라고 부르지 않습니다.

    항목 비교 원칙 이유
    카메라 수 동일 수량 채널 수가 늘면 프로세스 수, 소켓 수, I/O 큐 길이가 같이 변합니다
    해상도 동일 해상도 1080p와 4MP 혼용은 감지 비용과 저장량을 동시에 왜곡합니다
    프레임레이트 동일 FPS FPS 차이는 CPU, 네트워크, 저장 용량에 직접 반영됩니다
    코덱 가능하면 동일 코덱 H.264와 H.265는 디코딩 부담이 다르고 브라우저 호환성도 다릅니다
    녹화 정책 상시/모션 동일 이벤트 방식이 다르면 파일 생성 패턴과 메타데이터 갱신량이 달라집니다
    저장소 같은 SSD/HDD 조건 같은 소프트웨어도 저장장치가 바뀌면 체감 성능이 완전히 달라집니다
    하드웨어 가속 같은 정책으로 켜거나 끄기 VAAPI, QSV, CUDA 계열 적용 여부가 결과를 뒤집을 수 있습니다

    여기에 저는 두 가지를 더 넣습니다.

    • 브라우저 시나리오 통일: Live View 테스트에서 브라우저 탭 수, 레이아웃, 자동 재생 여부를 맞춥니다.
    • 보존 정책 통일: 파일 수 제한인지, 일 수 기준인지, 용량 기준인지에 따라 정리 부하가 달라집니다.

    실무에서 자주 깨지는 지점은 하드웨어 가속입니다. 한쪽은 실제로 VAAPI가 먹고 있고, 다른 쪽은 설정만 켜진 상태인데 CPU 디코드로 돌면 비교가 성립하지 않습니다. 그래서 저는 벤치마크 전에 항상 “가속을 켰는가”가 아니라 “가속 경로를 실제로 탔는가”를 확인합니다.

    4. 실전 구현: 홈랩 CCTV 벤치마크 절차

    설치 가이드는 환경마다 달라서, 여기서는 운영 중인 두 NVR 인스턴스를 같은 조건으로 측정하는 절차에 집중하겠습니다. bare metal, VM, LXC, Docker 중 무엇을 쓰든 핵심은 같습니다. 입력, 저장, 관측 방식을 통일하는 겁니다.

    4-1. 카메라 스트림 상태 먼저 확인

    NVR이 느린 것처럼 보여도 실제 원인은 입력 스트림 불안정인 경우가 많습니다. 먼저 각 RTSP 스트림의 해상도, 평균 FPS, 코덱, GOP 간격이 의도한 값과 맞는지 확인합니다.

    ffprobe -hide_banner -rtsp_transport tcp rtsp://USER:PASS@CAMERA_IP:554/stream1
    ffprobe -hide_banner -rtsp_transport tcp rtsp://USER:PASS@CAMERA_IP:554/stream2

    가능하면 아래 항목을 따로 메모해 두세요.

    • 메인/서브 스트림 해상도
    • 실제 코덱(H.264/H.265)
    • 명시 FPS와 실측 FPS 차이
    • 키프레임 간격이 비정상적으로 긴지
    • TCP/UDP 전송 방식 차이

    제가 경험상 제일 빨리 걸러내는 문제는 카메라가 표시하는 FPS와 실제 전송 FPS가 다른 경우입니다. 특히 저가형 카메라나 무선 구간이 끼는 환경에서는 설정값과 실제가 다를 수 있습니다. 이 상태로 벤치마크하면 NVR 비교가 아니라 입력 품질 비교가 됩니다.

    4-2. 서버 자원 사용량 수집

    관측 도구는 화려할 필요가 없습니다. 오히려 단순해야 반복하기 쉽습니다. 저는 최소한 pidstat, iostat, vmstat를 같이 봅니다.

    pidstat -rud -h 1
    iostat -xm 1
    vmstat 1

    프로세스 단위로 좁혀 볼 때는 관련 프로세스를 먼저 식별합니다.

    pgrep -af 'shinobi|node|zm|zmc|zma|ffmpeg'
    pidstat -rud -p ALL 1

    여기서 중요한 건 “한 번 보고 느낌으로 판단”하지 않는 겁니다. 저는 보통 아래처럼 구간을 나눠 최소 10분 이상 유지합니다.

    1. Idle 10분: 브라우저 접속 없음, 이벤트 최소화
    2. Live View 10분: 4카메라, 8카메라, 2개 브라우저 세션 순서로 증가
    3. Continuous Recording 10분: 상시 녹화만 켜고 보기 세션 제거
    4. Motion Detection 10분: 동일 감지 영역, 동일 FPS로 서버 감지 활성화

    여기서 특히 보는 건 usr/sys/iowait 분해입니다. CPU 총합만 보면 놓치는 게 많습니다. 예를 들어 iowait가 높으면 NVR이 무거운 게 아니라 저장소 응답이 밀리는 걸 수 있고, sys 비중이 높으면 작은 파일 처리나 네트워크/커널 경로가 병목일 수 있습니다.

    하드웨어 가속 검증도 이 단계에서 같이 합니다. 리눅스라면 최소한 아래 항목은 확인해 두는 편이 좋습니다.

    ffmpeg -hwaccels
    ls -l /dev/dri
    vainfo
    intel_gpu_top
    journalctl --since '10 minutes ago' | grep -Ei 'vaapi|qsv|cuda|nvdec|nvenc'

    장치가 보인다고 끝은 아닙니다. 실제 디코드/인코드 경로가 GPU를 타는지까지 봐야 합니다. 컨테이너라면 디바이스 패스스루, 그룹 권한, 런타임 제약도 같이 확인하는 게 안전합니다.

    Shinobi 성능과 ZoneMinder 비교를 위한 테스트 구성 이미지

    동일한 카메라 수, 동일한 스트림 조건, 동일한 저장소 정책으로 테스트하는 구성 흐름 예시입니다.

    4-3. 로그와 스토리지 증가량 기록

    CPU만 보면 진짜 중요한 문제를 놓칩니다. 저는 저장 용량 증가, 파일 수 증가, 로그 에러를 반드시 같이 봅니다.

    # ZoneMinder는 배포판과 스토리지 설정에 따라 경로가 달라질 수 있습니다.
    du -sh /var/cache/zoneminder 2>/dev/null || du -sh /var/lib/zoneminder
    find /var/cache/zoneminder -type f 2>/dev/null | wc -l
    journalctl -u zoneminder --since '10 minutes ago'
    
    # Shinobi는 systemd 서비스명 또는 PM2 사용 여부를 먼저 확인하세요.
    journalctl -u shinobi --since '10 minutes ago' 2>/dev/null || pm2 logs --lines 100

    가능하면 용량뿐 아니라 파일 개수를 같이 기록해 보세요. 홈랩에서 HDD 기반 저장소가 느려지는 대표적인 이유는 총 용량 부족보다도 작은 파일이 너무 많아져 메타데이터 작업이 밀리는 것입니다. 용량 그래프는 멀쩡한데 삭제 작업이 겹칠 때만 끊기는 패턴이 이때 자주 나옵니다.

    그리고 로그는 “에러가 있냐 없냐”보다 에러가 어떤 구간에 몰리는지가 중요합니다. 저는 아래 네 가지 메시지가 보이면 바로 원인 축소에 들어갑니다.

    • 재연결 반복: 카메라 응답 지연, RTSP keepalive, 스위치/PoE 불안정 가능성
    • 디코드 실패: 코덱 불일치, 손상 프레임, 가속 경로 실패 가능성
    • DB 관련 지연: 이벤트 인덱싱, 정리 작업, 메타데이터 병목 가능성
    • 권한 오류: 저장 경로, GPU 장치, 컨테이너 볼륨 마운트 문제 가능성

    4-4. 반복 가능한 기록 시트 만들기

    엑셀도 좋고 노션도 좋지만, 핵심은 나중에 같은 기준으로 다시 재현할 수 있느냐입니다. 저는 아래 같은 단순 시트를 씁니다.

    Scenario,CPU usr/sys/iowait,Memory RSS,Disk Write MB/s,File Count Growth,Event Delay,Reconnect Stability,Notes
    Idle, , , , , , ,
    Live View 4 cams, , , , , , ,
    Live View 8 cams, , , , , , ,
    Continuous Recording, , , , , , ,
    Motion Detection Enabled, , , , , , ,
    Retention Cleanup Window, , , , , , ,

    여기서 포인트는 메트릭보다 해석 단서를 남기는 것입니다. 예를 들어 Notes 칸에 아래처럼 적어두면 다음 테스트 품질이 확 올라갑니다.

    • “서브 스트림 보기에서는 안정, 메인 스트림 다중 보기에서 브라우저가 먼저 버벅임”
    • “Motion 켜는 순간 CPU보다 iowait가 먼저 튐”
    • “카메라 6번만 주기적으로 reconnect 발생”
    • “보존 정책 정리 시점에 삭제 지연과 DB 정리 동시 발생”

    이런 메모가 있어야 나중에 제품 차이와 환경 문제를 분리하기 훨씬 쉽습니다.

    5. Shinobi 성능과 ZoneMinder 비교 포인트

    이제 구조적인 차이를 성능 관점으로 읽어보겠습니다. 특정 수치를 찍기보다, 어떤 조건에서 어떤 비용이 커지는지를 보는 게 맞습니다.

    비교 포인트 Shinobi에서 보기 좋은 부분 ZoneMinder에서 보기 좋은 부분
    초기 UI 접근성 웹 화면에서 스트림 흐름과 카메라별 설정 반복이 비교적 빠릅니다 전통적인 관리 흐름에 익숙하면 구성 요소 역할을 구분해 보기가 쉽습니다
    스트림 관리 체감 메인/서브 스트림 분리 실험과 카메라별 조정이 잦을 때 유리한 편입니다 소스, 이벤트, 저장 구조를 나눠 보고 원인을 좁히는 흐름에 잘 맞습니다
    운영 난이도 빨리 붙여보고 병목을 눈으로 확인하기 좋습니다 초기 이해 비용은 있지만 장기 운영 원인을 추적하기 좋습니다
    디버깅 접근 스트림 단위 관찰과 FFmpeg 경로 확인이 핵심입니다 프로세스, DB, 저장 구조를 같이 보는 습관이 중요합니다
    홈랩 적합성 반복 테스트와 빠른 설정 변경 중심의 홈랩에 잘 맞습니다 운영 정책을 명확히 세우고 길게 굴리는 스타일에 잘 맞습니다

    제 판단 기준을 조금 더 솔직하게 말하면 이렇습니다.

    • Shinobi는 “구성을 자주 바꾸며 최적점을 찾는 사람”에게 잘 맞습니다. 카메라별 차이를 빨리 체감하기 좋고, 동적 서브스트림 같은 기능을 실험하기도 편한 편입니다.
    • ZoneMinder는 “정책을 먼저 정하고, 그 정책이 장기적으로 버티는지 검증하는 사람”에게 더 잘 맞습니다. 다만 저해상도 보조 스트림 활용은 버전과 설정에 따라 성숙도가 달라서, 실제 테스트를 꼭 해보는 게 좋습니다.

    다만 여기서 중요한 반전이 하나 있습니다. 실제 홈랩에서는 제품 차이보다도 아래 결정이 결과를 더 크게 바꿉니다.

    • 실시간 보기를 메인 스트림으로 할지, 서브 스트림으로 분리할지
    • 모션 감지를 카메라 이벤트에 맡길지, 서버 소프트웨어로 할지
    • 상시 녹화 원본을 재인코딩 없이 저장할지
    • 저장소를 SSD 캐시와 HDD 보관 계층으로 나눌지
    • 보존 정책을 하루 단위 정리로 둘지, 용량 임계치 기준으로 둘지

    즉, ZoneMinder 비교나 Shinobi 성능을 말할 때 제품명 자체보다 운영 설계가 더 큰 변수입니다. 저는 그래서 “무엇이 더 빠르냐”보다 “어떤 아키텍처를 강요하느냐”를 더 중요하게 봅니다.

    6. 주의사항과 트러블슈팅

    벤치마크를 망치는 실패 모드는 생각보다 정형화돼 있습니다. 중요한 건 증상보다 근본 원인을 읽는 겁니다.

    6-1. 하드웨어 가속이 적용된 줄 알았는데 사실은 CPU가 다 먹는 경우

    Hardware Acceleration은 설정값만으로 판단하면 한 번쯤은 꼭 헷갈립니다. 실제로는 아래 셋 중 하나가 많습니다.

    • 가속 장치는 보이지만 애플리케이션 프로세스 권한이 없음
    • 컨테이너에 /dev/dri 또는 GPU 장치가 전달되지 않음
    • 입력 코덱이나 픽셀 포맷이 가속 경로와 맞지 않아 소프트웨어 폴백 발생

    이때 증상은 비슷합니다. 평소엔 버틸 만한데 Live View나 Motion Detection에서 CPU가 갑자기 치솟고, 로그에는 가속 관련 문구만 애매하게 남습니다. 해결의 핵심은 “가속 활성화”가 아니라 실제 디코드/인코드 경로를 관측 가능한 상태로 만드는 것입니다. 장치 파일, 그룹 권한, 컨테이너 런타임 옵션, 로그를 같이 봐야 합니다.

    6-2. 모션 감지 때문에 디스크보다 CPU가 먼저 터지는 경우

    모션 감지는 생각보다 계산량이 큽니다. 특히 아래 조합이 위험합니다.

    • 메인 스트림 해상도로 감지
    • FPS를 높게 유지
    • 감지 영역을 화면 전체로 설정
    • 야간 노이즈가 많은 카메라

    근본 원인은 단순합니다. 감지 알고리즘은 프레임 차이를 계속 계산하니, 해상도와 프레임 수가 올라가면 비용도 빠르게 증가합니다. 야간 IR 노이즈가 심하면 실제 사람보다 센서 노이즈를 더 열심히 잡기도 하고요. 이럴 때는 감지는 서브 스트림, 보관은 메인 스트림으로 역할을 나누는 편이 현실적입니다. 이거 분리하고 나면 체감이 꽤 달라지더라고요.

    6-3. 저장 공간은 충분한데 I/O wait가 치솟는 경우

    이건 용량 문제가 아니라 저장 패턴 문제인 경우가 많습니다. 흔한 원인은 아래 셋입니다.

    • 작은 파일이 지나치게 많이 생성됨
    • 보존 정책 정리와 녹화 쓰기가 같은 시간대에 겹침
    • HDD에서 메타데이터 작업과 순차 쓰기가 동시에 경쟁함

    그래서 저는 항상 이렇게 판단합니다. “몇 TB 남았는가”보다 “삭제와 생성이 어떤 리듬으로 일어나는가”. 특히 HDD 단일 볼륨에서는 삭제 작업이 길어질수록 체감이 급격히 나빠질 수 있습니다. SSD 캐시 구간을 두거나, 적어도 정리 작업이 피크 시간과 겹치지 않게 분리하는 편이 안전합니다.

    6-4. 시간 동기화가 어긋나서 이벤트 시간이 이상한 경우

    NTP가 어긋나면 벤치마크 기록도 오염됩니다. 이건 단순히 이벤트 시간이 헷갈리는 수준에서 끝나지 않습니다. 카메라, NVR 서버, 브라우저, VM 호스트 시간이 어긋나면 이벤트 순서 해석, 재연결 시점 분석, 로그 상관관계가 전부 꼬입니다. 특히 여러 VM과 여러 카메라를 쓰는 환경이라면 타임존과 NTP 소스를 같이 맞춰야 합니다.

    제가 벤치마크 전에 체크하는 최소 항목은 아래와 같습니다.

    체크 항목 왜 중요한가 실패 시 보이는 증상
    카메라/NVR/호스트 시간 일치 로그 상관관계를 맞추기 위해 이벤트 재생 시점이 맞지 않음
    메인/서브 스트림 분리 여부 보기와 감지 비용을 분리하기 위해 Live View와 Motion 결과가 뒤엉킴
    가속 경로 실사용 확인 CPU 폴백을 걸러내기 위해 설정은 켰는데 CPU만 높음
    보존 정책 실행 시간 삭제 피크를 분리하기 위해 특정 시간대에만 끊김 발생
    파일 수 증가율 메타데이터 병목을 보기 위해 용량은 남는데 반응성 저하
    NVR 소프트웨어 벤치마크 결과를 보여주는 리소스 모니터링 대시보드

    Idle, Live View, Motion Detection 구간별로 CPU와 디스크 I/O가 어떻게 달라지는지 시각적으로 보여주는 예시입니다.

    7. NVR 소프트웨어 벤치마크 결과는 시나리오로 읽어야 합니다

    제가 NVR 소프트웨어 벤치마크 결과를 해석할 때 마지막으로 보는 건 단일 최대 채널 수가 아닙니다. 그 숫자는 눈길은 끌지만, 실제 운영 의사결정에는 의외로 도움이 적습니다. 대신 아래 질문이 훨씬 유용합니다.

    1. 아무도 안 볼 때 조용하게 잘 도는가
    2. 여러 명이 동시에 실시간 보기를 열면 어느 자원이 먼저 포화되는가
    3. 모션 이벤트가 몰릴 때 지연이 CPU 때문인지, 저장소 때문인지 구분되는가
    4. 서비스 재시작 후 스트림이 자동으로 안정적으로 복구되는가
    5. 일주일 이상 돌렸을 때 로그, 이벤트 DB, 저장소 정리 흐름이 예측 가능한가

    정리하면 판단은 이런 식으로 하시면 됩니다.

    운영 시나리오 우선 체크할 제품 판단 기준
    빠르게 홈랩 CCTV를 붙여보고 싶다 Shinobi UI 흐름, 카메라별 설정 반복 속도, 메인/서브 스트림 조정 편의성
    전통적인 Linux 운영 흐름이 익숙하다 ZoneMinder 프로세스 구조 파악, 로그 해석, 장기 운영 관리성
    카메라 수가 늘어날 예정 둘 다 테스트 필요 디스크 I/O 패턴, 삭제 부하, 감지 비용의 증가 방식
    낮은 전력과 저소음 홈서버가 중요하다 둘 다 동일 조건 필수 비교 Idle 사용량, 하드웨어 가속 실적용 여부, 서브 스트림 활용성

    제가 현장에서 가장 많이 하는 조언은 이겁니다. 제품을 고르기 전에 먼저 운영 시나리오를 고르세요. 상시 녹화 중심인지, 실시간 보기 중심인지, 감지를 카메라에서 올릴지 서버에서 계산할지부터 정해야 합니다. 그다음에야 Shinobi가 더 편한지, ZoneMinder가 더 맞는지가 보입니다. 관련해서 홈랩 CCTV 저장소 설계나 GPU 패스스루 글도 같이 보면 판단이 훨씬 빨라집니다.

    실무 체크리스트도 하나 남겨보겠습니다. 새 환경에서 둘을 비교할 때 저는 아래 항목을 모두 통과해야 “쓸 만한 구성”이라고 봅니다.

    실무 체크리스트 통과 기준 실패 시 조치 방향
    RTSP 입력 안정성 재연결 없이 일정 시간 유지 카메라 펌웨어, 스위치, PoE, TCP/UDP 전송 방식 점검
    Live View 다중 접속 브라우저 수 증가 시 급격한 끊김 없음 서브 스트림 보기 분리, 브라우저 레이아웃 축소
    상시 녹화 CPU보다 I/O 패턴이 안정적 저장 경로 분리, 재인코딩 여부 재검토
    모션 감지 이벤트 지연이 누적되지 않음 감지 해상도, FPS, 영역 축소 또는 카메라 이벤트 활용
    보존 정책 정리 삭제 시점에 서비스 품질 급락 없음 정리 시간 분산, SSD 캐시, 파일 수 감소 전략
    재시작 복구 카메라 세션이 자동 복구 서비스 의존성, 네트워크 타이밍, 타임아웃 설정 점검

    8. 정리: 어떤 분에게 어떤 선택이 맞을까요?

    이번 ZoneMinder 비교를 마무리하면서 제 결론을 짧게 정리하면 이렇습니다.

    • Shinobi는 빠르게 붙여보고 자주 만지면서 최적점을 찾는 홈랩 스타일에 잘 맞습니다.
    • ZoneMinder는 구조를 이해하고 운영 규칙을 세운 뒤 길게 가져가는 스타일에 잘 맞습니다.
    • 둘 중 무엇이 더 빠른지는 절대값보다 입력 스트림, 감지 방식, 저장소 구조, 가속 경로가 더 크게 좌우합니다.

    저는 NVR을 평가할 때 제품 자체보다 워크로드 분리 능력을 더 중요하게 봅니다. 실시간 보기와 녹화를 분리할 수 있는지, 감지와 보관을 분리할 수 있는지, 문제가 났을 때 그 원인을 CPU인지 디스크인지 네트워크인지 빠르게 좁힐 수 있는지가 핵심입니다. 이 기준으로 보면 벤치마크는 숫자 놀이보다 운영 리스크를 줄이는 설계 검증에 더 가깝습니다.

    혹시 바로 테스트에 들어가실 거라면 저는 아래 순서를 권합니다.

    1. 동일 모델 카메라 2~4대로 메인/서브 스트림 특성부터 기록
    2. 상시 녹화와 모션 감지를 분리해 각각 부하 측정
    3. Live View 다중 접속과 보존 정책 정리 시점을 별도 테스트
    4. SSD와 HDD에서 파일 수 증가, 삭제 부하, 재시작 복구까지 확인

    이 과정을 거치면 다음에 카메라를 늘릴 때도 덜 흔들립니다. 결국 오픈소스 NVR 선택은 제품명보다 운영 기준이 먼저입니다. 그 기준만 잘 세워두면 Shinobi든 ZoneMinder든 훨씬 덜 억울하게 굴릴 수 있습니다.

    홈랩 CCTV용 Shinobi 성능과 ZoneMinder 비교 요약 인포그래픽

    Shinobi 성능과 ZoneMinder 비교 포인트를 한눈에 정리한 요약 인포그래픽 자리입니다.

    자주 묻는 질문

    Q1. 홈랩 초보자는 어떤 쪽이 더 쉬운가요?

    설정 변경을 자주 하면서 체감을 빨리 얻고 싶다면 Shinobi 쪽이 더 편하게 느껴질 수 있습니다. 반대로 Linux 서비스 구조와 장기 운영 습관이 익숙하다면 ZoneMinder도 충분히 괜찮은 선택입니다. 중요한 건 초보 여부보다 운영 방식을 얼마나 자주 바꿀지입니다.

    Q2. 벤치마크는 몇 대 카메라부터 의미가 있나요?

    최소 2대 이상은 있어야 동시 보기, 저장 부하, 재연결 안정성 차이가 드러납니다. 다만 진짜 의미 있는 비교를 하려면 수량보다도 같은 모델, 같은 코덱, 같은 FPS로 맞추는 게 더 중요합니다.

    Q3. CPU보다 디스크가 더 중요할 때도 있나요?

    네, 꽤 자주 그렇습니다. 특히 상시 녹화와 이벤트 파일이 같이 쌓이고 오래된 파일 정리까지 겹치면 저장 장치 응답성이 전체 체감 성능을 좌우합니다. 용량 여유가 많아도 파일 수와 삭제 패턴 때문에 느려질 수 있습니다.