13년차의 서버실

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

[태그:] 홈랩

  • [Proxmox] Proxmox GPU 패스스루: Error 43·IOMMU 문제 완벽 해결 가이드

    [Proxmox] Proxmox GPU 패스스루: Error 43·IOMMU 문제 완벽 해결 가이드

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘은 홈랩(Homelab)에서 Proxmox VE를 운영하면서 정말 많은 분들이 도전하고 그만큼 많은 삽질을 하는 주제, 바로 Proxmox GPU 패스스루(Passthrough)와 GPU 가상화에 대해 이야기해보려고 합니다. 저도 처음에는 ‘이거 뭐 별거 있겠어?’ 하고 덤볐다가, 밤새도록 머리 싸매고 고민했던 경험이 한두 번이 아니거든요. 특히 Error 43이나 IOMMU 관련 문제들은 정말이지… 멘탈을 흔들리게 하죠. 😅

    하지만 결국 해결하고 나면, 가상머신(Virtual Machine, VM)에서 직접 게임을 돌리거나, AI/ML(머신러닝) 작업을 하거나, 고화질 미디어 서버를 구축하는 등 무궁무진한 가능성이 열리게 됩니다. 제가 직접 부딪히고 해결했던 경험들을 바탕으로, 여러분들이 겪을 수 있는 흔한 문제들과 그 해결 전략을 자세히 알려드릴게요. 멘토처럼 든든하게 옆에서 알려드리는 마음으로 시작해볼까요?

    Proxmox GPU 패스스루의 전체적인 구조를 한눈에 볼 수 있는 다이어그램입니다. 호스트 서버의 물리 GPU가 가상머신으로 직접 연결되는 방식이죠.

    Proxmox GPU 패스스루, vGPU, 그리고 IOMMU 개념 이해하기

    본격적인 설정에 들어가기 전에, 몇 가지 핵심 개념을 확실히 짚고 넘어가야 합니다. 이게 헷갈리면 나중에 삽질의 깊이가 더 깊어지거든요. 제가 처음 그랬습니다. ㅎㅎ

    GPU 패스스루 (GPU Passthrough)

    • 무엇인가요? 쉽게 말해, Proxmox 호스트 서버에 꽂혀 있는 물리적인 GPU(그래픽 카드)를 특정 가상머신이 단독으로 사용할 수 있도록 넘겨주는 기술입니다. 마치 그 VM에 GPU가 직접 꽂혀 있는 것처럼 만들어주는 거죠.
    • 왜 필요한가요? 게임, 영상 편집, 딥러닝(Deep Learning) 학습 등 높은 그래픽 성능이나 GPU 병렬 연산 능력이 필요한 작업들을 가상머신 환경에서 효율적으로 처리하려면 필요해요. 일반적인 가상화 환경에서는 GPU 자원을 공유하기 때문에 제 성능을 내기 어렵거든요.

    vGPU (Virtual GPU)

    • 무엇인가요? GPU 패스스루가 하나의 GPU를 하나의 VM에 통째로 넘겨주는 방식이라면, vGPU는 하나의 물리 GPU를 여러 가상머신이 나눠서 사용할 수 있도록 하는 기술입니다.
    • 홈랩에선 어떤가요? 사실 vGPU는 NVIDIA GRID 같은 특정 엔터프라이즈용 GPU와 드라이버, 라이선스 정책이 필요해서 홈랩 환경에서 쉽게 구현하기는 어렵더라고요. 저도 여러 번 시도해봤지만, 비용과 복잡성 때문에 결국 패스스루에 집중하게 되었어요. 혹시 나중에 더 발전된 오픈소스 vGPU 솔루션이 나온다면 꼭 다뤄보고 싶네요!

    IOMMU (Input/Output Memory Management Unit)

    • 무엇인가요? IOMMU는 CPU와 PCIe 장치(GPU 포함) 사이의 메모리 접근을 관리하는 장치예요. 쉽게 말해, 가상머신이 물리 장치에 직접 접근할 수 있도록 메모리 주소를 매핑(Mapping)해주는 역할을 하죠.
    • 왜 중요한가요? GPU 패스스루를 위해서는 IOMMU가 필수적으로 활성화되어야 해요. 그래야 Proxmox 호스트가 GPU를 VM에 안전하고 효율적으로 넘겨줄 수 있거든요. 이게 비활성화되어 있으면 아무리 다른 설정을 잘해도 패스스루는 불가능합니다. 저도 처음에 BIOS에서 이걸 빼먹어서 몇 시간을 날렸던 기억이 생생하네요 😭.

    Error 43

    • 무엇인가요? Windows 가상머신에 NVIDIA 또는 AMD 그래픽 드라이버를 설치했을 때, 장치 관리자에서 ‘이 장치에 대한 드라이버를 로드할 수 없습니다. (코드 43)’이라는 메시지가 뜨는 현상이에요.
    • 왜 발생하나요? 대부분의 GPU 드라이버는 가상머신 환경에서 설치되는 것을 막기 위한 보호 장치를 가지고 있어요. VM 환경임을 감지하면 드라이버가 제대로 동작하지 않도록 하는 거죠. 이걸 우회하는 방법을 찾아야 합니다.

    Proxmox GPU 패스스루 실전 구현: 단계별 설정

    자, 이제 개념을 알았으니 직접 Proxmox에 GPU 패스스루를 설정해보겠습니다. 제가 겪었던 시행착오들을 줄여드리기 위해 핵심만 콕콕 짚어드릴게요.

    1. BIOS/UEFI 설정 변경

    가장 기본 중의 기본입니다. 메인보드 BIOS/UEFI 설정으로 들어가서 아래 항목들을 활성화해야 합니다.

    • Intel VT-d (Intel CPU 사용자) 또는 AMD-V (AMD CPU 사용자): 가상화 기술 및 IOMMU를 활성화하는 옵션이에요.
    • SR-IOV (Supported if available): PCIe 장치 가상화 기술인데, GPU 패스스루에 직접적인 필수 요소는 아니지만 활성화해두면 좋아요.
    • Above 4G Decoding: 일부 메인보드에서 GPU 메모리 주소 할당을 위해 필요합니다. GPU 패스스루 시 안정성을 높여줄 수 있어요.
    • CSM (Compatibility Support Module) 비활성화: UEFI 부팅 환경을 제대로 활용하기 위함입니다.

    설정을 변경한 후에는 반드시 저장하고 재부팅해주세요.

    2. Proxmox VE 호스트 설정

    이제 Proxmox 쉘에 접속하여 필요한 모듈을 로드하고 GRUB 설정을 변경해야 합니다.

    2.1. 필수 모듈 로드

    IOMMU와 VFIO(Virtual Function I/O) 관련 모듈을 로드해야 해요. /etc/modules 파일을 열어 아래 내용을 추가합니다.

    echo "vfio" >> /etc/modules
    echo "vfio_iommu_type1" >> /etc/modules
    echo "vfio_pci" >> /etc/modules
    echo "vfio_virqfd" >> /etc/modules
    
    # 변경사항 적용을 위해 모듈 로드 (재부팅해도 유지됨)
    update-initramfs -u -k all
    

    이후 reboot 명령으로 재부팅하는 것을 추천합니다.

    2.2. GRUB 설정 변경 (IOMMU 활성화 및 GPU 블랙리스트)

    GRUB 부트로더 설정을 통해 IOMMU를 활성화하고, Proxmox 호스트가 패스스루할 GPU를 사용하지 못하도록 블랙리스트에 추가해야 해요.

    nano /etc/default/grub
    

    GRUB_CMDLINE_LINUX_DEFAULT 라인을 찾아서 아래와 같이 수정합니다.

    • Intel CPU: GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
    • AMD CPU: GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"

    여기에 GPU 블랙리스트 설정을 추가합니다. 먼저 패스스루할 GPU의 벤더 ID와 장치 ID를 확인해야 해요. 쉘에서 다음 명령어를 실행합니다.

    lspci -nn | grep -i vga
    

    출력 결과는 보통 이런 식일 겁니다:

    01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1)
    01:00.1 Audio device [0403]: NVIDIA Corporation GP107 High Definition Audio Controller [10de:0fb9] (rev a1)
    

    여기서 [10de:1c82]와 [10de:0fb9]가 각각 GPU 비디오 장치와 오디오 장치의 벤더 ID:장치 ID예요. 이 값들을 콤마(,)로 구분하여 GRUB 설정에 추가합니다.

    # Intel CPU 예시 (GTX 1050 Ti 기준)
    GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt video=efifb:off,vesafb:off disable_vga=1 vfio_pci.ids=10de:1c82,10de:0fb9"
    

    ⚠️ 중요: video=efifb:off,vesafb:off disable_vga=1 옵션은 Proxmox 호스트가 해당 GPU를 초기화하지 않도록 하여 패스스루 시 발생할 수 있는 충돌을 방지해요. 이 옵션 없이는 Error 43이 발생할 확률이 매우 높아집니다.

    GRUB 설정을 변경했으면, 반드시 업데이트하고 initramfs를 다시 생성해야 합니다.

    update-grub
    update-initramfs -u -k all
    reboot
    
    Proxmox VM 설정에서 PCIe 장치 추가 화면

    Proxmox 웹 UI에서 가상머신에 GPU 장치를 추가하는 모습입니다. 올바르게 설정되었다면 목록에 GPU가 보일 거예요.

    3. 가상머신 (VM) 설정

    Proxmox 호스트 설정이 끝났으니, 이제 GPU를 사용할 가상머신을 설정할 차례예요.

    3.1. VM 생성 시 주의사항

    • OS Type: Windows 사용 시 ‘Microsoft Windows’ 선택.
    • BIOS: ‘OVMF (UEFI)’를 선택해야 해요. 레거시 BIOS는 패스스루 시 문제가 많더라고요.
    • Machine: ‘q35’를 선택하는 게 좋아요. PCIe 장치 관리에 더 최적화되어 있거든요.
    • CPU: ‘Host’로 설정하여 호스트 CPU의 모든 기능을 VM에서 활용할 수 있도록 합니다.
    • Disk: SATA 또는 SCSI(VirtIO)를 사용하되, SCSI VirtIO를 추천합니다. 성능이 더 좋아요.

    3.2. GPU 장치 추가

    VM 생성 후, VM의 ‘Hardware’ 탭으로 이동하여 ‘Add’ -> ‘PCI Device’를 선택합니다.

    • PCIe Device: 패스스루할 GPU의 비디오 장치와 오디오 장치를 각각 추가해요.
    • All Functions: 활성화합니다.
    • Primary GPU: 패스스루할 GPU를 VM의 주 GPU로 설정하려면 활성화합니다.
    • ROM-Bar: 활성화하는 것을 추천해요. 특히 구형 GPU에서 문제가 생길 때 해결책이 될 수 있거든요.

    3.3. QEMU/KVM 추가 설정 (중요!)

    이제 Error 43을 피하기 위한 핵심 설정입니다. Proxmox 웹 UI에서는 설정할 수 없는 고급 옵션이므로, Proxmox 쉘에서 qm set 명령어를 사용해야 해요.

    # VM ID는 여러분의 VM ID로 변경해주세요 (예: 100)
    # 'kvm=off,hv_vendor_id=null' 옵션으로 VM 환경 감지 우회
    qm set <VM_ID> -args '-cpu host,kvm=off,hv_vendor_id=null'
    # VM ID 100번에 대한 예시
    qm set 100 -args '-cpu host,kvm=off,hv_vendor_id=null'
    

    또는 VM 설정 파일 (/etc/pve/qemu-server/<VM_ID>.conf)을 직접 수정하여 args: 라인을 추가할 수도 있어요.

    # /etc/pve/qemu-server/100.conf 예시
    args: -cpu host,kvm=off,hv_vendor_id=null
    

    이 설정은 QEMU/KVM이 가상화 환경임을 숨기도록 도와주며, NVIDIA나 AMD 드라이버가 VM 환경을 감지하지 못하게 해요. 저도 이 설정 하나로 몇 번의 Error 43을 해결했었습니다. 💡

    ⚠️ 흔한 문제와 해결 전략 (삽질 경험 공유)

    제가 Proxmox GPU 패스스루를 하면서 가장 많이 겪었던 문제들과 그 해결 전략을 솔직하게 공유합니다. 여러분은 저처럼 삽질하지 마세요!

    1. IOMMU 그룹 문제 (가장 흔하고 골치 아픈 문제)

    문제: IOMMU가 활성화되었는데도 GPU가 VM에 제대로 패스스루되지 않거나, VM이 부팅되지 않는 경우가 있어요. lspci -nnk 명령으로 보면 분명 VFIO 드라이버가 붙어있는데 말이죠.

    원인: 이는 대부분 IOMMU 그룹 문제 때문입니다. IOMMU는 장치들을 ‘그룹’으로 묶어서 관리하는데, 만약 GPU와 다른 중요 장치(예: USB 컨트롤러, PCIe 브리지)가 같은 그룹에 묶여 있다면, GPU만 개별적으로 패스스루하기 어렵더라고요. IOMMU는 그룹 단위로 장치를 격리하려 하기 때문이죠.

    확인 방법: 다음 명령어로 현재 시스템의 IOMMU 그룹을 확인해볼 수 있어요.

    for iommu_group in $(find /sys/kernel/iommu_groups/ -maxdepth 1 -mindepth 1 -type d); do echo "IOMMU Group $(basename "$iommu_group")"; for device in $(ls -v "$iommu_group"/devices/); do echo -n $'\t'; lspci -nns "$device"; done; done
    

    이 결과를 보고 GPU와 그에 딸린 오디오 장치만 하나의 그룹에 묶여 있고, 다른 장치들과는 분리되어 있는지 확인해야 해요. 만약 GPU와 다른 장치들이 같은 그룹에 묶여 있다면… 아, 골치 아파지는 거죠.

    해결 전략:

    • 다른 PCIe 슬롯 사용: 메인보드마다 PCIe 슬롯의 IOMMU 그룹 분리 방식이 달라요. GPU를 다른 PCIe 슬롯에 꽂아보세요. 의외로 간단하게 해결되는 경우가 많습니다.
    • BIOS/UEFI 설정 재확인: ‘Above 4G Decoding’이나 ‘PCIe ARI Support’ 같은 옵션들이 IOMMU 그룹화에 영향을 줄 수 있어요. 활성화/비활성화해보면서 테스트해볼 필요가 있습니다.
    • 듀얼 GPU 사용: 만약 내장 그래픽(iGPU)이 있거나 다른 저렴한 그래픽 카드가 있다면, 호스트 Proxmox는 그 GPU를 사용하고, 패스스루할 GPU는 완전히 VM에 전용으로 넘겨주는 방식이 가장 확실해요. 이렇게 하면 IOMMU 그룹 문제가 발생할 확률이 현저히 줄어들어요. 저도 결국 이렇게 구성해서 안정적으로 사용하고 있습니다.
    • (고급) ACS Override Patch: 일부 커뮤니티에서는 ACS(Access Control Services) Override 패치를 커널에 적용하여 IOMMU 그룹을 강제로 분리하는 방법도 사용하지만, 이는 커널 컴파일이 필요하고 시스템 안정성에 영향을 줄 수 있어서 초보자에게는 절대 추천하지 않습니다. 잘못하면 시스템이 벽돌이 될 수 있어요.

    2. Windows VM에서 Error 43 발생

    문제: 위에서 설명한 `qm set` 명령어를 통한 `kvm=off,hv_vendor_id=null` 설정에도 불구하고 여전히 Error 43이 발생하는 경우가 있어요.

    원인: NVIDIA나 AMD 드라이버가 VM 환경을 감지하는 방식이 점점 더 교묘해지고 있어요. 또는 BIOS/UEFI 설정이 제대로 안 되어 있을 수도 있습니다.

    해결 전략:

    • BIOS/UEFI 설정 재확인: ‘Above 4G Decoding’이 활성화되어 있는지, ‘CSM’이 비활성화되어 있는지 다시 한번 확인해주세요. 이 두 가지는 Error 43과 깊은 연관이 있어요.
    • 최신 드라이버 vs. 구형 드라이버: 때로는 최신 드라이버보다 약간 구형 드라이버가 VM 환경에서 더 잘 작동하는 경우가 있어요. 특히 NVIDIA 드라이버는 특정 버전에서 VM 감지 로직이 더 강화되기도 합니다. 몇 가지 드라이버 버전을 시도해보는 것도 방법이에요.
    • Windows 업데이트 확인: Windows 업데이트가 드라이버와 충돌을 일으키는 경우도 있어요. 드라이버 설치 전 Windows를 최신 상태로 업데이트하거나, 반대로 특정 업데이트를 제거해보는 것도 시도해볼 수 있습니다.

    3. VM 부팅 시 화면 출력 안 됨

    문제: VM은 시작되는데 모니터에 아무것도 출력되지 않거나, Proxmox 웹 UI의 콘솔 화면만 보이고 GPU 출력은 나오지 않는 경우가 있어요.

    원인: GPU 초기화 문제, 또는 VM의 메인 디스플레이 설정 문제입니다.

    해결 전략:

    • BIOS/UEFI의 Primary Graphics Adapter 설정: 메인보드 BIOS에서 Primary Graphics Adapter(주 그래픽 어댑터) 설정을 ‘PCIe’ 또는 ‘Discrete Graphics’로 변경해보세요.
    • Proxmox GRUB 설정 재확인: video=efifb:off,vesafb:off disable_vga=1 옵션이 제대로 적용되었는지 확인해요. 이 옵션이 없으면 Proxmox 호스트가 GPU를 먼저 잡아버려서 VM이 사용할 수 없게 되는 경우가 많더라고요.
    • VM에 가상 디스플레이 제거: VM의 ‘Hardware’ 탭에서 ‘Display’ 장치를 ‘none’으로 설정해보세요. GPU 패스스루 시에는 가상 디스플레이가 필요 없으며, 오히려 충돌을 일으킬 수 있거든요.

    ✅ 검증 및 결과 확인

    모든 설정을 마치고 VM을 부팅했다면, 이제 드디어 결과물을 확인할 차례예요! 이 순간이 제일 두근거리고, 성공하면 희열이 엄청나죠. 🎉

    1. Windows 장치 관리자 확인

    Windows VM에 접속하여 ‘장치 관리자(Device Manager)’를 열어보세요. ‘디스플레이 어댑터(Display Adapters)’ 항목 아래에 패스스루한 GPU가 정상적으로 인식되고, 경고 표시(느낌표) 없이 드라이버가 설치되어 있다면 성공이에요!

    만약 아직도 Error 43이 보인다면, 위에 제시된 트러블슈팅 방법을 다시 한번 꼼꼼히 확인하고 재부팅을 반복해보세요. 때로는 인내심이 필요해요. 끈기가 정답이더라고요.

    2. GPU-Z 또는 FurMark로 테스트

    GPU-Z 같은 유틸리티를 설치해서 GPU 정보가 정확하게 표시되는지 확인하고, FurMark 같은 벤치마크 툴로 간단한 스트레스 테스트를 진행해보세요. 이때 GPU 온도가 정상적으로 표시되고, 프레임이 잘 나온다면 완벽하게 패스스루가 된 거예요. 저는 이때마다 ‘드디어 됐다!’ 하고 외치곤 합니다. 😂

    Windows VM에서 GPU-Z를 통한 Proxmox GPU 패스스루 확인

    Windows VM 안에서 GPU-Z를 실행한 모습입니다. 물리 GPU 정보가 그대로 잘 보이죠? 이 화면을 보면 얼마나 뿌듯한지 몰라요!

    마무리하며: 삽질 끝의 뿌듯함, 그리고 다음 단계

    Proxmox GPU 패스스루, 결코 쉽지 않은 여정입니다. 특히 IOMMU 그룹 문제나 Error 43 같은 예상치 못한 복병들을 만나면 ‘이거 그냥 포기할까?’ 하는 생각도 들죠. 저도 수도 없이 그런 생각을 했었어요. 하지만 포기하지 않고 끈기 있게 해결해나가다 보면, 결국 원하는 결과를 얻게 되고 그 과정에서 정말 많은 것을 배우게 됩니다.

    이렇게 GPU 패스스루를 성공하고 나면, 여러분의 Proxmox 홈랩은 한 단계 더 강력해질 거예요. 고성능 게이밍 머신, AI 개발 환경, 또는 고화질 트랜스코딩이 가능한 미디어 서버 등 게임부터 AI 개발까지 뭐든 가능해지는 기반이 마련되는 거죠.

    다음번에는 이렇게 구축된 강력한 VM 환경을 활용하여 Docker 컨테이너 환경을 효율적으로 관리하는 방법이나, ZFS 또는 Ceph를 이용한 스토리지 구성에 대해 다뤄볼 수도 있겠네요. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요! 제 삽질 경험이 여러분의 시간을 아껴주기를 바라며, 오늘의 이야기는 여기서 마치겠습니다. 긴 글 읽어주셔서 감사합니다!

    Proxmox GPU 패스스루 성공과 실패 시나리오 비교 인포그래픽

    Proxmox GPU 패스스루의 성공과 실패 시나리오를 한눈에 비교해주는 인포그래픽입니다. 성공했을 때의 짜릿함과 실패했을 때의 좌절감이 느껴지시나요?

  • [Proxmox] Ansible로 Proxmox 환경 1년 자동화 회고: 효율과 함정

    [Proxmox] Ansible로 Proxmox 환경 1년 자동화 회고: 효율과 함정

    Ansible로 Proxmox 환경 1년 자동화 회고: 효율과 함정

    안녕하세요, 13년차 서버실 지킴이입니다. 인프라 엔지니어로 일하다 보면 반복적인 작업에 지칠 때가 많죠. 특히 홈랩(Homelab)을 운영하는 저 같은 경우는 작은 규모라도 손이 많이 가는 일들이 부지기수입니다. 새로운 가상 머신(Virtual Machine, VM)을 만들고, 네트워크를 설정하고, 백업을 돌리는 일들은 매번 마우스 클릭과 타이핑의 연속이었거든요. ‘이걸 좀 더 효율적으로 할 수 없을까?’ 하는 고민은 늘 저를 따라다녔습니다. 그러다 1년 전, 제 Proxmox(프록스목스) 환경에 Ansible(앤서블)을 도입하기로 결심했고, 지금까지 정말 많은 것을 배우고 경험했습니다.

    오늘은 제가 지난 1년간 Ansible Proxmox 자동화를 구축하고 운영하면서 느꼈던 효율성과, 예상치 못했던 함정들, 그리고 그 해결 과정까지 솔직하게 풀어보려고 합니다. 혹시 저처럼 수동 작업의 굴레에서 벗어나고 싶은 인프라 엔지니어 분들이 계시다면, 이 글이 작은 힌트가 되기를 바랍니다.

    Ansible과 Proxmox를 활용한 홈랩 자동화 아키텍처 다이어그램

    Proxmox와 Ansible이 어떻게 연동되어 홈랩 인프라를 자동화하는지 보여주는 추상적인 아키텍처 다이어그램입니다. Ansible 컨트롤러가 SSH를 통해 Proxmox 노드와 통신하며, Proxmox API를 활용하여 VM, LXC 컨테이너, 스토리지, 네트워크 등의 리소스를 관리하는 모습을 나타냅니다.

    Ansible과 Proxmox, 왜 찰떡궁합일까요?

    먼저, 두 기술의 핵심 개념을 짧게 짚고 넘어가죠.

    • Ansible (앤서블): 에이전트리스(Agentless) 방식의 IT 자동화 도구입니다. 관리 대상 서버에 별도의 에이전트(Agent)를 설치할 필요 없이 SSH(Secure Shell) 연결을 통해 명령을 실행하고 작업을 수행하더라고요. YAML(야믈) 기반의 플레이북(Playbook)으로 인프라의 상태를 코드화(Infrastructure as Code, IaC)할 수 있다는 게 가장 큰 장점입니다.
    • Proxmox VE (프록스목스 가상 환경): 오픈소스 기반의 강력한 가상화 플랫폼입니다. 리눅스 컨테이너(LXC)와 KVM(Kernel-based Virtual Machine) 기반의 가상 머신을 모두 지원하며, 웹 기반 UI를 통해 쉽게 관리할 수 있죠. 엔터프라이즈급 기능을 홈랩에서도 무료로 활용할 수 있다는 점에서 인기가 많습니다.

    Ansible은 Proxmox가 제공하는 풍부한 API(Application Programming Interface)를 활용해 VM 생성, 네트워크 설정, 스냅샷 관리 등 거의 모든 작업을 자동화할 수 있더라고요. Ansible Proxmox 모듈 덕분에 복잡한 API 호출 없이도 YAML 플레이북으로 직관적인 작업이 가능합니다. 말 그대로 인프라를 코드로 관리하는 거죠.

    1년 간의 Ansible Proxmox 자동화, 실전 구현 경험

    처음엔 저도 ‘이게 진짜 될까?’ 싶었거든요. 그런데 막상 해보니 생각보다 강력하더라고요. 기본적인 VM 생성부터 복잡한 네트워크 설정, 백업 관리까지 Ansible 플레이북 하나로 뚝딱 해결되는 걸 보고 감탄했습니다.

    기본적인 VM 생성 플레이북

    가장 많이 사용하는 작업이죠. 새로운 VM을 만들고, OS 이미지를 마운트하고, 기본적인 리소스(CPU, RAM, 디스크)를 할당하는 플레이북입니다. 저는 주로 community.general.proxmox 컬렉션의 모듈들을 활용했습니다.

    # vm_create.yml
    ---
    - name: Proxmox에 새로운 VM 생성 및 설정
      hosts: proxmox_host
      gather_facts: no
      vars:
        vm_id: 101
        vm_name: my-test-vm
        vm_memory: 2048 # MB
        vm_cores: 2
        vm_disk_size: 30 # GB
        vm_iso: local:iso/ubuntu-22.04-live-server-amd64.iso # Proxmox ISO 스토리지 경로
        vm_bridge: vmbr0
        vm_ip: 192.168.1.101/24
        vm_gw: 192.168.1.1
        vm_dns: 8.8.8.8
    
      tasks:
        - name: VM 생성
          community.general.proxmox_kvm:
            api_host: "{{ inventory_hostname }}"
            api_user: root@pam
            api_password: "{{ proxmox_root_password }}" # ansible vault로 관리
            vmid: "{{ vm_id }}"
            name: "{{ vm_name }}"
            memory: "{{ vm_memory }}"
            cores: "{{ vm_cores }}"
            scsihw: virtio-scsi-pci
            bootdisk: scsi0
            iso: "{{ vm_iso }}"
            net:
              - name: net0
                model: virtio
                bridge: "{{ vm_bridge }}"
            state: present
          register: create_vm_result
    
        - name: 디스크 추가 (VM 생성 시 디스크 옵션이 없는 경우)
          community.general.proxmox_disk:
            api_host: "{{ inventory_hostname }}"
            api_user: root@pam
            api_password: "{{ proxmox_root_password }}"
            vmid: "{{ vm_id }}"
            disk: scsi0
            size: "{{ vm_disk_size }}G"
            storage: local-lvm # Proxmox 스토리지 이름
            format: qcow2
            state: present
          when: create_vm_result.changed # VM이 새로 생성되었을 때만 디스크 추가
    
        - name: VM 부팅
          community.general.proxmox_kvm:
            api_host: "{{ inventory_hostname }}"
            api_user: root@pam
            api_password: "{{ proxmox_root_password }}"
            vmid: "{{ vm_id }}"
            state: started
          when: create_vm_result.changed

    위 플레이북은 특정 Proxmox 호스트(proxmox_host)에 새로운 VM을 만들고 시작하는 예시입니다. proxmox_root_password 같은 민감 정보는 나중에 설명할 Ansible Vault(볼트)로 암호화해서 관리하는 게 좋습니다. 그리고 VM 생성 시 디스크 옵션을 한 번에 주기 어려운 모듈 버전도 있어서, 저는 디스크 추가 태스크를 따로 분리하기도 했어요.

    네트워크 설정 자동화 (Bridge, VLAN)

    Proxmox 노드의 네트워크 인터페이스 설정도 Ansible로 자동화하더라고요. 특히 브릿지(Bridge)나 VLAN(Virtual Local Area Network) 태그를 적용할 때 정말 유용했습니다.

    # network_config.yml
    ---
    - name: Proxmox 노드 네트워크 설정
      hosts: proxmox_host
      become: yes
      tasks:
        - name: 네트워크 인터페이스 백업
          ansible.builtin.copy:
            src: /etc/network/interfaces
            dest: /etc/network/interfaces.bak_{{ ansible_date_time.iso8601_basic_short }}
            remote_src: yes
    
        - name: 새로운 네트워크 설정 적용
          ansible.builtin.template:
            src: interfaces.j2
            dest: /etc/network/interfaces
            owner: root
            group: root
            mode: '0644'
          notify: 네트워크 서비스 재시작
    
      handlers:
        - name: 네트워크 서비스 재시작
          ansible.builtin.systemd:
            name: networking
            state: restarted
    # templates/interfaces.j2
    # /etc/network/interfaces - Proxmox Host Network Configuration
    
    auto lo
    iface lo inet loopback
    
    auto vmbr0
    iface vmbr0 inet static
      address {{ vmbr0_ip }}
      netmask {{ vmbr0_netmask }}
      gateway {{ vmbr0_gateway }}
      bridge-ports {{ physical_nic }}
      bridge-stp off
      bridge-fd 0
    
    # VLAN 10 for Management
    auto vmbr0.10
    iface vmbr0.10 inet static
      address {{ vmbr0_10_ip }}
      netmask {{ vmbr0_10_netmask }}
      vlan-raw-device vmbr0

    template 모듈을 사용해서 Jinja2 템플릿 파일(interfaces.j2)로 Proxmox 호스트의 /etc/network/interfaces 파일을 관리했습니다. 이렇게 하면 네트워크 구성이 코드화되어 버전 관리도 쉽고, 여러 노드에 동일한 설정을 적용하기도 편리하죠. notify 핸들러를 사용해서 변경 사항 적용 후 네트워크 서비스를 자동으로 재시작하도록 했습니다.

    백업 및 스냅샷 관리

    데이터는 소중하니까요. 자동화된 백업과 스냅샷 관리는 필수입니다. 특히 홈랩에서는 실수로 날려버리는 경우가 많아서 꼭 필요하더라고요.

    # backup_snapshot.yml
    ---
    - name: Proxmox VM 백업 및 스냅샷 관리
      hosts: proxmox_host
      gather_facts: no
      vars:
        vmid_to_manage: 101
        backup_storage: backup_pool # Proxmox 백업 스토리지 이름
    
      tasks:
        - name: VM 스냅샷 생성
          community.general.proxmox_snap:
            api_host: "{{ inventory_hostname }}"
            api_user: root@pam
            api_password: "{{ proxmox_root_password }}"
            vmid: "{{ vmid_to_manage }}"
            snapname: "daily-snapshot-{{ ansible_date_time.iso8601_basic_short }}"
            description: "Daily snapshot by Ansible"
            state: present
    
        - name: VM 백업 실행 (shell 명령 사용)
          ansible.builtin.shell: |
            vzdump {{ vmid_to_manage }} \
              --storage {{ backup_storage }} \
              --compress zstd \
              --mode stop
          register: backup_result
          async: 3600
          poll: 60

    proxmox_snap 모듈로 특정 VM의 스냅샷을 생성했습니다. 백업의 경우, Proxmox 환경에 따라 직접 vzdump 유틸리티를 호출하는 방식도 많이 사용합니다. 이렇게 하면 더 세밀한 제어가 가능하더라고요. async: 3600과 poll: 60을 사용해서 백업이 완료될 때까지 Ansible이 기다려줍니다.

    Ansible 플레이북 실행 성공 결과 터미널 화면

    Ansible 플레이북이 성공적으로 실행되어 Proxmox 환경에 VM이 생성되고 네트워크 설정이 적용되는 과정을 보여주는 터미널 화면입니다. 각 태스크의 성공 여부와 변경 사항이 녹색으로 표시되어 자동화가 잘 작동하고 있음을 나타냅니다.

    ⚠️ 삽질 대잔치! Proxmox Ansible 자동화의 함정과 해결책

    자동화가 늘 쉬웠던 건 아닙니다. 저도 꽤 많은 삽질을 했거든요. 특히 Proxmox는 업데이트 주기가 빠르고, Ansible 모듈도 계속 발전하다 보니 버전 호환성이나 예상치 못한 동작으로 골머리를 앓기도 했습니다.

    모듈 버전 호환성 문제

    초기에는 community.general 컬렉션의 proxmox 모듈이 자주 업데이트되면서, 특정 옵션이 사라지거나 이름이 바뀌는 경우가 많았습니다. 제가 작성했던 플레이북이 갑자기 에러를 뿜어낼 때마다 당황했죠.

    • 해결책: `ansible-galaxy collection install community.general:==X.Y.Z` 명령으로 특정 버전의 컬렉션을 설치하고 고정했습니다. 프로덕션 환경이라면 더더욱 버전을 명확히 관리하는 것이 중요하더라고요.

    인증 방식의 복잡함

    Proxmox API에 접근하려면 인증이 필요합니다. 처음엔 root 계정의 비밀번호를 직접 플레이북에 넣었는데, 보안상 너무 위험하다는 걸 깨달았죠.

    • 해결책: Proxmox의 API 토큰(API Token) 기능을 활용했습니다. 특정 사용자에게만 권한을 부여한 API 토큰을 발급받아 사용하고, 이 토큰 정보는 Ansible Vault (앤서블 볼트)로 암호화해서 관리했습니다. ansible-vault encrypt_string 'MY_PROXMOX_API_TOKEN' --name 'proxmox_api_token' 같은 명령어로 쉽게 암호화할 수 있습니다.

    네트워크 인터페이스 이름 충돌

    VM을 생성할 때 네트워크 인터페이스를 여러 개 추가하다 보면, Proxmox 내부적으로 할당되는 인터페이스 이름(예: net0, net1) 때문에 예상치 못한 문제가 발생하기도 했습니다. 특히 net 배열 인덱스를 잘못 지정하면 원하는 브릿지에 연결이 안 되는 경우가 있었어요.

    • 해결책: 플레이북 내에서 net 옵션을 사용할 때 인덱스 순서를 명확히 하고, Proxmox 웹 UI에서 VM 생성 후 실제 할당된 인터페이스 이름과 비교하며 디버깅했습니다. 그리고 네트워크 설정 변경 후 Proxmox 호스트 재부팅이 필요한 경우도 있어서, reboot 모듈을 활용하기도 했습니다.

    비동기 작업 처리

    VM 생성이나 백업 같은 작업은 시간이 오래 걸릴 수 있습니다. Ansible은 기본적으로 동기(Synchronous) 방식으로 동작하기 때문에, Proxmox에서 작업이 완료되기 전에 Ansible 태스크가 타임아웃(Timeout)되거나 다음 태스크로 넘어가 버리는 문제가 있었습니다.

    • 해결책: async와 poll 옵션을 활용했습니다. 예를 들어, async: 300은 해당 태스크를 백그라운드에서 최대 300초(5분) 동안 실행하고, poll: 10은 10초마다 작업 완료 여부를 확인하는 방식입니다. 이렇게 하면 Ansible이 Proxmox의 비동기 작업을 기다려줍니다.
    Proxmox 웹 UI에서 Ansible로 자동화된 VM 리스트 확인

    Ansible 자동화로 생성 및 설정된 Proxmox VM들의 리스트를 Proxmox 웹 UI에서 보여주는 화면입니다. 플레이북으로 지정된 이름과 ID, 그리고 현재 상태(Running)가 명확하게 표시되어 자동화가 잘 작동하고 있음을 나타냅니다.

    🎉 1년 간의 성과와 얻은 교훈 (검증 및 결과)

    지난 1년간 Ansible과 Proxmox를 함께 사용하면서 얻은 가장 큰 성과는 역시 효율성입니다. 이제는 새로운 서버를 구축하거나 기존 서버 환경을 재구성할 때 클릭 몇 번이 아니라, 플레이북 실행 한 번으로 모든 것이 해결됩니다. 삽질도 많이 했지만, 그만큼 얻은 것도 많아요.

    압도적인 효율성 증가

    예전에는 새 VM 하나 만드는데 10분 이상 걸렸지만, 이제는 플레이북 실행부터 VM 부팅까지 2~3분이면 충분합니다. 특히 여러 대의 VM을 동시에 배포할 때는 그 효과가 정말 크더라고요. 재현 가능한(Idempotent) 인프라를 구축했다는 점도 중요합니다. 언제든 동일한 상태로 인프라를 복원하거나 재구축할 수 있거든요.

    인프라 문서화 효과

    플레이북 자체가 인프라의 현재 상태를 설명하는 가장 정확한 문서가 됩니다. 누가 봐도 이 플레이북이 어떤 VM을 어떤 설정으로 만들고 있는지 한눈에 들어오더라고요. 팀원들과의 협업이나 나중에 인프라를 다시 파악할 때 정말 큰 도움이 됩니다.

    홈랩 운영의 즐거움

    무엇보다 홈랩 운영이 훨씬 즐거워졌습니다. 새로운 기술을 실험하고 싶을 때, 클릭 몇 번으로 환경을 만들고 망가뜨려도 부담이 없거든요. 실패해도 플레이북만 다시 돌리면 그만이니까요. 이게 진짜 홈랩 자동화 경험의 핵심 아닐까요?

    Ansible Proxmox 자동화의 장점과 단점을 비교하는 표

    Ansible을 활용한 Proxmox 자동화의 주요 장점과 고려해야 할 단점을 비교하여 보여주는 표입니다. 효율성, 재현성, 문서화 등의 장점과 함께 학습 곡선, 복잡성 관리 등의 단점을 요약합니다.

    마무리하며: 다음 단계와 독자에게 전하는 말

    Ansible과 Proxmox의 조합은 저의 인프라 관리 방식을 완전히 바꿔놓았습니다. 물론 완벽하지는 않았지만, 수동 작업으로 인한 피로감을 줄이고, 더 본질적인 문제 해결에 집중할 수 있게 해주었죠. Ansible 플레이북과 Proxmox 인프라 관리에 대한 경험은 저에게 큰 자산이 되었습니다.

    혹시 아직 자동화를 시작하지 않으셨다면, 작은 부분부터라도 꼭 도전해보시길 권합니다. 처음엔 어렵게 느껴질 수 있지만, 한 번 손에 익으면 인프라 관리의 새로운 지평이 열릴 겁니다. Ansible 외에도 Terraform(테라폼) 같은 다른 IaC 도구들도 많으니, 여러분의 환경과 필요에 맞는 도구를 찾아보는 것도 좋은 방법입니다. 저는 다음 단계로 Proxmox 클러스터 관리나 더 복잡한 스토리지 연동 자동화에 도전해볼 생각입니다. 여러분의 자동화 여정도 응원할게요! 궁금한 점이나 공유하고 싶은 경험이 있다면 언제든 댓글로 남겨주세요.

  • [Nas] Jellyfin 트랜스코딩 성능 벤치마크: N100 미니PC vs Ryzen 5700G NAS

    [Nas] Jellyfin 트랜스코딩 성능 벤치마크: N100 미니PC vs Ryzen 5700G NAS

    안녕하세요, 13년차 서버실 지킴이입니다. 😅 요즘 홈랩에서 미디어 서버 가지고 씨름하는 재미에 푹 빠져 있네요. 다들 Plex나 Emby 많이 쓰시겠지만, 저는 오픈소스의 매력에 빠져 Jellyfin을 열심히 굴리고 있습니다. 개인 미디어 라이브러리를 구축하고 언제 어디서든 원하는 콘텐츠를 스트리밍하는 건 정말 매력적인 일이거든요.

    근데 여기서 늘 고민되는 부분이 있죠? 바로 트랜스코딩(Transcoding) 성능입니다. 원본 파일을 그대로 재생할 수 없는 기기(낮은 대역폭, 특정 코덱 미지원 등)에서 미디어를 보려면, 서버에서 실시간으로 변환해줘야 하거든요. 이 과정이 서버에 꽤나 큰 부하를 줍니다.

    그래서 제가 직접 한번 비교해봤습니다. 요즘 가성비 미니PC로 핫한 N100 미니PC와, 제가 주력으로 쓰는 Ryzen 5700G 기반의 NAS에서 Jellyfin 트랜스코딩 성능이 얼마나 차이 나는지 말이죠. 혹시 어떤 하드웨어로 미디어 서버를 구축할지 고민 중이셨다면, 오늘 제 삽질 경험과 벤치마크 결과가 작은 힌트가 될 거예요! 함께 보시죠. 🚀

    N100 미니PC와 Ryzen 5700G NAS를 활용한 Jellyfin 미디어 서버 홈랩 아키텍처 다이어그램

    N100 미니PC와 Ryzen 5700G NAS를 활용한 Jellyfin 미디어 서버 홈랩 아키텍처 다이어그램

    💡 Jellyfin 트랜스코딩, 왜 중요할까요?

    자, 먼저 트랜스코딩(Transcoding)이 뭔지 쉽게 설명해 드릴게요. 쉽게 말해, ‘실시간 동영상 변환’이라고 생각하시면 됩니다. 우리가 스마트폰으로 4K 고화질 영화를 보려고 하는데, 인터넷 회선이 느리거나 스마트폰이 4K HEVC(H.265) 코덱을 제대로 지원하지 못할 때가 있잖아요? 이럴 때 Jellyfin 서버가 ‘아, 알겠어! 그럼 이 4K HEVC 영상을 1080p H.264로 바꿔서 보내줄게!’ 하고 실시간으로 변환해서 보내주는 게 바로 트랜스코딩입니다.

    이 과정이 중요한 이유는, 모든 기기가 모든 코덱과 해상도를 지원하지 않기 때문이에요. 특히 외부에서 접속할 때는 네트워크 대역폭 문제로 트랜스코딩이 필수적일 때가 많습니다. 문제는 이 변환 작업이 CPU 자원을 엄청나게 잡아먹는다는 점이죠. 그래서 우리는 하드웨어 트랜스코딩(Hardware Transcoding)에 주목해야 합니다.

    하드웨어 트랜스코딩은 CPU가 아닌 GPU(Graphics Processing Unit)에 내장된 전용 인코더와 디코더를 활용해서 처리하는 방식입니다. 인텔 CPU의 Quick Sync Video (QSV)나 AMD CPU의 Video Core Next (VCN) 같은 기술들이 여기에 해당하죠. 이 기능을 활용하면 CPU 부하를 획기적으로 줄이고, 훨씬 많은 동시 스트림을 처리할 수 있게 됩니다. Jellyfin은 이러한 하드웨어 가속 기능을 아주 잘 지원하는 고마운 미디어 서버입니다. 🎉

    🛠️ 실전 구현: Jellyfin 벤치마크 환경 구성

    본격적인 벤치마크를 위해 두 가지 시스템에 Jellyfin을 설치하고 설정해봤습니다. 둘 다 리눅스 기반 환경에서 Docker 컨테이너로 Jellyfin을 운영했습니다.

    1. N100 미니PC 환경

    • 프로세서: Intel N100 (4코어, 4스레드)
    • 내장 GPU: Intel UHD Graphics (Quick Sync Video 지원)
    • RAM: 16GB DDR5
    • OS: Ubuntu Server 22.04 LTS
    • Jellyfin 설치: Docker Compose

    N100은 저전력 프로세서로 유명하죠. 하지만 Quick Sync Video 덕분에 미디어 트랜스코딩 성능은 예상외로 꽤나 괜찮더라고요. 특히 전성비(전력 대비 성능)가 아주 훌륭합니다.

    2. Ryzen 5700G NAS 환경

    • 프로세서: AMD Ryzen 7 5700G (8코어, 16스레드)
    • 내장 GPU: Radeon Graphics (Video Core Next, VCN 지원)
    • RAM: 32GB DDR4
    • OS: Unraid OS (내부적으로 Linux 기반)
    • Jellyfin 설치: Docker Compose

    Ryzen 5700G는 CPU 성능도 강력하고, 내장 그래픽 성능도 꽤 괜찮아서 다재다능한 NAS 구축에 인기가 많죠. 과연 Jellyfin 트랜스코딩 성능에서는 어떤 모습을 보여줄까요?

    Jellyfin Docker 설치 및 하드웨어 가속 설정 (공통)

    기본적인 Jellyfin Docker Compose 설정은 다음과 같습니다. 하드웨어 가속을 사용하려면 /dev/dri 장치를 컨테이너에 매핑해주는 것이 핵심입니다.

    version: "3.8"
    services:
      jellyfin:
        image: jellyfin/jellyfin:latest
        container_name: jellyfin
        network_mode: host
        volumes:
          - /path/to/jellyfin/config:/config
          - /path/to/jellyfin/cache:/cache
          - /path/to/your/media:/media
          # 하드웨어 가속을 위한 장치 매핑
          - /dev/dri:/dev/dri
        devices:
          # 추가적으로 디바이스 직접 매핑 (N100/Intel QSV의 경우 필수)
          - /dev/dri/renderD128:/dev/dri/renderD128
          - /dev/dri/card0:/dev/dri/card0
        restart: unless-stopped
        environment:
          - PUID=1000
          - PGID=1000
          - TZ=Asia/Seoul
          # 추가적인 환경 변수 (예: NVIDIA GPU 사용 시)
          # - NVIDIA_VISIBLE_DEVICES=all
          # - NVIDIA_DRIVER_CAPABILITIES=all
    

    Jellyfin 웹 UI에 접속한 후, 관리자 대시보드 > 재생(Playback) 설정에서 하드웨어 가속 옵션을 선택해야 합니다. N100은 VAAPI를, Ryzen 5700G는 AMF 또는 VAAPI를 선택하고 선호하는 인코더/디코더를 설정해줍니다.

    Jellyfin 미디어 서버의 하드웨어 트랜스코딩 설정 화면 예시

    Jellyfin 미디어 서버의 하드웨어 트랜스코딩 설정 화면 예시

    ⚠️ 주의사항 및 트러블슈팅: 삽질은 나의 힘!

    제가 이 과정에서 겪었던 삽질 경험을 솔직하게 공유해 드릴게요. 😭

    1. 드라이버 문제: 하드웨어 가속은 단순히 장치를 매핑한다고 끝나는 게 아닙니다. 리눅스 환경에서 해당 GPU를 위한 드라이버와 라이브러리가 제대로 설치되어 있어야 하거든요.
      • N100 (Intel QSV): intel-media-va-driver-non-free (Debian/Ubuntu 계열) 패키지 설치가 필수였습니다. 처음엔 이것 때문에 VAAPI가 제대로 작동하지 않아서 ‘N100이 생각보다 별로인가?’ 하고 좌절했었죠. 🤦
      • Ryzen 5700G (AMD VCN): AMD는 인텔보다 드라이버 세팅이 까다로운 경우가 종종 있더라고요. mesa-va-drivers 같은 VAAPI 관련 패키지는 물론이고, 커널 모듈 (amdgpu)이 제대로 로드되어 있는지 확인해야 합니다. Unraid 같은 OS는 기본적으로 잘 세팅되어 있지만, 순수 Ubuntu에서는 조금 더 신경 써야 할 수 있어요.
    2. 권한 문제: Docker 컨테이너가 /dev/dri 장치에 접근할 권한이 없어서 문제가 발생하기도 합니다. Docker Compose 파일에 devices 섹션을 추가하고, 호스트 시스템에서 Jellyfin을 실행하는 사용자(PUID/PGID)가 video 그룹에 속해 있는지 확인하는 것이 중요합니다.
    3. 코덱 지원: 모든 GPU가 모든 코덱을 하드웨어 가속으로 지원하는 건 아닙니다. 특히 VP9, AV1 같은 최신 코덱이나 10비트 HEVC 같은 특정 포맷은 하드웨어 지원 여부를 확인해야 합니다. 제가 주로 테스트한 4K HEVC -> 1080p H.264 변환은 대부분의 최신 GPU에서 잘 지원하더라고요.

    이런 문제들 때문에 밤늦게까지 구글링하고 포럼을 뒤적거렸던 기억이 생생하네요. 😅 하지만 해결하고 나면 그 성취감이란! 여러분은 저처럼 삽질하지 마시라고 이렇게 자세히 적어봅니다. 💪

    📊 Jellyfin 트랜스코딩 벤치마크 검증 및 결과

    본격적인 벤치마크는 다음과 같은 방식으로 진행했습니다.

    • 테스트 파일: 4K HEVC (H.265) 10비트 영상
    • 트랜스코딩 목표: 1080p H.264
    • 측정 방법: 여러 클라이언트에서 동시에 스트리밍을 시작하며, 각 서버의 CPU 및 GPU 사용률, 그리고 버퍼링 발생 여부 확인
    • 모니터링 도구: htop (CPU), intel_gpu_top (N100 GPU), radeontop (Ryzen 5700G GPU), Jellyfin 대시보드 로그

    N100 미니PC 결과

    예상보다 훨씬 뛰어난 성능을 보여줬습니다! N100의 Intel Quick Sync Video는 정말 대단하더군요. 3개 정도의 4K HEVC -> 1080p H.264 동시 트랜스코딩은 무리 없이 소화해냈습니다. GPU 사용률은 70~80% 정도로 올라갔지만, CPU 사용률은 20~30% 정도로 매우 낮게 유지되면서 안정적인 스트리밍을 제공했습니다. 4개 스트림부터는 가끔 버퍼링이 발생하기 시작했거든요.

    저전력 미니PC에서 이 정도 성능이라니, 정말 인상 깊었습니다. 전력 소모도 아이들 시 10W 내외, 트랜스코딩 시 20W 내외로 매우 착했습니다. 💡

    Ryzen 5700G NAS 결과

    역시 Ryzen 5700G는 강력했습니다. 5~6개 정도의 4K HEVC -> 1080p H.264 동시 트랜스코딩도 거뜬히 처리했습니다. GPU 사용률은 50~60% 수준에서 안정적이었고, CPU 사용률도 N100과 마찬가지로 낮게 유지되었습니다. 7개 이상부터는 간헐적인 버퍼링이 발생하기 시작했거든요.

    N100보다 더 많은 동시 스트림을 처리할 수 있었고, 여전히 CPU 자원에 여유가 있어서 다른 NAS 작업(가상 머신, 다른 Docker 컨테이너 등)을 병행하기에도 충분했습니다. 다만, 전력 소모는 아이들 시 30W 내외, 트랜스코딩 시 50~60W 정도로 N100보다는 높았습니다.

    정리하자면 다음과 같습니다.

    Jellyfin 트랜스코딩 N100과 Ryzen 5700G의 동시 스트림 및 CPU/GPU 사용률 비교 그래프

    Jellyfin 트랜스코딩 N100과 Ryzen 5700G의 동시 스트림 및 CPU/GPU 사용률 비교 그래프

    마무리: 당신의 선택은?

    자, 이제 결론을 내릴 시간입니다. 어떤 시스템이 더 좋은 선택일까요?

    특성 N100 미니PC Ryzen 5700G NAS
    트랜스코딩 성능 (동시 스트림) 3~4개 (4K HEVC -> 1080p H.264) 5~6개 이상 (4K HEVC -> 1080p H.264)
    전력 소모 매우 낮음 (아이들 10W, 풀로드 20W 내외) 보통 (아이들 30W, 풀로드 50~60W 내외)
    가격 저렴 (10~20만원대) 높음 (30만원 이상, NAS 구성 시 더 높음)
    다용도성 Jellyfin 전용 미디어 서버로 최적 Jellyfin + NAS + VM 등 다용도 서버로 최적
    주요 장점 압도적인 전성비, 저렴한 구축 비용 강력한 CPU 성능, 높은 동시 스트림 처리, 높은 확장성
    추천 대상 1~3인 가구, 전력 소모에 민감한 사용자 3인 이상 가구, 다양한 서버 기능 원하는 사용자

    개인적인 생각으로는, 순수하게 Jellyfin 미디어 서버만을 위한 시스템을 구축한다면 N100 미니PC의 가성비와 전성비는 정말 타의 추종을 불허하더라고요. 저렴한 가격에 이 정도 성능을 뽑아준다는 건 정말 놀라운 일입니다. 혼자 또는 가족과 함께 사용하는 미디어 서버로는 충분하고도 남습니다.

    하지만 만약 NAS에 더 많은 기능(파일 서버, 백업, 가상 머신, 다른 Docker 서비스 등)을 올리고 싶고, 동시 스트림 사용자 수가 많다면 Ryzen 5700G 같은 더 강력한 프로세서가 탑재된 NAS가 좋은 선택이 될 거예요. 저처럼 홈랩에서 이것저것 실험하는 분들에게는 이쪽이 더 매력적일 수 있습니다.

    이번 벤치마크를 통해 하드웨어 가속의 중요성을 다시 한번 깨달았더라고요. 그리고 드라이버 세팅의 중요성도요! 😅 여러분도 자신의 환경과 예산에 맞춰 현명한 선택을 하시길 바랍니다. 다음번에는 Jellyfin과 Emby의 좀 더 심층적인 비교 분석으로 찾아올게요! 그때까지 즐거운 홈랩 생활 되세요! 👋

    N100 미니PC와 Ryzen 5700G NAS의 Jellyfin 미디어 서버 장단점 비교 인포그래픽

    N100 미니PC와 Ryzen 5700G NAS의 Jellyfin 미디어 서버 장단점 비교 인포그래픽

  • [Proxmox] Proxmox HAOS 마이그레이션: VMware ESXi에서 안전하게 이전하기

    [Proxmox] Proxmox HAOS 마이그레이션: VMware ESXi에서 안전하게 이전하기

    홈랩 서버실 | Proxmox HAOS 마이그레이션: VMware ESXi에서 안전하게 이전하기

    안녕하세요, 13년차의 서버실입니다. 홈랩을 운영하면서 여러 하이퍼바이저(Hypervisor)를 오가다 보면, 언젠가 한 번쯤은 겪게 되는 고민이 있어요. 바로 기존 가상머신(Virtual Machine, VM)을 새로운 플랫폼으로 옮기는 마이그레이션(Migration)입니다. 특히 스마트 홈의 핵심인 Home Assistant OS(HAOS) 같은 친구들은 안정성이 중요해서 마이그레이션할 때 더욱 신중해지죠. 저도 최근에 오랫동안 잘 써오던 VMware ESXi 환경에서 Proxmox VE로 HAOS를 안전하게 이전하면서 꽤 삽질을 했거든요. 오늘은 그 경험을 바탕으로 여러분께 자세히 알려드리려고 합니다.

    혹시 여러분도 ESXi의 라이선스 정책 변화나, Proxmox VE의 유연한 기능들, 혹은 단순히 새로운 환경에 대한 호기심 때문에 마이그레이션을 고민하고 계신가요? 그렇다면 이 글이 큰 도움이 될 겁니다. 제가 직접 겪었던 시행착오와 해결 과정을 솔직하게 공유하면서, 여러분의 Proxmox HAOS 마이그레이션 여정을 조금 더 부드럽게 만들어 드릴게요. 자, 그럼 시작해 볼까요?

    VMware ESXi에서 Proxmox VE로 Home Assistant OS 마이그레이션 전체 개요

    VMware ESXi에서 Proxmox VE로 Home Assistant OS 마이그레이션 전체 개요 다이어그램입니다.

    1. 왜 VMware ESXi에서 Proxmox VE로 옮겨야 할까요?

    사실 VMware ESXi는 오랫동안 안정적인 가상화 플랫폼의 대명사였어요. 특히 엔터프라이즈 환경에서는 거의 표준이라고 할 수 있었죠. 저도 덕분에 많은 프로젝트를 ESXi 위에서 진행했었고요. 하지만 홈랩(Home Lab) 환경에서는 몇 가지 고민이 생기더라고요.

    • 라이선스 정책 변화: ESXi 무료 버전의 기능 제한이 점점 심해지고 있습니다. 특히 vSphere API 접근 제한은 백업이나 관리 자동화에 큰 걸림돌이 되죠.
    • 오픈소스의 유연성: Proxmox VE(Virtual Environment)는 완벽한 오픈소스 가상화 플랫폼입니다. KVM(Kernel-based Virtual Machine)과 LXC(Linux Containers)를 모두 지원해서 VM과 컨테이너를 한 곳에서 관리할 수 있다는 점이 정말 매력적이더라고요.
    • 커뮤니티와 학습: Proxmox는 활발한 커뮤니티와 방대한 자료 덕분에 새로운 기술을 익히고 실험하기에 좋은 환경을 제공합니다. 저처럼 이것저것 만져보는 걸 좋아하는 사람에게는 최고의 놀이터죠.

    이런 이유들로 저는 Proxmox VE로의 전환을 결심했고, 그 첫 타자로 홈랩의 심장인 Home Assistant OS를 옮겨보기로 했습니다.

    2. 개념 설명: Proxmox VE와 Home Assistant OS 간략히

    마이그레이션에 앞서, 핵심 개념들을 간단히 짚고 넘어갈까요?

    • Proxmox VE (Virtual Environment): 쉽게 말해, 서버 한 대에서 여러 개의 가상 서버를 돌릴 수 있게 해주는 운영체제라고 생각하시면 됩니다. VMware ESXi와 비슷한 역할을 하지만, 오픈소스라는 점이 가장 큰 차이점이에요. 웹 기반 인터페이스를 제공해서 관리도 편리하고요.
    • Home Assistant OS (HAOS): 스마트 홈 기기들을 한곳에 모아 관리하고 자동화할 수 있게 해주는 오픈소스 플랫폼입니다. 전용 OS 형태로 제공되어 가상머신 위에 설치하는 경우가 많죠. 저도 Z-Wave, Zigbee 동글(dongle)을 연결해서 사용하고 있습니다.
    • 마이그레이션 (Migration): 기존에 잘 돌아가던 가상머신을 다른 가상화 환경으로 옮기는 과정입니다. 단순히 파일을 복사하는 것 이상으로, 새로운 환경에 맞게 설정들을 조정해줘야 하죠.

    3. 실전 구현: VMware ESXi에서 HAOS VM 내보내기

    자, 이제 본론입니다. 제가 직접 해보니 몇 가지 포인트만 잘 지키면 생각보다 어렵지 않더라고요.

    3.1. Home Assistant OS VM 백업 및 종료

    가장 먼저 할 일은 HAOS VM을 백업하는 것입니다. 혹시 모를 상황에 대비해서 꼭 스냅샷(Snapshot)을 찍어두거나, Home Assistant 자체의 백업 기능을 활용해서 전체 백업 파일을 받아두세요. 그리고 VM을 정상적으로 종료(Shut Down)해야 합니다. 전원 끄기(Power Off) 말고, 반드시 OS 내부에서 종료 명령을 내려주세요. 데이터 무결성(Data Integrity)을 위해 아주 중요하거든요.

    3.2. OVF/OVA 형식으로 내보내기

    VMware ESXi에서 가상머신을 다른 환경으로 옮길 때 가장 일반적인 방법은 OVF(Open Virtualization Format) 또는 OVA(Open Virtual Appliance) 형식으로 내보내는 것입니다. OVA는 OVF 파일과 관련 디스크 이미지를 하나의 파일로 묶어둔 아카이브(Archive) 형태라고 보시면 돼요. 저는 OVA로 내보냈습니다.

    1. vSphere Client 또는 ESXi 웹 UI에 접속합니다.
    2. 내보낼 Home Assistant OS VM을 선택하고 마우스 오른쪽 버튼을 클릭합니다.
    3. ‘템플릿(Template)’ > ‘OVF 템플릿 내보내기(Export OVF Template…)’를 선택합니다.
    4. 저장할 위치와 파일명을 지정하고 ‘확인’을 누르면 내보내기가 시작됩니다. 시간이 좀 걸릴 수 있어요.
    VMware ESXi에서 Home Assistant OS 가상 머신을 OVF/OVA 형식으로 내보내는 과정

    VMware ESXi 웹 UI에서 Home Assistant OS 가상 머신을 OVF/OVA 형식으로 내보내는 화면입니다.

    3.3. 디스크 이미지 변환 (VMDK → QCOW2)

    Proxmox VE는 일반적으로 QCOW2나 RAW 형식의 디스크 이미지를 사용합니다. ESXi에서 내보낸 OVA 파일 안에는 VMDK 형식의 디스크 이미지가 들어있죠. 이걸 Proxmox에서 사용할 수 있는 형식으로 변환해야 합니다. 저는 Proxmox 서버에서 직접 변환하는 방법을 선택했어요. 이게 가장 빠르고 편하더라고요.

    1. 내보낸 OVA 파일을 Proxmox VE 서버의 특정 디렉토리(예: /var/lib/vz/template/iso/)로 옮깁니다. scp 명령어를 사용하면 편리합니다.
    2. Proxmox VE 서버에 SSH로 접속합니다.
    3. OVA 파일 압축을 해제합니다. OVA는 사실 .tar 아카이브 파일이에요.
    cd /var/lib/vz/template/iso/
    tar -xvf homeassistant.ova
    

    압축을 풀면 .ovf 파일과 .vmdk 파일이 보일 겁니다.

    1. qemu-img 도구를 사용하여 VMDK 파일을 QCOW2로 변환합니다.
    qemu-img convert -f vmdk homeassistant-disk1.vmdk -O qcow2 homeassistant-disk1.qcow2
    

    이 명령어가 성공적으로 실행되면, homeassistant-disk1.qcow2 파일이 생성됩니다. 이 파일이 Proxmox VE에서 사용할 디스크 이미지예요.

    4. 실전 구현: Proxmox VE로 HAOS VM 가져오기

    이제 변환된 디스크 이미지를 Proxmox VE에 VM으로 등록할 차례입니다.

    4.1. 새 가상머신 생성 (디스크 없이)

    Proxmox VE 웹 UI에서 새로운 VM을 생성합니다. 이때 ‘하드 디스크(Hard Disk)’ 단계에서는 디스크를 추가하지 않고 넘어가는 것이 중요해요. 나중에 변환한 디스크를 직접 연결할 거거든요.

    1. Proxmox VE 웹 UI에 접속합니다.
    2. 우측 상단 ‘VM 생성(Create VM)’ 버튼을 클릭합니다.
    3. ‘일반(General)’ 탭에서 이름과 VM ID를 지정합니다. (예: homeassistant-new, ID 100)
    4. ‘OS’ 탭에서 ‘미디어 사용 안 함(Do not use any media)’을 선택합니다.
    5. ‘시스템(System)’ 탭에서 기본값으로 두거나 필요한 경우 수정합니다. (저는 VMWare에서 사용하던 BIOS 대신 UEFI를 선택했어요.)
    6. ‘디스크(Disks)’ 탭에서 ‘디스크를 추가하지 않음(Do not add a disk)’을 선택하고 다음으로 넘어갑니다.
    7. ‘CPU’ 및 ‘메모리(Memory)’를 적절히 설정합니다. HAOS는 2코어, 4GB RAM 정도면 충분해요.
    8. ‘네트워크(Network)’ 탭에서 원하는 브리지(Bridge)를 선택합니다. (일반적으로 vmbr0)
    9. 마지막 ‘확인(Confirm)’ 단계에서 설정을 다시 확인하고 ‘완료(Finish)’를 클릭합니다.

    4.2. 변환된 디스크 이미지 임포트

    생성된 VM에 아까 변환했던 .qcow2 디스크 이미지를 연결합니다.

    1. Proxmox VE 서버에 SSH로 접속합니다.
    2. 다음 명령어를 사용하여 디스크 이미지를 생성한 VM (예: ID 100)으로 임포트합니다. local-lvm은 디스크 이미지를 저장할 스토리지 풀 이름입니다. 여러분의 환경에 맞게 변경해주세요.
    qm importdisk 100 /var/lib/vz/template/iso/homeassistant-disk1.qcow2 local-lvm
    

    이 명령어가 완료되면, Proxmox VE 웹 UI의 VM 하드웨어 설정에 ‘사용하지 않는 디스크(Unused Disk)’로 추가된 것을 볼 수 있어요.

    4.3. 임포트된 디스크 연결 및 부팅 순서 설정

    1. Proxmox VE 웹 UI에서 생성한 VM (ID 100)을 선택하고 ‘하드웨어(Hardware)’ 탭으로 이동합니다.
    2. ‘사용하지 않는 디스크(Unused Disk)’를 더블클릭하거나 선택 후 ‘편집(Edit)’ 버튼을 눌러 디스크를 VM에 연결합니다. ‘버스/장치(Bus/Device)’는 ‘SATA’나 ‘VirtIO Block’ 중 선택하는데, 저는 일반적으로 성능이 좋은 VirtIO Block을 선호해요. ‘캐시(Cache)’ 설정도 필요에 따라 조정해주세요.
    3. ‘옵션(Options)’ 탭으로 이동하여 ‘부팅 순서(Boot Order)’를 편집합니다. 방금 연결한 디스크를 첫 번째 부팅 장치로 설정하고 ‘확인(OK)’을 클릭합니다.
    Proxmox VE에서 새로운 가상 머신 생성 후 Home Assistant OS 디스크 이미지를 임포트하는 과정

    Proxmox VE 웹 UI에서 새로운 VM을 생성하고, SSH 터미널에서 qm importdisk 명령어를 통해 Home Assistant OS 디스크 이미지를 임포트하는 과정입니다.

    5. 주의사항 및 트러블슈팅 ⚠️ (삽질 경험 공유)

    제가 이 과정에서 겪었던 몇 가지 ‘삽질’과 그 해결 방법을 공유합니다. 여러분은 저 같은 실수를 하지 않으시길 바랍니다! ㅎㅎ

    • 네트워크 인터페이스 문제: 마이그레이션 후 HAOS가 네트워크를 잡지 못하는 경우가 있어요. ESXi와 Proxmox는 가상 네트워크 카드 드라이버가 다를 수 있거든요. HAOS는 보통 eth0을 사용하지만, Proxmox에서 VirtIO 네트워크 카드를 사용하면 이름이 바뀌거나 설정이 꼬일 수 있습니다.

      • 해결책: Proxmox VM 설정에서 네트워크 카드를 VirtIO 대신 E1000으로 변경해보세요. 또는 HAOS 콘솔에 접속해서 네트워크 설정을 수동으로 확인하고 network update 명령어를 통해 DHCP를 다시 시도해볼 수 있습니다.
    • VMware Tools 잔재: ESXi에서 설치했던 VMware Tools는 Proxmox에서는 필요 없어요. 이게 남아있으면 불필요한 리소스를 잡아먹거나 충돌을 일으킬 수도 있습니다. 사실 HAOS는 자체적으로 VM Tools 같은 걸 설치하지 않아서 크게 문제가 되지는 않지만, 다른 Linux VM을 마이그레이션할 때는 꼭 확인해주세요.

      • 해결책: 마이그레이션 전 ESXi에서 VM Tools를 제거하거나, Proxmox로 옮긴 후 해당 VM에 접속해서 제거하는 것이 좋습니다.
    • USB 패스스루(USB Passthrough): Zigbee나 Z-Wave 동글을 사용하신다면, Proxmox에서 USB 패스스루 설정을 새로 해줘야 합니다. ESXi에서 사용하던 설정이 그대로 넘어오지 않거든요.

      • 해결책: Proxmox 웹 UI에서 VM 하드웨어 설정에 들어가 ‘추가(Add)’ > ‘USB 장치(USB Device)’를 선택하고, ‘벤더/제품 ID 사용(Use vendor/product ID)’ 또는 ‘USB 포트 사용(Use USB Port)’을 통해 해당 동글을 연결해줍니다.
    • 디스크 용량 문제: HAOS VMDK 파일이 크면 변환 및 임포트 과정에서 시간이 오래 걸리고, 스토리지 용량을 충분히 확보해야 합니다. 저도 한 번은 Proxmox 서버의 /tmp 디렉토리 용량이 부족해서 변환이 실패한 적이 있었어요. 😅

      • 해결책: 변환 파일을 저장할 디렉토리에 충분한 여유 공간이 있는지 미리 확인하세요.

    6. 검증 및 결과 🎉 (드디어 새로운 둥지에서 HAOS 가 움직인다!)

    모든 설정이 끝나고 VM을 부팅하면, 이제 HAOS가 Proxmox VE 위에서 새로운 시작을 알릴 겁니다. 저는 부팅 후 다음 사항들을 꼼꼼히 확인했어요.

    1. HAOS 웹 인터페이스 접속: 가장 먼저 HAOS의 웹 UI에 접속해서 대시보드가 정상적으로 로드되는지 확인합니다.
    2. 네트워크 연결 상태: ‘설정(Settings)’ > ‘시스템(System)’ > ‘네트워크(Network)’에서 IP 주소와 게이트웨이(Gateway) 설정이 올바른지 확인합니다.
    3. 통합(Integrations) 작동 여부: 스마트 기기 연동(예: Philips Hue, SmartThings, Zigbee/Z-Wave 동글)이 제대로 작동하는지 하나씩 테스트해봅니다.
    4. 애드온(Add-ons) 상태: 설치했던 애드온들이 모두 정상적으로 실행되는지 확인해요.
    5. 리소스 사용량: Proxmox VE 웹 UI에서 VM의 CPU, 메모리, 디스크 I/O 사용량을 모니터링하여 ESXi 때와 비교해봅니다. 보통 Proxmox가 더 효율적인 경우가 많더라고요.

    이 모든 과정이 끝나고 HAOS 대시보드가 완벽하게 작동하는 것을 보니, 그동안의 삽질이 싹 잊히는 기분이었어요. 드디어 성공했구나! 하는 성취감이 밀려오더라고요. 🥳

    Proxmox VE에서 성공적으로 마이그레이션되어 작동 중인 Home Assistant OS 대시보드

    Proxmox VE 위에서 성공적으로 마이그레이션되어 작동 중인 Home Assistant OS 대시보드 화면입니다.

    7. 마무리: 배운 점과 다음 단계

    VMware ESXi에서 Proxmox VE로 Home Assistant OS를 마이그레이션하는 과정은 단순히 파일을 옮기는 것을 넘어, 새로운 가상화 환경에 대한 이해와 트러블슈팅 능력을 길러주는 좋은 경험이었습니다. 제가 얻은 몇 가지 교훈은 이렇습니다.

    • 사전 백업의 중요성: 아무리 강조해도 지나치지 않아요.
    • 문서화의 습관화: 제가 어떤 설정을 했는지, 어떤 삽질을 했는지 기록해두면 다음번에 훨씬 수월합니다.
    • 오픈소스 커뮤니티의 힘: 막히는 부분이 있을 때 Proxmox 포럼이나 Home Assistant 커뮤니티에서 많은 도움을 받을 수 있었어요.

    이제 여러분의 Home Assistant OS는 Proxmox VE라는 새로운 환경에서 더욱 안정적이고 유연하게 동작할 겁니다. 다음 단계로는 Proxmox VE의 강력한 백업 기능(Proxmox Backup Server와 연동)을 활용하여 HAOS 백업 전략을 수립하거나, VM에 GPU 패스스루를 설정하여 미디어 서버 등 다른 서비스를 구축하는 것도 좋은 아이디어가 될 수 있겠죠. 물론 이 부분은 다음 글에서 자세히 다룰 예정입니다. 😉

    오늘도 제 글이 여러분의 홈랩 운영에 작은 도움이 되었기를 바라며, 궁금한 점은 언제든 댓글로 남겨주세요! 13년차의 서버실은 언제나 여러분의 삽질을 응원합니다. 감사합니다!

  • [AI] Haystack 기반 AI 에이전트 구축, 실패 사례로 배우는 설계 함정

    [AI] Haystack 기반 AI 에이전트 구축, 실패 사례로 배우는 설계 함정

    안녕하세요, 13년차 서버실 지킴이, ’13년차의 서버실’ 블로그 주인장입니다. 오늘은 제가 최근 홈랩에서 Haystack 기반 AI 에이전트(AI Agent)를 구축하다가 겪었던 뼈아픈 실패 경험과 거기서 배운 설계 함정들에 대해 솔직하게 이야기해보려고 합니다. 😅

    요즘 LLM(Large Language Model)이 워낙 핫하잖아요? 저도 이 친구들을 그냥 채팅만 시킬 게 아니라, 좀 더 능동적으로 제 업무를 도와주는 ‘에이전트’로 만들어보고 싶다는 욕심이 생기더라고요. 그래서 오픈소스 프레임워크 중 하나인 Haystack을 선택해서 무작정 뛰어들었죠. 처음엔 “와, 이거 진짜 대박인데?” 싶었는데, 막상 실전에 들어가 보니 생각보다 만만치 않더군요. 삽질 좀 했습니다 ㅎㅎ

    혹시 여러분도 AI 에이전트 개발에 관심 있으신가요? 아니면 이미 도전 중이신데 뭔가 잘 안 풀리는 부분이 있으신가요? 제 경험담이 여러분의 시간과 노력을 아끼는 데 조금이나마 도움이 되기를 바랍니다. 💡

    Haystack 기반 AI 에이전트 아키텍처 다이어그램

    그림 1: Haystack 기반 AI 에이전트의 일반적인 아키텍처 개요.

    AI 에이전트, 그리고 Haystack (쉽게 말해~)

    먼저, AI 에이전트(AI Agent)가 정확히 뭔지부터 짚고 넘어갈까요? 쉽게 말해, 스스로 목표를 세우고, 필요한 도구(Tool)들을 활용해서 그 목표를 달성해 나가는 인공지능 시스템이라고 보시면 됩니다. 마치 비서처럼 “오늘 뉴스 요약해 줘”라고 하면, 에이전트가 알아서 뉴스 검색 도구를 쓰고, 내용을 요약해서 저에게 알려주는 식이죠. 🤖

    그럼 Haystack은 뭘까요? Haystack은 오픈소스 LLM 프레임워크(Open-source LLM Framework) 중 하나인데요, LLM 기반의 애플리케이션, 특히 검색 증강 생성(RAG, Retrieval Augmented Generation)이나 AI 에이전트 같은 복잡한 시스템을 쉽게 구축할 수 있도록 도와주는 도구들의 모음이라고 생각하면 돼요. 파이프라인(Pipelines), 문서 저장소(Document Stores), 생성기(Generators), 검색기(Retrievers) 등 다양한 컴포넌트(Component)들을 조합해서 원하는 기능을 만들 수 있게 되어 있거든요. 마치 레고 블록처럼요!

    제가 Haystack을 선택한 이유는 유연한 모듈 구조와 활발한 커뮤니티 덕분이었습니다. 다양한 LLM과 벡터 DB를 플러그인(Plug-in) 방식으로 연결할 수 있다는 점도 매력적이었고요. 🚀

    실전 구현, 그리고 기대와 현실의 괴리

    제가 처음 목표했던 건 ‘주어진 질문에 대해 최신 정보를 스스로 찾아 답변하고, 필요하면 특정 API를 호출해 액션을 취하는 에이전트’였습니다. 예를 들어, “오늘 날씨는 어때?”라고 물으면 날씨 API를 호출하고, “최근 AI 트렌드는?”이라고 물으면 웹 검색 후 요약해주는 식이었죠.

    기본적인 Haystack 에이전트 구성은 다음과 같았습니다.

    1. LLM 연결: OpenAI GPT-4나 로컬에서 돌리는 Llama 2 같은 대규모 언어 모델을 연결합니다.
    2. 도구(Tools) 정의: 웹 검색 도구, 날씨 API 호출 도구, 데이터베이스 조회 도구 등을 만듭니다. 각 도구는 특정 기능을 수행하는 함수로 구현됩니다.
    3. 에이전트 설정: LLM이 어떤 도구들을 사용할 수 있는지 알려주고, 어떤 방식으로 추론(Reasoning)하고 행동(Acting)할지 지시합니다.

    간단한 파이썬 코드로 이 구성의 뼈대를 잡을 수 있었죠. 대략 이런 느낌이랄까요?

    from haystack.agents import Agent, Tool
    from haystack.components.generators import OpenAIChatGenerator
    # ... 다른 컴포넌트 임포트
    
    # 예시 Tool 정의 (실제로는 더 복잡합니다)
    def web_search(query: str):
        """Performs a web search for the given query and returns relevant results."""
        print(f"DEBUG: Performing web search for: {query}")
        # 실제 웹 검색 로직 (e.g., Google Search API 호출)
        return f"Search results for '{query}': AI 에이전트 관련 최신 뉴스 요약..."
    
    def get_weather(location: str):
        """Retrieves the current weather for a specified location."""
        print(f"DEBUG: Getting weather for: {location}")
        # 실제 날씨 API 호출 로직
        return f"Current weather in {location}: 맑음, 25도"
    
    # Tool 인스턴스 생성
    web_search_tool = Tool(name="web_search", function=web_search, description="웹 검색을 수행하여 최신 정보를 찾습니다.")
    weather_tool = Tool(name="get_weather", function=get_weather, description="특정 지역의 현재 날씨 정보를 가져옵니다.")
    
    # LLM 생성기 설정 (예시)
    llm_generator = OpenAIChatGenerator(model="gpt-4", api_key="YOUR_API_KEY")
    
    # Agent 생성
    my_agent = Agent(llm=llm_generator, tools=[web_search_tool, weather_tool])
    
    # 에이전트 실행 (이 부분부터 삽질이 시작됩니다...)
    # result = my_agent.run("오늘 서울 날씨는 어때?")
    # result = my_agent.run("최근 AI 에이전트 개발 트렌드에 대해 알려줘")
    

    이렇게 코드를 짜놓고 “이제 알아서 척척 해주겠지?” 하는 기대감에 부풀어 있었죠. 하지만 현실은… 제 에이전트는 제가 의도했던 대로 동작하지 않는 경우가 태반이었습니다. 😭

    AI 에이전트의 복잡한 로직 처리 실패 다이어그램

    그림 2: AI 에이전트가 복잡한 로직에서 혼란을 겪는 모습.

    ⚠️ 실패 사례로 배우는 설계 함정들

    제 삽질 경험을 통해 얻은 가장 중요한 교훈은 “LLM은 만능이 아니다”라는 겁니다. 그리고 “Haystack 에이전트 설계는 프롬프트 엔지니어링 그 이상”이라는 사실이었죠. 몇 가지 주요 실패 사례와 해결법을 공유합니다.

    1. LLM에 과도한 로직 의존 (The “Do Everything” LLM Fallacy)

    • 문제점: 처음엔 모든 판단과 로직을 LLM에게 맡기려고 했습니다. “이런 상황에선 저 도구를 쓰고, 저런 상황에선 이렇게 판단해” 식으로요. 하지만 LLM은 복잡한 다단계 로직이나 정교한 조건 분기(Conditional Branching)를 정확히 수행하기 어렵더라고요. 추론 과정이 길어질수록 오류 확률이 높아지고, 엉뚱한 방향으로 빠지기 일쑤였습니다. 🤯
    • 해결법: 명확한 비즈니스 로직은 코드로 구현하고, LLM은 ‘언어 이해’와 ‘도구 선택’에 집중하도록 역할을 분담했습니다. 예를 들어, 특정 조건에서는 무조건 특정 도구를 호출하도록 파이프라인(Pipeline) 자체를 설계하고, LLM은 그 도구에 넘겨줄 인자(Argument)를 추출하는 역할만 맡기는 식이죠.

    2. 어설픈 도구(Tool) 설계와 오용

    • 문제점:
      • 너무 많은 도구: 에이전트에게 너무 많은 도구를 한꺼번에 주니, LLM이 어떤 도구를 써야 할지 혼란스러워했습니다. 마치 초보 운전자가 복잡한 계기판을 보고 당황하는 것과 같았죠.
      • 모호한 도구 설명: 각 도구의 <code>description(설명)이 불분명하거나, 입력/출력 형식이 모호하면 LLM이 올바르게 사용하지 못했습니다. 예를 들어, ‘데이터 조회’ 도구가 있는데, 어떤 데이터를 어떻게 조회하는지 명확하지 않으니 LLM이 엉뚱한 쿼리를 날리는 경우가 많았습니다.
      • 도구 간 충돌: 기능이 겹치는 도구들이 있으면, LLM이 어떤 도구를 선택해야 할지 갈팡질팡했습니다.
    • 해결법:
      • 도구 개수 최소화: 꼭 필요한 도구만 제공하고, 복잡한 기능은 여러 도구가 아닌 하나의 도구 내부에서 처리하도록 했습니다.
      • 명확하고 간결한 설명: 각 도구의 name과 description, 그리고 기대하는 input(입력)과 output(출력) 형식을 매우 구체적으로 정의했습니다. “이 도구는 언제, 무엇을 위해 사용하며, 어떤 정보를 입력해야 가장 좋은 결과를 얻을 수 있다”는 점을 명시했죠.
      • 도구 체이닝(Tool Chaining): 복잡한 작업은 에이전트가 아닌, 미리 정의된 파이프라인 안에서 여러 도구를 순차적으로 호출하도록 설계했습니다.

    3. 컨텍스트 윈도우(Context Window) 관리 실패

    • 문제점: 에이전트가 이전 대화나 작업 이력을 계속 기억하게 하려니 컨텍스트 윈도우가 빠르게 꽉 차버렸습니다. LLM의 토큰(Token) 제한에 걸려 중요한 정보를 놓치거나, 비용이 폭증하는 문제가 발생했죠. 💸 특히 RAG를 적용했을 때, 검색된 문서들이 컨텍스트를 과도하게 차지하는 경우가 많았습니다.
    • 해결법:
      • 요약(Summarization): 이전 대화 이력을 주기적으로 요약해서 컨텍스트에 포함시켰습니다. Haystack의 요약 컴포넌트(Summarizer Component)를 활용했죠.
      • 관련성 필터링: 모든 이력을 넣는 대신, 현재 태스크와 가장 관련성 높은 정보만 선별적으로 컨텍스트에 주입했습니다.
      • 토큰 예산 설정: 각 단계별로 사용할 토큰의 최대치를 정해두고, 넘어가면 요약을 강제하거나 오래된 정보를 제거하는 로직을 추가했습니다.

    4. 평가(Evaluation)와 반복(Iteration)의 부재

    • 문제점: 처음에 “잘 되겠지” 하고 대충 만들고는, 몇 번 테스트해보고 안 되면 그냥 갈아엎는 식으로 개발했습니다. 어떤 부분에서 실패했는지, 왜 실패했는지에 대한 체계적인 기록이나 평가 없이 주먹구구식으로 접근했죠. 결국 똑같은 실수를 반복하고, 개선 속도가 매우 느렸습니다. 🐢
    • 해결법:
      • 테스트 케이스 작성: 다양한 시나리오에 대한 테스트 케이스(Test Case)를 만들고, 각 케이스별 에이전트의 응답을 기록했습니다.
      • 평가 지표 정의: ‘정확도’, ‘관련성’, ‘도구 사용의 적절성’ 등 평가 지표를 정의하고, 결과를 수치화해서 개선점을 명확히 파악했습니다. Haystack은 자체적으로 평가 도구를 제공하기도 합니다.
      • 디버깅(Debugging) 환경 구축: 에이전트의 추론 과정(Thought Process), 사용된 도구, LLM의 최종 출력 등을 쉽게 확인할 수 있는 로깅(Logging) 및 모니터링(Monitoring) 시스템을 구축했습니다.
    AI 에이전트 성능 모니터링 대시보드

    그림 3: AI 에이전트 성능 모니터링 대시보드 예시.

    그래서, 어떻게 개선했고 어떤 결과를 얻었나?

    위에서 언급한 설계 함정들을 하나씩 고쳐나가면서 제 Haystack 에이전트는 훨씬 더 견고해질 수 있었습니다. 특히 LLM의 역할을 명확히 하고, 도구 설계를 정교하게 가져간 것이 주효했어요. 삽질 끝에 드디어 에이전트가 제가 의도한 대로 동작하는 모습을 봤을 때의 희열이란! 🎉

    물론 아직 완벽하진 않지만, 이전처럼 엉뚱한 답변을 하거나 무한 루프에 빠지는 일은 현저히 줄었습니다. 특히 복잡한 정보 검색과 요약 작업에서 높은 효율을 보여주고 있어요. 이젠 단순한 질문 답변을 넘어, 특정 스케줄에 맞춰 필요한 정보를 자동으로 가져와 요약해주는 수준까지 발전시켰답니다.

    가장 크게 배운 점은 “LLM 에이전트 개발은 일반적인 소프트웨어 개발과 다르지 않다”는 것입니다. 단순히 프롬프트만 잘 쓴다고 되는 게 아니라, 아키텍처(Architecture) 설계, 모듈화(Modularization), 테스트(Testing), 디버깅(Debugging) 등 소프트웨어 엔지니어링의 기본 원칙들이 그대로 적용된다는 사실을 다시 한번 깨달았습니다.

    # 개선된 에이전트의 동작 예시 (Haystack 2.x 기반 개념적 코드)
    # 최신 API는 공식 문서를 참고하세요.
    # 핵심은 LLM의 역할은 '이해'와 '도구 인자 추출'에 집중하고,
    # 복잡한 로직은 파이프라인이나 외부 함수로 분리하는 것.
    
    from haystack.agents import Agent, Tool
    from haystack.components.generators import OpenAIChatGenerator
    from haystack.components.retrievers import InMemoryBM25Retriever
    from haystack.components.document_stores import InMemoryDocumentStore
    from haystack.components.builders.answer_builder import AnswerBuilder
    from haystack import Pipeline
    
    # 1. DocumentStore와 Retriever 설정 (RAG 예시)
    document_store = InMemoryDocumentStore()
    # document_store.write_documents(...) # 실제 문서 로딩
    retriever = InMemoryBM25Retriever(document_store=document_store)
    
    # 2. Tools 정의 (명확한 설명과 입력/출력)
    def perform_complex_analytics(data_query: str):
        """
        고급 분석을 수행하고 보고서를 생성합니다.
        입력: 'data_query' - 분석할 데이터에 대한 구체적인 쿼리 문자열 (예: "지난달 판매량 추이").
        출력: 분석 결과 요약 텍스트.
        """
        print(f"DEBUG: Performing complex analytics for: {data_query}")
        # 실제 복잡한 분석 로직 (외부 시스템 연동 등)
        return f"분석 결과: {data_query}에 대한 심층 분석 보고서가 생성되었습니다."
    
    analytics_tool = Tool(
        name="complex_analytics_tool",
        function=perform_complex_analytics,
        description="주어진 데이터 쿼리에 따라 복잡한 데이터 분석을 수행하고 요약 보고서를 생성합니다."
    )
    
    # 3. LLM Generator
    llm_generator = OpenAIChatGenerator(model="gpt-4", api_key="YOUR_API_KEY")
    
    # 4. Agent 생성 (이제 에이전트는 Tool과 LLM에 의존)
    my_improved_agent = Agent(llm=llm_generator, tools=[analytics_tool])
    
    # 5. Pipeline 구축 (에이전트가 복잡한 파이프라인의 한 단계로 동작할 수도 있습니다)
    # 이 예시에서는 에이전트 자체가 메인 역할을 하도록 단순화.
    # 실제로는 RAG Pipeline -> Agent -> Answer Builder 등으로 구성될 수 있습니다.
    
    # 예시: 에이전트 실행 및 결과 확인
    # print(my_improved_agent.run("지난달 서울 지역 판매량 추이에 대한 분석 보고서를 만들어줘."))
    # 결과는 이전보다 훨씬 의도에 맞게, 적절한 도구를 사용하며 나옵니다.
    

    마무리하며: 멘토로서 드리는 조언

    AI 에이전트 개발은 정말 흥미로운 분야입니다. 하지만 제가 겪었던 것처럼 많은 시행착오가 따를 수밖에 없습니다. 13년차 인프라 엔지니어로서, 그리고 홈랩에서 직접 삽질하며 배운 점을 정리하자면 이렇습니다. ✅

    항목 실패 사례 (Bad Practice) 성공 사례 (Good Practice)
    LLM 역할 모든 복잡한 로직을 LLM에 맡김 LLM은 ‘이해’와 ‘도구 선택’에 집중, 복잡한 로직은 코드로 분리
    도구(Tool) 설계 너무 많거나 모호한 설명, 기능 중복 최소한의 도구, 명확하고 구체적인 설명, 단일 책임 원칙
    컨텍스트 관리 모든 이력 저장, 토큰 제한 무시 요약, 관련성 필터링, 토큰 예산 설정
    개발 프로세스 주먹구구식 개발, 체계적인 평가 부재 테스트 케이스, 평가 지표, 디버깅 환경 구축
    실패를 통해 성장한 인프라 엔지니어

    그림 4: 실패를 딛고 성장한 인프라 엔지니어의 모습.

    AI 에이전트는 앞으로 우리 업무 방식에 큰 변화를 가져올 잠재력을 가지고 있습니다. 여러분도 저처럼 포기하지 않고 꾸준히 실험하고 개선해나간다면 분명 멋진 결과물을 만들어낼 수 있을 겁니다. 저도 다음번에는 더 고도화된 Haystack 에이전트 구축 경험이나, 특정 도구를 연동하는 방법에 대해 이야기해볼게요.

    궁금한 점이나 함께 나눌 경험이 있다면 언제든지 댓글로 남겨주세요! 다음 글에서 만나요! 👋

  • [CI/CD] Kubernetes 환경 Jenkins: 1년 운영하며 겪은 성능 최적화와 안정화 사례

    [CI/CD] Kubernetes 환경 Jenkins: 1년 운영하며 겪은 성능 최적화와 안정화 사례

    [CI/CD] Kubernetes 환경 Jenkins: 1년 운영하며 겪은 성능 최적화와 안정화 사례

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘은 Kubernetes(쿠버네티스) 환경에서 Jenkins(젠킨스)를 1년 넘게 운영하며 겪었던 삽질과 그 과정에서 얻은 성능 최적화, 안정화 노하우를 풀어볼까 합니다. 많은 분들이 CI/CD(지속적 통합/지속적 배포) 파이프라인 구축에 Jenkins를 사용하고 계실 텐데, 특히 Kubernetes 위에서 Jenkins를 돌리면서 “이게 맞나?” 싶었던 경험들 있으실 거예요. 제가 딱 그랬거든요. 😅

    처음엔 마냥 좋다고 생각했던 Jenkins on Kubernetes가 생각보다 많은 운영 난이도를 요구했습니다. 툭하면 죽는 빌드 에이전트, 느려터진 빌드 시간, 예상치 못한 마스터 다운까지… 정말이지 밤낮없이 씨름했던 기억이 생생합니다. 이 글에서는 제가 직접 부딪히며 해결했던 문제들과 그 과정을 여러분들께 멘토처럼 상세히 알려드릴게요. 혹시 비슷한 고민을 하고 계셨다면, 제 경험이 조금이나마 도움이 되기를 바랍니다! 🙏

    Kubernetes 환경 Jenkins 아키텍처 개요: 마스터와 동적 에이전트 파드들의 상호작용

    Jenkins on Kubernetes, 왜 선택했을까요? (개념 설명)

    Jenkins는 워낙 유명한 CI/CD 자동화 도구죠. 프로젝트 빌드, 테스트, 배포 등 개발 프로세스의 여러 단계를 자동화해주는 역할을 합니다. 그런데 이걸 왜 굳이 Kubernetes 위에서 돌리냐고요? 간단히 말해서, 유연성과 확장성 때문입니다.

    • 동적 에이전트 프로비저닝 (Dynamic Agent Provisioning): Kubernetes의 가장 큰 장점 중 하나인데요, 빌드가 필요할 때만 Jenkins Agent(젠킨스 에이전트) Pod(파드)를 생성하고, 빌드가 끝나면 자동으로 제거할 수 있습니다. 덕분에 리소스를 효율적으로 사용할 수 있고, 동시에 여러 빌드를 처리할 때도 필요한 만큼 에이전트를 늘릴 수 있죠.
    • 높은 가용성 (High Availability): Kubernetes는 컨테이너화된 애플리케이션의 고가용성을 보장합니다. Jenkins Master(젠킨스 마스터) Pod가 문제가 생겨도 Kubernetes가 자동으로 다른 노드에 재시작해주니, 서비스 중단 시간을 최소화할 수 있습니다.
    • 환경 일관성 (Environment Consistency): 모든 빌드가 컨테이너 안에서 이뤄지므로, 개발 환경과 동일한 환경에서 빌드 및 테스트를 진행할 수 있어서 “내 로컬에서는 되는데 서버에서는 안 돼요!” 하는 문제를 줄일 수 있습니다.

    이런 장점들 덕분에 저도 Kubernetes 환경으로 Jenkins를 옮기기로 결정했었죠. 하지만 현실은 녹록지 않았습니다. 😅

    실전 구현: Jenkins Kubernetes 플러그인과 Pod Template

    Kubernetes에서 Jenkins를 운영하려면 Jenkins Kubernetes Plugin(젠킨스 쿠버네티스 플러그인)이 필수인데요. 이 플러그인이 Jenkins Master와 Kubernetes 클러스터 간의 통신을 담당하면서 동적으로 에이전트 Pod를 생성하고 관리하는 핵심 역할을 수행합니다. 플러그인 설치 후, 가장 중요한 설정은 Pod Template(파드 템플릿)입니다.

    Pod Template은 Jenkins Agent Pod가 어떤 이미지로, 어떤 리소스를 가지고 생성될지 정의하는 YAML 설정이거든요. 처음에는 기본 설정으로 시작했지만, 곧바로 성능 병목에 부딪혔습니다. 다음은 제가 주로 사용했던 Pod Template의 핵심 부분입니다.

    apiVersion: v1
    kind: Pod
    spec:
      containers:
      - name: jnlp
        image: jenkins/inbound-agent:4.11.2-1-jdk11
        resources:
          requests:
            cpu: "500m"
            memory: "1Gi"
          limits:
            cpu: "1"
            memory: "2Gi"
        # ... (생략) ...
      - name: build-tools
        image: my-private-registry/custom-build-image:latest
        command: ["cat"]
        tty: true
        resources:
          requests:
            cpu: "1"
            memory: "2Gi"
          limits:
            cpu: "2"
            memory: "4Gi"
        # ... (생략) ...
      volumes:
      - name: jenkins-workspace
        emptyDir: {}
      # ... (생략) ...
    

    여기서 `jnlp` 컨테이너는 Jenkins와 통신하는 기본 에이전트고, `build-tools`는 실제 빌드 도구들이 들어간 커스텀 이미지더라고요. 여기서 중요한 부분은 바로 resources 섹션입니다. 처음에는 이 부분을 대충 설정했다가 빌드 에이전트들이 자꾸 죽는 현상을 겪었어요. ⚠️

    Jenkins UI Kubernetes 플러그인 설정 화면: Pod Template 구성 예시

    Jenkins UI 내 Kubernetes 플러그인 설정 화면: Pod Template 구성 예시

    ⚠️ 1년 운영하며 겪은 삽질과 성능 최적화/안정화 사례

    자, 이제 본론입니다. 1년 동안 겪었던 문제들과 그 해결책들을 공유해 드릴게요.

    1. 빌드 에이전트 OOMKilled (메모리 부족) 현상

    가장 흔하게 겪었던 문제입니다. 빌드 도중에 에이전트 Pod가 갑자기 OOMKilled(Out Of Memory Killed, 메모리 부족으로 종료됨) 상태가 되면서 빌드가 실패하는 거죠. 처음엔 원인을 몰라 헤맸는데, 알고 보니 Pod Template의 resources.limits.memory 설정이 너무 낮았던 것이었습니다.

    • 문제점: 빌드 프로세스가 예상보다 많은 메모리를 사용하면서 Kubernetes가 Pod를 강제로 종료시킴.
    • 해결책: resources.requests와 resources.limits를 현실적으로 설정하는 것이 중요합니다. requests는 Pod가 스케줄링될 때 필요한 최소한의 리소스, limits는 Pod가 최대로 사용할 수 있는 리소스예요. 처음에 빌드를 여러 번 돌려보면서 실제로 필요한 메모리 양을 측정했습니다. 빌드 과정에서 peak 메모리 사용량을 모니터링해서 넉넉하게 잡아주는 게 중요하더라고요.
        resources:
          requests:
            cpu: "1"
            memory: "2Gi" # 최소 2GB 요청
          limits:
            cpu: "2"
            memory: "4Gi" # 최대 4GB까지 사용 허용
    

    💡 팁: requests는 Kubernetes 스케줄러가 노드를 선택하는 기준이 되고, limits는 CGroup(컨트롤 그룹)에 의해 Pod의 최대 리소스 사용량을 제한합니다. 너무 타이트하면 OOMKilled 되고, 너무 넉넉하면 리소스 낭비가 될 수 있으니 적절한 튜닝이 필요합니다.

    2. 느린 이미지 풀링 (Image Pull) 시간

    동적 에이전트의 가장 큰 장점 중 하나지만, 매번 빌드 에이전트 Pod가 생성될 때마다 필요한 Docker Image(도커 이미지)를 다운로드(Pull)해야 한다는 단점도 있습니다. 특히 빌드 에이전트 이미지가 크거나, 여러 개의 이미지를 사용하는 경우 빌드 시작 시간이 너무 길어지는 문제가 발생했습니다.

    • 문제점: 빌드 시작 시 Docker Image Pull 시간 때문에 전체 빌드 시간이 길어짐.
    • 해결책:
      1. 로컬 Docker Registry(도커 레지스트리) 사용: 사설 Docker Registry를 클러스터 내부에 구축하고, 이미지를 미리 이곳에 캐싱해두면 Pull 속도가 훨씬 빨라집니다.
      2. Node에 Image Pre-pulling: Kubernetes Worker Node(워커 노드)에 DaemonSet(데몬셋)을 이용해서 자주 사용하는 에이전트 이미지를 미리 Pull 해두는 방법도 효과적이었습니다. 이렇게 하면 Pod가 스케줄링될 때 이미지가 이미 로컬에 있어 바로 실행될 수 있죠.
      3. Jenkins Agent 이미지 최적화: 빌드에 필요한 최소한의 도구만 포함된 경량 이미지를 만들었습니다. 불필요한 레이어를 줄이고, 베이스 이미지를 최신으로 유지하는 것도 중요하더라고요.

    3. Jenkins Master의 안정성 확보

    아무리 에이전트가 잘 돌아가도 Master가 불안정하면 모든 CI/CD 파이프라인이 멈춥니다. Master Pod의 잦은 재시작이나 데이터 손실은 정말 끔찍하죠.

    • 문제점: Master Pod의 리소스 부족, 데이터 손실 위험.
    • 해결책:
      1. Persistent Volume Claim (PVC) 사용: Jenkins Master의 /var/jenkins_home 디렉터리는 빌드 설정, 플러그인, 빌드 이력 등 중요한 데이터가 저장되는 곳이거든요. 반드시 Persistent Volume Claim(PVC, 영구 볼륨 클레임)을 사용하여 데이터를 영구적으로 저장해야 합니다. 저는 NFS(네트워크 파일 시스템) 기반의 PV(영구 볼륨)를 사용했는데, 클라우드 환경에서는 EBS, Azure Disk, GCE Persistent Disk 등을 활용할 수 있습니다.
      2. Master Pod 리소스 충분히 할당: Master Pod도 적절한 CPU와 Memory를 할당해야 합니다. 너무 적으면 UI가 느려지거나, 플러그인 로딩 중 OOMKilled 될 수 있습니다.
      3. Configuration as Code (CasC) 도입: Jenkins 설정을 YAML 파일로 관리하는 CasC(Configuration as Code)는 Master의 안정성을 높이는 데 크게 기여했습니다. 설정을 Git에 버전 관리하고, Jenkins 재시작 시 자동으로 적용되도록 하여 휴먼 에러를 줄이고 재현 가능한 환경을 구축할 수 있었습니다.
    최적화 전후 Jenkins 빌드 시간 및 Kubernetes 리소스 사용량 비교 Grafana 대시보드

    최적화 후 빌드 시간 단축 및 리소스 사용량 안정화 결과

    4. 네트워크 성능 문제 (대용량 아티팩트 전송)

    빌드 결과물(Artifacts, 아티팩트)이 크거나, 빌드 과정에서 외부 리소스(Maven Repository 등)를 자주 다운로드받는 경우 네트워크 병목이 발생할 수 있습니다.

    • 문제점: 대용량 아티팩트 전송 및 외부 리소스 다운로드로 인한 빌드 지연.
    • 해결책:
      1. 클러스터 내부 캐싱 프록시: Maven, npm 등의 패키지 매니저를 위한 캐싱 프록시(예: Sonatype Nexus, Artifactory)를 클러스터 내부에 구축하여 외부 네트워크 트래픽을 줄였습니다.
      2. 빠른 스토리지 사용: PVC에 사용하는 스토리지가 느리면 빌드 과정에서 파일 I/O(입출력) 성능 저하가 발생합니다. 가능한 한 SSD 기반의 고성능 스토리지를 사용하는 것이 좋습니다.
      3. 불필요한 아티팩트 최소화: 빌드 후 Jenkins에 저장하거나 전송하는 아티팩트의 크기를 최소화하도록 파이프라인을 최적화했습니다.

    검증 및 결과: 드디어 안정화! 🎉

    위에 언급된 최적화들을 적용하고 나니, 거짓말처럼 Jenkins 환경이 안정화되기 시작했습니다. 빌드 실패율은 현저히 줄었고, 빌드 시간도 평균 30% 이상 단축되는 성과를 얻을 수 있었습니다. 특히 동적 에이전트의 효율적인 리소스 사용 덕분에 클러스터 비용도 절감할 수 있었죠.

    • 빌드 성공률: 60% → 95% 이상으로 향상
    • 평균 빌드 시간: 5분 → 3분 내외로 단축 (프로젝트별 상이)
    • 리소스 사용 효율: 유휴 에이전트 감소로 클러스터 리소스 활용률 증가

    이러한 개선 사항들은 Prometheus(프로메테우스)와 Grafana(그라파나)를 통해 모니터링하면서 실시간으로 확인했습니다. 특히 빌드 성공/실패율, 큐(Queue)에 대기 중인 빌드 수, 에이전트 Pod의 리소스 사용량 등을 꾸준히 트래킹하면서 추가적인 개선점을 찾아나갔습니다. 📈

    Kubernetes Jenkins 운영 최적화 주요 포인트 요약 인포그래픽

    Kubernetes Jenkins 운영 최적화 주요 포인트 요약 인포그래픽

    마무리: 멘토로서의 조언과 다음 단계

    Kubernetes 환경에서 Jenkins를 운영하는 것은 분명 도전적인 일입니다. 처음에는 “이거 왜 이렇게 어렵지?” 싶다가도, 하나하나 문제를 해결해나가면서 시스템이 안정화되는 모습을 보면 정말 뿌듯하더라고요. 제가 13년 동안 인프라 엔지니어로 일하면서 느낀 건, 삽질은 결국 성장의 밑거름이 된다는 겁니다.

    이 글에서 다룬 내용들이 여러분의 Kubernetes Jenkins 운영에 조금이나마 도움이 되었으면 좋겠습니다. 혹시 더 깊은 질문이나 다른 경험담이 있다면 언제든지 댓글로 남겨주세요! 저도 처음엔 헷갈렸던 부분이 많았거든요.

    다음 단계로는 GitOps(깃옵스) 철학을 기반으로 Argo CD(아르고 CD) 같은 도구를 Jenkins와 연동하여 배포 파이프라인을 더욱 자동화하고 고도화하는 방안을 고민하고 있습니다. CI/CD 여정은 끝이 없는 것 같아요. 계속해서 배우고 실험하며 더 나은 방법을 찾아나가야겠죠? 😊

  • [k8s] Argo CD 멀티 클러스터 동기화 문제 해결: 실제 운영 사례와 디버깅 팁

    [k8s] Argo CD 멀티 클러스터 동기화 문제 해결: 실제 운영 사례와 디버깅 팁

    Argo CD 멀티 클러스터 동기화 문제 해결: 실제 운영 사례와 디버깅 팁

    안녕하세요, 13년차 서버실 지킴이입니다. 요즘 쿠버네티스(Kubernetes) 환경에서 GitOps(깃옵스)를 도입하는 기업들이 정말 많죠? 저도 홈랩과 회사 프로젝트에서 Argo CD(아르고 CD)를 활용해서 여러 쿠버네티스 클러스터를 관리하고 있는데요. 처음엔 “와, 이거 정말 편하겠다!” 싶었는데, 막상 실제 운영 환경에서 멀티 클러스터(Multi-Cluster) 동기화 문제를 만나면 머리가 지끈거릴 때가 많더라고요.

    특히 여러 클러스터에 동일한 애플리케이션을 배포하거나, 환경별로 미묘하게 다른 설정을 적용해야 할 때 ApplicationSet(애플리케이션셋)을 쓰면 정말 유용하거든요. 그런데 간혹 특정 클러스터만 동기화가 안 되거나, 알 수 없는 에러로 삽질을 거듭하곤 해요. 제가 직접 겪었던 Argo CD 멀티 클러스터 동기화 문제들과 그 해결 과정을 솔직하게 공유해볼까 해요. 혹시 비슷한 경험으로 고생하고 계시다면, 이 글이 작은 힌트라도 되었으면 좋겠네요! 💡

    Argo CD 멀티 클러스터 관리 아키텍처: Git을 중심으로 Argo CD가 여러 쿠버네티스 클러스터에 애플리케이션을 배포하고 동기화하는 흐름을 보여줍니다.

    Argo CD, GitOps, 그리고 멀티 클러스터, 이게 뭔가요?

    본격적인 문제 해결에 앞서, 핵심 개념들을 간단히 짚고 넘어갈게요. “아, 이건 다 아는데!” 하시는 분들도 계시겠지만, 혹시 처음 접하시는 분들을 위해 쉽게 풀어 설명해 드릴게요.

    • Argo CD (아르고 CD): 쿠버네티스 환경에서 GitOps를 구현하기 위한 대표적인 선언적(Declarative) CD(Continuous Delivery, 지속적 배포) 툴입니다. Git 저장소를 애플리케이션의 ‘원본 소스(Single Source of Truth)’로 삼아, 쿠버네티스 클러스터의 실제 상태가 Git에 정의된 상태와 일치하도록 지속적으로 동기화(Synchronization)를 시도하죠. 즉, Git에 코드를 푸시하면 Argo CD가 알아서 클러스터에 배포해주는 방식입니다.

    • GitOps (깃옵스): Git을 운영의 중심(Operational Hub)으로 삼아 인프라와 애플리케이션의 모든 상태를 관리하는 방식입니다. 모든 변경 사항이 Git에 기록되므로, 누가 언제 무엇을 변경했는지 추적하기 쉽고, 문제가 생겼을 때 쉽게 롤백(Rollback)할 수 있다는 장점이 있습니다. “Git에서 정의한 대로 클러스터 상태를 유지한다”는 철학이죠.

    • 멀티 클러스터 (Multi-Cluster): 여러 개의 쿠버네티스 클러스터를 동시에 관리하는 환경을 의미합니다. 개발, 스테이징, 프로덕션 환경을 분리하거나, 재해 복구(Disaster Recovery)를 위해 여러 리전에 클러스터를 운영할 때 등 다양한 이유로 멀티 클러스터 환경을 구축합니다. Argo CD는 하나의 중앙 집중식(Centralized) 컨트롤 플레인(Control Plane)으로 여러 원격 클러스터(Remote Cluster)를 관리할 수 있게 해줍니다.

    Argo CD 멀티 클러스터 설정과 흔한 문제 시나리오

    Argo CD를 이용해 멀티 클러스터를 관리하려면, 먼저 Argo CD 컨트롤 플레인이 설치된 클러스터(보통 ‘관리 클러스터’)에 다른 클러스터들을 등록해야 합니다. argocd cluster add 명령어가 이걸 해주죠. 이때 Argo CD가 원격 클러스터에 접근할 수 있는 ServiceAccount와 ClusterRoleBinding을 생성해줍니다.

    그리고 여러 클러스터에 애플리케이션을 배포할 때는 주로 ApplicationSet을 사용합니다. 예를 들어, 다음과 같은 ApplicationSet으로 여러 클러스터에 nginx-app을 배포한다고 가정해봅시다.

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
      name: my-nginx-appset
    spec:
      generators:
      - clusters: {}
      template:
        metadata:
          name: '{{name}}-nginx-app'
        spec:
          project: default
          source:
            repoURL: https://github.com/my-org/my-gitops-repo.git
            targetRevision: HEAD
            path: apps/nginx
          destination:
            server: '{{server}}'
            namespace: default
          syncPolicy:
            automated:
              prune: true
              selfHeal: true
    

    이렇게 설정하면 Argo CD가 등록된 모든 클러스터에 apps/nginx 경로의 매니페스트를 배포하려고 시도합니다. 그런데 여기서 문제가 발생하곤 해요. 어떤 클러스터는 잘 동기화(SYNCED)되는데, 다른 클러스터는 계속 OutOfSync 상태이거나 Failed 상태를 벗어나지 못하는 거죠. “아니, 분명 다 똑같이 설정했는데 왜 이럴까?” 하는 생각이 절로 듭니다. 저도 처음엔 정말 막막했는데, 몇 가지 공통적인 원인이 있더라고요. 😅

    Argo CD UI의 ApplicationSet 대시보드: 문제 발생 상황

    Argo CD UI의 ApplicationSet 대시보드: 여러 클러스터에 배포된 애플리케이션들의 동기화 상태를 한눈에 볼 수 있으며, 문제가 발생한 애플리케이션을 쉽게 식별할 수 있습니다.

    ⚠️ 실제 운영 사례로 본 Argo CD 동기화 문제와 디버깅 팁

    제가 13년 동안 쌓은 삽질 경험을 바탕으로, Argo CD 멀티 클러스터 환경에서 자주 발생하는 동기화 문제와 그 해결책을 알려드릴게요. 하나씩 체크하다 보면 분명 원인을 찾을 수 있을 겁니다!

    1. RBAC (Role-Based Access Control) 권한 문제
      가장 흔하면서도 놓치기 쉬운 문제예요. Argo CD 컨트롤 플레인이 관리 대상 클러스터에 배포할 권한이 없는 경우입니다. argocd cluster add 명령어가 자동으로 필요한 권한을 생성해주지만, 간혹 수동으로 클러스터를 추가했거나, 보안 정책상 특정 권한이 누락된 경우가 있거든요.

      💡 디버깅 팁:

      • 관리 대상 클러스터에서 kubectl get serviceaccount argocd-manager -n argocd 명령어로 argocd-manager ServiceAccount가 잘 생성되었는지 확인하세요.
      • 이 ServiceAccount에 연결된 ClusterRoleBinding과 ClusterRole의 권한을 확인해보세요. 보통 cluster-admin 권한이 부여되는데, 만약 특정 리소스에 대한 권한만 있다면 해당 리소스 배포가 실패할 수 있거든요. kubectl describe clusterrole argocd-manager-role 명령으로 권한 목록을 볼 수 있습니다.
      • 특히 ServiceAccount가 아닌 다른 방식으로 인증(예: kubeconfig 파일)을 사용한다면, 해당 kubeconfig 파일에 정의된 사용자에게 충분한 권한이 있는지 확인해야 합니다.
    2. 네트워크 연결 문제
      Argo CD 서버가 관리 대상 클러스터의 쿠버네티스 API 서버에 접근할 수 없는 경우네요. 방화벽, VPN, VPC 피어링 등 네트워크 구성을 꼼꼼히 확인해야 합니다. “어제까진 잘 됐는데?” 하다가 네트워크 변경 때문에 막히는 경우가 꽤 있거든요.

      💡 디버깅 팁:

      • Argo CD UI에서 해당 클러스터의 상태가 Unavailable인지 확인하세요.
      • Argo CD 컨트롤러 파드(Pod)에서 관리 대상 클러스터의 API 서버로 curl 같은 명령어로 접속을 시도해보세요. 파드에 접속하는 방법은 kubectl exec -it <argocd-repo-server-pod> -n argocd -- bash 후 curl -k https://<cluster-api-server-ip>:<port>/healthz 와 같이 시도해볼 수 있습니다.
      • argocd cluster add 시 입력한 API 서버 주소가 올바른지 다시 한번 확인해보세요. 내부망 주소여야 할 때 외부망 주소를 입력하거나, 그 반대인 경우도 있거든요.
    3. ApplicationSet 설정 오류
      ApplicationSet의 generator나 template 설정이 잘못된 경우예요. 특히 cluster 필터링이나 Git 저장소 경로(path)를 잘못 지정했을 때 문제가 발생하곤 합니다.

      💡 디버깅 팁:

      • argocd appset get <application-set-name> 명령으로 ApplicationSet의 현재 상태와 생성된 Application 목록을 확인해보세요.
      • ApplicationSet의 template 섹션에서 source.path가 Git 저장소의 실제 경로와 일치하는지 확인하세요. 대소문자나 오타가 있을 수 있거든요.
      • generator에서 특정 클러스터만 선택하도록 설정했다면, 해당 클러스터의 라벨(labels)이 selector와 정확히 일치하는지 확인해보세요.
    4. 배포하려는 리소스 매니페스트 오류
      Git 저장소에 있는 쿠버네티스 매니페스트(YAML 파일) 자체에 문제가 있는 경우네요. 문법 오류, 잘못된 필드 이름, 존재하지 않는 StorageClass 참조 등이 있을 수 있거든요.

      💡 디버깅 팁:

      • Argo CD UI에서 Application 상태를 확인하고, Events(이벤트) 탭이나 Logs(로그) 탭을 자세히 살펴보세요. 어떤 리소스에서 어떤 에러가 발생했는지 상세하게 나옵니다. 예를 들어, "admission webhook denied the request" 같은 메시지는 Admission Controller(어드미션 컨트롤러)나 OPA Gatekeeper 같은 정책 엔진에 의해 배포가 거부되었음을 의미할 수 있어요.
      • 해당 클러스터에 직접 kubectl apply -f <problematic-manifest.yaml> --dry-run=client 로 배포를 시도하여 문법 오류 등을 미리 확인해보세요.
      • kubectl describe <resource-type> <resource-name> -n <namespace> 명령으로 대상 클러스터에 배포된(혹은 배포 실패한) 리소스의 상세 상태를 확인하세요.
    5. Argo CD 컨트롤러 파드 문제
      아주 드물지만, Argo CD 자체의 argocd-application-controller나 argocd-repo-server 파드에 문제가 생겨 동기화가 제대로 작동하지 않을 수 있어요. 리소스 부족, 네트워크 문제, 버그 등이 원인일 수 있거든요.

      💡 디버깅 팁:

      • kubectl get pods -n argocd 명령으로 Argo CD 관련 파드들이 모두 Running 상태인지 확인해보세요.
      • 문제 있어 보이는 파드의 로그를 확인해보세요: kubectl logs -f <argocd-pod-name> -n argocd. 특히 argocd-application-controller 로그에 동기화 관련 오류 메시지가 나타날 수 있거든요.
      • 파드를 재시작하거나, 리소스(CPU, Memory)를 늘려주는 것도 방법이 될 수 있습니다.

    ✅ 문제 해결 후 검증하기

    위의 팁들을 바탕으로 문제를 해결했다면, 이제 제대로 동기화가 되는지 확인해야겠죠? 드디어 SYNCED 상태를 봤을 때의 그 쾌감이란! 🎉

    1. Argo CD UI/CLI 확인
      가장 먼저 Argo CD UI에 접속해서 해당 Application 또는 ApplicationSet의 상태가 Synced 그리고 Healthy로 표시되는지 확인합니다. CLI를 선호한다면 argocd app list 나 argocd app get <application-name> 명령어를 사용해보세요.

    2. 대상 클러스터 직접 확인
      Argo CD UI/CLI에서 Synced로 표시되더라도, 혹시 모르니 해당 클러스터에 접속해서 kubectl get <resource-type> -n <namespace> 명령으로 실제로 리소스가 배포되었는지, 상태는 어떤지 직접 확인하는 게 좋습니다. 특히 Deployment나 StatefulSet의 파드들이 정상적으로 Running 상태인지 확인해 주세요.

    3. 새로운 변경사항 푸시 테스트
      Git 저장소에 간단한 변경사항(예: ConfigMap 내용 변경)을 푸시해서 Argo CD가 변경을 감지하고 자동으로 동기화하는지 확인해봅니다. 이 과정이 원활하게 진행된다면 성공적으로 문제를 해결한 거예요!

    Argo CD UI의 성공적인 동기화 대시보드

    성공적인 Argo CD 동기화 대시보드: 모든 애플리케이션이 문제없이 배포되고 동기화되어 안정적인 운영 상태를 보여줍니다.

    마무리하며: 삽질은 경험이 되고, 경험은 자산이 됩니다.

    오늘은 Argo CD 멀티 클러스터 동기화 문제 해결에 대한 저의 경험과 디버깅 팁을 공유해드렸습니다. 솔직히 저도 처음엔 정말 막막해서 밤늦게까지 삽질 좀 했거든요. “왜 안 되는 거지?” 하면서 클러스터 여기저기를 들쑤시고 다녔던 기억이 생생해요. 😂

    하지만 이런 삽질 과정이 결국 저의 노하우가 되고, 여러분 같은 동료 인프라 엔지니어분들께 도움이 되는 자산이 된다는 사실을 깨달았습니다. Argo CD는 정말 강력한 툴이지만, 그만큼 복잡한 환경에서는 예상치 못한 문제들이 발생할 수 있어요. 중요한 건 문제를 만났을 때 당황하지 않고, 차근차근 원인을 찾아 해결해나가는 자세라고 생각합니다.

    이번 글이 Argo CD 멀티 클러스터 환경에서 고군분투하는 분들께 조금이나마 도움이 되었기를 바랍니다. 다음 글에서는 Argo CD의 Progressive Delivery(점진적 배포) 전략인 Argo Rollouts에 대해 다뤄볼게요. 그때까지 GitOps 즐겁게 운영하시길 바랍니다! 궁금한 점이 있다면 언제든 댓글 남겨주세요! 😊

    Argo CD 멀티 클러스터 동기화 문제 해결 체크리스트: RBAC, 네트워크, ApplicationSet 설정, 매니페스트 오류, Argo CD 파드 상태 등 주요 점검 사항을 요약한 인포그래픽입니다.

  • [AI] 로컬 LLM 성능 최적화: Ollama와 Claude Sonnet 비교 및 최신 동향

    [AI] 로컬 LLM 성능 최적화: Ollama와 Claude Sonnet 비교 및 최신 동향

    로컬 LLM, 왜 이렇게까지? 클라우드와 온프레미스의 갈림길에서

    안녕하세요, 13년차의 서버실 주인장입니다. 요즘 LLM(Large Language Model, 대규모 언어 모델) 얘기가 정말 많잖아요? 저도 인프라 엔지니어이다 보니, 이 기술이 불러올 변화에 늘 촉각을 곤두세우고 있습니다. 그런데 클라우드에서 API(Application Programming Interface)를 호출해서 쓰는 LLM 서비스들, 솔직히 비용이 만만치 않더라고요. 그리고 민감한 데이터를 다룰 때는 프라이버시 문제도 신경 쓰이고요.

    그래서 저처럼 홈랩을 운영하는 인프라 덕후들은 늘 고민합니다. ‘이 비싼 거, 내가 직접 돌릴 수는 없을까?’ 이 질문에서부터 저의 로컬 LLM 삽질이 시작됐습니다. 특히 최근 각광받는 Ollama(올라마)를 활용해서 로컬 LLM 환경을 구축하고, 제가 평소에 자주 쓰는 Claude Sonnet(클로드 소네트)과 성능을 비교해 보면서 어떤 점이 좋고 아쉬웠는지 솔직하게 이야기해보려고 합니다. NPU(Neural Processing Unit, 신경망 처리 장치) 활용이 얼마나 중요한지도 함께 다뤄볼게요. 혹시 여러분도 이런 고민 해보신 적 있으신가요? 제 경험이 작은 도움이 되기를 바랍니다. 이 모든 과정은 효율적인 엣지 AI 및 퍼스널 AI 환경 구축을 위한 여정입니다.

    Ollama 기반 로컬 LLM과 Claude Sonnet 클라우드 LLM 아키텍처 비교 다이어그램

    로컬 LLM과 클라우드 LLM의 대략적인 아키텍처 비교 다이어그램입니다. 로컬 환경에서 Ollama를 통해 모델을 실행하는 모습과 클라우드 API를 호출하는 구조를 시각적으로 보여줍니다.

    Ollama, 로컬 LLM의 든든한 동반자

    Ollama가 무엇이냐고요? 쉽게 말해, 로컬 환경에서 다양한 LLM을 쉽게 설치하고 실행할 수 있도록 도와주는 오픈소스 프레임워크입니다. 마치 Docker(도커)로 컨테이너 이미지를 다루듯이, Ollama를 사용하면 Llama 3(라마 3), Phi-3(파이-3), Mistral(미스트랄) 같은 최신 모델들을 명령줄 한 줄로 다운로드하고 바로 실행할 수 있어요. 처음엔 ‘이게 뭔가 싶었는데, 써보니까 진짜 편하더라고요! 이는 진정한 온프레미스 AI, 엣지 AI 환경을 구축하는 핵심적인 단계입니다.

    Ollama의 가장 큰 장점은 바로 하드웨어 가속을 적극적으로 활용한다는 점입니다. 특히 요즘 나오는 CPU(Central Processing Unit, 중앙 처리 장치)에 내장된 NPU나, 강력한 외장 GPU(Graphics Processing Unit, 그래픽 처리 장치)를 활용해서 LLM 추론(inference) 성능을 비약적으로 끌어올릴 수 있거든요. 클라우드 LLM, 예를 들어 Claude Sonnet 같은 서비스는 모든 컴퓨팅 자원을 클라우드 제공자가 관리해줘서 편리하지만, Ollama는 내 손으로 직접 자원을 최적화할 수 있다는 매력이 있죠. 이는 AI 비용 최적화에도 큰 도움이 됩니다.

    홈랩에 Ollama 설치하고 로컬 LLM 돌려보기

    자, 그럼 이제 제 홈랩에 Ollama를 설치하고 LLM을 한번 돌려볼까요? 저는 주로 Docker를 많이 쓰지만, Ollama는 바이너리 설치도 아주 쉽습니다. 여기서는 macOS(맥OS) 기준으로 설명해볼게요.

    1. Ollama 설치:
      터미널에서 다음 명령어를 실행하면 끝입니다. 맥용 앱이나 리눅스/윈도우 설치 가이드도 공식 홈페이지에 잘 나와 있어요.

      curl -fsSL https://ollama.com/install.sh | sh

      ✅ 설치가 완료되면, ollama --version 명령어로 제대로 설치되었는지 확인할 수 있습니다.

    2. 모델 다운로드 및 실행:
      Ollama는 다양한 모델을 제공합니다. 저는 가볍게 시작하기 위해 llama3 모델을 선택했어요. (또는 phi3 같은 경량 모델도 좋습니다.)

      ollama run llama3

      이 명령어를 입력하면 llama3 모델이 자동으로 다운로드되고 바로 채팅 세션이 시작됩니다. 정말 간단하죠? 처음엔 모델 다운로드하는 데 시간이 좀 걸릴 수 있습니다.

    3. Modelfile을 통한 커스터마이징 맛보기:
      Ollama는 Modelfile이라는 걸 이용해서 모델을 커스터마이징할 수 있어요. 예를 들어, 시스템 프롬프트(System Prompt)를 미리 설정해두거나, 특정 파라미터(Parameter)를 조절할 수 있습니다. 저는 모델이 항상 친절하게 답변하도록 설정해봤어요.

      # Modelfile 생성
      FROM llama3
      SYSTEM You are a friendly, helpful, and concise assistant.
      
      # Modelfile로 새 모델 생성
      ollama create my-friendly-llama -f ./Modelfile
      
      # 새 모델 실행
      ollama run my-friendly-llama

      💡 팁: FROM llama3:8b-instruct-q4_0처럼 특정 양자화(quantization)된 모델을 지정해서 더 작은 용량, 더 빠른 속도를 얻을 수도 있습니다. 이에 대해서는 뒤에서 더 자세히 이야기할게요.

    💡 팁: Ollama는 REST API를 제공하여 다른 애플리케이션과 쉽게 연동할 수 있습니다. ollama serve 명령으로 서버를 실행한 후, 다양한 언어의 라이브러리(예: LangChain, LiteLLM)를 통해 접근해보세요.

    Ollama로 Llama 2 모델을 실행하여 터미널에서 대화하는 화면

    Ollama를 통해 Llama 2 모델을 실행하고 대화하는 터미널 화면입니다. 모델이 성공적으로 로드되고 답변을 생성하는 모습을 보여줍니다.

    NPU/GPU 활용, 성능의 핵심

    로컬 LLM 성능에서 가장 중요한 요소는 바로 하드웨어 가속입니다. 특히 NPU나 GPU가 있고 없고에 따라 체감 성능이 하늘과 땅 차이거든요. 제 맥북 프로 M1 Max에서 Ollama를 돌려보니, 내장된 뉴럴 엔진(Neural Engine, NPU) 덕분에 생각보다 빠른 응답 속도를 보여줬습니다. Ollama는 자동으로 시스템의 NPU나 GPU를 감지해서 활용하려고 노력합니다.

    예를 들어, NVIDIA(엔비디아) GPU가 있는 시스템이라면 CUDA(쿠다)를 통해 GPU를, Apple Silicon(애플 실리콘) 맥이라면 Metal(메탈) API를 통해 뉴럴 엔진을 활용하는 식이죠. 이런 하드웨어 가속이 없다면, 모든 연산이 CPU에서만 이루어져서 LLM 추론이 매우 느려질 수밖에 없습니다. GPU 추론 가속은 로컬 LLM의 핵심입니다. ⚠️ 만약 NPU나 GPU가 없는 구형 시스템이라면, 로컬 LLM 활용에 제약이 많을 수 있다는 점을 꼭 기억해야 합니다.

    삽질의 시간: 메모리 부족과 느린 응답 속도

    솔직히 처음부터 모든 게 순조로웠던 건 아닙니다. 제가 처음엔 맥북 에어 M1으로 llama3:70b 같은 큰 모델을 돌려보려고 했었거든요. 🤦‍♂️ 결과는 처참했습니다. 메모리(RAM)가 16GB(기가바이트)밖에 안 되는데 70B(700억 개 파라미터) 모델을 돌리려니, 모델 로딩부터 한세월이고, 겨우 실행해도 응답 속도가 너무 느려서 사실상 사용하기 어려웠어요. 계속 스와핑(Swapping)이 일어나면서 디스크만 죽어라 읽어대는 소리가 들리더라고요.

    이때 깨달았습니다. 로컬 LLM은 하드웨어 스펙, 특히 메모리와 NPU/GPU의 성능에 크게 좌우된다는 것을요.

    해결책은 몇 가지가 있었습니다.

    • 더 작은 모델 선택: Llama 3 8B(80억 개 파라미터)나 Phi-3 3.8B 같은 모델들은 비교적 적은 메모리로도 충분히 돌릴 수 있습니다.
    • 양자화(Quantization)된 모델 활용: 모델을 8비트(bit)나 4비트 등으로 양자화하면, 모델의 크기를 줄이고 메모리 사용량을 절감할 수 있습니다. 물론 약간의 성능 저하는 있을 수 있지만, 체감상 큰 차이가 없는 경우가 많아 로컬 환경에서는 아주 유용합니다. Ollama는 기본적으로 여러 양자화된 버전을 제공하며, 이는 GGUF(GPT-Generated Unified Format) 포맷을 기반으로 합니다. (예: llama3:8b-instruct-q4_0)
    • 더 좋은 하드웨어: 결국 이게 가장 확실한 해결책입니다. 제가 M1 Max로 바꾸고 나서는 훨씬 쾌적하게 로컬 LLM을 돌릴 수 있게 되었죠.

    Claude Sonnet과 Ollama, 무엇이 달랐나?

    자, 이제 클라우드 LLM의 대표주자인 Claude Sonnet과 Ollama를 비교해볼 차례입니다. 제가 직접 사용해보면서 느낀 점들을 정리해봤어요.

    구분 Ollama (로컬 LLM) Claude Sonnet (클라우드 LLM)
    성능 (체감)
    • 하드웨어 스펙에 따라 편차 큼 (NPU/GPU 필수)
    • 일반적으로 클라우드 대비 느림 (특히 대형 모델)
    • 네트워크 지연 없음
    • 압도적인 성능과 속도 (대용량 입력/출력에 강점)
    • 안정적인 응답 시간
    • 네트워크 지연 존재
    비용
    • 초기 하드웨어 투자 비용 발생
    • 이후 전기세 정도의 운영 비용
    • 무료 모델 사용 가능
    • 토큰(Token) 사용량에 비례한 비용 발생
    • 대량 사용 시 비용 부담 큼
    데이터 프라이버시
    • 데이터가 로컬 환경에만 저장, 외부 유출 위험 없음
    • 매우 높은 프라이버시 보장
    • 클라우드 제공자에게 데이터 전송
    • 보안 정책에 따라 다르지만, 로컬만큼은 아님
    사용 편의성
    • 설치 및 환경 설정 필요 (초기 장벽)
    • API 연동 등 개발 필요
    • 별도 설치 없이 바로 사용 가능 (웹 UI, API)
    • 높은 접근성
    모델 다양성/최신성
    • 다양한 오픈소스 모델 (Llama 3, Phi-3 등 최신 모델 포함), GGUF 포맷 지원
    • 최신, 고성능 상용 모델 제공 (Claude 3.5 Sonnet 등 지속 업데이트)
    • 빠른 업데이트 및 기능 추가

    Ollama를 이용한 로컬 LLM 환경과 Claude Sonnet API를 이용한 클라우드 LLM 환경의 성능, 비용, 프라이버시 등을 비교 분석한 표입니다.

    Claude Sonnet은 역시 압도적인 편의성과 성능을 자랑합니다. 복잡한 요청이나 긴 문서 요약 같은 작업은 클라우드 LLM이 훨씬 빠르고 정확하더라고요. 하지만 Ollama는 비용적인 측면에서, 그리고 무엇보다 데이터 프라이버시 측면에서 강력한 장점을 가집니다. 제 개인적인 데이터나 회사 기밀 데이터를 다룰 때는 Ollama가 훨씬 안심이 되거든요.

    13년차 엔지니어의 선택: 상황에 따른 현명한 활용

    결론적으로, Ollama와 Claude Sonnet 중 무엇이 더 좋다고 단정하기는 어렵습니다. 둘 다 각자의 쓰임새가 명확하게 존재하더라고요. 13년차 인프라 엔지니어로서 제가 내린 결론은 이렇습니다.

    • Ollama (로컬 LLM): 개인적인 학습 및 실험, 민감한 개인/회사 데이터를 다루는 프라이빗 환경, 인터넷 연결이 불안정한 환경, 모델의 내부 동작을 깊이 있게 이해하고 커스터마이징하고 싶을 때 아주 유용합니다. 비용 절감 효과도 무시할 수 없고요.
    • Claude Sonnet (클라우드 LLM): 높은 성능과 안정성이 필요한 상업 서비스, 대규모 사용자 트래픽 처리, 최신 정보를 기반으로 한 빠른 응답이 필요할 때, 그리고 초기 인프라 구축 비용을 줄이고 싶을 때 최적의 선택입니다.

    저는 이제 두 가지 방법을 병행해서 사용하고 있습니다. 간단한 테스트나 개인적인 아이디어 구상에는 Ollama를, 실제 프로덕션(Production)에 적용하거나 복잡하고 긴급한 업무에는 Claude Sonnet을 활용하는 식이죠. 이렇게 유연하게 접근하니 훨씬 효율적이더라고요. 삽질 끝에 드디어 저만의 LLM 활용 노하우를 찾은 것 같아 뿌듯합니다! 🎉

    Ollama 생태계의 진화: 더욱 강력해진 기능들

    최근 2개월간 Ollama 생태계는 더욱 빠르게 진화하고 있습니다. 단순한 로컬 모델 실행을 넘어, 더욱 다양한 활용 시나리오를 지원하며 로컬 LLM 최적화의 가능성을 넓히고 있습니다.

    • 최신 모델 지원 강화: Llama 3, Phi-3와 같은 최신 소형 및 중형 모델들이 빠르게 Ollama 라이브러리에 추가되어, 적은 자원으로도 뛰어난 성능을 경험할 수 있게 되었습니다. 특히 GGUF 포맷의 최적화로 메모리 효율성이 더욱 향상되었습니다.
    • 확장된 API 연동: Ollama는 OpenAI API와 호환되는 엔드포인트를 제공하여, LangChain, LiteLLM 등 기존 LLM 개발 프레임워크와의 연동이 더욱 쉬워졌습니다. 이를 통해 로컬 환경에서도 복잡한 AI 에이전트나 로컬 RAG(Retrieval Augmented Generation) 시스템을 구축하기 용이해졌습니다.
    • 커뮤니티 기반 UI 및 도구: 공식 CLI 외에도 다양한 커뮤니티 개발자들이 Ollama Web UI, 데스크톱 애플리케이션 등 사용자 친화적인 인터페이스를 제공하여 로컬 LLM 접근성을 높이고 있습니다.
    • 모델 병합(Model Merging) 기능: 여러 모델의 장점을 결합하여 새로운 모델을 생성하는 모델 병합 기능이 실험적으로 도입되어, 사용자 맞춤형 모델을 만들 수 있는 길이 열렸습니다.

    이러한 변화들은 Ollama가 단순한 로컬 LLM 런타임을 넘어, 강력한 퍼스널 AI 및 온프레미스 AI 개발 플랫폼으로 자리매김하고 있음을 보여줍니다. 이제 로컬 환경에서도 클라우드 못지않은 유연성과 기능을 기대할 수 있게 되었습니다.

    다음 글에서는 Ollama를 활용해서 나만의 데이터를 학습시키는 모델 미세조정(Fine-tuning)이나, 외부 데이터베이스(Database)와 연동하는 RAG(Retrieval Augmented Generation) 기법에 대해 더 깊이 있게 다뤄볼 예정입니다. 기대해주세요!

    🔄 마지막 업데이트: 2026년 09월

    로컬 LLM과 클라우드 LLM의 최적 활용 시나리오를 요약한 인포그래픽

    로컬 LLM(Ollama)과 클라우드 LLM(Claude Sonnet)의 강점을 바탕으로 각각의 최적 활용 시나리오를 요약한 인포그래픽입니다.

  • [k8s] Kubernetes 스토리지: Longhorn vs Rook-Ceph 성능 벤치마크

    [k8s] Kubernetes 스토리지: Longhorn vs Rook-Ceph 성능 벤치마크

    안녕하세요, 13년차 서버실 지킴이입니다. 🤓

    Kubernetes(쿠버네티스) 환경에서 애플리케이션을 운영하다 보면, 영구 스토리지(Persistent Storage)의 중요성을 정말 뼈저리게 느끼게 됩니다. Pod(파드)가 재시작되거나 다른 노드로 옮겨가도 데이터는 안전하게 보존되어야 하니까요. 특히 저처럼 홈랩(Homelab)에서 이것저것 실험하며 삽질을 즐기는 사람이라면, 안정적이고 성능 좋은 스토리지를 찾는 데 꽤 많은 시간을 투자하게 되죠. 저도 그랬거든요! 🤔

    오늘은 제가 직접 홈랩에서 Longhorn(롱혼)과 Rook-Ceph(룩-세프) 두 가지 Kubernetes 영구 스토리지 솔루션을 설치하고, FIO(Flexible I/O Tester) 벤치마크 툴로 성능을 비교해본 경험을 공유해드리려고 합니다. 과연 어떤 녀석이 제 환경에 더 적합했을까요? 저의 삽질 여정을 따라오시면서 여러분의 Kubernetes 스토리지 선택에 도움이 되시길 바랍니다!

    왜 영구 스토리지가 필요할까요?

    Kubernetes는 기본적으로 컨테이너화된 애플리케이션을 배포하고 관리하는 데 최적화되어 있어요. 그런데 컨테이너는 휘발성(ephemeral)이라는 특징을 가지고 있거든요. 즉, 컨테이너가 죽거나 재시작되면 그 안에 저장된 데이터는 사라져 버린다는 뜻이죠. 데이터베이스나 파일 서버처럼 데이터를 영구적으로 보관해야 하는 애플리케이션에는 치명적일 수 있습니다.

    이런 문제를 해결하기 위해 Kubernetes에서는 영구 볼륨(Persistent Volume, PV)과 영구 볼륨 클레임(Persistent Volume Claim, PVC)이라는 개념을 도입했어요. 쉽게 말해, 스토리지 시스템으로부터 저장 공간을 할당받아 Pod가 데이터를 안정적으로 쓰고 읽을 수 있게 해주는 기능이라고 보시면 됩니다. 그리고 이런 영구 볼륨을 동적으로 프로비저닝(provisioning)해주는 역할을 하는 것이 바로 CSI(Container Storage Interface) 드라이버를 기반으로 하는 분산 스토리지 솔루션들이죠. 오늘 다룰 Longhorn과 Rook-Ceph가 바로 그런 친구들입니다.

    Kubernetes 영구 스토리지 아키텍처 개요

    Kubernetes 클러스터에서 영구 스토리지가 어떻게 작동하는지 대략적인 아키텍처를 그려봤어요. Pod가 PVC를 요청하면, CSI 드라이버가 백엔드 스토리지 시스템에 PV를 생성하고 Pod에 연결해주는 방식이죠. 이 과정에서 Longhorn이나 Rook-Ceph 같은 분산 스토리지 솔루션이 그 백엔드 역할을 하는 겁니다.

    Longhorn과 Rook-Ceph, 넌 누구니?

    본격적인 벤치마크에 앞서, 두 솔루션에 대해 간략하게 알아볼까요? 저도 처음엔 이름만 듣고 뭐가 뭔지 헷갈렸거든요. 😅

    Longhorn (롱혼)

    • 특징: Rancher Labs에서 개발한 오픈소스 분산 블록 스토리지 솔루션이에요. Kubernetes 위에서 컨테이너 형태로 동작하며, 각 노드의 로컬 스토리지를 모아 분산 볼륨을 생성합니다. CSI 드라이버를 기본으로 제공해서 Kubernetes와 통합이 정말 쉬워요. 제가 직접 써보니, 설치도 비교적 간단하고 웹 UI가 직관적이어서 관리하기 편하더라고요. 홈랩이나 소규모 환경에 많이 추천되는 이유를 알겠더군요.
    • 장점: 가벼움, 설치 및 관리 용이, 스냅샷/백업/복원 기능 내장, DR(재해 복구) 지원.

    Rook-Ceph (룩-세프)

    • 특징: Rook는 Kubernetes 네이티브 스토리지 오케스트레이터이고, Ceph(세프)는 강력하고 성숙한 오픈소스 분산 스토리지 시스템이에요. Rook는 Ceph를 Kubernetes 클러스터 위에 배포하고 관리하는 오퍼레이터(Operator) 역할을 합니다. 오브젝트 스토리지(Object Storage), 블록 스토리지(Block Storage), 파일 스토리지(File Storage)를 모두 지원하는 만능 재주꾼이죠. 다만, Ceph 자체가 워낙 복잡한 시스템이라 Rook를 통해 배포해도 초기 설정과 관리에 공수가 좀 들어가는 편이에요. 대규모 프로덕션 환경에서 많이 사용됩니다.
    • 장점: 뛰어난 확장성, 높은 가용성, 다양한 스토리지 타입 지원, 강력한 성능(하드웨어에 따라).

    두 솔루션의 주요 특징을 표로 정리해봤어요.

    구분 Longhorn Rook-Ceph
    개발사/기반 Rancher Labs / 분산 블록 스토리지 Rook (오퍼레이터) + Ceph (분산 스토리지)
    스토리지 타입 블록 스토리지 블록, 오브젝트, 파일 스토리지
    설치 난이도 쉬움 (Helm) 중간 ~ 어려움 (kubectl apply)
    관리 UI 직관적인 웹 UI Ceph Dashboard (별도 설치), Grafana 연동
    확장성 중간 (수십 TB 규모) 매우 높음 (페타바이트 규모)
    주요 사용처 홈랩, 소규모 개발/테스트, 엣지 컴퓨팅 대규모 프로덕션, 클라우드 인프라

    벤치마크 환경 구축하기

    저의 홈랩 환경은 다음과 같았습니다. 실제 벤치마크 결과는 하드웨어 스펙, 네트워크 환경, Kubernetes 설정 등에 따라 크게 달라질 수 있다는 점을 미리 말씀드려요! ⚠️

    • Kubernetes Cluster: 3 Node (1 Master, 2 Worker)
    • OS: Ubuntu Server 22.04 LTS
    • CPU: Intel i5-10400 (각 노드당 6코어 12스레드)
    • RAM: 32GB (각 노드당)
    • Storage: NVMe SSD 500GB (각 노드당, 스토리지용으로 할당)
    • Network: 1Gbps Ethernet

    벤치마크 툴로는 FIO (Flexible I/O Tester)를 사용했어요. FIO는 디스크 I/O 성능을 측정하는 데 널리 사용되는 강력한 도구예요. 순차 읽기/쓰기, 랜덤 읽기/쓰기 등 다양한 패턴으로 I/O를 발생시켜 실제 워크로드와 유사한 환경을 만들 수 있거든요.

    Longhorn 설치 및 설정

    Longhorn은 Helm(헬름) 차트를 이용하면 정말 쉽게 설치할 수 있어요. 저도 처음엔 혹시나 복잡할까 봐 걱정했는데, 생각보다 너무 간단해서 놀랐어요. 🎉

    1. Helm Repository 추가:
      helm repo add longhorn https://charts.longhorn.io
      helm repo update
      
    2. Longhorn 설치: (기본 설정으로 설치했습니다)
      helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace
      
    3. Longhorn UI 접속:

      설치가 완료되면, Longhorn UI에 접속할 수 있도록 Ingress(인그레스, 외부 트래픽 진입점)나 NodePort(노드포트)를 설정해줘요. 저는 간단하게 NodePort로 열어서 접속했어요.

      kubectl -n longhorn-system get svc longhorn-frontend
      # 출력되는 NodePort를 확인하여 접속
      
    4. StorageClass 확인 및 볼륨 생성:

      Longhorn이 설치되면 기본적으로 longhorn이라는 StorageClass(스토리지클래스)가 생성돼요. 이걸 이용해서 PVC를 생성하고 Pod에 마운트하면 끝입니다.

      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: longhorn-pvc
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: longhorn
        resources:
          requests:
            storage: 10Gi
      

    Longhorn 웹 UI에 접속하면 현재 클러스터의 스토리지 상태, 노드별 디스크 사용량, 볼륨 목록 등을 한눈에 확인할 수 있어요. 시각적으로 깔끔하고 직관적이라서 관리하기 정말 편하더라고요. 💡

    Longhorn 웹 UI 대시보드 화면

    Longhorn 대시보드 화면이에요. 생성된 볼륨, 노드의 스토리지 상태 등을 쉽게 확인할 수 있어요.

    Rook-Ceph 설치 및 설정

    Rook-Ceph는 Longhorn보다 설정할 부분이 많아서, 처음엔 “이게 다 뭔가…” 싶었어요. 하지만 공식 문서(Official Documentation)를 차근차근 따라가면 충분히 설치할 수 있어요. 저는 helm 대신 kubectl apply -f 방식으로 진행했습니다.

    1. Rook Operator 배포:

      먼저 Rook Operator를 배포해요. 이 오퍼레이터가 Ceph 클러스터를 관리하는 역할을 합니다.

      git clone --single-branch --branch v1.11.x https://github.com/rook/rook.git
      cd rook/deploy/examples
      kubectl create -f crds.yaml -f common.yaml -f operator.yaml
      
    2. Ceph Cluster 배포:

      Ceph 클러스터를 배포해요. 이 과정에서 어떤 디스크를 Ceph 스토리지로 사용할지 지정해줘야 해요. 저는 각 워커 노드의 NVMe SSD를 사용하도록 설정했습니다.

      kubectl create -f cluster.yaml
      

      cluster.yaml 파일에서 storage 섹션에 useAllNodes: true와 useAllDevices: true를 설정하여 모든 노드의 모든 디바이스를 사용하도록 했어요. 실제 운영 환경에서는 특정 디스크만 사용하도록 명시하는 것이 좋습니다.

    3. Ceph Block Pool 및 StorageClass 생성:

      Ceph 클러스터가 안정화되면, 블록 스토리지를 위한 CephBlockPool(세프 블록 풀)과 StorageClass를 생성해요.

      kubectl create -f csi/rbd/storageclass.yaml
      

      storageclass.yaml 파일은 다음과 비슷하게 생겼을 겁니다.

      apiVersion: ceph.rook.io/v1
      kind: CephBlockPool
      metadata:
        name: replicapool
        namespace: rook-ceph
      spec:
        failureDomain: host
        replicated:
          size: 3 # 데이터 복제본 수
      ---
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: rook-ceph-block
      provisioner: rook-ceph.rbd.csi.ceph.com
      parameters:
        clusterID: rook-ceph
        pool: replicapool
        imageFormat: "2"
        imageFeatures: "layering"
        csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
        csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
        csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
        csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
      reclaimPolicy: Delete
      allowVolumeExpansion: true
      mountOptions:
        - discard
      
    4. PVC 생성:

      Longhorn과 마찬가지로, 생성된 rook-ceph-block StorageClass를 이용해 PVC를 생성해요.

      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: rook-ceph-pvc
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: rook-ceph-block
        resources:
          requests:
            storage: 10Gi
      
    Rook-Ceph on Kubernetes 클러스터 구성도

    Rook-Ceph 클러스터의 내부 구성도예요. Ceph의 여러 구성요소(MON, OSD, MGR)가 Pod 형태로 Kubernetes 클러스터 위에서 동작하는 것을 볼 수 있어요.

    FIO 벤치마크 실행 및 결과 분석

    두 스토리지 솔루션의 PVC를 각각 생성한 후, FIO를 실행할 Pod를 띄워 벤치마크를 진행했어요. 저는 주로 랜덤 읽기/쓰기(Random Read/Write)와 순차 읽기/쓰기(Sequential Read/Write) 성능을 중점적으로 살펴봤습니다. 블록 크기(Block Size)는 4KB와 1MB로 다양하게 테스트해봤거든요.

    FIO Pod를 위한 YAML 예시예요. 이 Pod가 PVC를 마운트하여 FIO 테스트를 수행합니다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: fio-test-pod
    spec:
      restartPolicy: Never
      containers:
      - name: fio-container
        image: ghcr.io/fio/fio:latest # FIO 이미지를 사용합니다.
        command: ["/bin/bash", "-c"]
        args:
          - |
            mkdir -p /mnt/data
            fio --name=random-write --ioengine=libaio --iodepth=64 --rw=randwrite --bs=4k --size=1G --numjobs=1 --runtime=60 --group_reporting --filename=/mnt/data/testfile
            fio --name=random-read --ioengine=libaio --iodepth=64 --rw=randread --bs=4k --size=1G --numjobs=1 --runtime=60 --group_reporting --filename=/mnt/data/testfile
            fio --name=sequential-write --ioengine=libaio --iodepth=16 --rw=write --bs=1M --size=2G --numjobs=1 --runtime=60 --group_reporting --filename=/mnt/data/testfile_seq
            fio --name=sequential-read --ioengine=libaio --iodepth=16 --rw=read --bs=1M --size=2G --numjobs=1 --runtime=60 --group_reporting --filename=/mnt/data/testfile_seq
        volumeMounts:
        - name: test-volume
          mountPath: /mnt/data
      volumes:
      - name: test-volume
        persistentVolumeClaim:
          claimName: longhorn-pvc # 또는 rook-ceph-pvc
    

    여러 번의 테스트를 통해 제가 관찰한 결과는 대략 이랬어요. (⚠️ 이 수치는 제 특정 홈랩 환경에서 얻은 결과이며, 환경에 따라 크게 달라질 수 있음을 다시 한번 강조합니다!)

    테스트 항목 (4KB BS) Longhorn (평균) Rook-Ceph (평균) 비고
    랜덤 쓰기 IOPS 약 1,500 ~ 2,500 약 3,000 ~ 5,000 Rook-Ceph가 우위
    랜덤 쓰기 대역폭 (MB/s) 약 6 ~ 10 약 12 ~ 20 Rook-Ceph가 우위
    랜덤 읽기 IOPS 약 2,000 ~ 3,500 약 4,000 ~ 6,500 Rook-Ceph가 우위
    랜덤 읽기 대역폭 (MB/s) 약 8 ~ 14 약 16 ~ 26 Rook-Ceph가 우위
    테스트 항목 (1MB BS) Longhorn (평균) Rook-Ceph (평균) 비고
    순차 쓰기 대역폭 (MB/s) 약 80 ~ 120 약 150 ~ 250 Rook-Ceph가 우위
    순차 읽기 대역폭 (MB/s) 약 100 ~ 150 약 200 ~ 300 Rook-Ceph가 우위

    결과를 보면, 제 홈랩 환경에서는 Rook-Ceph가 전반적으로 Longhorn보다 더 높은 IOPS와 대역폭 성능을 보여줬어요. 특히 작은 블록 크기의 랜덤 I/O에서 Ceph의 강점이 두드러지더라고요. Longhorn은 복제본(replica) 수가 늘어날수록 성능 하락이 좀 더 눈에 띄는 경향이 있었고, Ceph는 안정적으로 성능을 유지했습니다.

    Longhorn과 Rook-Ceph FIO 벤치마크 결과 비교 그래프

    Longhorn과 Rook-Ceph의 FIO 벤치마크 결과를 시각적으로 비교한 그래프예요. Rook-Ceph가 전반적으로 높은 성능을 보였습니다.

    삽질 경험과 트러블슈팅 ⚠️

    솔직히 말씀드리면, 순조롭게만 진행된 건 아니었어요. 삽질 좀 했습니다 ㅎㅎ. 몇 가지 기억에 남는 경험을 공유해볼게요.

    • Longhorn: 네트워크 대역폭과 복제본 수
      처음 Longhorn을 설치하고 테스트했을 때, 생각보다 성능이 안 나와서 당황했어요. 알고 보니, Longhorn은 네트워크를 통해 볼륨 데이터를 복제(replication)하기 때문에, 노드 간 네트워크 대역폭이 정말 중요하더라고요. 제 1Gbps 네트워크 환경에서는 복제본 수를 3개로 설정했을 때 쓰기 성능이 꽤 많이 떨어지는 것을 확인했어요. 복제본 수가 늘어날수록 안정성은 높아지지만, 그만큼 네트워크 오버헤드(overhead)가 커져서 성능에 영향을 미친다는 점! 홈랩에서는 2개로 줄여서 사용하니 훨씬 나아졌습니다.
    • Rook-Ceph: OSD 인식 문제와 초기 설정
      Rook-Ceph는 초기 설정이 Longhorn보다 훨씬 복잡했어요. 특히 OSD(Object Storage Daemon)가 디스크를 제대로 인식하지 못해서 한참을 헤맸거든요. 디스크가 이미 파티셔닝(partitioning)되어 있거나, 이전에 다른 용도로 사용된 흔적이 남아있으면 Rook-Ceph가 제대로 디스크를 잡지 못하는 경우가 있더라고요. ceph-volume lvm zap --destroy /dev/sdX 같은 명령어로 디스크를 완전히 초기화해주거나, lsblk -f 명령어로 디스크의 파일 시스템(filesystem) 정보를 꼼꼼히 확인해서 해결했어요. Ceph의 CRD(Custom Resource Definition)와 설정 파일에 대한 이해가 필요하다는 것을 다시 한번 깨달았습니다.

    결론 및 선택 가이드

    제 홈랩 벤치마크 경험을 바탕으로 Longhorn과 Rook-Ceph에 대한 저의 생각을 정리해봤어요.

    솔루션 장점 단점 추천 시나리오
    Longhorn
    • 설치 및 관리가 매우 쉬움
    • 직관적인 웹 UI 제공
    • 스냅샷, 백업, 재해 복구 기능 내장
    • 가벼운 리소스 사용
    • 네트워크 대역폭에 민감 (복제본 수 증가 시)
    • Rook-Ceph 대비 낮은 최대 성능
    • 블록 스토리지에 한정
    • 홈랩, 개발/테스트 환경
    • 소규모 프로덕션 (수십 TB 이하)
    • 간편한 관리와 사용성을 중시할 때
    Rook-Ceph
    • 최고 수준의 성능 (적절한 하드웨어 시)
    • 뛰어난 확장성 (페타바이트 규모)
    • 블록, 오브젝트, 파일 스토리지 모두 지원
    • 엔터프라이즈급 안정성과 기능
    • 설치 및 설정 난이도가 높음
    • 초기 학습 곡선이 김
    • 상대적으로 많은 리소스 사용
    • 문제 발생 시 트러블슈팅 복잡
    • 대규모 프로덕션 환경
    • 고성능, 고가용성이 필수적인 경우
    • 다양한 스토리지 타입이 필요한 경우

    제 홈랩에서는 아무래도 설치 및 관리의 용이성 때문에 Longhorn에 손이 더 자주 가더라고요. 하지만 성능 자체만 놓고 본다면 Rook-Ceph가 확실히 우위를 점했어요. 결국 어떤 Kubernetes 스토리지 솔루션을 선택할지는 여러분의 환경과 요구사항에 따라 달라진다는 것이 저의 결론입니다.

    여러분도 직접 설치해보시고, 여러분의 워크로드에 맞는 벤치마크를 수행해보시면 좋은 인사이트를 얻으실 수 있을 거예요. 저의 삽질 경험이 여러분의 시행착오를 조금이나마 줄여줄 수 있다면 좋겠습니다! 😊

    다음 글에서는 Kubernetes에서 Grafana(그라파나)와 Prometheus(프로메테우스)를 이용해 스토리지 성능을 모니터링하는 방법에 대해 다뤄볼 예정이에요. 기대해주세요!

    Longhorn과 Rook-Ceph 중 어떤 솔루션이 당신에게 맞을지 요약한 인포그래픽입니다.

  • [HomeLabs] 10G 네트워크 비용, 홈랩에 정말 필요할까? 비용 효율성 심층 분석

    [HomeLabs] 10G 네트워크 비용, 홈랩에 정말 필요할까? 비용 효율성 심층 분석

    10G 네트워크 비용, 홈랩에 정말 필요할까? 비용 효율성 심층 분석

    안녕하세요, 13년차 서버실 지킴이 ’13년차의 서버실’입니다. 홈랩을 운영하시는 분들이라면 한 번쯤은 “우리 집 네트워크도 10G로 업그레이드해볼까?” 하는 고민, 해보셨을 거예요. 저도 그랬거든요. 1G(Gigabit Ethernet) 환경에서 뭔가 답답함을 느낄 때마다 10G(10 Gigabit Ethernet)가 주는 시원한 속도감을 상상하곤 했습니다.

    특히 대용량 파일 전송이 잦거나, 여러 대의 가상 머신(Virtual Machine, VM)이나 컨테이너(Container)를 운영하면서 스토리지 서버(Storage Server)와의 병목 현상(Bottleneck)을 경험해보신 분들이라면 10G 네트워크에 대한 갈증이 상당하실 텐데요. 하지만 막상 구축하려고 보면 생각보다 높은 10G 네트워크 비용 때문에 망설여지기 마련입니다. 과연 이 투자가 홈랩 네트워크 환경에서 비용 효율성이 있을지, 그리고 그 투자 가치는 충분한지 저의 솔직한 경험을 바탕으로 심층 분석해보려고 합니다.

    제가 직접 삽질했던 경험들을 솔직하게 공유하면서, 여러분의 현명한 선택에 조금이나마 도움이 되었으면 좋겠습니다. 자, 그럼 시작해볼까요?

    홈랩 1G 및 10G 네트워크 구성 개요 다이어그램

    홈랩 네트워크의 진화: 1G에서 10G로의 업그레이드 흐름을 보여주는 다이어그램입니다.

    1. 10G 네트워크, 왜 필요한지부터 따져봅시다

    많은 분들이 “1G도 충분한데 굳이 10G까지?”라고 생각하실 수 있습니다. 저도 처음엔 그랬습니다. 하지만 특정 사용 시나리오에서는 10G가 주는 이점이 명확하더라고요.

    1.1. 10G 이더넷(Ethernet)이란?

    10G 이더넷(10 Gigabit Ethernet)은 초당 10기가비트(Gbps)의 데이터 전송 속도를 제공하는 네트워크 표준입니다. 기존 1G 이더넷(1Gbps)에 비해 이론적으로 10배 빠른 속도를 낼 수 있죠. 이 정도 속도면 대용량 파일(예: 4K 영상, 가상 머신 이미지)을 빠르게 전송하거나, 여러 대의 서버가 동시에 스토리지에 접근할 때 병목 현상을 크게 줄일 수 있죠.

    1.2. 홈랩에서 10G가 필요한 순간들

    • 대용량 파일 전송: NAS(Network Attached Storage)에서 여러 워크스테이션으로 수십 GB 이상의 파일을 자주 옮길 때 체감 속도가 확 달라지죠. 특히 미디어 서버를 운영하거나 영상 편집 작업을 하신다면 필수적이라고 느낄 수 있어요.
    • 가상화 환경: Proxmox VE나 VMware ESXi 같은 가상화 플랫폼에서 여러 가상 머신이 동시에 네트워크 스토리지를 사용하거나, 가상 머신 이미지를 이동시킬 때 10G는 빛을 발합니다. 저는 VM 간의 라이브 마이그레이션(Live Migration) 시 속도 차이를 크게 경험했습니다.
    • 고성능 스토리지: NVMe SSD 기반의 고성능 스토리지 서버를 구축했다면, 1G 네트워크는 이 스토리지의 잠재력을 다 끌어내지 못하죠. 10G는 스토리지의 진정한 성능을 발휘하게 해주죠.
    • 다중 사용자 환경: 가족 구성원이 많거나, 여러 사람이 동시에 고대역폭을 요구하는 작업을 할 때 1G는 금방 한계에 부딪히곤 합니다.

    자, 그럼 이제 어떤 장비들이 필요한지 한번 살펴볼까요?

    2. 10G 네트워크 구축의 핵심 요소와 비용 분석

    10G 네트워크를 구축하려면 크게 세 가지 핵심 구성 요소가 필요합니다: 네트워크 인터페이스 카드(NIC), 스위치(Switch), 그리고 케이블(Cable)입니다. 각 요소별로 선택지와 10G 네트워크 구축 비용 효율성을 따져봐야죠.

    2.1. 네트워크 인터페이스 카드 (NIC)

    서버나 워크스테이션에 10G 연결 기능을 제공하는 장치입니다. 주로 PCIe(Peripheral Component Interconnect Express) 슬롯에 장착하는 카드를 사용합니다.

    • RJ45 (Cat6a): 일반적인 랜 케이블(UTP)처럼 생겼습니다. Cat6a(Category 6 Augmented) 케이블을 사용하며, 최대 100m까지 10G 속도를 지원합니다. 설치가 간편하다는 장점이 있지만, 발열이 심하고 전력 소모가 상대적으로 높다는 단점이 있죠.
    • SFP+ (Small Form-Factor Pluggable Plus): 광케이블이나 DAC(Direct Attach Cable)을 사용하는 방식입니다. RJ45 방식보다 저전력, 저발열이며, 보통 더 저렴한 중고 NIC를 구할 수 있다는 장점이 있습니다. 하지만 광케이블과 트랜시버(Transceiver)가 추가로 필요할 수 있어 초보자에게는 다소 복잡하게 느껴질 수도 있어요. 홈랩에서는 DAC 케이블을 많이 사용하는데, 최대 7m 정도의 짧은 거리에서 저렴하게 10G 연결을 할 수 있습니다.

    제가 처음 10G를 구축할 때는 RJ45 방식이 익숙해서 Cat6a NIC를 먼저 써봤습니다. 근데 생각보다 발열이 심해서 깜짝 놀랐습니다. 나중에는 SFP+ NIC와 DAC 케이블 조합으로 바꾸면서 전력 소모와 발열을 모두 잡았었죠. 중고 시장에서 널리 쓰이는 Intel 기반의 SFP+ NIC들은 꽤 합리적인 가격에 구할 수 있습니다.

    2.2. 10G 스위치 (Switch)

    여러 장치를 10G 속도로 연결해주는 핵심 장비입니다. 선택지가 다양합니다.

    • 언매니지드 스위치 (Unmanaged Switch): 별도의 설정 없이 바로 연결하면 작동합니다. 가장 저렴하지만, VLAN(Virtual Local Area Network) 설정 등 고급 기능을 사용할 수 없죠. 홈랩에서는 간단한 포인트-투-포인트(Point-to-Point) 연결에 적합합니다.
    • 매니지드 스위치 (Managed Switch): 웹 인터페이스나 CLI(Command Line Interface)를 통해 다양한 네트워크 설정을 할 수 있죠. VLAN, LACP(Link Aggregation Control Protocol), QoS(Quality of Service) 등 고급 기능을 활용하려면 필수적입니다. 중고 시장을 잘 찾아보면 합리적인 가격의 매니지드 10G 스위치를 구할 수 있습니다.

    저도 처음에는 언매니지드 스위치로 시작했는데, 나중에 VLAN을 나눠야 할 일이 생겨서 결국 매니지드 스위치로 갈아탔습니다. 그때 또 한 번의 삽질이 있었죠. 펌웨어 업데이트부터 설정까지, 처음 해보는 분들은 조금 헤맬 수도 있죠.

    2.3. 케이블 (Cable)

    앞서 언급했듯이, RJ45 방식은 Cat6a 케이블을, SFP+ 방식은 DAC 케이블이나 광케이블(Optical Fiber Cable)과 트랜시버를 사용합니다.

    • Cat6a 케이블: 일반 랜 케이블과 비슷하지만 더 두껍고 실드(Shield) 처리가 잘 되어 있습니다. 짧은 거리에서는 Cat6도 10G를 지원하는 경우가 있지만, 안정적인 10G 연결을 위해서는 Cat6a를 권장하죠.
    • DAC 케이블: SFP+ 포트 간의 짧은 거리를 연결할 때 가장 비용 효율적인 솔루션이거든요. 트랜시버가 케이블에 통합되어 있어 별도로 구매할 필요가 없습니다.
    • 광케이블 및 SFP+ 트랜시버: 장거리 연결이나 전기적 노이즈에 민감한 환경에 적합합니다. 트랜시버 종류(싱글 모드/멀티 모드)와 광케이블 종류를 잘 맞춰야 합니다.

    10G NIC 또는 스위치의 네트워크 설정 화면 예시입니다.

    3. 제가 직접 겪은 삽질 경험과 해결 과정 ⚠️

    13년차 엔지니어라고 해도 새로운 기술을 도입할 때마다 삽질은 피할 수 없더라고요. 10G 네트워크 구축도 예외는 아니었습니다. 몇 가지 기억나는 삽질과 해결책을 공유해볼게요.

    3.1. 드라이버(Driver) 문제와 OS 호환성

    구형 10G NIC를 중고로 구매했을 때, 최신 리눅스 커널(Kernel)에서 드라이버가 제대로 잡히지 않아 애를 먹었습니다. 특히 Proxmox VE 같은 가상화 환경에서는 커널 버전이 중요하거든요. 제조사 홈페이지에서 최신 드라이버를 찾아 수동으로 설치하거나, 호환성 리스트를 꼼꼼히 확인하는 것이 중요합니다. 저는 결국 드라이버 지원이 더 확실한 NIC로 교체했었는데, 이 과정에서 시간과 비용이 좀 들었죠.

    3.2. SFP+ 트랜시버(Transceiver) 호환성

    이게 진짜 골치 아픈 부분이었는데요. SFP+ 트랜시버는 특정 제조사 스위치와 호환되는 경우가 많습니다. ‘Generic’이라고 되어 있어도 실제로는 작동하지 않는 경우가 있더라고요. 제 경우, A사 스위치에 B사 트랜시버를 꽂았더니 인식이 안 되는 문제가 있었습니다. 결국 스위치 제조사가 권장하는 트랜시버를 찾아 구매하거나, 호환성이 검증된 저렴한 타사 제품을 찾아야 했습니다. “호환성 리스트”를 반드시 확인하고 구매하세요! 아니면 DAC 케이블처럼 트랜시버가 내장된 제품을 사용하는 것이 더 속 편할 수도 있습니다.

    3.3. 케이블 품질과 길이의 중요성

    싼 게 비지떡이라는 말을 10G 케이블에서도 절실히 느꼈습니다. Cat6a 케이블이라고 해서 저렴한 제품을 샀는데, 간헐적으로 속도 저하가 발생하거나 링크(Link)가 끊기는 현상이 있었어요. 결국 좀 더 비싸더라도 인증된 브랜드의 Cat6a 케이블로 교체하고 나서야 안정적인 10G 속도를 경험할 수 있었습니다. 또한, RJ45 방식은 케이블 길이가 길어질수록 신호 손실이 커지기 때문에, 100m 이내라도 너무 길게 사용하는 것은 피하는 게 좋습니다.

    4. 10G 네트워크, 과연 성능 향상이 있었을까요? ✅

    이 모든 삽질과 투자를 거쳐 드디어 10G 네트워크를 완성했습니다. 가장 먼저 해본 것은 당연히 속도 테스트였습니다. iPerf3를 이용해서 서버 간의 대역폭(Bandwidth)을 측정해봤죠.

    4.1. iPerf3를 이용한 성능 검증

    iPerf3는 네트워크 성능을 측정하는 데 널리 사용되는 도구입니다. 서버와 클라이언트 모드로 동작하며, TCP나 UDP 트래픽을 생성하여 대역폭, 지연 시간(Latency), 손실률(Packet Loss) 등을 측정할 수 있죠. 저는 주로 TCP 모드로 대역폭을 측정했습니다.

    
    # 서버에서 iPerf3 실행
    iperf3 -s
    
    # 클라이언트에서 iPerf3 실행 (서버 IP 주소 입력)
    iperf3 -c [서버_IP_주소] -P 8 # -P는 병렬 스트림 수
    

    1G 환경에서는 아무리 잘 나와도 940Mbps 정도였는데, 10G 환경에서는 9.x Gbps가 찍히는 것을 보고 정말 감격스러웠습니다! 🎉 물론 실제 파일 전송 속도는 파일 크기, 스토리지 성능, CPU 부하 등 여러 요인에 따라 달라지지만, 네트워크 자체의 잠재력은 확실히 끌어올린 거죠.

    4.2. 체감 성능 변화와 투자 가치 분석

    실제로 10G 네트워크를 사용하면서 가장 크게 체감한 것은 NAS로의 대용량 백업 및 복원 속도였습니다. 예전에는 수백 GB 백업 한 번 하려면 잠시 자리를 비워야 했는데, 이제는 훨씬 짧은 시간에 끝낼 수 있게 되었죠. 가상 머신 이미지 파일(VMDK, QCOW2)을 옮기거나, Proxmox에서 VM 스토리지를 마이그레이션할 때도 엄청난 시간 단축 효과를 봤습니다.

    그렇다면 이 투자가 10G 네트워크 비용 효율성 측면에서 어땠을까요? 솔직히 말해서, “필요한 곳에만” 연결한다면 충분히 가치 있는 투자였습니다. 모든 장치를 10G로 연결할 필요는 없더라고요. 저처럼 NAS, 가상화 서버, 그리고 고성능 워크스테이션 딱 세 곳 정도만 10G로 연결하니 만족도가 높았습니다. 불필요한 곳까지 10G로 확장하려 하면 비용이 기하급수적으로 늘어나더라고요.

    iPerf3를 이용한 1G 및 10G 네트워크 속도 벤치마크 결과 그래프

    iPerf3를 이용한 1G와 10G 네트워크 속도 비교 결과 대시보드입니다.

    5. 결론: 홈랩 10G 네트워크, 누구에게 필요할까?

    지금까지 10G 네트워크 구축에 대한 저의 경험과 비용 효율성 분석을 해봤습니다. 결론부터 말씀드리자면, 홈랩 10G 네트워크는 “특정 요구사항이 있는 사용자에게는 10G 네트워크 비용 대비 매우 효과적인 투자”라고 생각합니다.

    다음과 같은 분들이라면 10G 네트워크를 진지하게 고려해보세요.

    • 대용량 파일(수십 GB 이상)을 자주 전송하는 분 (NAS, 미디어 서버 사용자)
    • 여러 대의 가상 머신이나 컨테이너를 운영하며 고성능 스토리지를 사용하는 분
    • 영상 편집, 3D 렌더링 등 고대역폭을 요구하는 작업을 하는 워크스테이션 사용자
    • 홈랩의 성능 병목이 확실히 네트워크에 있다고 판단되는 분

    반대로, 단순히 웹 서핑, 문서 작업, 스트리밍 시청 등 일반적인 용도로만 사용하신다면 1G 네트워크로도 충분해요. 굳이 비싼 돈 들여 10G를 구축할 필요는 없어요. 오히려 10G 장비의 추가적인 전력 소모와 발열이 더 부담으로 다가올 수도 있어요.

    💡 저의 팁: 처음부터 모든 것을 10G로 바꾸려고 하지 마세요. 가장 병목이 심한 구간(예: NAS와 메인 서버/워크스테이션)부터 SFP+ NIC와 DAC 케이블 조합으로 점진적으로 업그레이드하는 것이 10G 네트워크 비용 효율성 측면에서 가장 현명한 방법이더라고요. 중고 시장을 잘 활용하는 것도 좋은 방법이고요. 제가 그랬거든요.

    저의 13년차 삽질 경험이 여러분의 홈랩 네트워크 구축에 도움이 되었으면 좋겠습니다. 혹시 더 궁금한 점이 있으시다면 언제든지 댓글 남겨주세요! 다음번에는 10G 네트워크 환경에서 고성능 스토리지 구축하는 이야기도 한번 다뤄볼까 합니다. 기대해주세요!

    홈랩 1G vs 10G 네트워크 장단점 및 비용 효율성 비교 인포그래픽

    1G와 10G 네트워크의 장단점 및 비용 효율성 비교 인포그래픽입니다.