13년차의 서버실

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

[작성자:] admin

  • [k8s] 쿠버네티스 모니터링: Prometheus & Grafana 구축 및 대시보드 활용 가이드

    [k8s] 쿠버네티스 모니터링: Prometheus & Grafana 구축 및 대시보드 활용 가이드

    🚀 쿠버네티스 모니터링, 왜 중요할까요?

    안녕하세요, 13년차의 서버실 주인장입니다. 오늘은 쿠버네티스 모니터링의 핵심이라고 할 수 있는 Prometheus(프로메테우스)와 Grafana(그라파나) 구축 경험을 공유해볼까 합니다. 쿠버네티스 환경을 운영하다 보면 ‘도대체 내 파드는 잘 돌고 있나?’, ‘CPU 사용량이 왜 이렇게 높지?’, ‘메모리 누수가 생긴 건 아닐까?’ 같은 고민을 하게 되죠.

    저도 처음 쿠버네티스를 도입했을 때, 여러 파드들이 여기저기서 돌아다니는데 이걸 어떻게 한눈에 볼 수 있을까 막막했어요. 로그를 일일이 뒤져보는 것도 한계가 있고요. 그때 제 눈을 번쩍 뜨이게 한 것이 바로 Prometheus와 Grafana 조합이었습니다. 이 두 가지 도구 덕분에 제 홈랩의 쿠버네티스 클러스터는 24시간 감시 체제를 갖추게 되었고, 문제가 생기면 빠르게 파악하고 조치할 수 있게 됐죠. 마치 제 서버실에 똑똑한 비서 한 명을 들인 기분이랄까요?

    이번 글에서는 이 강력한 모니터링 스택을 어떻게 구축하고, 어떤 대시보드를 활용하면 좋을지 제 경험을 녹여서 자세히 알려드리겠습니다. 삽질했던 부분들도 솔직하게 공유할 테니, 여러분은 저처럼 헤매지 않으시길 바랍니다! 자, 그럼 시작해볼까요?

    쿠버네티스 환경에서 Prometheus와 Grafana를 이용한 모니터링 시스템의 전체 아키텍처 다이어그램입니다.

    💡 Prometheus와 Grafana, 핵심 개념 파헤치기

    자, 이제 본론으로 들어가서 쿠버네티스 모니터링의 양대 산맥인 Prometheus(프로메테우스)와 Grafana(그라파나)에 대해 좀 더 깊이 있게 알아볼까요? 처음엔 이름도 어렵고, 뭐가 뭔지 헷갈렸는데, 사실 원리만 이해하면 생각보다 간단하더라고요.

    Prometheus (프로메테우스): 지표 수집의 달인

    Prometheus는 오픈 소스 모니터링 시스템으로, 시계열 데이터베이스(Time-series Database, TSDB)를 기반으로 합니다. 쉽게 말해, 시간의 흐름에 따라 변화하는 다양한 지표(Metrics)들을 수집하고 저장하는 데 특화된 도구거든요. 기존의 많은 모니터링 시스템이 에이전트가 데이터를 서버로 밀어 넣는 Push 방식이었다면, Prometheus는 대상으로부터 데이터를 Pull(가져오는) 방식을 사용합니다. 이 방식은 서비스 디스커버리(Service Discovery)와 결합될 때 쿠버네티스 같은 동적인 환경에서 정말 큰 시너지를 내거든요.

    • Metrics (메트릭, 지표 데이터): CPU 사용량, 메모리 사용량, 네트워크 트래픽 등 서버나 애플리케이션의 상태를 나타내는 수치 데이터입니다.
    • Exporters (익스포터): Prometheus가 지표를 가져올 수 있도록 데이터를 노출하는 작은 에이전트예요. 예를 들어, 서버의 OS 지표를 수집하는 Node Exporter나 쿠버네티스 노드/파드의 지표를 수집하는 cAdvisor 같은 것들이죠.
    • Service Discovery (서비스 디스커버리): 쿠버네티스 환경에서는 파드(Pod)가 수시로 생성되고 사라집니다. Prometheus는 이런 변화를 감지해서 자동으로 새로운 모니터링 대상을 찾아냅니다. 정말 편리하더라고요.

    Grafana (그라파나): 시각화의 마법사

    Prometheus가 지표를 열심히 수집하고 저장해놨다면, Grafana는 그 데이터를 멋진 그래프와 대시보드로 시각화해주는 도구입니다. 복잡한 숫자들을 한눈에 알아보기 쉽게 보여주니까, 이상 징후를 빠르게 파악하고 문제 해결에 집중할 수 있게 도와주죠. 처음에는 Grafana UI가 좀 어려웠는데, 몇 번 써보니 이거 진짜 물건이더라고요! 다양한 데이터 소스(Prometheus 외에도 많은 DB를 지원합니다)를 연결해서 대시보드를 만들 수 있고, 커스텀도 자유롭습니다.

    🛠️ 실전! Helm으로 Prometheus & Grafana 배포하기

    이제 이론은 충분히 봤으니, 실제로 쿠버네티스 클러스터에 Prometheus와 Grafana를 배포해보겠습니다. 저는 Helm(헬름)을 이용하는 것을 선호하는데, 복잡한 쿠버네티스 리소스들을 한 번에 쉽게 배포하고 관리할 수 있거든요. 마치 패키지 매니저처럼요!

    사전 준비물

    1. 동작하는 쿠버네티스 클러스터 (저는 홈랩에서 Kubeadm으로 구성한 클러스터를 사용했습니다.)
    2. Helm v3 이상 설치
    3. (선택 사항) PersistentVolume(영구 볼륨)을 위한 StorageClass(스토리지 클래스) 설정 (데이터 유실 방지를 위해 권장합니다)

    Step 1: Helm Repository 추가

    Prometheus와 Grafana를 포함하는 <code>kube-prometheus-stack은 Helm 차트로 제공됩니다. 먼저 Helm 레포지토리를 추가해주세요.

    helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
    helm repo update

    Step 2: Prometheus 스택 배포

    이제 kube-prometheus-stack을 배포할 차례입니다. 이 차트 안에는 Prometheus, Grafana, Alertmanager(알림 관리자), Kube-State-Metrics(쿠버네티스 상태 지표 수집기), Node Exporter(노드 지표 수집기) 등 쿠버네티스 모니터링에 필요한 모든 것이 한 번에 들어있어서 정말 편합니다. 저는 보통 monitoring 네임스페이스에 배포합니다.

    ⚠️ 중요: 프로덕션 환경에서는 values.yaml 파일을 통해 스토리지 클래스, 리소스 제한, 서비스 타입 등을 반드시 커스터마이징해야 합니다. 저는 홈랩이라 기본 설정으로 진행했지만, 여러분은 꼭 신경 써주세요!

    # values.yaml 파일 예시 (간단하게 스토리지 클래스만 지정)
    # my-prometheus-values.yaml
    
    prometheus:
      prometheusSpec:
        storageSpec:
          volumeClaimTemplate:
            spec:
              storageClassName: my-storage-class # 본인의 StorageClass 이름으로 변경
              resources:
                requests:
                  storage: 10Gi
    
    grafana:
      persistence:
        enabled: true
        storageClassName: my-storage-class # 본인의 StorageClass 이름으로 변경
        size: 5Gi
    

    이제 위 values.yaml 파일을 적용하여 배포합니다.

    helm install prometheus prometheus-community/kube-prometheus-stack \
      --namespace monitoring --create-namespace \
      -f my-prometheus-values.yaml # 커스터마이징한 values.yaml 파일 적용

    배포가 완료되면 kubectl get pods -n monitoring 명령어로 파드들이 잘 떠 있는지 확인해보세요. 모든 파드가 Running 상태라면 성공입니다! 🎉

    Step 3: Grafana 접속 및 초기 설정

    Grafana는 기본적으로 ClusterIP 타입의 서비스로 배포됩니다. 외부에서 접속하려면 Ingress(인그레스, 외부 트래픽 진입점)를 설정하거나, 간단하게 포트 포워딩(Port-forwarding)을 이용할 수 있어요. 저는 테스트를 위해 포트 포워딩을 자주 사용합니다.

    kubectl port-forward svc/prometheus-grafana 3000:80 -n monitoring

    이제 웹 브라우저에서 http://localhost:3000으로 접속해보세요. 초기 로그인 정보는 다음과 같습니다.

    • User (사용자): admin
    • Password (비밀번호): prom-operator (또는 kubectl get secret prometheus-grafana -n monitoring -o jsonpath='{.data.admin-password}' | base64 --decode 명령어로 확인)

    로그인 후에는 비밀번호 변경을 요청할 겁니다. 안전하게 변경해주세요!

    Grafana에서 Prometheus 데이터 소스를 추가하는 설정 화면입니다.

    📊 나만의 쿠버네티스 대시보드 만들기 & 활용하기

    Grafana에 접속했다면 이제 Prometheus 데이터를 시각화할 차례예요. kube-prometheus-stack 덕분에 Prometheus 데이터 소스는 이미 설정되어 있을 겁니다. 혹시 없다면, Configuration > Data Sources에서 Prometheus를 추가하고 URL을 http://prometheus-kube-prometheus-prometheus.monitoring.svc.cluster.local:9090 (네임스페이스와 서비스 이름에 따라 다를 수 있음)으로 설정하면 됩니다.

    기본 대시보드 활용하기

    kube-prometheus-stack은 기본적으로 여러 유용한 대시보드들을 Grafana에 자동으로 임포트해줍니다. 왼쪽 메뉴에서 Dashboards > Manage로 이동해보세요. 아마 Kubernetes / Compute Resources, Node Exporter Full 같은 대시보드들을 볼 수 있을 겁니다. 저도 처음엔 이 대시보드들만으로도 충분히 모니터링이 가능해서 정말 편하더라고요.

    Grafana Labs 공유 대시보드 임포트하기

    만약 더 다양한 대시보드를 원한다면, Grafana Labs 웹사이트(https://grafana.com/grafana/dashboards/)에서 다른 사람들이 공유한 대시보드를 임포트할 수 있습니다. 예를 들어, Node Exporter Full 대시보드(ID: 1860)나 Kubernetes Cluster Overview(ID: 12150) 같은 것들이 인기가 많죠.

    1. Grafana 좌측 메뉴에서 + > Import를 클릭합니다.
    2. Import via grafana.com dashboard 칸에 대시보드 ID (예: 1860)를 입력하고 Load를 클릭합니다.
    3. 대시보드 이름, 폴더를 설정하고, Prometheus 데이터 소스를 선택한 후 Import를 클릭합니다.

    짜잔! 멋진 대시보드가 눈앞에 펼쳐질 겁니다. 🤩

    나만의 대시보드 커스터마이징

    기존 대시보드를 활용하는 것도 좋지만, 특정 애플리케이션이나 서비스에 특화된 대시보드 구축은 필수입니다. 저는 주로 복제(Duplicate) 기능을 이용해서 기존 대시보드를 복사한 다음, 필요한 패널(Panel)을 추가하거나 수정하는 방식으로 커스터마이징합니다. PromQL(프로메테우스 쿼리 언어)을 조금만 익히면 원하는 지표를 자유자재로 뽑아낼 수 있거든요.

    Prometheus와 Grafana를 통해 구현된 쿠버네티스 클러스터 모니터링 대시보드 예시입니다.

    ⚠️ 삽질 대잔치! 트러블슈팅 경험담

    인프라 엔지니어의 삶은 삽질의 연속이죠. 저도 Prometheus & Grafana 구축 과정에서 몇 번의 삽질을 경험했어요. 여러분은 이런 문제들을 미리 알고 피하시길 바랍니다.

    1. PersistentVolumeClaim (PVC) Pending 문제

    증상: Prometheus나 Grafana 파드가 Pending 상태에서 벗어나지 못하고, PVC가 Pending으로 남아있을 때입니다.

    원인: 대부분 StorageClass가 없거나, 잘못 지정되었을 때 발생합니다. Prometheus는 데이터를 저장하기 위해 영구 스토리지를 써야 하는데, 이를 위한 StorageClass가 없으면 볼륨을 프로비저닝할 수 없거든요.

    해결: 클러스터에 NFS CSI Driver나 Longhorn 같은 동적 프로비저너를 설치하고, 해당 StorageClass를 values.yaml에 올바르게 지정해주세요. 저는 처음에 이 부분을 놓쳐서 한참 헤맸습니다. 🤦‍♂️

    2. 리소스 부족으로 인한 OOMKilled

    증상: Prometheus 파드가 자꾸 재시작되면서 OOMKilled(Out Of Memory Killed) 오류가 발생합니다.

    원인: Prometheus는 수집하는 지표의 양이 많아질수록 메모리 사용량이 늘어나요. 기본 설정된 리소스 제한(Resource Limits)이 클러스터 규모에 비해 너무 낮을 때 발생하는 거죠.

    해결: values.yaml에서 Prometheus 파드의 resources.requests와 resources.limits를 적절히 늘려주세요. 특히 memory 부분을 여유 있게 설정하는 게 중요합니다. 너무 많이 늘리면 다른 파드에 영향을 줄 수 있으니, 모니터링하면서 점진적으로 조정하는 것을 추천합니다.

    3. Prometheus가 타겟을 찾지 못하는 문제

    증상: Grafana 대시보드에서 데이터가 보이지 않거나, Prometheus UI의 Targets 페이지에서 특정 파드가 DOWN 상태로 표시될 때입니다.

    원인: Prometheus의 서비스 디스커버리 설정이 잘못되었거나, 파드에 올바른 레이블(Label)이 지정되지 않았을 때, 혹은 네트워크 정책(Network Policy) 등으로 인해 Prometheus가 파드의 익스포터에 접근하지 못할 때 발생합니다.

    해결:

    • 해당 파드에 Prometheus가 찾을 수 있는 annotations나 labels가 잘 붙어있는지 확인합니다. (예: prometheus.io/scrape: "true")
    • ServiceMonitor 또는 PodMonitor 리소스가 올바르게 정의되어 있는지 확인합니다.
    • 네트워크 정책이 Prometheus 파드에서 대상 파드로의 트래픽을 허용하는지 검토합니다.

    제가 직접 커스텀 애플리케이션을 모니터링할 때 이 문제로 고생 좀 했어요. 애플리케이션 파드에 어노테이션을 빼먹어서 그랬더라고요. ㅎㅎ

    ✅ 구축 결과 확인 및 다음 단계 제안

    이제 여러분의 쿠버네티스 클러스터는 Prometheus와 Grafana라는 강력한 모니터링 시스템을 갖추게 되었습니다. Grafana 대시보드를 통해 노드의 CPU/메모리 사용량, 파드의 상태, 네트워크 트래픽 등 다양한 지표들을 한눈에 볼 수 있을 겁니다. 문제가 발생하면 즉시 알 수 있고, 미리 예방할 수도 있게 되는 거죠. 정말 뿌듯하지 않나요? 🎉

    저도 처음 이 대시보드를 보면서 ‘드디어 됐다!’ 싶었던 기억이 생생하네요. 덕분에 제 홈랩 클러스터가 훨씬 더 안정적으로 운영되고 있습니다.

    다음 단계로 나아가기

    여기서 멈추지 마세요! 쿠버네티스 모니터링은 계속 발전해야 합니다. 몇 가지 다음 단계를 제안해봅니다.

    • Alertmanager(알림 관리자) 설정: Prometheus가 수집한 지표를 기반으로 알림(Alert)을 생성하고, 이를 Slack, 이메일, PagerDuty 등으로 전송하도록 설정해보세요. 저는 슬랙으로 알림을 받는데, 긴급 상황 발생 시 정말 유용합니다.
    • Custom Exporter 개발: 여러분의 특정 애플리케이션에서만 나오는 커스텀 지표를 수집하고 싶다면, 직접 익스포터를 개발해보는 것도 좋은 경험입니다. Python이나 Go 언어로 쉽게 만들 수 있어요.
    • 장기 지표 저장 (Long-term Storage): Prometheus는 기본적으로 로컬 스토리지를 써요. 장기간 지표를 보관하고 싶다면 Thanos(타노스)나 Cortex(코텍스) 같은 솔루션을 연동하여 스케일 아웃 및 장기 보관 기능을 추가할 수 있습니다.

    Prometheus 및 Grafana 기반 쿠버네티스 모니터링 시스템의 주요 이점과 활용 방안을 요약한 인포그래픽입니다.

    맺음말: 13년차 엔지니어의 조언

    쿠버네티스 모니터링은 클러스터 운영의 핵심이자, 인프라 엔지니어의 역량을 보여주는 중요한 부분입니다. 처음에는 어렵게 느껴질 수 있지만, 이렇게 직접 구축하고 활용해보면서 얻는 경험은 그 어떤 이론보다 값지다고 생각합니다. 저도 13년차 엔지니어이지만, 여전히 새로운 기술을 배우고 직접 손으로 만져보면서 배우는 것이 가장 즐겁고 효과적하더라고요.

    오늘 제가 공유한 내용이 여러분의 쿠버네티스 여정에 작은 도움이 되었기를 바랍니다. 혹시 진행 중에 궁금한 점이나 막히는 부분이 있다면 언제든지 댓글로 남겨주세요. 제가 아는 선에서 최대한 도와드리겠습니다. 다음에는 Alertmanager 설정이나 Custom Exporter 개발 경험에 대해서도 다뤄볼 예정이니 기대해주세요!

    다음 글에서 또 만나요! ✋

  • [Proxmox] Proxmox GPU 패스스루 완벽 가이드: 가상 머신에서 고성능 그래픽 활용

    Proxmox GPU 패스스루 완벽 가이드: 가상 머신에서 고성능 그래픽 활용

    홈랩을 운영하다 보면 한 번쯤은 이런 생각이 드시죠. “Proxmox에서 가상 머신 하나에 GPU를 통째로 붙여서 쓸 수 없을까?” 저도 처음에 이 생각이 들었을 때, 반신반의하면서 시도했다가 꽤 오래 삽질을 했거든요. 결론부터 말씀드리면 — 됩니다. 그것도 꽤 잘 됩니다.

    Proxmox GPU 패스스루(GPU Passthrough)는 가상화 호스트에 장착된 물리 GPU를 특정 VM(가상 머신)에 직접 할당해서, 마치 그 VM이 GPU를 단독으로 소유한 것처럼 쓸 수 있게 해주는 기술입니다. 머신러닝 학습 환경 구성, 게임 VM, 렌더링 워크스테이션 등 다양한 용도로 활용할 수 있어서, 인프라 엔지니어로서 이 기능은 정말 게임 체인저였어요.

    이 글에서는 제가 직접 수십 번의 시도 끝에 정리한 Proxmox GPU 패스스루 설정 전 과정을 단계별로 공유해 드릴게요. NVIDIA와 AMD 양쪽 다 다뤄보겠습니다.

    ▲ Proxmox 호스트에서 GPU 패스스루가 이루어지는 전체 아키텍처. 물리 GPU가 IOMMU를 통해 VM에 직접 연결되는 구조를 보여줍니다.

    1. GPU 패스스루가 뭔지 먼저 이해하고 가요

    쉽게 말해서, 일반적인 가상화에서는 VM들이 가상화된(에뮬레이션된) 하드웨어를 씁니다. GPU도 마찬가지로 가상의 그래픽 카드를 쓰게 되죠. 근데 이렇게 하면 성능이 많이 깎여요. 특히 GPU 연산 집약적인 작업에서는 더더욱.

    패스스루(Passthrough)는 이 중간 에뮬레이션 레이어를 없애고, 물리 하드웨어를 VM에 직접 넘겨주는 방식이에요. 이걸 가능하게 해주는 핵심 기술이 바로 IOMMU(Input-Output Memory Management Unit)입니다.

    구분 일반 가상 GPU GPU 패스스루
    방식 소프트웨어 에뮬레이션 물리 하드웨어 직접 할당
    성능 물리 대비 크게 저하 물리 성능과 거의 동일
    여러 VM 공유 가능 불가능 (1 GPU = 1 VM)
    드라이버 Proxmox 제공 가상 드라이버 실제 벤더 드라이버 (NVIDIA/AMD)
    주요 용도 일반 데스크톱 환경 머신러닝, 게임, 렌더링

    한 가지 알아두실 점은, GPU 패스스루를 하면 해당 GPU는 그 VM 전용이 됩니다. Proxmox 호스트 자체나 다른 VM은 그 GPU를 쓸 수 없어요. 그래서 보통 홈랩에서는 GPU가 2개이거나, 아니면 iGPU(내장 그래픽)와 dGPU(외장 그래픽)를 분리해서 쓰는 경우가 많습니다.

    2. 사전 조건 확인: 이것부터 체크하세요

    본격적으로 시작하기 전에 몇 가지 반드시 확인해야 할 게 있어요. 여기서 막히면 아무리 설정을 잘 해도 안 됩니다. 제가 처음에 이걸 무시하고 진행했다가 몇 시간을 날렸거든요 😅

    하드웨어 요구사항

    • CPU: Intel VT-d 또는 AMD-Vi(AMD IOMMU) 지원 필수
    • 메인보드: IOMMU 지원 + BIOS/UEFI에서 활성화 가능해야 함
    • GPU: 패스스루할 GPU (NVIDIA, AMD 모두 가능)
    • Proxmox 버전: 7.x 이상 권장 (이 글은 Proxmox VE 7~8 기준)

    BIOS/UEFI 설정 확인

    메인보드 BIOS에 들어가서 다음 항목들을 활성화해야 합니다.

    • Intel 시스템: VT-d (Virtualization Technology for Directed I/O) 활성화
    • AMD 시스템: AMD-Vi 또는 IOMMU 활성화
    • 가능하다면 Above 4G Decoding도 활성화 권장

    💡 팁: BIOS 설정 후 Proxmox에서 IOMMU가 제대로 인식됐는지 확인하는 방법은 아래에서 알려드릴게요.

    3. Proxmox 호스트 설정: GRUB부터 손봐야 해요

    자, 이제 실제 설정 들어갑니다. Proxmox 호스트에 SSH로 접속하거나 콘솔을 열어주세요.

    3-1. GRUB 부트 파라미터 수정

    먼저 GRUB 설정 파일을 열어야 합니다.

    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"

    여기서 iommu=pt는 passthrough 모드를 뜻하는데, 이걸 넣어주면 IOMMU를 사용하지 않는 디바이스들의 성능 저하를 방지해줍니다. 꼭 같이 넣어주세요.

    수정 후 GRUB을 업데이트합니다.

    update-grub

    3-2. 필요한 커널 모듈 로드

    /etc/modules 파일에 VFIO 관련 모듈을 추가해줍니다. VFIO(Virtual Function I/O)는 리눅스에서 디바이스 패스스루를 담당하는 프레임워크예요.

    nano /etc/modules

    아래 내용을 파일 끝에 추가합니다.

    vfio
    vfio_iommu_type1
    vfio_pci
    vfio_virqfd

    3-3. GPU 드라이버 블랙리스트 처리

    이 부분이 정말 중요합니다. 호스트 OS가 패스스루할 GPU를 먼저 가져가버리면 VM에 넘겨줄 수가 없거든요. 그래서 호스트에서 해당 GPU 드라이버를 블랙리스트(blacklist) 처리해서 로드되지 않도록 해야 해요.

    NVIDIA GPU 블랙리스트:

    echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf
    echo "blacklist nvidia" >> /etc/modprobe.d/blacklist.conf
    echo "blacklist nvidiafb" >> /etc/modprobe.d/blacklist.conf

    AMD GPU 블랙리스트:

    echo "blacklist radeon" >> /etc/modprobe.d/blacklist.conf
    echo "blacklist amdgpu" >> /etc/modprobe.d/blacklist.conf

    3-4. VFIO가 GPU를 먼저 가져가도록 설정

    먼저 패스스루할 GPU의 PCI ID를 확인합니다.

    lspci -nn | grep -i nvidia
    # 또는 AMD의 경우
    lspci -nn | grep -i amd

    출력 결과에서 GPU와 관련된 항목들의 ID를 메모해두세요. 보통 이런 형식으로 나옵니다.

    01:00.0 VGA compatible controller [0300]: NVIDIA Corporation ... [10de:2204]
    01:00.1 Audio device [0403]: NVIDIA Corporation ... [10de:1aef]

    여기서 대괄호 안의 10de:2204, 10de:1aef 같은 숫자가 바로 Vendor ID:Device ID입니다. 이걸 VFIO 설정에 넣어줍니다.

    echo "options vfio-pci ids=10de:2204,10de:1aef" >> /etc/modprobe.d/vfio.conf

    ⚠️ 중요: 위 ID는 예시입니다. 반드시 본인 시스템에서 lspci -nn으로 확인한 실제 ID를 사용하세요!

    모든 설정을 적용하고 재부팅합니다.

    update-initramfs -u -k all
    reboot

    3-5. IOMMU 활성화 확인

    재부팅 후 IOMMU가 제대로 활성화됐는지 확인해봅시다.

    dmesg | grep -e DMAR -e IOMMU

    출력에 IOMMU enabled 또는 Intel-IOMMU: enabled 같은 문구가 보이면 성공입니다. 🎉

    VFIO가 GPU를 제대로 가져갔는지도 확인해봅시다.

    lspci -nnk | grep -A3 "VGA\|3D\|Display"
    # 출력에서 'Kernel driver in use: vfio-pci' 가 보이면 성공

    ▲ Proxmox 웹 UI의 VM 하드웨어 설정에서 PCI 디바이스(GPU)를 추가하는 과정. Add > PCI Device 메뉴에서 패스스루할 GPU를 선택합니다.

    4. VM에 GPU 연결하기: Proxmox 웹 UI 설정

    호스트 설정이 끝났으면 이제 VM에 GPU를 붙여줄 차례입니다. Proxmox 웹 UI에서 진행할 수 있어요.

    1. Proxmox 웹 UI에서 패스스루할 VM을 선택합니다.
    2. VM이 꺼진 상태에서 Hardware(하드웨어) 탭으로 이동합니다.
    3. Add > PCI Device를 클릭합니다.
    4. 드롭다운에서 패스스루할 GPU를 선택합니다.
    5. 다음 옵션들을 설정합니다:
    • All Functions: 체크 — GPU와 연결된 오디오 장치 등 모든 기능 함께 패스스루
    • ROM-Bar: 체크 — GPU ROM 접근 허용
    • PCI-Express: 체크 — PCIe 방식으로 연결 (성능상 유리)
    • Primary GPU: 체크 — VM의 주 GPU로 설정 (디스플레이 출력 필요시)

    CLI로 직접 VM 설정을 수정하고 싶다면 아래처럼 할 수도 있습니다.

    # VM ID가 100이고, GPU가 PCI 01:00.0에 있는 경우
    qm set 100 -hostpci0 01:00.0,allfunctions=1,pcie=1,rombar=1,x-vga=1

    VM 설정 파일 직접 확인

    cat /etc/pve/qemu-server/100.conf

    설정 파일에 아래와 같은 내용이 들어가 있으면 정상입니다.

    hostpci0: 0000:01:00,allfunctions=1,pcie=1,rombar=1,x-vga=1
    machine: q35
    bios: ovmf

    💡 중요한 포인트: GPU 패스스루에는 OVMF(UEFI 펌웨어)와 q35 머신 타입이 필수입니다. 구형 SeaBIOS나 i440fx 머신 타입에서는 동작하지 않는 경우가 많아요. VM 생성 시 처음부터 이 설정으로 만드는 게 나중에 편합니다.

    5. VM 내부 드라이버 설치

    VM을 부팅하면 이제 GPU가 인식될 겁니다. 여기서부터는 VM 안에서 작업합니다.

    Windows VM의 경우

    Windows VM에서 GPU 패스스루를 쓰는 가장 흔한 케이스죠. 부팅 후 장치 관리자를 열어보면 GPU가 인식은 되지만 드라이버가 없는 상태일 거예요.

    1. NVIDIA 또는 AMD 공식 사이트에서 드라이버를 다운로드합니다.
    2. 일반 드라이버 설치하듯 설치하면 됩니다.
    3. 설치 완료 후 재부팅하면 GPU가 정상 동작합니다.

    ⚠️ NVIDIA 드라이버 오류 코드 43 문제: NVIDIA GPU를 Windows VM에 패스스루할 때 오류 코드 43(Code 43)이 발생하는 경우가 있습니다. NVIDIA 드라이버가 가상 환경을 감지하고 동작을 거부하는 거예요. 이럴 때는 VM 설정 파일에 다음을 추가하면 대부분 해결됩니다.

    nano /etc/pve/qemu-server/100.conf
    # 아래 내용 추가
    args: -cpu host,kvm=off
    cpu: host,hidden=1,flags=+pcid

    또는 웹 UI의 Options > CPU에서 추가 CPU 플래그를 설정해도 됩니다. kvm=off는 VM에서 KVM을 쓰고 있다는 걸 드라이버가 감지하지 못하게 숨겨주는 역할을 합니다.

    Linux VM의 경우

    Ubuntu 기준으로 NVIDIA 드라이버를 설치해봅시다.

    # 사용 가능한 드라이버 확인
    ubuntu-drivers devices
    
    # 권장 드라이버 자동 설치
    sudo ubuntu-drivers autoinstall
    
    # 또는 특정 버전 지정 설치
    sudo apt install nvidia-driver-535
    
    # 재부팅 후 확인
    nvidia-smi

    AMD GPU라면 Ubuntu에서는 대부분 자동으로 amdgpu 드라이버가 잡힙니다. 추가 작업 없이도 동작하는 경우가 많아요.

    6. 트러블슈팅: 실제 겪은 문제들

    이 섹션이 가장 실용적일 거예요. 이론대로 안 되는 경우가 정말 많거든요. 제가 직접 겪었던 문제들만 정리했습니다.

    ▲ GPU 패스스루 설정 중 자주 만나는 오류 상황별 트러블슈팅 흐름도. 각 단계에서 확인해야 할 포인트를 정리했습니다.

    문제 1: VM이 부팅 자체가 안 돼요

    OVMF(UEFI) 설정이 제대로 안 됐거나, GPU가 IOMMU 그룹에서 다른 디바이스와 묶여 있는 경우입니다.

    IOMMU 그룹 확인 스크립트를 실행해보세요.

    #!/bin/bash
    for d in /sys/kernel/iommu_groups/*/devices/*; do
      n=${d#*/iommu_groups/*}; n=${n%%/*}
      printf 'IOMMU Group %s ' "$n"
      lspci -nns "${d##*/}"
    done

    GPU가 속한 IOMMU 그룹에 다른 디바이스(예: PCIe 루트 포트)가 함께 있다면, 해당 디바이스들도 전부 같이 패스스루해야 합니다. 이게 안 되면 ACS(Access Control Services) 패치 커널을 사용해야 하는데, 이건 꽤 복잡한 주제라 별도 글에서 다룰 예정입니다.

    문제 2: GPU는 인식되는데 드라이버 설치 후 화면이 안 나와요

    Primary GPU 옵션을 켰는데 Proxmox 기본 디스플레이(VirtIO 또는 Bochs)와 충돌하는 경우입니다. VM 설정에서 Display를 None으로 바꿔보세요.

    qm set 100 -vga none

    이렇게 하면 기본 가상 디스플레이가 비활성화되고 패스스루된 GPU의 출력만 사용하게 됩니다. 물리 모니터를 GPU에 직접 연결하거나, Looking Glass 같은 도구를 활용하면 됩니다.

    문제 3: NVIDIA Code 43 오류

    위에서 언급한 것처럼 kvm=off와 hidden=1 플래그로 해결하세요. 추가로 VM 설정 파일에 아래도 넣어보세요.

    # /etc/pve/qemu-server/100.conf 에 추가
    args: -cpu host,kvm=off
    cpu: host,hidden=1

    문제 4: 재부팅 후 vfio-pci 대신 원래 드라이버가 잡혀요

    update-initramfs를 빠뜨린 경우가 많습니다. 다시 실행해주세요.

    update-initramfs -u -k all
    reboot

    7. 결과 확인: 제대로 동작하는지 검증해봐요

    드라이버 설치까지 완료했다면, 이제 GPU가 정상 동작하는지 확인할 차례입니다.

    Linux VM에서 확인

    # NVIDIA
    nvidia-smi
    
    # 출력 예시
    +-----------------------------------------------------------------------------+
    | NVIDIA-SMI ...        Driver Version: ...       CUDA Version: ...           |
    |-------------------------------+----------------------+----------------------+
    | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
    ...
    
    # AMD
    rocm-smi  # ROCm 설치 후
    # 또는
    glxinfo | grep "OpenGL renderer"

    CUDA가 필요한 환경이라면 간단한 테스트도 해볼 수 있어요.

    import torch
    print(torch.cuda.is_available())  # True 가 나오면 성공!
    print(torch.cuda.get_device_name(0))  # GPU 이름 출력

    드디어 True와 GPU 이름이 출력되는 걸 처음 봤을 때의 그 쾌감… 진짜 잊을 수가 없더라고요. 😄

    Windows VM에서 확인

    • 장치 관리자에서 GPU에 노란 느낌표 없이 정상 표시
    • DirectX 진단 도구(dxdiag)에서 GPU 정보 확인
    • GPU-Z 같은 도구로 실제 물리 GPU 정보 확인

    ▲ GPU 패스스루 성공 후 Linux VM에서의 nvidia-smi 출력과 Windows VM에서의 GPU-Z 결과. 물리 GPU 정보가 그대로 보이는 것을 확인할 수 있습니다.

    8. 정리 및 다음 단계

    여기까지 따라오셨으면 Proxmox GPU 패스스루 기본 설정은 완료입니다! 전체 과정을 한 번 정리해볼게요.

    1. ✅ BIOS에서 VT-d / AMD-Vi 활성화
    2. ✅ GRUB에 IOMMU 파라미터 추가
    3. ✅ VFIO 모듈 로드 설정
    4. ✅ 호스트에서 GPU 드라이버 블랙리스트 처리
    5. ✅ VFIO가 GPU PCI ID 선점하도록 설정
    6. ✅ initramfs 업데이트 후 재부팅
    7. ✅ VM에 PCI 디바이스로 GPU 추가 (q35 + OVMF 필수)
    8. ✅ VM 내부에서 드라이버 설치 및 확인

    처음에는 복잡해 보이지만, 한 번 제대로 이해하고 나면 다음에는 훨씬 빠르게 할 수 있어요. 저도 지금은 새 시스템에 설정하는 데 30분도 안 걸립니다.

    다음에 다룰 주제들

    • 🔜 Looking Glass: 패스스루 GPU 화면을 호스트에서 보는 방법
    • 🔜 vGPU (MIG/SR-IOV): GPU 하나를 여러 VM에 나눠 쓰는 방법
    • 🔜 IOMMU 그룹 분리 (ACS 패치): 같은 그룹 문제 해결하기
    • 🔜 머신러닝 VM 구성 완전 가이드

    궁금하신 점이나 안 되는 부분이 있으면 댓글로 남겨주세요. 제가 직접 겪은 문제라면 같이 해결해 드릴 수 있을 것 같습니다. 그리고 혹시 이 글이 도움이 됐다면, 비슷한 삽질을 하고 있을 동료 엔지니어에게 공유해주시면 감사하겠습니다! 🙏


    자주 묻는 질문 (FAQ)

    Q. Proxmox GPU 패스스루에 특별한 GPU 모델이 필요한가요?

    아니요. 대부분의 NVIDIA GeForce, Quadro, AMD Radeon, Radeon Pro 등 PCIe GPU는 패스스루가 가능합니다. 단, 호스트 시스템이 IOMMU를 지원해야 합니다.

    Q. 패스스루한 GPU를 호스트와 VM이 동시에 쓸 수 있나요?

    기본 패스스루로는 불가능합니다. 패스스루된 GPU는 해당 VM 전용이 됩니다. 여러 VM이 GPU를 나눠 쓰려면 NVIDIA vGPU(엔터프라이즈 라이선스 필요) 또는 AMD SR-IOV 지원 GPU가 필요합니다.

    Q. VM이 꺼진 후 GPU를 다른 VM에 줄 수 있나요?

    네, 가능합니다. 한 VM을 종료하고 설정에서 GPU를 제거한 뒤, 다른 VM 설정에 추가하면 됩니다. 동시에 두 VM이 쓰는 건 안 되지만, 순차적으로는 얼마든지 가능합니다.

    Q. Proxmox 호스트 자체 콘솔이 까맣게 되는데 정상인가요?

    GPU를 블랙리스트 처리하면 호스트가 그 GPU로 화면 출력을 못 하게 됩니다. 만약 그 GPU가 유일한 그래픽이라면 호스트 화면은 안 나올 수 있어요. iGPU나 별도 GPU를 호스트용으로 쓰거나, SSH/웹 UI로만 관리하는 방식을 권장합니다.

  • [Proxmox] LXC 컨테이너 심화 활용: Docker 연동 및 성능 최적화 (Proxmox VE 9.1)

    [Proxmox] LXC 컨테이너 심화 활용: Docker 연동 및 성능 최적화 (Proxmox VE 9.1)

    LXC 컨테이너 심화 활용: Docker 연동 및 성능 최적화 (Proxmox VE 9.1)

    안녕하세요, 13년차 인프라 엔지니어 ’13년차의 서버실’ 블로그 주인장입니다. 오늘도 홈랩에서 밤샘 삽질 끝에 얻어낸 귀한 경험 하나를 공유해드리려고 해요. 💡

    홈랩을 운영하거나 소규모 서버를 돌리시는 분들이라면 Proxmox VE (Virtual Environment)의 매력에 빠져 계실 텐데요. 특히 LXC (Linux Containers)는 가벼운 오버헤드로 높은 성능을 내주기 때문에 저도 애용하고 있습니다. 여기에 Docker까지 연동하면 어떨까요? 효율은 물론이고 관리 유연성까지 챙길 수 있거든요.

    오늘은 Proxmox VE 9.1 환경에서 LXC 컨테이너 안에 Docker를 설치하고, 실제 서비스를 운영하면서 겪었던 삽질과 함께 성능 최적화 팁까지 아낌없이 풀어보겠습니다. LXC와 Docker 연동에 어려움을 겪으셨다면, 이 글이 든든한 가이드가 될 거예요!

    Proxmox VE 호스트에서 LXC 컨테이너와 Docker가 연동되는 아키텍처 다이어그램

    LXC와 Docker, 그리고 그들의 시너지

    먼저 LXC와 Docker가 뭔지 간단하게 짚고 넘어갈게요. 이미 잘 아시는 분들도 계시겠지만, 핵심만 콕 짚어드리겠습니다.

    • LXC (Linux Containers, 리눅스 컨테이너): 리눅스 커널의 컨테이너 기술을 활용한 OS 수준의 가상화예요. 쉽게 말해, 별도의 커널 없이 호스트의 커널을 공유하면서도 독립적인 운영체제 환경을 만들어주는 거죠. 가상 머신(VM)보다 훨씬 가볍고 빠르고, 부팅 시간도 거의 없어서 자원 소모가 정말 적어요.
    • Docker (도커): 애플리케이션 수준의 컨테이너 기술입니다. 특정 애플리케이션과 그에 필요한 모든 종속성(라이브러리, 설정 파일 등)을 하나의 이미지(Image)로 묶어서 어디서든 동일하게 실행되도록 해줘요. 개발 환경과 운영 환경의 불일치 문제를 한 번에 해결하는 거죠.

    그럼 이 둘을 왜 같이 쓸까요? 직접 써본 결과 이런 장점들이 정말 크더라고요.

    특징 LXC + Docker 연동의 장점 기존 방식 대비
    자원 효율성 LXC는 VM보다 오버헤드가 적어서, 그 위에 Docker를 올리면 VM 내 Docker보다 훨씬 적은 자원으로 많은 서비스를 돌릴 수 있어요. VM 내 Docker: 커널 이중화로 자원 낭비
    베어메탈 Docker: 호스트 OS 오염 우려
    격리성 LXC 컨테이너가 OS 수준의 격리를 제공하고, 그 안에 Docker가 또 한 번 애플리케이션 격리를 제공하니 보안성과 안정성이 정말 높아져요. 베어메탈 Docker: 호스트 OS와 Docker 컨테이너 간 완전한 격리가 어려움
    관리 용이성 Proxmox VE의 강력한 LXC 관리 기능(스냅샷, 마이그레이션 등)과 Docker의 애플리케이션 배포/관리 기능을 동시에 누릴 수 있어요. 각각의 장점을 한 번에!

    Proxmox VE 9.1 버전에서는 LXC 컨테이너 기능이 더욱 안정화되고 사용성이 개선되었기 때문에, Docker 연동할 때 시너지가 정말 커져요.

    Proxmox VE 9.1 환경에서 LXC 컨테이너 생성 및 Docker 연동 실전 가이드

    자, 이제 직접 해볼 시간입니다. 제가 홈랩에서 실제로 진행했던 과정을 그대로 따라하시면 돼요. 😎

    1. LXC 컨테이너 기본 생성

    먼저 Proxmox VE 웹 UI에 접속해서 LXC 컨테이너를 생성합니다. OS 템플릿은 Ubuntu 22.04 LTS (Jammy Jellyfish)를 추천할게요. 가장 안정적이고 Docker 지원도 정말 잘 되거든요.

    1. Proxmox VE 웹 UI (https://<Proxmox_IP>:8006) 에 접속합니다.
    2. Datacenter -> pve -> Local (pve) -> CT Templates -> Templates에서 원하는 Ubuntu 템플릿을 다운로드해요.
    3. pve 노드에서 CT 생성 버튼을 클릭합니다.
    4. 일반 설정: Hostname, Password 등을 설정하세요.
    5. 템플릿 설정: 다운로드한 Ubuntu 22.04 LTS 템플릿을 선택해요.
    6. 디스크 설정: 최소 8GB 이상으로 설정해주세요. Docker 이미지를 많이 올릴 계획이라면 더 크게 잡는 게 좋습니다.
    7. CPU 설정: 코어는 1개 이상, CPU units (CPU 유닛)은 기본값(1024)으로 둬도 되지만, 고성능이 필요하면 더 높게 설정할 수 있어요.
    8. 메모리 설정: 최소 512MB 이상을 권장합니다. Docker 앱에 따라 달라지니 유연하게 설정하세요.
    9. 네트워크 설정: Bridge (브리지) 모드로 설정하여 호스트와 동일한 네트워크 대역을 사용하도록 하세요.
    10. 마지막으로 확인을 눌러 LXC 컨테이너를 생성합니다.

    ⚠️ 중요 포인트: 비특권 컨테이너(Unprivileged Container) 설정!

    Docker를 LXC 컨테이너 안에서 안전하게 사용하려면 비특권 컨테이너로 만드는 게 좋아요. 보안상 훨씬 유리하거든요. 하지만 비특권 컨테이너에서 Docker가 제대로 동작하려면 몇 가지 추가 설정이 필요해요.

    1. 컨테이너 생성 후, Proxmox VE 웹 UI에서 해당 LXC 컨테이너를 선택하세요.
    2. 옵션 탭으로 이동합니다.
    3. Features (기능) 항목을 찾아서 편집을 클릭하세요.
    4. 여기서 Nesting (중첩)과 FUSE (파일 시스템 인 유저스페이스)를 활성화하고 확인을 눌러요.

    이 두 가지 옵션이 Docker의 스토리지 드라이버(특히 overlay2)가 LXC 내부에서 정상적으로 작동하도록 하는 핵심이에요. 제가 이걸 몰라서 며칠 밤낮을 삽질했던 기억이 생생하네요… 😅

    Proxmox VE 웹 UI에서 LXC 컨테이너 생성 시 Nesting 및 FUSE 기능을 활성화하는 화면

    2. Docker 설치 및 설정

    LXC 컨테이너가 준비되었다면, 이제 컨테이너에 접속해서 Docker를 설치해봅시다. Proxmox 웹 UI에서 해당 LXC 컨테이너를 선택하고 콘솔 탭으로 이동하면 바로 접속돼요.

    \n# LXC 컨테이너 내부에서 실행
    
    # 패키지 목록 업데이트 및 필요한 도구 설치
    sudo apt update
    sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
    
    # Docker 공식 GPG 키 추가
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    
    # Docker Stable 저장소 추가
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    # Docker 엔진 설치
    sudo apt update
    sudo apt install -y docker-ce docker-ce-cli containerd.io
    
    # Docker 서비스 시작 및 부팅 시 자동 시작 설정
    sudo systemctl start docker
    sudo systemctl enable docker
    
    # 현재 사용자에게 Docker 그룹 권한 추가 (선택 사항, sudo 없이 docker 명령어 사용 가능)
    sudo usermod -aG docker $USER
    
    # 변경 사항 적용을 위해 재로그인 또는 새 쉘 시작
    # exit 후 다시 접속하거나, su - $USER 명령으로 적용
    
    # Docker 설치 확인
    docker run hello-world
    

    docker run hello-world 명령을 실행했을 때 성공 메시지가 나온다면 🎉 Docker 설치가 완벽하게 된 거예요!

    3. 비특권(Unprivileged) 컨테이너의 Docker 이슈 해결 (추가 팁)

    위에서 Nesting과 FUSE를 활성화했지만, 간혹 비특권 컨테이너에서 Docker를 사용할 때 권한 문제나 스토리지 관련 이슈가 발생할 수 있어요. 특히 subuid, subgid 매핑이 제대로 안 되어있을 때 그렇습니다. 제가 겪었던 문제 중 하나였죠.

    만약 Docker 컨테이너 실행 시 권한 관련 오류가 발생한다면, Proxmox 호스트에서 다음 설정을 확인해보세요.

    1. Proxmox 호스트에 SSH로 접속합니다.
    2. /etc/subuid와 /etc/subgid 파일에 LXC 컨테이너의 UID/GID 범위가 매핑되어 있는지 확인하세요. 보통 LXC 생성 시 자동으로 추가돼요.
    3. 만약 특정 Docker 컨테이너가 호스트의 특정 디렉토리에 접근해야 하는데 권한 문제가 있다면, /etc/pve/lxc/<VMID>.conf 파일에 다음 라인을 추가하세요. (예시: 100은 컨테이너 ID)
      echo 'lxc.apparmor.profile: unconfined'
      echo 'lxc.mount.auto: cgroup:rw'
      

      이 설정은 컨테이너의 보안 프로파일을 완화하고 cgroup 마운트를 허용하여 Docker가 더 자유롭게 동작하도록 해요. 하지만 보안상 약간 취약해질 수 있으니 신중하게 사용하세요.

    4. 또 다른 방법으로는 Docker root directory를 변경하여 /var/lib/docker 대신 /mnt/data/docker처럼 별도의 마운트 경로를 사용하는 거예요. /etc/docker/daemon.json 파일을 생성(또는 편집)하고 다음 내용을 추가하세요.
      {
        "data-root": "/mnt/data/docker",
        "storage-driver": "overlay2"
      }
      

      그 후 Docker 서비스를 재시작해요: sudo systemctl restart docker. 이 방법은 특히 ZFS 같은 파일 시스템을 사용하는 Proxmox 호스트에서 LXC에 더 안정적인 Docker 스토리지 환경을 제공할 때 정말 유용해요.

    성능 최적화를 위한 팁과 트러블슈팅 ⚠️

    LXC 컨테이너에 Docker를 올리면 기본적으로 성능이 좋지만, 몇 가지 설정을 통해 더 극대화할 수 있어요.

    1. 컨테이너 자원 관리 (CPU, RAM)

    • CPU Core (CPU 코어) 할당: Proxmox VE 웹 UI에서 LXC 컨테이너의 CPU 코어를 필요한 만큼 할당하세요. 너무 많이 할당하면 다른 컨테이너나 VM의 자원을 잠식할 수 있으니, 실제 워크로드에 맞춰 조절하는 게 중요해요.
    • CPU Units (CPU 유닛) 조절: 여러 컨테이너가 CPU를 경쟁할 때, 특정 컨테이너에 더 높은 우선순위를 주고 싶다면 CPU units 값을 높게 설정해요. 기본값 1024가 공평한 분배라면, 2048은 두 배의 우선순위를 갖는 식이죠.
    • Memory (메모리) 및 Swap (스왑): Docker 컨테이너가 사용할 메모리를 충분히 할당해주세요. 스왑은 꼭 필요한 경우가 아니라면 비활성화하거나 최소한으로 설정하는 게 성능에 좋아요. 스왑이 너무 많이 발생하면 I/O 병목 현상이 생기거든요.

    2. I/O 성능 개선 (Storage)

    • SSD 사용: Proxmox VE 호스트의 OS 및 LXC 컨테이너 스토리지는 반드시 SSD (Solid State Drive)를 사용하는 게 좋아요. 특히 Docker는 이미지 다운로드, 레이어 관리 등으로 I/O 작업이 빈번하니 SSD의 이점이 극대화돼요. NVMe SSD라면 더할 나위 없겠죠.
    • LVM-Thin or ZFS: Proxmox에서 LXC 컨테이너를 생성할 때 LVM-Thin이나 ZFS 같은 스토리지를 사용하는 게 좋아요. 스냅샷 기능도 유용하고, 성능도 괜찮거든요. 개인적으로는 ZFS를 선호해요.
    • Bind Mount (바인드 마운트) 활용: Docker 컨테이너가 영구 데이터를 저장해야 한다면, LXC 컨테이너 내부의 디렉토리를 호스트의 물리 디스크에 바인드 마운트하는 게 좋아요. 예를 들어, 호스트에 별도의 데이터 디스크를 마운트하고, 이를 LXC 컨테이너에 다시 마운트한 뒤 Docker 볼륨으로 사용하는 방식이죠.
    \n# Proxmox 호스트에서 LXC 컨테이너 설정 파일 편집
    # /etc/pve/lxc/VMID.conf 에 다음 라인 추가
    lxc.mount.entry: /path/on/host /path/on/lxc none bind,create=dir 0 0
    

    3. 네트워크 설정 최적화

    • Bridge (브리지) 모드: LXC 컨테이너의 네트워크는 기본적으로 브리지 모드를 사용하는 게 가장 일반적이고 성능 저하가 적어요. 호스트의 브리지(vmbr0)에 직접 연결돼서 별도의 NAT 변환 없이 호스트와 동일한 네트워크에서 통신하거든요.
    • MacVLAN (맥브이랜): 특정 Docker 컨테이너에 별도의 물리적 MAC 주소를 할당하여 호스트 네트워크와 동등하게 통신하고 싶다면 MacVLAN을 고려할 수 있어요. 하지만 설정이 조금 복잡하고, 네트워크 장비가 이를 지원해야 해요. 저도 이 부분은 아직 삽질 중이지만, 특정 시나리오에서는 정말 유용할 수 있습니다.

    성능 검증 및 결과 확인 ✅

    모든 설정이 끝났다면, 이제 실제로 Docker 컨테이너를 띄워서 성능을 확인해볼 차례예요. docker stats 명령어로 각 Docker 컨테이너의 자원 사용량을 실시간으로 모니터링할 수 있거든요.

    \n# LXC 컨테이너 내부에서 실행
    docker stats
    

    이 명령어를 실행하면 CPU 사용량, 메모리 사용량, 네트워크 I/O, 블록 I/O 등을 한눈에 볼 수 있어요. 이 외에도 htop이나 glances 같은 도구를 LXC 컨테이너에 설치하여 전체적인 자원 사용량을 확인하는 것도 정말 좋습니다.

    제가 직접 여러 Docker 컨테이너(Nginx, MariaDB, Portainer 등)를 LXC에 올려서 테스트해보니, 확실히 VM에 올렸을 때보다 CPU와 메모리 사용량이 눈에 띄게 줄어드는 거더라고요. 특히 아이들(idle) 상태에서는 거의 자원을 사용하지 않아서 매우 만족스러웠어요. 🎉

    Proxmox LXC 내 Docker 컨테이너의 CPU, 메모리, I/O 성능 모니터링 대시보드

    마무리하며: 13년차 엔지니어의 한마디

    오늘은 Proxmox VE 9.1 환경에서 LXC 컨테이너와 Docker를 연동하고 성능을 최적화하는 방법을 심도 있게 다뤄봤어요. 제가 직접 겪었던 Nesting, FUSE 같은 핵심 삽질 포인트를 해결하는 방법을 공유하면서, 독자분들의 시간을 절약해드리고 싶었거든요. 😅

    이 조합은 가벼운 홈랩부터 소규모 프로덕션 환경까지, 자원을 효율적으로 사용하면서도 높은 유연성을 제공하는 정말 강력한 솔루션이에요. Proxmox의 안정적인 가상화 기반 위에 Docker의 강력한 애플리케이션 관리 능력을 더하는 거죠.

    물론 모든 기술이 그렇듯, 완벽한 솔루션은 없어요. 하지만 LXC와 Docker의 장점을 잘 이해하고 활용한다면, 여러분의 서버실도 더욱 스마트하고 효율적으로 운영될 수 있을 거예요. 다음에는 이 LXC + Docker 환경 위에 Docker Compose (도커 컴포즈)를 활용해서 여러 서비스를 한 번에 배포하는 방법을 다뤄볼까 하는데, 기대해주세요!

    궁금한 점이나 추가로 알고 싶은 내용이 있다면 언제든지 댓글로 남겨주세요. 여러분의 서버실 운영에 조금이나마 도움이 되었으면 좋겠고, 다음 글에서 또 만나요! 👋

    Proxmox LXC와 가상 머신(VM)에서 Docker를 실행할 때의 성능 및 자원 활용 비교 인포그래픽

  • [NAS] TrueNAS ZFS 스냅샷과 복제: 데이터 보호 전략

    [NAS] TrueNAS ZFS 스냅샷과 복제: 데이터 보호 전략

    데이터 유실의 악몽, 여러분은 준비되셨나요?

    안녕하세요, 13년차 인프라 엔지니어입니다. 서버실 관리하며 수많은 데이터 유실 사고를 직간접적으로 경험했어요. 실수로 파일을 지우거나, 하드디스크가 갑자기 고장 나거나, 심지어 랜섬웨어 공격까지… 상상만 해도 끔찍하죠?

    그래서 홈랩에서 TrueNAS를 운영하면서 데이터 보호에 정말 신경을 많이 씁니다. RAID만 믿고 있으면 안 된다는 걸 너무 잘 알거든요. RAID는 디스크 고장으로부터는 지켜주지만, 실수나 논리적 오류(Logical Error), 랜섬웨어 같은 위협에는 무력합니다. 이럴 때 필요한 게 바로 ZFS의 스냅샷(Snapshot)과 복제(Replication) 기능입니다. 제가 직접 써보니 이거 진짜 물건이더라고요!

    오늘은 여러분의 소중한 데이터를 지켜줄 TrueNAS ZFS 스냅샷과 복제 전략을 제 경험을 바탕으로 자세히 이야기해보겠습니다. 삽질하며 배운 노하우도 아낌없이 공유해 드릴게요!

    이 다이어그램은 TrueNAS ZFS 스냅샷과 복제 아키텍처의 개요를 보여줍니다. 로컬 TrueNAS에서 생성된 ZFS 스냅샷이 원격 TrueNAS로 복제되는 과정을 시각화했습니다.

    ZFS 스냅샷과 복제, 이게 뭔가요?

    처음엔 ZFS라는 이름도 생소하고, 스냅샷이니 복제니 하는 용어들이 헷갈리더라고요. 제가 직접 경험한 것을 바탕으로 쉽게 설명해 드릴게요.

    1. ZFS 스냅샷(Snapshot): 시간 여행 기능

    ZFS 스냅샷은 특정 시점의 파일 시스템 상태를 읽기 전용(Read-only)으로 저장하는 기능입니다. 비유하자면, 게임을 하다가 중요한 순간에 ‘저장’ 버튼을 누르는 것과 같아요. 언제든지 그 저장 지점으로 돌아갈 수 있거든요. 근데 일반적인 백업과는 좀 다릅니다. 변경된 데이터만 저장하는 CoW(Copy-on-Write) 방식을 사용해서 용량 효율이 정말 좋고, 생성도 눈 깜짝할 사이에 이루어져요. 제가 써보니 수백 GB짜리 데이터셋도 TrueNAS ZFS 스냅샷 뜨는데 1초도 안 걸리더라고요. 신기했습니다!

    • 즉시 생성 & 낮은 오버헤드: 순식간에 만들어지고 시스템 성능에 거의 영향을 주지 않습니다.
    • 공간 효율성: 변경된 블록만 저장하므로 추가 용량이 최소화됩니다.
    • 데이터 복구 용이성: 특정 시점으로 쉽게 롤백(Rollback)하거나, 스냅샷에서 파일을 직접 복사할 수 있습니다.
    • 불변성(Immutability): 생성된 스냅샷은 변경할 수 없어서 랜섬웨어 공격으로부터 안전합니다.

    2. ZFS 복제(Replication): 원격지 백업의 완성

    ZFS 복제는 이렇게 만들어진 스냅샷을 다른 ZFS 시스템(다른 TrueNAS 서버나 원격 서버)으로 전송(Transfer)하는 기능입니다. 게임 저장 파일을 친구 집 컴퓨터에 똑같이 복사해두는 것과 비슷해요. 내 컴퓨터가 고장 나도 친구 컴퓨터의 저장 파일로 다시 시작할 수 있는 거죠.

    TrueNAS 복제 기능은 ZFS의 델타(Delta) 전송을 활용합니다. 첫 번째 복제 때는 전체 스냅샷을 보내지만, 그 이후부턴 이전 스냅샷과 현재 스냅샷 간의 변경된 부분(Incremental changes)만 전송합니다. 실제로 홈랩에서 써보니 처음 한 번만 오래 걸리고 그 다음부턴 정말 빠르더라고요. 네트워크 트래픽도 확 줄고요. 이게 바로 재해 복구(Disaster Recovery, DR) 전략의 핵심입니다.

    • 원격지 백업: 로컬 시스템에 문제가 생겨도 원격지에 안전한 사본이 있습니다.
    • 대역폭 효율성: 증분(Incremental) 복제를 통해 네트워크 사용량을 최소화합니다.
    • 자동화 가능: TrueNAS UI에서 스케줄링하여 자동으로 복제를 수행할 수 있습니다.
    • 완전한 복구: 원격지에서 전체 데이터셋을 복구하거나, 특정 스냅샷으로 롤백하여 복구할 수 있습니다.

    TrueNAS ZFS 스냅샷 & 복제, 제가 직접 해보니

    이제 직접 TrueNAS에서 스냅샷과 복제 설정을 해볼 시간입니다. 처음엔 메뉴가 좀 복잡해 보여서 삽질 좀 했지만, 한 번 해보면 어렵지 않아요. 저를 따라오시면 금방 하실 수 있을 겁니다. 제가 쓰는 TrueNAS SCALE 기준으로 설명해 드릴게요.

    1. 자동 스냅샷 태스크 설정하기

    가장 먼저 할 일은 원하는 데이터셋에 주기적으로 ZFS 스냅샷을 찍도록 설정하는 겁니다. 저는 매일 밤 12시에 스냅샷을 찍고, 최근 7일치 스냅샷을 보관하도록 설정했어요.

    1. TrueNAS 웹 UI에 접속합니다.
    2. 왼쪽 메뉴에서 ‘Data Protection’ > ‘Snapshots’로 이동합니다.
    3. 우측 상단의 ‘ADD’ 버튼을 클릭합니다.
    4. 설정 화면에서 다음 항목들을 채워줍니다.
      • Dataset: 스냅샷을 찍을 데이터셋을 선택합니다. (예: Pool명/데이터셋명)
      • Recursive: 하위 데이터셋까지 스냅샷을 찍을지 여부입니다. 저는 보통 체크합니다.
      • Naming Schema: 스냅샷 이름 규칙입니다. 기본값 auto-%Y-%m-%d_%H-%M을 사용해도 충분합니다.
      • Schedule: 스냅샷을 찍을 주기입니다. 저는 ‘Daily’로 설정하고 ‘Time’을 ’00:00’으로 지정했습니다.
      • Keep for: 스냅샷을 얼마나 보관할지 설정합니다. 저는 ‘7 Days’로 설정했습니다.
    5. ‘SAVE’ 버튼을 클릭하여 저장합니다.

    ✅ 이제 지정된 시간에 자동으로 ZFS 스냅샷이 생성되고 오래된 스냅샷은 자동으로 삭제될 겁니다. ‘Snapshots’ 메뉴에서 생성된 스냅샷 목록을 확인할 수 있습니다.

    TrueNAS 웹 UI에서 ZFS 자동 스냅샷 태스크를 설정하는 화면입니다. 데이터셋, 스케줄, 보관 기간 등을 지정하여 원하는 스냅샷 정책을 만들 수 있습니다.

    2. ZFS 복제 태스크 설정하기 (원격 백업)

    스냅샷만으로는 부족합니다. 메인 TrueNAS 서버 자체가 망가지면 스냅샷도 소용없거든요. 그래서 저는 집의 다른 미니 PC에 TrueNAS를 설치해서 원격 복제 타겟으로 사용하고 있습니다. 물론 클라우드 스토리지(S3 등)로 복제하는 방법도 있지만, 저는 로컬 네트워크에서 빠르고 안정적인 TrueNAS 복제를 선호해서 이렇게 구성했어요.

    복제를 위해서는 먼저 원격 TrueNAS 서버에 SSH 접속을 위한 SSH Key를 설정해야 합니다. 보안상 ID/PW 방식보다는 SSH Key 방식을 추천합니다. 이 부분은 한 번 설정해두면 정말 편합니다.

    1. SSH Key 생성:
      • 복제를 시작할 TrueNAS(소스)에서 왼쪽 메뉴 ‘System Settings’ > ‘SSH Keypairs’로 이동합니다.
      • ‘ADD’를 클릭하고 키 이름을 지정한 후 ‘GENERATE KEYPAIR’를 선택하여 새 키를 생성합니다.
      • 생성된 Public Key 내용을 복사해둡니다.
    2. 원격 TrueNAS에 Public Key 등록:
      • 원격 TrueNAS(타겟) 웹 UI에 접속합니다.
      • ‘System Settings’ > ‘Users’로 이동하여 복제에 사용할 유저(예: root)를 선택하고 ‘EDIT’합니다.
      • ‘SSH Public Keys’ 필드에 아까 복사해둔 Public Key 내용을 붙여넣고 저장합니다.
      • 💡 팁: SSH 서비스가 활성화되어 있는지 확인하세요. ‘System Settings’ > ‘Services’에서 SSH 서비스를 ‘Running’ 상태로 변경하고 ‘Start Automatically’를 체크합니다.
    3. 복제 태스크 설정:
      • 다시 소스 TrueNAS로 돌아와서 ‘Data Protection’ > ‘Replication Tasks’로 이동합니다.
      • 우측 상단의 ‘ADD’ 버튼을 클릭합니다.
      • 설정 화면에서 다음 항목들을 채워줍니다.
        • Source: 복제할 데이터셋을 선택합니다. (예: Pool명/데이터셋명)
        • Destination: 원격 TrueNAS의 IP 주소와 SSH 포트(기본값 22)를 입력합니다. (예: ssh://192.168.1.100)
        • SSH Key: 아까 생성한 SSH Keypair를 선택합니다.
        • Remote Hostname: 원격 TrueNAS의 호스트명이나 IP를 다시 확인합니다.
        • Remote ZFS Filesystem: 원격 TrueNAS에서 ZFS 스냅샷이 저장될 데이터셋 경로를 지정합니다. (예: remote_pool/replicated_data)
        • Naming Schema: 원격지에 생성될 스냅샷 이름 규칙입니다. 소스와 동일하게 설정하는 것이 좋습니다.
        • Schedule: 복제 주기를 설정합니다. 저는 스냅샷 주기와 비슷하게 ‘Daily’로 설정했습니다.
        • Liveness Check: 원격지가 살아있는지 확인할 주기입니다.
        • Enable Replication: 체크박스를 활성화합니다.
      • ‘SAVE’ 버튼을 클릭하여 저장합니다.

    🎉 드디어 복제 태스크 설정이 완료되었습니다! 첫 복제는 소스 데이터셋의 크기에 따라 시간이 좀 걸릴 수 있습니다. ‘Replication Tasks’ 목록에서 진행 상황을 확인할 수 있어요. 완료되면 원격 TrueNAS에서도 복제된 스냅샷과 데이터셋을 확인할 수 있을 겁니다.

    ⚠️ 삽질 경험 & 주의사항

    제가 TrueNAS ZFS 복제를 세팅하면서 겪었던 몇 가지 삽질과 주의사항을 공유해 드릴게요. 여러분은 저처럼 고생하지 마시라고요!

    • SSH Key 설정 오류: 가장 많이 겪는 문제입니다. Public Key를 잘못 복사하거나, 원격 TrueNAS의 유저에게 Key가 제대로 등록되지 않았을 때 복제가 실패합니다. ‘System Settings’ > ‘Users’에서 해당 유저의 SSH Public Keys 필드에 제대로 들어갔는지 꼭 확인하세요. 줄 바꿈이나 공백 하나에도 민감하니까요.
    • 네트워크 문제: 소스 TrueNAS와 타겟 TrueNAS 간의 네트워크 연결이 불안정하거나 방화벽(Firewall)이 SSH 포트(기본 22)를 막고 있는 경우가 있습니다. ping 명령어로 연결 확인하고, 방화벽 설정을 꼭 확인해 주세요. 예를 들어, 소스 TrueNAS에서 원격 TrueNAS로 Ping 테스트를 해볼 수 있습니다.
      
      ping 192.168.1.100
      

      혹은 SSH 서비스가 제대로 실행 중인지 원격 TrueNAS에서 확인하는 것도 중요합니다.

    • 데이터셋 경로 오류: 원격 TrueNAS의 ‘Remote ZFS Filesystem’ 경로를 잘못 지정하면 복제가 실패합니다. 반드시 원격 TrueNAS에 해당 풀(Pool)이 존재해야 하고, 원하는 데이터셋 경로가 유효한지 확인해야 합니다. 저는 실수로 존재하지 않는 데이터셋에 복제하려고 해서 한참 헤맸던 기억이 있거든요.
    • 스냅샷 보존 정책: ZFS 스냅샷과 복제 태스크의 보존 정책(Keep for)을 잘 설정해야 합니다. 너무 짧게 설정하면 중요한 스냅샷이 삭제될 수 있고, 너무 길게 설정하면 스토리지 공간을 너무 많이 차지하게 됩니다. 데이터의 중요도와 스토리지 용량을 고려해서 적절한 기간을 설정하는 것이 중요합니다.
    • 초기 복제 시간: 첫 복제는 전체 데이터를 전송하므로 시간이 오래 걸릴 수 있습니다. 데이터 양이 많다면 네트워크 대역폭과 시간을 충분히 확보하고 시작하는 것이 좋습니다. 저는 1TB 넘는 데이터를 첫 TrueNAS 복제할 때 거의 하루 종일 걸리더라고요.

    이런 문제들 때문에 저도 초기에는 꽤나 애를 먹었습니다. 에러 메시지를 꼼꼼히 읽어보고, TrueNAS 포럼이나 커뮤니티를 검색해보는 것이 큰 도움이 됩니다. 결국 해결하고 나면 그렇게 뿌듯할 수가 없어요! 🎉

    ✅ 복제 결과 확인 및 데이터 복구 테스트

    설정이 끝났다고 마냥 손 놓고 있으면 안 되겠죠? 실제로 잘 작동하는지, 그리고 만약의 사태가 발생했을 때 제대로 복구할 수 있는지 검증(Verification)하는 과정이 정말 중요합니다. 제가 직접 해본 검증 방법들을 알려드릴게요.

    1. 원격 TrueNAS에서 스냅샷 확인

    가장 기본적인 확인 절차입니다. 원격 TrueNAS 웹 UI에 접속해서 ‘Data Protection’ > ‘Snapshots’ 메뉴로 이동해 보세요. 소스 TrueNAS에서 복제된 스냅샷들이 보인다면 일단 복제는 성공적으로 진행되고 있다는 뜻입니다.

    또한, ‘Storage’ > ‘Pools’ 메뉴에서 해당 데이터셋을 클릭하고 ‘Snapshots’ 탭으로 이동하면 해당 데이터셋의 스냅샷 목록을 볼 수 있습니다. 제가 확인해보니 소스에서 찍힌 ZFS 스냅샷 이름과 동일하게 잘 복제되어 있더라고요.

    2. 파일 복구 시뮬레이션

    실제로 데이터를 잃어버렸을 때 어떻게 복구하는지 미리 경험해보는 것이 좋습니다. 저는 테스트용 파일을 만들고 삭제한 다음, 스냅샷으로 복구하는 시뮬레이션을 해봤어요.

    1. 원격 TrueNAS의 복제된 데이터셋에 접속합니다. (SMB/NFS 등으로 마운트)
    2. 테스트용 파일을 몇 개 생성합니다.
    3. 강제로 이 파일들을 삭제하거나 내용을 변경합니다.
    4. ‘Storage’ > ‘Pools’에서 해당 데이터셋을 선택하고 ‘Snapshots’ 탭으로 이동합니다.
    5. 복구하고 싶은 시점의 ZFS 스냅샷을 선택하고 ‘Rollback’ 버튼을 클릭합니다. ⚠️ 롤백은 현재 상태를 스냅샷 시점으로 되돌리는 것이므로, 신중하게 진행해야 합니다. 보통은 스냅샷을 Clone(클론)하여 새로운 데이터셋으로 만든 후, 필요한 파일만 복사하는 방식을 더 많이 사용합니다.
    6. 아니면 스냅샷 내부로 들어가 필요한 파일만 복사해 올 수도 있습니다. 스냅샷은 읽기 전용으로 마운트될 수 있거든요.

    제가 해보니 롤백은 너무 강력한 기능이라 신중해야 하고, 보통은 스냅샷을 마운트해서 특정 파일만 가져오는 게 훨씬 안전하고 편리했습니다. 💡

    TrueNAS 웹 UI에서 복제된 ZFS 데이터셋의 스냅샷 목록을 확인하고, 필요시 롤백 또는 클론 기능을 사용하는 화면입니다.

    마무리하며: 데이터 보호는 선택이 아닌 필수

    TrueNAS의 ZFS 스냅샷과 복제 기능은 정말 강력한 데이터 보호 및 재해 복구 전략을 구축할 수 있게 해줍니다. 저도 처음엔 설정이 좀 복잡하게 느껴졌지만, 한 번 제대로 구축해두니 마음이 정말 편안하더라고요. 홈랩의 소중한 사진, 영상, 문서 파일들이 안전하게 보호되고 있다는 생각에 뿌듯했습니다.

    데이터 유실은 언제든 일어날 수 있는 일입니다. 중요한 건 문제가 생겼을 때 얼마나 빨리, 그리고 얼마나 완벽하게 복구할 수 있느냐입니다. ZFS 스냅샷은 로컬에서의 빠른 복구를, ZFS 복제는 원격지 재해 발생 시의 데이터 복구를 보장해 줍니다.

    여러분도 오늘 제가 공유해드린 내용을 바탕으로 TrueNAS ZFS 스냅샷과 복제 기능을 꼭 활용해 보셨으면 좋겠습니다. 혹시 설정 중에 궁금한 점이나 막히는 부분이 있다면 언제든지 댓글로 남겨주세요. 제가 아는 선에서 최대한 도와드리겠습니다!

    다음 글에서는 이렇게 복제된 데이터를 활용하여 다른 용도로 쓰는 방법이나, 클라우드 스토리지로의 복제 등 좀 더 심화된 내용들을 다뤄볼까 합니다. 기대해 주세요!

    TrueNAS ZFS 스냅샷과 복제 기능이 제공하는 핵심 장점들을 요약한 인포그래픽입니다.

  • [Proxmox] HA 클러스터 구축 및 트러블슈팅: 고가용성 완벽 가이드

    서버 한 대가 죽어도 서비스는 살아있어야 한다

    새벽 2시에 전화 받아본 적 있으신가요? “서비스가 안 된다”는 그 전화. 저는 인프라 엔지니어 생활 초반에 그 경험을 꽤 많이 했습니다. 물리 서버 한 대가 죽으면서 거기서 돌아가던 VM(가상 머신)들이 전부 같이 꺼져버리는 그 상황. 정말 식은땀 나거든요.

    그때부터 고민하기 시작했습니다. “어떻게 하면 서버 한 대가 죽어도 서비스가 자동으로 다른 서버에서 살아날 수 있을까?” 그 답이 바로 Proxmox HA(High Availability, 고가용성) 클러스터입니다.

    오늘은 제가 홈랩에서 직접 구축하면서 겪은 삽질들과 함께, Proxmox HA 클러스터를 처음부터 끝까지 완벽하게 셋업하는 방법을 공유해 드리려고 합니다. 트러블슈팅 경험도 솔직하게 담았으니 끝까지 읽어보시면 분명 도움이 될 거예요.

    ▲ Proxmox HA 클러스터의 전체 구성 아키텍처 — 3노드 구성과 쿼럼(Quorum), 공유 스토리지의 관계를 보여줍니다.

    Proxmox HA가 뭔지 먼저 제대로 알고 가자

    HA(고가용성)의 핵심 개념

    쉽게 말해서, HA는 “어떤 노드(서버)가 죽어도 거기 있던 VM이나 컨테이너가 다른 노드에서 자동으로 다시 켜지는 것”입니다. 사람이 새벽에 일어나서 수동으로 VM을 다른 서버로 옮기지 않아도 되는 거죠.

    Proxmox VE에서 HA를 구현하려면 크게 세 가지가 필요합니다.

    • 클러스터(Cluster): 여러 Proxmox 노드를 하나로 묶는 것. 최소 3개 노드 권장 (쿼럼 때문에 — 아래에서 설명)
    • 쿼럼(Quorum): 클러스터 내 다수결 투표 시스템. 과반수 노드가 살아있어야 클러스터가 정상 동작
    • 공유 스토리지(Shared Storage): 모든 노드가 같은 VM 디스크에 접근할 수 있어야 함 (Ceph, NFS, iSCSI 등)

    왜 노드가 최소 3개여야 할까?

    이게 처음엔 저도 이해가 안 됐었는데요. 노드 2개로 구성하면 한 노드가 죽었을 때 남은 노드가 “내가 살아있고 상대방이 죽은 건지, 아니면 네트워크가 끊겨서 서로 연결이 안 되는 건지” 판단을 못 합니다. 이걸 스플릿 브레인(Split-Brain) 상황이라고 하는데, 이 경우 양쪽 노드가 서로 자기가 주인이라고 착각해서 데이터 충돌이 생길 수 있거든요.

    3개 노드면 2:1로 다수결이 가능하니까 “이 노드가 죽었다”는 판단을 명확하게 할 수 있습니다. 2노드로 구성할 수는 있지만 그러면 QDevice(외부 쿼럼 장치)가 추가로 필요해요.

    구성 최소 생존 노드 장점 단점
    2노드 2개 (QDevice 필요) 비용 절감 QDevice 추가 필요, 관리 복잡
    3노드 2개 쿼럼 자체 해결, 안정적 서버 3대 필요
    5노드 3개 최고 가용성 비용 높음

    Proxmox HA 클러스터 구축 — 단계별 실전 가이드

    사전 준비 사항

    시작하기 전에 체크해야 할 것들이 있습니다. 저도 처음에 이 부분을 건너뛰었다가 나중에 다 뒤집어엎었던 기억이 있어서요. 꼭 확인하세요.

    • ✅ Proxmox VE가 설치된 서버 최소 3대
    • ✅ 모든 노드에서 시간 동기화(NTP) 설정 완료
    • ✅ 모든 노드 간 네트워크 통신 가능 (핑 테스트)
    • ✅ 각 노드의 호스트명이 고유하고 DNS/hosts 파일에 등록됨
    • ✅ 공유 스토리지 준비 (Ceph, NFS, iSCSI 중 선택)

    Step 1: /etc/hosts 파일 설정

    모든 노드에서 서로를 알아볼 수 있도록 hosts 파일을 설정합니다. DNS가 없는 환경이라면 특히 중요해요.

    # 모든 노드에서 동일하게 설정 (/etc/hosts)
    192.168.1.101  pve-node1  pve-node1.local
    192.168.1.102  pve-node2  pve-node2.local
    192.168.1.103  pve-node3  pve-node3.local

    Step 2: 첫 번째 노드에서 클러스터 생성

    pve-node1에서 클러스터를 만들어 줍니다. 클러스터 이름은 나중에 바꾸기 어려우니 신중하게 정하세요.

    # pve-node1에서 실행
    pvecm create my-ha-cluster
    
    # 생성 확인
    pvecm status

    정상적으로 생성되면 이런 출력이 나옵니다.

    Cluster information
    ------------------
    Name:             my-ha-cluster
    Config Version:   1
    Transport:        knet
    Secauth:          on
    
    Quorum information
    ------------------
    Date:             ...
    Quorum provider:  corosync_votequorum
    Nodes:            1
    Node votes:       1
    Expected votes:   1
    Total votes:      1
    Quorum:           1
    Flags:            Quorate

    Step 3: 나머지 노드를 클러스터에 참가시키기

    pve-node2와 pve-node3에서 각각 실행합니다. 여기서 중요한 점은 기존 노드의 IP를 정확히 입력해야 한다는 거예요.

    # pve-node2에서 실행
    pvecm add 192.168.1.101
    
    # 비밀번호 입력 후 참가 완료
    # pve-node3에서도 동일하게 실행
    pvecm add 192.168.1.101

    참가 후 상태 확인:

    pvecm status
    
    # 출력 예시
    Quorum information
    ------------------
    Nodes:            3
    Expected votes:   3
    Total votes:      3
    Quorum:           2  # 이 숫자가 핵심! 과반수

    Step 4: 공유 스토리지 연결 (NFS 예시)

    Proxmox HA가 동작하려면 VM 디스크가 공유 스토리지에 있어야 합니다. 여기서는 NFS를 예시로 들겠습니다. Ceph를 사용하신다면 별도 구성이 필요한데, 이건 나중에 별도 글로 다룰 예정이에요.

    # 모든 노드에서 NFS 마운트 확인
    showmount -e 192.168.1.200
    
    # Proxmox Web UI에서도 추가 가능하지만 CLI로 하면:
    # /etc/pve/storage.cfg에 추가됨 (클러스터 전체 공유)
    pvesm add nfs shared-storage \
      --server 192.168.1.200 \
      --export /mnt/nfs/proxmox \
      --content images,iso,backup

    ▲ Proxmox Web UI의 HA 관리 화면 — HA 그룹 생성과 리소스 등록 과정을 보여줍니다.

    Step 5: HA 그룹(HA Group) 생성

    HA 그룹은 “이 VM들은 이 노드들에서만 돌아야 해”라는 규칙을 정의합니다. Web UI에서도 가능하지만 CLI가 더 직관적이더라고요.

    # HA 그룹 생성
    ha-manager groupadd production \
      --nodes pve-node1:2,pve-node2:1,pve-node3:1 \
      --restricted 0 \
      --nofailback 0
    
    # 그룹 확인
    ha-manager groupconfig production

    여기서 숫자(2, 1, 1)는 우선순위(Priority)입니다. 숫자가 높을수록 해당 노드를 선호해요. 장애 복구 후 원래 노드로 돌아오는 Failback 기능도 설정 가능합니다.

    Step 6: VM을 HA 리소스로 등록

    이제 특정 VM을 HA 관리 대상으로 등록합니다. VM ID가 100번이라고 가정할게요.

    # VM 100번을 HA 리소스로 추가
    ha-manager add vm:100 \
      --group production \
      --max_restart 3 \
      --max_relocate 3
    
    # 등록된 리소스 확인
    ha-manager status
    # 정상 등록 시 출력 예시
    quorum OK
    master pve-node1 (active, Wed Nov  1 10:00:00 2023)
    
    Active HA resources:
    
      vm:100
               Status: started
               Node: pve-node1
               Managed: 1

    🎉 여기까지 오면 기본 HA 설정은 완료입니다! 이제 테스트를 해볼 차례예요.

    HA 동작 테스트 — 실제로 노드를 죽여보자

    페일오버(Failover) 테스트

    이 부분이 진짜 재미있습니다. 직접 노드를 강제로 다운시켜서 VM이 다른 노드로 이동하는지 확인해 보는 거거든요. 물론 프로덕션에서는 절대 이렇게 하시면 안 되고, 테스트 환경에서만요 ㅎㅎ

    # 방법 1: 노드를 maintenance 모드로 전환 (안전한 방법)
    ha-manager crm-command node-maintenance enable
    
    # 방법 2: pve-node1에서 corosync 서비스 강제 중단 (더 극단적인 테스트)
    systemctl stop corosync
    
    # 다른 노드에서 HA 상태 확인
    ha-manager status
    watch -n 2 ha-manager status  # 2초마다 갱신하며 모니터링

    정상적으로 동작한다면 약 1~2분 내에 VM이 다른 노드에서 기동되는 것을 확인할 수 있습니다. 처음에 드디어 이게 됐을 때 저 혼자 “오오…” 했던 기억이 나네요.

    ⚠️ 트러블슈팅: 제가 직접 겪은 문제들

    문제 1: 클러스터 참가 후 쿼럼이 잡히지 않음

    증상: pvecm status에서 Quorate: No가 표시되고, Web UI에서 “cluster not ready – no quorum” 오류가 나오는 경우입니다.

    원인 대부분은 시간 동기화 문제이거나 방화벽이 포트를 막고 있는 경우였습니다.

    # 시간 동기화 확인
    timedatectl status
    
    # NTP 서비스 재시작
    systemctl restart chrony
    # 또는
    systemctl restart systemd-timesyncd
    
    # Corosync가 사용하는 포트 확인 (UDP 5404, 5405)
    ss -lunp | grep corosync
    
    # 방화벽 규칙 확인 (Proxmox는 기본적으로 iptables 사용)
    iptables -L -n | grep 5404

    문제 2: HA 리소스가 계속 “error” 상태

    이거 저도 한참 고생했습니다. VM이 HA로 등록됐는데 상태가 계속 error로 나오는 경우예요.

    # HA Manager 로그 확인
    journalctl -u pve-ha-lrm -f
    journalctl -u pve-ha-crm -f
    
    # VM 상태 강제 리셋
    ha-manager set vm:100 --state started
    
    # HA 서비스 재시작
    systemctl restart pve-ha-crm
    systemctl restart pve-ha-lrm

    제 경우엔 VM 디스크가 공유 스토리지가 아닌 로컬 스토리지에 있어서 발생한 문제였습니다. HA는 반드시 공유 스토리지에 VM 디스크가 있어야 한다는 걸 다시 한번 강조하고 싶네요.

    문제 3: Fencing(펜싱)이 동작하지 않아 VM 이동이 안 됨

    Fencing이란 죽은 노드가 정말 죽었는지 확인하고, 확실하게 “격리”하는 메커니즘입니다. 이게 없으면 Proxmox HA는 안전을 위해 VM을 다른 노드로 이동시키지 않습니다.

    # Fencing 설정 확인
    cat /etc/pve/datacenter.cfg
    
    # 소프트웨어 watchdog 기반 Fencing 설정 예시
    # datacenter.cfg에 추가
    fencing: watchdog-dbus
    
    # watchdog 상태 확인
    ls /dev/watchdog*
    
    # pve-ha-crm이 watchdog을 사용하는지 확인
    journalctl -u pve-ha-crm | grep watchdog

    💡 팁: 홈랩 환경에서 IPMI 같은 하드웨어 펜싱 장치가 없다면, Proxmox의 소프트웨어 watchdog을 사용할 수 있습니다. 다만 프로덕션 환경에서는 하드웨어 펜싱(IPMI, iLO 등)을 강력히 권장합니다.

    문제 4: 클러스터 노드 중 하나가 “UNKNOWN” 상태

    # 클러스터 노드 상태 확인
    pvecm nodes
    
    # Corosync 링크 상태 확인
    corosync-cfgtool -s
    
    # Corosync 설정 파일 확인
    cat /etc/corosync/corosync.conf
    
    # 문제 노드에서 corosync 재시작
    systemctl restart corosync pve-cluster

    ▲ HA 페일오버 테스트 결과 — 노드 장애 발생 후 VM이 다른 노드로 자동 이동된 것을 확인하는 화면입니다.

    HA 클러스터 운영 시 꼭 알아야 할 설정들

    HA 정책 세부 조정

    # VM의 HA 설정 상세 조회
    ha-manager config vm:100
    
    # max_restart: 같은 노드에서 재시작 시도 횟수
    # max_relocate: 다른 노드로 이동 시도 횟수
    ha-manager set vm:100 --max_restart 3 --max_relocate 2
    
    # 특정 VM을 HA에서 제거 (삭제가 아닌 관리 해제)
    ha-manager remove vm:100

    마이그레이션(Migration) 설정

    HA 환경에서 노드 간 VM 이동 시 네트워크 대역폭 제한도 설정할 수 있습니다. 프로덕션에서는 이게 꽤 중요하더라고요.

    # /etc/pve/datacenter.cfg에서 마이그레이션 설정
    migration: secure
    migration_unsecure: 0
    
    # 또는 CLI로
    pvesh set /cluster/options --migration type=secure

    클러스터 전체 상태 모니터링

    # 전체 클러스터 상태 한눈에 보기
    pvecm status
    ha-manager status
    pct list  # 컨테이너 목록
    qm list   # VM 목록
    
    # 실시간 모니터링
    watch -n 5 'pvecm status && echo "---" && ha-manager status'

    Proxmox HA 구축 완료 — 최종 점검 체크리스트

    설정을 모두 마쳤다면 아래 체크리스트로 최종 확인해 보세요.

    ▲ Proxmox HA 클러스터 구축 완료 체크리스트 — 운영 전 반드시 확인해야 할 항목들을 정리한 요약 인포그래픽입니다.

    ✅ 최종 확인 체크리스트

    1. pvecm status에서 Quorate: Yes 확인
    2. 모든 노드가 Web UI 좌측 트리에 표시됨
    3. 공유 스토리지가 모든 노드에서 접근 가능
    4. HA 그룹이 생성되고 VM이 리소스로 등록됨
    5. ha-manager status에서 VM 상태가 started
    6. Fencing 메커니즘 설정 완료
    7. 실제 페일오버 테스트 완료
    8. NTP 시간 동기화 모든 노드에서 정상

    자주 묻는 질문 (FAQ)

    Q. VM이 HA로 등록되면 항상 공유 스토리지에 있어야 하나요?
    A. 네, 맞습니다. 로컬 스토리지에 있는 VM은 다른 노드로 이동할 수 없기 때문에 HA의 의미가 없습니다. 반드시 공유 스토리지(NFS, Ceph, iSCSI 등)에 VM 디스크를 두어야 합니다.

    Q. Proxmox HA와 일반 Live Migration의 차이는 뭔가요?
    A. Live Migration은 관리자가 수동으로 VM을 다른 노드로 옮기는 것이고, HA Failover는 노드 장애 시 자동으로 VM을 다른 노드에서 재시작하는 것입니다. 라이브 마이그레이션은 무중단이지만, HA 페일오버는 VM이 재시작되는 시간만큼의 다운타임이 발생합니다.

    Q. 2노드 구성으로도 HA를 쓸 수 있나요?
    A. 가능하지만 QDevice(쿼럼 장치)가 추가로 필요합니다. Raspberry Pi 같은 소형 장치에 corosync-qnetd를 설치해서 쿼럼 역할을 맡길 수 있습니다.

    마무리: 서버실에서 배운 것

    Proxmox HA 클러스터를 처음 구축했을 때, 노드 하나를 강제로 죽이고 VM이 다른 노드에서 자동으로 살아나는 걸 보면서 진짜 뿌듯했습니다. 13년 동안 인프라 일하면서 이런 순간들이 있거든요. 기술이 의도한 대로 딱 작동하는 그 순간.

    물론 처음엔 쿼럼 개념도 헷갈리고, 펜싱 설정 때문에 VM이 안 옮겨가서 한참 삽질도 했습니다. 하지만 그 과정을 통해서 HA의 원리를 제대로 이해하게 된 것 같아요.

    핵심을 정리하면:

    • 최소 3노드로 구성해서 쿼럼을 확보하세요
    • VM 디스크는 반드시 공유 스토리지에
    • Fencing 설정을 빠뜨리지 마세요 — 없으면 HA가 안전 때문에 동작을 안 합니다
    • 설정 후 반드시 실제 페일오버 테스트를 해보세요

    다음 글에서는 Proxmox에서 Ceph 분산 스토리지를 구축하는 방법을 다룰 예정입니다. 공유 스토리지로 외부 NAS 없이 Proxmox 노드들 자체로 고가용성 스토리지를 만드는 내용인데요, HA와 함께 쓰면 정말 강력한 홈랩 환경이 만들어집니다. 기대해 주세요!

    질문이나 다른 삽질 경험 있으신 분들은 댓글로 공유해 주세요. 저도 아직 배우는 중이라 같이 이야기 나눠보면 좋을 것 같습니다 😊

  • [AI] vLLM 실전 가이드: 고성능 LLM 추론 및 API 서빙 최적화

    [AI] vLLM 실전 가이드: 고성능 LLM 추론 및 API 서빙 최적화

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

    요즘 LLM(Large Language Model, 대규모 언어 모델)을 활용한 서비스들이 정말 많아졌죠? 저도 홈랩에서 이것저것 돌려보면서 LLM이 우리의 일상을 어떻게 바꿀지 매일매일 흥미진진하게 지켜보고 있습니다. 그런데 이 LLM이라는 친구, 성능은 기가 막히지만 막상 서비스에 적용하려면 만만치 않은 챌린지들이 있더라고요. 특히 GPU 자원을 효율적으로 사용하면서 여러 요청을 동시에 처리하는 게 정말 큰 숙제였습니다.

    저도 처음엔 Hugging Face의 Transformers 라이브러리로 모델을 로드해서 API를 만들었는데, 트래픽이 조금만 몰려도 GPU 메모리가 부족하다거나, 응답 시간이 길어지는 문제에 직면하곤 했습니다. “아니, 이 좋은 GPU를 왜 이렇게밖에 못 쓰지?” 하는 자괴감도 들었고요. 그러다가 vLLM이라는 친구를 만나게 되었는데, 이거 정말 물건이더라고요! LLM 추론 성능을 획기적으로 개선하고 API 서빙까지 아주 쉽게 만들어주는 라이브러리거든요. 오늘은 저의 삽질 경험을 바탕으로 vLLM을 어떻게 실전에 적용할 수 있을지 자세히 알려드리려고 합니다.

    vLLM은 PagedAttention이라는 혁신적인 기술을 통해 LLM 추론 시 GPU 메모리 효율을 극대화합니다. 이는 동시 처리량과 응답 속도 향상으로 이어지죠.

    vLLM, 도대체 뭘까요? (feat. PagedAttention)

    vLLM은 LLM 추론(inference)을 위한 오픈소스 라이브러리거든요. 가장 큰 특징은 바로 PagedAttention(페이지드 어텐션)이라는 혁신적인 어텐션 알고리즘을 사용한다는 점이에요. 이게 무슨 말인지 쉽게 설명해 드릴게요.

    LLM은 문장을 생성할 때 이전에 생성된 토큰(token)들을 기억해야 합니다. 이 기억이 저장되는 공간을 KV Cache (Key-Value Cache, 키-값 캐시)라고 부르는데, vLLM에서 이 캐시 관리가 핵심이거든요. 일반적인 LLM 서빙 방식에서는 이 KV Cache가 고정된 크기로 할당되곤 합니다. 문제는 사용자마다 입력하는 문장 길이도 다르고, 생성되는 문장 길이도 다르다는 점이에요. 그래서 가장 긴 문장을 기준으로 KV Cache를 할당하면, 짧은 문장을 처리할 때는 메모리가 낭비되고, 그렇다고 짧게 할당하면 긴 문장을 처리할 수 없는 딜레마에 빠지게 됩니다.

    PagedAttention은 이 문제를 운영체제의 가상 메모리 페이징 기법처럼 영리하게 해결하는 거예요. KV Cache를 고정된 블록(block) 단위로 나누고, 필요한 블록만 동적으로 할당하고 해제하는 방식이죠. 마치 우리가 컴퓨터에서 메모리가 부족할 때 하드디스크의 일부를 가상 메모리로 사용하는 것과 비슷합니다.

    이 덕분에 vLLM은 다음과 같은 엄청난 장점을 가집니다:

    • 높은 처리량 (High Throughput): GPU 메모리를 효율적으로 사용하니 더 많은 동시 요청을 처리할 수 있어요.
    • 낮은 지연 시간 (Low Latency): KV Cache 관리가 최적화되어 응답 속도가 정말 빨라집니다.
    • 쉬운 사용성 (Ease of Use): 몇 줄의 코드만으로 고성능 LLM API 서버를 구축할 수 있다는 게 정말 편해요.

    제가 직접 써보니까, 정말 GPU 활용률이 확 올라가는 걸 체감할 수 있었어요. 특히 여러 사용자가 동시에 다양한 길이의 프롬프트(prompt)를 보낼 때 vLLM의 진가가 드러나더라고요.

    vLLM, 실전에서 써봅시다! (설치부터 API 서빙까지)

    이제 vLLM을 직접 설치하고 API 서버를 띄워볼 시간입니다. 저와 함께 차근차근 따라오시면 돼요. 저는 Ubuntu 환경에서 NVIDIA GPU와 CUDA를 사용하고 있다고 가정하고 진행할게요.

    1. vLLM 설치

    vLLM은 Python 패키지로 제공되기 때문에 <code>pip로 아주 쉽게 설치할 수 있습니다. 다만 CUDA 버전이 정말 중요해요!

    # CUDA 12.1 이상을 사용하는 경우 (권장)
    pip install vllm
    
    # 특정 CUDA 버전을 사용하는 경우 (예: CUDA 11.8)
    # pip install vllm==0.3.3 --pre --extra-index-url https://download.pytorch.org/whl/cu118
    # 버전 확인은 vLLM 공식 문서에서 최신 정보를 확인하는 것이 좋습니다.
    

    설치가 완료되면, python -c "import vllm; print(vllm.__version__)" 명령어로 제대로 설치되었는지 확인할 수 있습니다. 저도 처음엔 CUDA 버전 때문에 한참 삽질했는데, 꼭 본인의 환경에 맞는 vLLM 버전을 확인하고 설치하시길 바랍니다. ⚠️

    2. LLM 모델 로드 및 API 서버 실행

    vLLM은 Hugging Face 모델들을 바로 로드하여 사용할 수 있습니다. 여기서는 가볍게 테스트할 수 있는 meta-llama/Llama-2-7b-hf 모델을 예시로 들어볼게요. 물론 실제 서비스에서는 더 크고 성능 좋은 모델을 사용하시겠죠?

    python -m vllm.entrypoints.api_server \
        --model meta-llama/Llama-2-7b-hf \
        --port 8000 \
        --host 0.0.0.0 \
        --tensor-parallel-size 1 # 단일 GPU 사용 시
    

    위 명령어를 실행하면 vLLM API 서버가 백그라운드에서 실행됩니다. --model 인자에는 Hugging Face 모델 이름을 넣어주면 되고요. --tensor-parallel-size는 모델을 여러 GPU에 분산할 때 사용하는데, 저는 홈랩에서 GPU 하나로 테스트하기 때문에 1로 설정했거든요. 만약 여러 GPU가 있다면 이 값을 조절해서 더 큰 모델을 로드하거나 LLM 추론 처리량을 늘릴 수 있어요.

    성공적으로 vLLM API 서버가 시작되면 위와 같은 메시지가 터미널에 출력됩니다. 이제 이 서버로 LLM 추론 요청을 보낼 수 있습니다!

    3. API 요청 보내기

    서버가 잘 동작하는지 확인하기 위해 curl이나 Python 코드로 요청을 보내봅시다. 저는 Python requests 라이브러리를 사용해서 간단하게 테스트하는 코드를 보여드릴게요.

    import requests
    import json
    
    API_URL = "http://localhost:8000/generate"
    
    headers = {"Content-Type": "application/json"}
    data = {
        "prompt": "안녕하세요, 13년차 서버실 지킴이입니다. LLM에 대해 자세히 설명해주세요.",
        "max_tokens": 128,
        "temperature": 0.7,
        "top_p": 0.9,
        "n": 1, # 생성할 응답의 개수
        "stream": False # 스트리밍 응답 여부
    }
    
    try:
        response = requests.post(API_URL, headers=headers, data=json.dumps(data))
        response.raise_for_status() # HTTP 에러 발생 시 예외 처리
    
        result = response.json()
        print("응답 내용:", result['outputs'][0]['text'])
    
    except requests.exceptions.RequestException as e:
        print(f"API 요청 중 에러 발생: {e}")
        if response:
            print(f"서버 응답: {response.status_code}, {response.text}")
    except json.JSONDecodeError as e:
        print(f"JSON 응답 디코딩 에러: {e}")
        if response:
            print(f"서버 원본 응답: {response.text}")
    
    

    이 코드를 실행하면 vLLM이 제 프롬프트에 답변을 생성해서 돌려줄 겁니다. max_tokens는 생성할 최대 토큰 수, temperature는 응답의 창의성을 조절하는 파라미터예요. 여러 번 테스트해보면서 LLM의 응답을 확인해보세요. 🎉

    ⚠️ 삽질 경험담: GPU 메모리 부족과 버전 호환성

    제가 vLLM을 처음 도입했을 때 가장 많이 겪었던 문제는 역시 GPU 메모리 부족이었습니다. “분명 PagedAttention이 메모리 효율적이라는데 왜?” 싶었죠. 알고 보니 제가 사용하는 GPU(RTX 3060 12GB)에 너무 큰 모델(예: Llama-2-13b)을 올리려고 했던 것이 원인이었습니다. vLLM이 아무리 효율적이라도, 모델 자체의 크기를 무시할 수는 없더라고요. 제 삽질 경험을 토대로 몇 가지 팁을 드리자면:

    1. 모델 크기 확인: 사용하려는 모델이 본인의 GPU 메모리에 적합한지 먼저 확인하세요. Hugging Face 모델 페이지에 가면 모델 크기가 나와 있거든요.
    2. 양자화(Quantization) 모델 사용: int8이나 fp4 같은 양자화 기법을 사용한 모델은 훨씬 적은 메모리를 써요. vLLM도 양자화된 모델을 지원하니, 메모리가 부족하면 이 방법을 써보세요. (예: --quantization gptq)
    3. 배치 사이즈 조절: 동시 처리하는 요청의 최대 배치 사이즈를 조절하여 메모리 사용량을 제어할 수 있어요. (예: --max-model-len, --max-num-seqs)
    4. CUDA/PyTorch 버전: vLLM은 특정 CUDA 및 PyTorch 버전에 최적화되어 있습니다. 설치 시 본인의 환경과 호환되는 버전을 정확히 맞춰야 오류를 줄일 수 있어요. 저처럼 무작정 최신 버전만 고집하다가 호환성 문제로 시간을 날리지 마세요! 😅

    이런 시행착오를 겪으면서 “역시 인프라는 환경이 제일 중요하구나”를 다시 한번 느꼈습니다.

    vLLM, 얼마나 빨라졌을까? (성능 검증)

    vLLM을 사용하면 실제로 얼마나 성능이 개선되는지 궁금하실 겁니다. 저도 이 부분이 가장 기대되었는데요. 간단하게 GPU 사용량과 처리량(throughput)을 비교해볼 수 있습니다.

    1. GPU 사용량 모니터링

    서버를 띄운 상태에서 watch -n 0.5 nvidia-smi 명령어로 GPU 메모리 사용량을 확인해보세요. vLLM 서버에 요청을 보낼 때 메모리 사용량이 어떻게 변화하는지 볼 수 있어요. 일반적인 Hugging Face 모델 서빙 방식과 비교하면, 특히 여러 요청이 동시에 들어올 때 메모리 점유율이 훨씬 안정적인 것을 확인할 수 있을 거예요.

    2. 처리량 비교

    vLLM은 자체적으로 벤치마킹 툴을 제공하기도 합니다. 하지만 간단하게는 ab (ApacheBench) 같은 툴이나 직접 작성한 스크립트를 통해 동시 요청을 보내면서 초당 처리되는 토큰 수나 응답 시간을 측정해볼 수 있어요.

    # 간단한 벤치마크 예시 (Python 스크립트 작성 필요)
    # vLLM 공식 문서의 examples/llm_bench.py 참고
    

    제가 직접 테스트해봤을 때, 동시 요청 처리량은 2배 이상, 경우에 따라 5배까지도 증가하는 것을 경험했습니다. 특히 길이가 다양한 프롬프트가 섞여 들어올 때 vLLM의 PagedAttention이 정말 빛을 발하더라고요. 응답 지연 시간(latency)도 확연히 줄어들었습니다. 👍

    위 그래프는 vLLM이 기존 LLM 서빙 방식 대비 얼마나 효율적인 GPU 메모리 사용과 높은 처리량을 제공하는지 시각적으로 보여줍니다.

    마무리: vLLM, LLM 서비스의 핵심 병기!

    오늘은 13년차 서버실 지킴이로서, LLM 추론 및 API 서빙을 최적화하는 데 필수적인 vLLM에 대해 자세히 알아봤습니다. PagedAttention이라는 독특한 기술 덕분에 GPU 자원을 아껴 쓰고, 더 많은 요청을 빠르게 처리할 수 있다는 점이 가장 인상 깊었거든요.

    저처럼 홈랩에서 LLM을 돌리거나, 실제 서비스에 LLM을 적용하려는 분들이라면 vLLM은 정말 강력한 도구가 될 겁니다. 처음엔 vLLM 설치나 설정에서 약간의 삽질이 있을 수 있지만, 일단 성공적으로 구축하고 나면 얻을 수 있는 성능 향상은 그 모든 노력을 보상하고도 남을 만큼 값집니다.

    vLLM은 높은 처리량, 낮은 지연 시간, 그리고 효율적인 GPU 메모리 사용이라는 세 가지 핵심 장점을 통해 LLM 서빙의 새로운 기준을 제시합니다.

    다음번에는 vLLM과 함께 사용할 수 있는 양자화(Quantization) 기술이나, 여러 모델을 동시에 서빙하는 방법 등 좀 더 심화된 내용을 다뤄볼까 합니다. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요! 여러분의 LLM 여정에 조금이나마 도움이 되었기를 바랍니다. 감사합니다! 😊

  • [Nas] TrueNAS SMB 성능 최적화: 고속 파일 공유를 위한 설정 가이드

    [Nas] TrueNAS SMB 성능 최적화: 고속 파일 공유를 위한 설정 가이드

    안녕하세요! 13년차 서버실 지킴이, 13년차 인프라 엔지니어입니다. 오늘은 홈랩(Homelab)에서 많은 분들이 겪는 고질적인 문제, 바로 TrueNAS SMB 성능 최적화에 대해 이야기해보려고 해요. NAS 파일 공유 속도가 답답해서 속 터지는 경험, 혹시 저만 해본 건 아니겠죠? 저도 처음 TrueNAS를 구축하고 파일을 옮기는데, ‘이게 뭔가 싶을 정도로’ 느려서 한참을 삽질했던 기억이 납니다. 😅

    특히 고용량 파일이나 다수의 작은 파일을 자주 주고받아야 할 때, 느린 SMB(Server Message Block) 속도는 정말이지 작업 효율을 떨어뜨리는 주범이 됩니다. 그래서 오늘은 제가 직접 경험하고 설정하면서 얻은 TrueNAS 네트워크 최적화와 SMB 설정 노하우를 아낌없이 공유해 드릴게요. 여러분의 TrueNAS가 쾌적한 고속 파일 공유 서버로 거듭나도록 도와드리겠습니다!

    홈랩에서 TrueNAS SMB 성능을 최적화하기 위한 기본적인 네트워크 구성도입니다. 10GbE 스위치와 클라이언트, TrueNAS 간의 연결을 보여줍니다.

    TrueNAS SMB, 왜 느릴까요? 핵심 개념부터 알아보기

    우선, SMB(Server Message Block)가 뭔지 간단히 짚고 넘어갈까요? 쉽게 말해, 윈도우나 macOS 같은 운영체제에서 네트워크를 통해 파일이나 프린터를 공유하기 위해 사용하는 통신 프로토콜입니다. 우리가 흔히 ‘네트워크 드라이브’나 ‘공유 폴더’라고 부르는 것들이 바로 이 SMB를 통해 작동하더라고요.

    그럼 왜 TrueNAS의 SMB 성능이 기대만큼 나오지 않을까요? 원인은 다양한데, 크게 몇 가지로 나눠볼 수 있어요.

    • 네트워크 병목(Network Bottleneck): 가장 흔한 원인 중 하나예요. 1기가비트 이더넷(Gigabit Ethernet, GbE) 환경에서는 이론상 최대 125MB/s의 속도를 내지만, 실제로는 100~110MB/s 정도가 한계거든요. 10기가비트 이더넷(10GbE)으로 업그레이드하면 훨씬 빨라집니다.
    • 디스크 I/O 성능(Disk I/O Performance): 아무리 네트워크가 빨라도 디스크 자체가 느리면 소용없어요. 특히 HDD(Hard Disk Drive)는 SSD(Solid State Drive)에 비해 랜덤 I/O 성능이 현저히 떨어지더라고요.
    • CPU 및 RAM(CPU & RAM): SMB 서비스 자체도 어느 정도의 CPU 연산과 RAM을 사용합니다. 특히 동시 접속자 수가 많거나 암호화 기능을 사용하면 더 많은 자원이 필요해요.
    • TrueNAS 설정 문제: 오늘 다룰 핵심이죠. TrueNAS 내부의 SMB 설정이나 ZFS 데이터셋(Dataset) 설정에 따라 성능이 크게 달라질 수 있습니다.

    이런 요소들을 종합적으로 고려해서 최적의 TrueNAS SMB 성능을 끌어내는 것이 우리의 목표입니다.

    고속 파일 공유를 위한 준비물과 사전 점검 💡

    본격적인 설정에 들어가기 전에, 몇 가지 준비물과 사전 점검 사항을 확인해 봅시다. 제가 처음엔 이런 것들을 간과하고 무작정 설정만 바꾸려다가 꽤나 삽질 좀 했거든요. ㅎㅎ

    1. 하드웨어 점검

    • 10기가비트 이더넷(10GbE) 환경:
      • TrueNAS 서버 NIC(네트워크 인터페이스 카드): 서버에 10GbE NIC가 장착되어 있는지 확인하세요. 저는 인텔 X540-T2 같은 NIC를 주로 사용하는데, 안정적이고 성능도 괜찮더라고요.
      • 클라이언트 PC NIC: 파일을 주고받을 클라이언트 PC에도 10GbE NIC가 있어야 합니다.
      • 10GbE 스위치(Switch): 모든 10GbE 장비가 연결될 스위치 역시 10GbE를 지원해야 해요. 저렴한 언매니지드(Unmanaged) 스위치도 괜찮지만, 장기적으로는 관리형(Managed) 스위치가 유용할 때가 많습니다.
    • 스토리지(Storage):
      • 성능이 중요한 공유라면 SSD 풀(Pool)을 사용하는 게 좋습니다. 특히 NVMe SSD는 압도적인 성능을 보여주더라고요.
      • HDD 풀이라도 스트라이프(Stripe)나 미러(Mirror) 구성보다는 RAID-Z1, Z2 같은 방식이 쓰기 성능에 유리할 수 있습니다.

    2. 네트워크 케이블 확인

    10GbE 환경에서는 CAT6a 이상의 이더넷 케이블을 사용하는 게 중요합니다. CAT5e나 CAT6 케이블은 짧은 거리에서는 작동할 수 있지만, 장거리에서는 신뢰성과 성능 저하를 일으킬 수 있어요. 저는 처음에 집에 있던 오래된 CAT5e 케이블로 연결했다가 속도가 안 나와서 애를 먹었는데, 케이블을 바꾸니 바로 해결되더라고요! 😅

    TrueNAS SMB 성능 최적화를 위한 설정 가이드

    이제 본격적으로 TrueNAS 설정에 들어가 볼까요? 여기서는 TrueNAS SCALE SMB를 기준으로 설명하지만, CORE 버전에서도 유사한 설정들이 많으니 참고하시면 됩니다.

    1. SMB 서비스 설정

    TrueNAS 웹 UI에 접속하여 서비스(Services) 메뉴에서 SMB(CIFS) 서비스를 찾아 설정합니다.

    1. 고급 설정 활성화: SMB 서비스 설정 화면에서 ‘고급 옵션 표시(Show Advanced Options)’를 클릭하세요.
    2. 서버 최소/최대 프로토콜(Server Minimum/Maximum Protocol):
      • Server Minimum Protocol: SMB2 (SMB1은 보안에 취약하고 성능도 좋지 않습니다. 레거시 시스템이 아니라면 사용하지 마세요.)
      • Server Maximum Protocol: SMB3_11 또는 SMB3 (최신 프로토콜을 사용하는 게 성능과 보안에 모두 유리합니다.)
    3. 보조 매개변수(Auxiliary Parameters): 여기에 성능 향상을 위한 추가 옵션들을 넣어줄 거예요. 한 줄씩 추가합니다.
    4. 
      # 비동기 I/O (Asynchronous I/O)를 위한 설정
      aio_pthread_cases = 100
      aio_pthread_read_cases = 100
      aio_pthread_write_cases = 100
      
      # SMB Multi-channel (멀티채널) 활성화 (TrueNAS SCALE만 해당, CORE는 Link Aggregation 사용)
      # 여러 네트워크 인터페이스를 사용하여 대역폭을 늘리고 지연 시간을 줄입니다.
      # 클라이언트와 서버 모두 Multi-channel을 지원해야 합니다.
      server multi channel = yes
      
      # 메타데이터 캐싱 최적화 (Disk I/O 감소)
      cache_read_metadata = yes
      
      # 엄격한 할당 비활성화 (쓰기 성능 향상, 주의 필요)
      # 클라이언트가 요청한 크기만큼 블록을 미리 할당하지 않도록 합니다.
      # 파일 시스템 단편화가 증가할 수 있으나, 쓰기 성능 향상에 기여할 수 있습니다.
      strict allocate = no
              

      💡 팁: strict allocate = no는 파일 시스템 단편화를 유발할 수 있으니, 아주 민감한 환경이 아니라면 기본값(yes)을 유지하는 것도 좋은 선택입니다. 저는 홈랩 환경에서 쓰기 성능을 끌어올리기 위해 사용하곤 하는데, 환경에 따라 선택하시면 됩니다.

    5. 저장(Save) 버튼을 눌러 설정을 적용합니다.

    TrueNAS 웹 UI에서 SMB 서비스의 고급 설정을 변경하는 화면입니다. 보조 매개변수에 주요 최적화 옵션들이 입력되어 있습니다.

    2. 네트워크 인터페이스(NIC) 설정

    네트워크 인터페이스 설정도 TrueNAS 네트워크 최적화의 핵심입니다.

    1. 점보 프레임(Jumbo Frames) 활성화:
      • 네트워크(Network) → 인터페이스(Interfaces) 메뉴로 이동하여 사용할 10GbE NIC를 선택합니다.
      • MTU(Maximum Transmission Unit) 값을 9000으로 변경합니다. (기본값은 1500입니다.)
      • ⚠️ 중요: TrueNAS 서버, 스위치, 클라이언트 PC의 모든 장비가 동일하게 MTU 9000을 지원하고 설정되어야 합니다! 하나라도 다르면 오히려 통신 오류나 성능 저하가 발생할 수 있어요. 제가 이 부분에서 삽질 좀 했거든요. 스위치와 TrueNAS는 설정했는데 클라이언트 MTU를 잊어서 한참을 헤맸던 기억이 나네요.
    2. Link Aggregation(링크 어그리게이션) 또는 SMB Multi-channel(멀티채널):
      • TrueNAS CORE: 여러 개의 1GbE NIC를 묶어 대역폭을 확장하는 Link Aggregation(LACP)을 주로 사용합니다. 스위치에서 LACP 설정을 해주어야 해요.
      • TrueNAS SCALE: SMB Multi-channel을 적극 활용하는 게 좋습니다. 위에 SMB 보조 매개변수에서 server multi channel = yes를 설정했다면, TrueNAS 서버에 여러 개의 NIC가 연결되어 있고 클라이언트도 Multi-channel을 지원하면 자동으로 대역폭을 합쳐서 사용해요. 훨씬 간편하고 효과적입니다.

    3. ZFS 데이터셋(Dataset) 설정 (⚠️ 매우 중요!)

    SMB 공유에 사용할 ZFS 데이터셋의 설정도 성능에 큰 영향을 미칩니다. 특히 동기 쓰기(Synchronous Write)와 관련된 설정은 데이터 안전성과 성능 사이의 트레이드오프가 존재하므로 신중해야 합니다.

    1. 데이터셋(Datasets) 메뉴로 이동하여 공유할 데이터셋을 선택합니다.
    2. 고급 옵션(Advanced Options)에서 동기화(Sync) 설정을 확인합니다.
      • Sync: Standard (기본값)
        • 대부분의 경우 이 설정이 가장 안전하고 균형 잡힌 성능을 제공해요.
        • 데이터 무결성(Data Integrity)을 보장하며, 쓰기 작업이 완료되기 전에 데이터가 디스크에 기록되었는지 확인합니다.
      • Sync: Always (가장 안전하지만, 성능 저하)
        • 모든 쓰기 작업이 디스크에 기록될 때까지 기다리므로, 전력 손실 등의 상황에서도 데이터 손실 위험이 거의 없습니다. 하지만 쓰기 성능은 가장 낮아요.
      • Sync: Disabled (최대 성능, 데이터 손실 위험)
        • 쓰기 작업이 디스크에 기록되었는지 확인하지 않고, 운영체제 캐시에서 쓰기 완료를 보고합니다. 이로 인해 쓰기 성능은 가장 뛰어나지만, 전력 손실이나 시스템 크래시 시 데이터 손실 또는 손상 위험이 매우 높습니다.
        • ⚠️ 경고: 이 옵션은 극단적인 성능이 필요하고 데이터 손실 위험을 감수할 수 있는 경우에만 사용해야 합니다. 절대 중요한 데이터에 사용하지 마세요! 저는 특정 임시 파일 공유나 벤치마크 테스트 시에만 제한적으로 사용해봤습니다.

    주의사항 및 트러블슈팅: 제가 겪었던 삽질들 😱

    자, 이렇게 설정하면 웬만해선 TrueNAS SMB 성능이 확 올라갈 겁니다. 하지만 저처럼 예상치 못한 문제에 부딪힐 수도 있겠죠? 제가 겪었던 몇 가지 삽질 경험과 해결 팁을 공유해 드릴게요.

    1. 점보 프레임 설정 후 오히려 속도가 느려지거나 접속 불가

    앞서 강조했지만, MTU 9000은 TrueNAS, 스위치, 클라이언트 모두 동일하게 설정되어야 해요. 저는 스위치와 TrueNAS는 설정했지만, 윈도우 클라이언트 PC의 NIC 드라이버 설정에서 MTU를 바꾸는 걸 깜빡해서 한참을 헤맸어요. 윈도우에서는 PowerShell이나 NIC 속성에서 설정할 수 있습니다.

    
    # 현재 MTU 확인 (Windows)
    Get-NetAdapterAdvancedProperty -Name "이더넷 어댑터 이름" | Where-Object {$_.DisplayName -eq "Jumbo Packet"}
    
    # MTU 9000으로 설정 (Windows)
    Set-NetAdapterAdvancedProperty -Name "이더넷 어댑터 이름" -RegistryKeyword "JumboPacket" -RegistryValue 9000
    

    2. 클라이언트 PC의 SMB 캐싱 문제

    가끔 클라이언트 PC에서 이전에 연결했던 SMB 공유의 캐시가 남아있어 성능 측정이 정확하지 않은 경우가 있어요. 이럴 때는 클라이언트 PC를 재부팅하거나, 네트워크 드라이브 연결을 완전히 끊고 다시 연결해보세요.

    3. TrueNAS 리소스 부족

    SMB 성능은 네트워크뿐만 아니라 TrueNAS 서버 자체의 리소스(CPU, RAM)에도 영향을 받습니다. 특히 TrueNAS SCALE SMB는 컨테이너 기반으로 작동하므로, 시스템 리소스 할당이 중요할 수 있어요. TrueNAS 대시보드에서 CPU 사용률, RAM 사용률, 디스크 I/O 등을 모니터링하면서 병목 지점을 찾아보세요.

    TrueNAS 웹 UI에서 시스템 리소스 사용 현황을 모니터링하는 대시보드 화면입니다. CPU, RAM, 네트워크 트래픽 등의 지표를 확인할 수 있습니다.

    검증 및 결과 확인 🎉

    자, 이제 설정도 끝났으니 실제로 NAS 파일 공유 속도가 얼마나 빨라졌는지 확인해 볼 시간입니다! 저는 주로 두 가지 툴을 사용해서 성능을 측정합니다.

    1. iPerf3를 이용한 네트워크 대역폭 테스트

    SMB 성능은 결국 네트워크 대역폭에 크게 좌우됩니다. iPerf3는 네트워크 자체의 최대 처리량을 측정하는 데 정말 유용해요. TrueNAS SCALE에서는 앱(Apps)으로 iPerf3를 설치하거나, CLI에서 직접 실행할 수 있습니다.

    
    # TrueNAS 서버에서 iPerf3 서버 실행
    iperf3 -s
    
    # 클라이언트 PC에서 iPerf3 클라이언트 실행 (TrueNAS 서버 IP로)
    iperf3 -c [TrueNAS_IP] -P 8 -t 30
    

    이렇게 측정했을 때 10GbE 환경이라면 9~9.5Gbps 정도가 나와야 정상입니다. 만약 1Gbps에 머무르거나 그 이하로 나온다면, 네트워크 케이블, NIC 드라이버, 스위치 설정 등을 다시 점검해 봐야 합니다.

    2. CrystalDiskMark (Windows) 또는 Blackmagic Disk Speed Test (macOS)

    실제 SMB 공유 폴더에 파일을 읽고 쓰는 성능을 측정하는 데 정말 유용한 툴들이예요. 클라이언트 PC에서 공유 폴더를 네트워크 드라이브로 연결한 후 테스트를 진행합니다.

    • CrystalDiskMark: 윈도우 환경에서 가장 널리 사용되는 디스크 벤치마크 툴입니다. 순차 읽기/쓰기, 랜덤 읽기/쓰기 성능을 직관적으로 보여줍니다.
    • Blackmagic Disk Speed Test: macOS 사용자에게 정말 좋은 선택지예요. 비디오 편집 워크로드에 초점을 맞춰 성능을 측정합니다.

    설정 전후로 이 툴들을 돌려보면 확연히 빨라진 TrueNAS SMB 성능을 체감하실 수 있을 거예요. 저는 순차 읽기/쓰기 속도가 110MB/s에서 700~800MB/s (10GbE 환경, SSD 풀)까지 올라가는 걸 보고 쾌재를 불렀습니다! 🎉

    TrueNAS SMB 성능 최적화 전후의 CrystalDiskMark 벤치마크 결과 비교표입니다. 순차 읽기/쓰기 속도 변화를 보여줍니다.

    마무리하며: 쾌적한 홈랩을 위한 끊임없는 탐구

    오늘은 제가 13년차 인프라 엔지니어로 홈랩을 운영하면서 겪었던 TrueNAS SMB 성능 최적화 과정을 공유해 드렸습니다. NAS 파일 공유 속도를 높이기 위해 SMB 설정부터 네트워크 환경, 그리고 ZFS 데이터셋 설정까지 다양한 요소를 고려해야 한다는 걸 다시 한번 깨달으셨을 거예요. 때로는 복잡하고 삽질의 연속일 수 있지만, 드디어 원하는 성능이 나왔을 때의 그 쾌감은 정말 최고랍니다! 😄

    오늘 다룬 내용 외에도 ZFS 캐싱(L2ARC, SLOG)을 활용하거나, 더 고급스러운 네트워크 튜닝을 통해 TrueNAS 네트워크 최적화를 시도해 볼 수도 있어요. 하지만 오늘 제가 알려드린 기본 설정만으로도 대부분의 홈랩 환경에서는 충분히 만족할 만한 TrueNAS SMB 성능 향상을 경험하실 수 있을 겁니다. 여러분의 쾌적한 홈랩 생활에 제 경험이 조금이나마 도움이 되었기를 바랍니다!

    다음번에는 TrueNAS SCALE의 컨테이너 앱 활용법 같은 재미있는 주제로 찾아올게요. 그때까지 즐거운 홈랩 라이프 보내세요! 👋

  • [Cloud] Cloudflare Workers 실전 가이드: 서버리스 엣지 컴퓨팅 및 최신 AI 활용 전략

    [Cloud] Cloudflare Workers 실전 가이드: 서버리스 엣지 컴퓨팅 및 최신 AI 활용 전략

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

    오늘은 제가 최근 홈랩에서 이것저것 실험하면서 꽤나 재미를 보고 있는 기술, 바로 Cloudflare Workers(클라우드플레어 워커스)에 대한 이야기를 풀어볼까 합니다. 다들 서버리스(Serverless)니, 엣지 컴퓨팅(Edge Computing)이니 하는 말 많이 들어보셨을 텐데요. 이게 실제 제 서비스에 어떻게 적용될 수 있을까 궁금하셨던 분들에게 좋은 길잡이가 되었으면 합니다.

    사실 저도 처음엔 ‘이게 뭔가 싶었는데’ 직접 써보고 나니 ‘와, 이거 진짜 물건이네!’ 싶더라고요. 특히 작은 API나 특정 요청을 처리해야 할 때, 굳이 무거운 서버 인스턴스를 띄우지 않고 전 세계 Cloudflare(클라우드플레어) 엣지 네트워크에서 코드를 바로 실행할 수 있다는 점이 매력적이었습니다. 저지연(low-latency)을 줄이고, 비용 효율적으로 서비스를 운영할 수 있는 강력한 도구거든요.

    이번 글에서는 Cloudflare Workers가 무엇인지부터 시작해서, 제가 직접 겪었던 삽질 경험과 해결 과정까지, Cloudflare Workers 실전 가이드: 서버리스 엣지 컴퓨팅 활용 전략을 자세히 알려드리겠습니다. 그럼, 함께 떠나볼까요? 🎉

    Cloudflare Workers와 엣지 컴퓨팅 아키텍처 개요 다이어그램

    Cloudflare Workers의 전체적인 아키텍처와 엣지 컴퓨팅 개념을 시각적으로 보여주는 다이어그램입니다.

    Cloudflare Workers, 대체 뭘까요?

    쉽게 말해, Cloudflare Workers는 Cloudflare의 전 세계 분산된 엣지 네트워크(Edge Network) 위에서 JavaScript나 WebAssembly 코드를 실행할 수 있게 해주는 서버리스 실행 환경(Serverless Execution Environment)이에요. 기존의 서버리스 함수(Lambda, Cloud Functions 등)와 비슷하지만, 사용자와 물리적으로 가장 가까운 엣지 로케이션에서 실행된다는 점에서 엣지 컴퓨팅의 특성을 지니고 있습니다.

    제가 처음 접했을 때 가장 놀랐던 건 ‘콜드 스타트(Cold Start)’ 거의 없이 극도로 빠른 응답 시간을 보여준다는 점이었어요. 기존 서버리스 함수들은 유휴 상태일 때 첫 요청에서 약간의 지연이 생길 수 있거든요. 하지만 Workers는 그런 걱정을 거의 할 필요가 없었습니다.

    Cloudflare Workers의 주요 장점

    • 극도로 낮은 지연 시간(Ultra-low Latency): 사용자와 가장 가까운 엣지 서버에서 코드가 실행되므로, 응답 시간이 획기적으로 단축됩니다. 이는 글로벌 네트워크를 활용한 개발자 경험 개선에 크게 기여합니다.
    • 뛰어난 확장성(Massive Scalability): Cloudflare의 강력한 인프라 위에서 작동해서 트래픽이 폭증해도 자동으로 확장되고 안정적인 서비스를 제공합니다. 제가 직접 트래픽 테스트를 해보니 정말 안정적이더라고요.
    • 비용 효율성(Cost-effectiveness): 사용량 기반(pay-per-use) 요금 모델로, 요청 수에 비례하여 비용이 발생합니다. 특히 무료 티어(Free Tier)가 넉넉해서 소규모 서비스나 실험에 정말 좋습니다.
    • 개발자 친화적(Developer-friendly): JavaScript/TypeScript 기반으로 익숙한 언어로 개발할 수 있고, Wrangler CLI 등 개발 도구도 잘 갖춰져 있어요.

    Cloudflare Workers 실전 구현: 첫 Workers 배포하기

    자, 이제 이론은 충분히 봤으니 직접 한번 만들어보고 배포해보는 시간을 가져볼까요? 제가 홈랩에서 했던 과정을 그대로 따라 해보시면 금방 첫 Workers를 만나보실 수 있을 겁니다. 준비물은 Cloudflare 계정 하나면 충분해요!

    1. Cloudflare 계정 생성 및 로그인

    아직 Cloudflare 계정이 없으시다면, Cloudflare 웹사이트에서 가입하세요. 무료 계정으로도 충분합니다. 계정을 생성하고 로그인하면 준비 끝입니다.

    2. Wrangler CLI 설치

    Cloudflare Workers를 개발하고 배포하는 데는 wrangler라는 CLI(Command Line Interface) 도구를 사용하는 것이 가장 편리합니다. Node.js가 설치되어 있어야 합니다.

    
    npm install -g wrangler
    

    설치가 완료되면, 터미널에서 wrangler login 명령어를 실행하여 Cloudflare 계정에 로그인합니다.

    
    wrangler login
    

    이 명령어를 실행하면 브라우저가 열리고 Cloudflare 인증 페이지로 리다이렉트됩니다. 인증을 완료하면 터미널로 돌아와 성공 메시지를 확인할 수 있습니다.

    3. 새 Workers 프로젝트 생성

    이제 새로운 Workers 프로젝트를 생성해봅시다. 최신 개발 환경을 위해 npm create cloudflare 명령어를 사용하는 것을 권장합니다. 여기서는 TypeScript 템플릿을 기반으로 진행하겠습니다.

    
    npm create cloudflare my-first-worker -- --typescript
    cd my-first-worker
    

    이 명령어는 TypeScript 템플릿을 사용하여 기본적인 Workers 프로젝트 구조를 만들어줍니다. my-first-worker 디렉토리로 이동해주세요.

    4. 간단한 Worker 코드 작성

    src/index.ts 파일을 열어보면 다음과 같은 코드를 볼 수 있습니다. 이게 기본 ‘Hello World’ Workers 코드에요.

    
    /**
     * Welcome to Cloudflare Workers! This is your first worker. 
     *
     * - Run `npm run dev` in your terminal to start a development server
     * - Open a browser tab at http://localhost:8787/ to see your worker in action
     * - Run `npm run deploy` to publish your worker
     *
     * Learn more at https://developers.cloudflare.com/workers/ 
     */
    
    export interface Env {
    	// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime/kv/
    	// MY_KV_NAMESPACE: KVNamespace;
    	//	// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime/durable-objects/
    	// MY_DURABLE_OBJECT: DurableObjectNamespace;
    	//	// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime/r2/
    	// MY_BUCKET: R2Bucket;
    }
    
    export default {
    	async fetch(
    		request: Request,
    		env: Env,
    		ctx: ExecutionContext
    	): Promise<Response> {
    		return new Response('Hello Cloudflare Workers!');
    	},
    };
    

    여기서 new Response('Hello Cloudflare Workers!'); 부분을 원하는 메시지로 바꿔볼 수도 있어요. 예를 들어, 제가 좋아하는 메시지인 ’13년차 서버실에서 보내는 메시지!’로 바꿔보겠습니다.

    
    // ... (생략)
    
    export default {
    	async fetch(
    		request: Request,
    		env: Env,
    		ctx: ExecutionContext
    	): Promise<Response> {
    		return new Response('13년차 서버실에서 보내는 메시지! 안녕하세요!');
    	},
    };
    

    5. Workers 배포

    코드를 수정했다면, 이제 Cloudflare 엣지 네트워크에 배포할 차례입니다. wrangler deploy 명령어를 사용하면 됩니다. 배포 시 Workers의 이름은 wrangler.toml 파일에 설정된 name을 따릅니다.

    
    wrangler deploy
    

    이 명령어를 실행하면 코드가 컴파일되고 Cloudflare에 업로드됩니다. 몇 초 안에 배포가 완료되고, 배포된 Workers의 URL을 터미널에서 확인할 수 있어요. 🎉 드디어 됐다!

    Cloudflare Workers 대시보드 배포 설정 화면

    Cloudflare Workers 대시보드에서 방금 배포한 Workers의 설정 화면을 보여주는 스크린샷입니다.

    ⚠️ 주의사항 및 트러블슈팅 경험

    제가 Cloudflare Workers를 사용하면서 겪었던 몇 가지 주의사항과 ‘아, 이거 때문에 삽질 좀 했네’ 싶었던 경험들을 공유합니다.

    1. 런타임 환경 이해하기

    • Node.js 호환성: Workers는 Node.js 런타임이 아니에요. V8 엔진 기반의 독자적인 런타임인 Workers Runtime을 사용하거든요. 그래서 Node.js의 모든 내장 모듈(fs, http 등)을 사용할 수 없습니다. 특정 Node.js 모듈이 필요하다면, Cloudflare의 Node.js 호환성 문서를 참고하거나, WebAssembly(Wasm) 같은 대안을 고려해봐야 합니다. 처음엔 Node.js 모듈을 무심코 사용했다가 에러를 뿜어서 당황했던 기억이 있네요.
    • 글로벌 객체: Node.js의 process나 브라우저의 window 같은 전역 객체(Global Object)는 Workers 환경에 없어요. 대신 self나 globalThis를 사용해야 합니다.

    2. 리소스 제한(Limits)

    Cloudflare Workers는 매우 강력하지만, 엣지 환경의 특성상 몇 가지 리소스 제한이 있습니다. 저도 이 제한 때문에 몇 번 코드를 수정해야 했었죠. (이 정보는 2026년 9월 현재 기준입니다.)

    • CPU 시간: 단일 요청 처리 시 50ms(무료 플랜) ~ 30초(유료 플랜)의 CPU 시간이 주어집니다. 복잡한 연산보다는 빠르고 가벼운 요청 처리에 적합합니다.
    • 메모리: 약 128MB의 메모리 제한이 있어요. 큰 데이터를 처리하거나 복잡한 상태를 유지하는 데는 한계가 있을 수 있습니다.
    • 요청/응답 본문 크기: 기본적으로 50MB로 제한돼요. 큰 파일을 업로드하거나 다운로드하는 프록시를 만들 때는 이 점을 고려해야 합니다.

    3. 로컬 개발 환경

    wrangler dev 명령어를 사용하면 로컬에서 Workers를 개발하고 테스트할 수 있어요. 이게 진짜 편하더라고요. 실제 배포하기 전에 충분히 테스트해보는 습관을 들이는 것이 중요합니다. 💡

    
    wrangler dev
    

    이 명령어를 실행하면 로컬 개발 서버가 시작되고, 브라우저에서 http://localhost:8787/로 접속하여 Workers의 동작을 바로 확인할 수 있습니다.

    검증 및 결과 확인

    배포된 Workers가 잘 작동하는지 확인하는 과정도 중요하죠. 제가 배포한 my-first-worker의 URL을 확인한 뒤, curl 명령어나 웹 브라우저로 접속해보면 됩니다.

    1. Workers URL 확인

    배포가 성공적으로 끝나면 터미널에 다음과 비슷한 URL이 표시됩니다.

    
    # 예시 URL
    https://my-first-worker.YOUR_ACCOUNT_ID.workers.dev/
    

    2. 웹 브라우저 또는 curl로 확인

    해당 URL로 접속하면, 우리가 작성했던 메시지 '13년차 서버실에서 보내는 메시지! 안녕하세요!'가 잘 출력되는 것을 확인할 수 있어요.

    
    curl https://my-first-worker.YOUR_ACCOUNT_ID.workers.dev/
    

    응답으로 13년차 서버실에서 보내는 메시지! 안녕하세요!가 보인다면 성공입니다! ✅

    Cloudflare 대시보드에서도 Workers의 로그와 분석(Analytics)을 확인할 수 있어요. 얼마나 많은 요청이 들어왔고, CPU 시간은 얼마나 사용했는지 등을 한눈에 볼 수 있어서 모니터링하기 정말 편리하더라고요.

    Cloudflare Workers 성능 지표 대시보드 그래프

    Cloudflare Workers 대시보드에서 Workers의 요청 수, CPU 시간 등 성능 지표를 보여주는 그래프나 차트입니다.

    🌟 최신 Cloudflare Workers의 주요 변화 및 신기능

    Cloudflare Workers는 지난 몇 달간 눈부신 발전을 거듭하며 엣지 컴퓨팅의 지평을 넓히고 있습니다. 특히 AI 시대의 엣지 컴퓨팅을 선도하는 새로운 기능들이 대거 추가되었어요. 이 변화들을 통해 Workers는 단순한 서버리스 함수를 넘어, 강력한 엣지 애플리케이션 플랫폼으로 진화하고 있습니다.

    • Workers AI: 엣지 AI의 새로운 지평을 열었습니다. Workers AI를 통해 개발자들은 Cloudflare의 글로벌 네트워크에서 직접 AI 모델을 배포하고 추론을 실행할 수 있습니다. 텍스트 생성, 이미지 분류, 임베딩 등 다양한 AI 작업을 저지연으로 처리할 수 있어, 사용자에게 더 빠르고 개인화된 경험을 제공할 수 있게 되었죠. 이는 서버리스 AI의 가능성을 극대화합니다.
    • Vectorize (벡터 데이터베이스): Workers AI와 함께 사용하기 좋은 벡터 데이터베이스 서비스인 Vectorize가 정식 출시되었습니다. 벡터 임베딩을 저장하고 검색하여 AI 기반 검색, 추천 시스템 등을 엣지에서 구현할 수 있게 해줍니다.
    • D1 (서버리스 SQL 데이터베이스): Cloudflare D1은 SQLite 기반의 서버리스 SQL 데이터베이스로, Workers와 긴밀하게 통합되어 엣지에서 데이터를 영속적으로 저장하고 관리할 수 있게 합니다. 이제 Workers만으로도 복잡한 데이터 기반 애플리케이션을 구축하는 것이 더욱 쉬워졌습니다.
    • Cloudflare Queues: 안정적인 메시징 시스템인 Cloudflare Queues를 통해 Workers 간 또는 외부 시스템과의 비동기 통신을 더욱 견고하게 구축할 수 있습니다. 이는 복잡한 분산 시스템 아키텍처를 엣지에서 구현하는 데 필수적인 요소입니다.

    이러한 기능들은 Workers가 단순한 “함수”를 넘어, 완전한 “엣지 애플리케이션 플랫폼”으로 성장했음을 보여줍니다. 글로벌 네트워크를 활용한 개발자 경험은 더욱 풍부해지고 있습니다.

    마무리하며: 엣지 컴퓨팅의 미래를 Workers와 함께

    오늘은 13년차 인프라 엔지니어의 시선으로 Cloudflare Workers에 대해 자세히 알아보고, 직접 배포까지 해보는 실전 가이드를 소개해드렸습니다. 최신 업데이트를 통해 Workers가 단순한 서버리스 함수를 넘어, 엣지 AI와 데이터 스토리지를 결합한 강력한 플랫폼으로 진화하고 있음을 확인했습니다.

    처음엔 단순히 ‘클라우드플레어에서 코드 돌리는 건가?’ 싶었는데, 직접 써보고 삽질도 해보면서 엣지 컴퓨팅의 진정한 가치를 깨달았네요. 특히 지연 시간에 민감한 서비스나 글로벌 사용자들을 대상으로 하는 서비스에는 Workers가 정말 강력한 대안이 될 수 있겠다는 생각이 들었습니다.

    Cloudflare Workers의 다양한 활용 사례

    • API 게이트웨이(API Gateway): 복잡한 백엔드 없이 간단한 API 엔드포인트를 만들거나, 기존 API에 대한 인증/인가 레이어를 추가할 수 있어요.
    • 정적 웹사이트 라우팅(Static Site Routing): 여러 정적 사이트를 하나의 도메인 아래에서 라우팅하거나, A/B 테스트를 위한 트래픽 분배에 활용할 수 있습니다.
    • 이미지 최적화(Image Optimization): 요청 시점에 이미지를 동적으로 리사이징하거나 포맷을 변환하여 전달할 수 있어요.
    • 보안 및 필터링(Security & Filtering): 악성 트래픽을 차단하거나, 특정 IP 대역의 접근을 제한하는 등 보안 로직을 엣지에서 구현할 수 있습니다.
    • 데이터 캐싱(Data Caching): 자주 요청되는 데이터를 엣지에 캐싱하여 백엔드 부하를 줄이고 응답 속도를 높일 수 있어요.
    • 엣지 AI 애플리케이션: Workers AI를 활용하여 실시간 번역, 콘텐츠 모더레이션, 개인화된 추천 등 AI 기반 서비스를 엣지에서 직접 구현할 수 있습니다.

    저의 작은 경험과 삽질이 Cloudflare Workers를 시작하려는 분들에게 조금이나마 도움이 되었기를 바랍니다. 다음번에는 Workers KV나 Durable Objects 같은 Workers의 고급 기능들을 활용하는 방법에 대해 더 깊이 파고들어 볼 예정입니다. 기대해주세요! 😄

    Cloudflare Workers 주요 장점 및 활용 사례 요약 인포그래픽

    Cloudflare Workers의 주요 장점과 활용 사례를 요약하는 인포그래픽입니다.

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

  • [k8s] Kubernetes Longhorn 스토리지: 설치부터 운영까지 완벽 가이드

    [k8s] Kubernetes Longhorn 스토리지: 설치부터 운영까지 완벽 가이드

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

    오늘은 제가 홈랩에서 Kubernetes(쿠버네티스) 클러스터를 운영하면서 겪었던 스토리지 고민과 그 해결책, 바로 Longhorn(롱혼)에 대해 이야기해보려고 합니다. 쿠버네티스에서 컨테이너 애플리케이션을 운영하다 보면, 데이터 영속성(Persistence) 확보가 정말 중요한데요. 특히 분산 스토리지(Distributed Storage)는 고가용성(High Availability)과 데이터 안정성을 위해 필수적이죠. 처음엔 로컬 스토리지를 PersistentVolume(PV)으로 직접 연결해보기도 하고, NFS(Network File System)를 써보기도 했는데, 이게 또 생각보다 관리하기가 만만치 않더라고요.

    그러다 우연히 Longhorn을 알게 되었고, 직접 설치하고 운영해보니 “아, 이거다!” 싶었습니다. 설치도 비교적 간단하고, 웹 UI로 볼륨 관리도 직관적이라 삽질 좀 덜 수 있었거든요. 오늘은 저의 경험을 바탕으로 Longhorn을 처음 접하시는 분들도 쉽게 따라할 수 있도록 설치부터 운영까지 완벽 가이드를 준비해봤습니다. 함께 시작해볼까요?

    쿠버네티스 클러스터 내 롱혼 분산 스토리지 아키텍처 개요

    이미지 캡션: 쿠버네티스 클러스터 내 롱혼 분산 스토리지 아키텍처 개요. 여러 노드에 걸쳐 데이터가 복제되고 관리되는 모습을 보여줍니다.

    💡 Longhorn, 넌 누구냐? (핵심 개념 설명)

    자, 그럼 먼저 Longhorn이 뭔지부터 알아봐야겠죠? Longhorn은 CNCF(Cloud Native Computing Foundation) 프로젝트 중 하나로, 쿠버네티스를 위한 분산 블록 스토리지 시스템입니다. 쉽게 말해, 쿠버네티스 클러스터 내의 여러 노드에 있는 디스크들을 모아서 하나의 거대한 가상 스토리지 풀을 만들고, 이 풀에서 PersistentVolume(영구 볼륨)을 생성하여 파드(Pod)에 제공해주는 역할을 해요.

    • 분산 스토리지 (Distributed Storage): 여러 노드의 저장 공간을 하나로 묶어 사용하며, 데이터가 여러 노드에 복제되어 저장되기 때문에 특정 노드에 문제가 생겨도 데이터 유실 걱정을 덜 수 있습니다.
    • CSI (Container Storage Interface, 컨테이너 스토리지 인터페이스) 호환: 쿠버네티스의 표준 스토리지 인터페이스인 CSI를 완벽하게 지원하기 때문에, 쿠버네티스에서 제공하는 StorageClass(스토리지 클래스), PersistentVolumeClaim(PVC, 영구 볼륨 클레임) 등의 기능을 문제없이 쓸 수 있습니다.
    • 스냅샷 및 백업/복구: 볼륨의 스냅샷을 찍고, S3와 같은 오브젝트 스토리지(Object Storage)나 NFS로 백업 및 복구를 쉽게 할 수 있어요. 제가 써보니 이 기능이 정말 편하더라고요!

    사실 처음엔 이런 분산 스토리지가 복잡하고 어렵게 느껴졌는데, Longhorn은 웹 UI가 워낙 직관적이라 금방 익숙해질 수 있었습니다. 덕분에 홈랩에서 여러 스테이트풀(Stateful) 애플리케이션들을 안정적으로 운영할 수 있게 되었죠. 예를 들어, 제 홈랩의 Prometheus(프로메테우스)나 Grafana(그라파나) 같은 모니터링 툴의 데이터도 Longhorn 볼륨에 저장해서 쓰고 있거든요.

    🛠️ 실전 구현: Longhorn 설치부터 PersistentVolume 사용까지

    이제 본격적으로 Longhorn을 설치하고 사용하는 방법을 알아볼 차례입니다. 저는 Helm(헬름)을 이용해서 설치하는 방법을 선호합니다. 배포가 간편하고 버전 관리가 용이하거든요. 시작하기 전에 몇 가지 준비물이 필요합니다.

    ✅ 준비물 확인

    1. Kubernetes 클러스터: 최소 3개 이상의 노드(Node)를 권장합니다. 각 노드에 충분한 여유 디스크 공간이 필요합니다.
    2. iSCSI initiator 설치: Longhorn은 iSCSI 프로토콜을 사용합니다. 각 노드에 <code>iscsi-initiator-utils (CentOS/RHEL) 또는 open-iscsi (Ubuntu/Debian) 패키지가 설치되어 있어야 합니다. 제가 처음 이걸 몰라서 삽질 좀 했습니다. 꼭 확인하세요!
      
      # CentOS/RHEL 계열
      sudo yum install -y iscsi-initiator-utils
      sudo systemctl enable --now iscsid
      
      # Ubuntu/Debian 계열
      sudo apt update
      sudo apt install -y open-iscsi
      sudo systemctl enable --now iscsid
      
    3. Helm 설치: 클러스터에 Helm이 설치되어 있어야 합니다.

    🚀 Longhorn 설치하기

    Helm을 이용하면 Longhorn 설치는 정말 간단합니다. 다음 명령어를 순서대로 실행해주세요.

    
    # 1. Longhorn Helm 레포지토리 추가
    helm repo add longhorn https://charts.longhorn.io
    helm repo update
    
    # 2. Longhorn 네임스페이스 생성 (선택 사항이지만 권장)
    kubectl create namespace longhorn-system
    
    # 3. Longhorn 설치 (기본 설정으로 충분합니다)
    helm install longhorn longhorn/longhorn --namespace longhorn-system --version 1.5.3 # 버전은 최신 안정 버전을 확인하세요!
    

    설치가 완료되면, 잠시 기다리면 모든 Longhorn 컴포넌트 파드들이 longhorn-system 네임스페이스에 배포되고 실행될 겁니다. kubectl get pods -n longhorn-system 명령으로 확인해보세요. 모든 파드가 Running 상태인지 확인하는 것이 중요합니다.

    🌐 Longhorn UI 접속하기

    Longhorn은 웹 UI를 제공해서 볼륨 상태, 노드 상태 등을 직관적으로 확인할 수 있습니다. UI에 접속하려면 보통 Ingress(인그레스, 외부 트래픽 진입점)를 설정하거나 NodePort(노드포트) 서비스를 생성하는 방법이 있습니다. 저는 간단하게 포트 포워딩(Port Forwarding)으로 접속해보겠습니다.

    
    # Longhorn UI 서비스 찾기
    kubectl get svc -n longhorn-system
    
    # UI 서비스 포트 포워딩 (예: longhorn-frontend 서비스)
    kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80
    

    이제 웹 브라우저에서 http://localhost:8080으로 접속하면 Longhorn 대시보드를 볼 수 있습니다. 🎉 드디어 Longhorn의 세상으로 들어온 것을 환영합니다!

    롱혼 웹 UI 대시보드 화면. 클러스터 노드 스토리지 상태 및 볼륨 목록 확인

    이미지 캡션: Longhorn 웹 UI 대시보드 화면. 클러스터 내 노드들의 스토리지 상태와 생성된 볼륨 목록을 한눈에 확인할 수 있습니다.

    ⚙️ StorageClass 생성 및 PersistentVolumeClaim 사용

    Longhorn이 설치되었으니, 이제 쿠버네티스에서 스토리지를 사용할 차례입니다. Longhorn은 기본 StorageClass(스토리지 클래스)를 자동으로 생성하지만, 필요에 따라 커스터마이징할 수 있습니다. 저는 기본 StorageClass를 사용하되, 예시로 PVC를 만들어보겠습니다.

    
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: my-longhorn-pvc
    spec:
      accessModes:
        - ReadWriteOnce # 하나의 파드에서만 읽기/쓰기 가능
      storageClassName: longhorn # Longhorn이 생성한 기본 StorageClass 이름
      resources:
        requests:
          storage: 5Gi # 5GB 볼륨 요청
    

    위 YAML 파일을 pvc.yaml로 저장하고 적용합니다.

    
    kubectl apply -f pvc.yaml
    kubectl get pvc my-longhorn-pvc
    

    PVC가 Pending에서 Bound 상태로 바뀌면 성공입니다. 이제 이 PVC를 사용하는 파드를 배포해봅시다. 간단한 Nginx(엔진엑스) 파드를 예시로 들어보겠습니다.

    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-with-longhorn
    spec:
      selector:
        matchLabels:
          app: nginx
      replicas: 1
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx:latest
              ports:
                - containerPort: 80
              volumeMounts:
                - name: longhorn-volume
                  mountPath: /usr/share/nginx/html # Nginx 웹 페이지 경로
          volumes:
            - name: longhorn-volume
              persistentVolumeClaim:
                claimName: my-longhorn-pvc # 위에서 생성한 PVC 이름
    

    위 YAML 파일을 nginx-deployment.yaml로 저장하고 적용하면, Nginx 파드가 Longhorn 볼륨을 마운트하여 시작됩니다. 이제 /usr/share/nginx/html 경로에 데이터를 써보면, 파드가 재시작되거나 다른 노드로 옮겨가도 데이터가 그대로 유지되는 것을 확인할 수 있습니다. 💡

    ⚠️ 삽질 경험: 주의사항 및 트러블슈팅

    제가 Longhorn을 운영하면서 겪었던 몇 가지 삽질 경험과 주의사항을 공유해드릴게요. 독자분들은 저처럼 고생하지 마시라고요! ㅎㅎ

    • iSCSI initiator 문제: 가장 흔한 문제 중 하나입니다. 노드에 iscsi-initiator-utils나 open-iscsi가 설치되어 있지 않거나, iscsid 서비스가 실행 중이 아니라서 볼륨 마운트가 실패하는 경우가 많습니다. 꼭 설치 여부와 서비스 상태를 확인해주세요!
    • 네트워크 대역폭: Longhorn은 데이터를 여러 노드에 복제하기 때문에, 노드 간 네트워크 대역폭이 충분해야 합니다. 네트워크가 느리면 볼륨 성능 저하로 이어질 수 있습니다. 특히 홈랩 환경에서는 유선 네트워크를 사용하는 것이 좋습니다.
    • 디스크 공간 관리: Longhorn은 노드의 가용 디스크 공간을 사용합니다. 볼륨을 너무 많이 생성하거나 스냅샷을 자주 찍으면 노드의 디스크 공간이 부족해질 수 있습니다. Longhorn UI에서 각 노드의 디스크 사용량을 주기적으로 확인하고, 불필요한 스냅샷이나 백업은 정리하는 습관을 들이는 것이 좋습니다.
    • 노드 드레인(Drain) 시 주의: 쿠버네티스 노드를 유지보수할 때 kubectl drain 명령을 사용하죠. Longhorn 볼륨이 마운트된 파드가 있는 노드를 드레인할 때는 Longhorn이 해당 볼륨을 다른 노드로 안전하게 옮길 수 있도록 충분한 시간을 주어야 합니다. 급하게 드레인하면 볼륨이 Faulted 상태가 될 수도 있더라고요.

    ✅ 검증 및 결과: Longhorn 볼륨 확인하기

    모든 설치와 설정이 끝났으니, 이제 제대로 작동하는지 확인해볼 차례입니다. 저는 Nginx 파드에 접속해서 간단한 HTML 파일을 만들어보고, 파드를 삭제 후 다시 생성하여 데이터 영속성을 확인하는 방법을 즐겨 씁니다.

    
    # Nginx 파드 이름 확인
    kubectl get pods -l app=nginx
    
    # Nginx 파드 내부로 접속하여 파일 생성
    kubectl exec -it <nginx-pod-name> -- bash
    echo "Hello from Longhorn!" > /usr/share/nginx/html/index.html
    exit
    
    # Nginx 서비스 포트 포워딩 (테스트용)
    kubectl port-forward svc/<nginx-service-name> 8080:80 # Nginx 서비스가 없다면 배포해야 합니다.
    
    # 웹 브라우저에서 http://localhost:8080 접속하여 내용 확인
    

    이제 Nginx 파드를 삭제했다가 다시 생성해보세요. 그리고 다시 접속해보면, 이전에 작성했던 “Hello from Longhorn!” 메시지가 그대로 남아있는 것을 확인할 수 있을 겁니다. 🎉 드디어 영구적인 스토리지를 성공적으로 구축한 거죠!

    Nginx 파드에 마운트된 Longhorn 볼륨과 데이터 영속성 확인 화면

    이미지 캡션: Nginx 파드에 성공적으로 마운트된 Longhorn 볼륨과 데이터 영속성을 확인하는 모습. 웹 브라우저를 통해 저장된 내용을 확인하고 있습니다.

    마무리: 13년차 서버실 지킴이의 한마디

    오늘은 Kubernetes Longhorn(쿠버네티스 롱혼) 스토리지의 설치부터 운영까지 제가 직접 경험한 내용을 바탕으로 자세히 설명해드렸습니다. Longhorn은 쿠버네티스 환경에서 영구 볼륨(Persistent Volume)을 안정적이고 유연하게 제공하는 훌륭한 분산 스토리지 솔루션이라고 생각합니다. 특히 홈랩이나 소규모 클러스터 환경에서는 초기 구축 비용이나 복잡성 측면에서 다른 엔터프라이즈 솔루션보다 훨씬 매력적이죠. 저는 Longhorn 덕분에 홈랩에서 다양한 스테이트풀 애플리케이션들을 걱정 없이 운영하고 있습니다. 백업과 스냅샷 기능도 정말 유용하구요!

    물론 Longhorn도 만능은 아닙니다. 대규모 엔터프라이즈 환경에서는 Ceph(세프)나 NetApp(넷앱) 같은 더욱 강력하고 복잡한 솔루션들이 필요할 수도 있습니다. 하지만 “간단하게 시작해서 점진적으로 확장하고 싶다”는 분들에게는 Longhorn이 정말 좋은 선택지가 될 거라고 확신합니다. 제 경험상, 중요한 건 어떤 툴을 쓰느냐보다, 그 툴을 얼마나 잘 이해하고 자신의 환경에 맞게 활용하느냐인 것 같아요. 저도 아직 배워야 할 게 많지만, 계속해서 새로운 기술들을 실험하고 삽질하면서 여러분께 유용한 정보를 공유해드리겠습니다.

    다음 글에서는 Longhorn의 백업 및 복구 기능에 대해 더 자세히 다뤄보거나, Longhorn 볼륨의 성능 튜닝 방법에 대해 이야기해볼까 합니다. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요! 다음에 또 유익한 정보로 찾아뵙겠습니다. 감사합니다! 😊

    Longhorn 주요 특징, 장단점 및 다른 쿠버네티스 스토리지 솔루션 비교 인포그래픽

    이미지 캡션: Longhorn의 주요 특징과 장단점을 요약한 인포그래픽. 쿠버네티스 스토리지 선택에 있어 Longhorn의 위치를 보여줍니다.

  • [Cloud] Podman vs Docker: Rootless 컨테이너 보안 및 활용 가이드

    [Cloud] Podman vs Docker: Rootless 컨테이너 보안 및 활용 가이드

    Podman vs Docker: Rootless 컨테이너 보안 및 활용 가이드

    인프라 엔지니어의 고민: 더 안전한 컨테이너 운영을 찾아서

    안녕하세요, 13년차 서버실 지킴이입니다. 요즘 인프라 엔지니어라면 컨테이너(Container) 기술을 빼놓고 이야기하기 어렵죠. 저도 처음엔 도커(Docker)가 세상에 나왔을 때, ‘와, 이거 진짜 물건이다!’ 하면서 열광했던 기억이 납니다. 개발 환경부터 운영 환경까지 컨테이너 덕분에 정말 많은 게 편리해졌거든요. 그런데 말입니다, 편리함 뒤에는 늘 그림자가 따르기 마련이더라고요. 특히 보안(Security) 측면에서는 늘 마음 한편이 불안했어요.

    도커 데몬(Docker daemon)이 항상 루트(root) 권한으로 실행된다는 점, 그리고 컨테이너 탈출(Container Escape) 같은 잠재적인 보안 위협들은 저를 계속해서 고민하게 만들었죠. 홈랩(Home Lab)에서 다양한 서비스를 돌리면서 ‘어떻게 하면 좀 더 안전하게 컨테이너를 운영할 수 있을까?’ 하는 고민은 저만의 숙제는 아닐 겁니다. 혹시 여러분도 이런 고민 해보신 적 있으신가요? 이 지점에서 제가 눈여겨보게 된 것이 바로 Podman(팟맨)입니다.

    오늘 글에서는 컨테이너 보안의 새로운 대안으로 떠오른 Podman과 우리가 오랫동안 써왔던 Docker를 비교하고, 특히 Rootless 컨테이너(Rootless Container) 개념을 중심으로 Podman의 장점과 활용법을 자세히 알려드리려고 합니다. 제가 직접 삽질해가며 얻은 경험들을 바탕으로, 여러분의 컨테이너 운영에 도움이 될 만한 실질적인 팁들을 공유해볼게요! 💡

    Podman과 Docker 아키텍처 비교 다이어그램

    Podman과 Docker의 아키텍처 비교. Podman은 데몬 없이 사용자 프로세스로 실행되어 보안 이점을 가집니다.

    Rootless 컨테이너란 무엇인가?

    쉽게 말해, Rootless 컨테이너는 루트(root) 권한 없이, 일반 사용자(non-root user) 권한으로 실행되는 컨테이너를 말합니다. 기존 도커는 도커 데몬(Docker Daemon)이 호스트(Host) 시스템에서 루트 권한으로 실행되고, 이 데몬이 컨테이너를 관리하는 구조였죠. 이게 왜 문제가 되냐면, 만약 컨테이너에 보안 취약점이 있어서 외부 공격자가 컨테이너를 탈출(Escape)하는 데 성공하면, 호스트 시스템의 루트 권한까지 획득할 수 있는 심각한 상황이 발생할 수 있기 때문입니다. ⚠️

    Podman은 태생부터 이런 보안 문제를 염두에 두고 설계되었어요. Podman은 도커와 달리 데몬(Daemon)이 없거든요. 즉, 백그라운드에서 항상 루트 권한으로 돌아가는 프로세스가 없다는 뜻입니다. Podman 명령어를 실행하면, 해당 명령어는 일반 사용자 권한으로 컨테이너를 생성하고 관리하게 됩니다. 처음 이걸 알았을 땐 ‘아, 이거다!’ 싶었거든요. 이렇게 되면 컨테이너를 탈출하더라도 일반 사용자 권한까지만 접근할 수 있으니, 호스트 시스템의 피해를 최소화할 수 있습니다. 보안적인 측면에서 봤을 때 정말 큰 이점이라고 할 수 있어요.

    Podman vs Docker: 핵심 차이점 비교

    Podman과 Docker는 컨테이너를 관리하는 도구라는 공통점이 있지만, 내부적으로는 꽤 많은 차이가 있습니다. 제가 직접 써보면서 느낀 주요 차이점들을 표로 정리해봤어요.

    특징 Podman Docker
    아키텍처 (Architecture) 데몬리스 (Daemonless): 백그라운드 데몬 없음. 사용자 프로세스로 실행. 데몬 기반 (Daemon-based): Docker 데몬이 루트 권한으로 백그라운드 실행.
    루트 권한 (Root Privileges) 기본 Rootless: 일반 사용자 권한으로 컨테이너 실행. 데몬이 루트 권한으로 실행. Rootless 모드는 실험적 또는 부가 기능.
    보안 (Security) 호스트 시스템에 대한 공격 표면(Attack Surface) 감소. 데몬 취약점 발생 시 호스트 시스템 전체 위험.
    이미지 빌드 (Image Build) Buildah(빌다)를 내부적으로 활용 (podman build). Docker Build 엔진 사용 (docker build).
    오케스트레이션 (Orchestration) Kubernetes Pods(쿠버네티스 파드)와 높은 호환성. podman generate kube로 YAML 생성 가능. Docker Swarm(도커 스웜), Docker Compose(도커 컴포즈) 지원.
    명령어 호환성 (CLI Compatibility) 대부분의 docker 명령어와 유사 (podman으로 대체 가능). 표준 Docker CLI.
    Podman vs Docker 핵심 차이점 비교 인포그래픽

    Podman과 Docker의 주요 특징들을 한눈에 비교한 표입니다. 특히 데몬리스 아키텍처와 Rootless 기본 지원이 Podman의 가장 큰 차이점이죠.

    Podman으로 Rootless 컨테이너 실행하기

    그럼 이제 Podman으로 Rootless 컨테이너를 직접 실행해보면서 그 편리함을 느껴볼 시간입니다! 제가 홈랩에서 주로 쓰는 CentOS Stream 9나 Fedora, 또는 Ubuntu 22.04 이상 환경에서는 Podman 설치가 아주 쉬워요.

    1. 팟맨 설치하기

    저는 CentOS Stream 9에서 진행했습니다. 여러분의 OS에 맞게 설치해주세요.

    
    # CentOS/Fedora
    sudo dnf install podman -y
    
    # Ubuntu/Debian
    sudo apt update
    sudo apt install podman -y
    

    설치가 완료되면, podman info 명령어로 잘 설치되었는지 확인해볼 수 있습니다.

    
    podman info
    

    여기서 중요한 건, 루트 권한 없이 실행해도 잘 동작한다는 점이에요! 🎉

    2. Rootless 컨테이너 실행해보기

    가장 기본적인 컨테이너 실행부터 시작해볼까요? Alpine 리눅스 컨테이너를 실행해보겠습니다.

    
    podman run --rm -it alpine sh
    

    컨테이너 내부에서 whoami 명령어를 실행해보면, root로 나올 거예요. ‘어? Rootless라면서 왜 컨테이너 안에서는 root지?’ 하고 저처럼 당황하실 수 있는데, 이건 컨테이너 내부의 루트 사용자가 호스트 시스템의 일반 사용자에 매핑(mapping)되었기 때문입니다. 즉, 컨테이너 안에서는 여전히 강력한 권한을 가지지만, 호스트 시스템에는 아무런 영향을 주지 못하는 ‘가짜 루트’인 셈이죠.

    이제 Nginx 웹 서버를 Rootless로 띄워볼까요?

    
    podman run -d --name my-nginx -p 8080:80 nginx
    

    잘 실행되었는지 확인하고, 웹 브라우저나 curl로 접속해보세요.

    
    podman ps
    curl localhost:8080
    

    정상적으로 Nginx 초기 페이지가 보인다면 성공입니다! ✅

    3. Podman의 파드(Pod) 기능 활용해보기

    Podman은 쿠버네티스(Kubernetes)의 파드(Pod) 개념을 네이티브(Native)하게 지원합니다. 여러 컨테이너가 동일한 네트워크 네임스페이스(Network Namespace)와 저장소(Storage)를 공유해야 할 때 아주 유용하거든요. 제가 이걸 처음 써봤을 때, ‘홈랩에 간이 쿠버네티스를 구축한 느낌인데?’ 하면서 감탄했어요. 🤭

    먼저 파드를 생성합니다.

    
    podman pod create --name my-web-app-pod -p 8080:80
    

    이제 이 파드 안에 Nginx와 다른 컨테이너를 넣어보죠.

    
    podman run -d --pod my-web-app-pod --name webserver nginx
    podman run -d --pod my-web-app-pod --name data-logger alpine sleep 1h
    

    podman pod ps 명령어로 파드와 그 안에 있는 컨테이너들을 확인할 수 있습니다.

    
    podman pod ps
    

    4. 이미지 빌드하기

    Podman은 Dockerfile을 이용한 이미지 빌드도 완벽하게 지원합니다. docker build 대신 podman build를 사용하면 되죠. Dockerfile 예시를 하나 만들어볼게요.

    
    # Dockerfile
    FROM alpine:latest
    RUN apk add --no-cache nginx
    COPY ./index.html /usr/share/nginx/html/index.html
    EXPOSE 80
    CMD ["nginx", "-g", "daemon off;"]
    

    그리고 간단한 index.html 파일도 만들어줍니다.

    
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello Podman!</title>
    </head>
    <body>
        <h1>Welcome to my Rootless Nginx with Podman!</h1>
    </body>
    </html>
    

    이제 이 두 파일이 있는 디렉토리에서 이미지를 빌드합니다.

    
    podman build -t my-custom-nginx .
    

    빌드된 이미지를 확인하고 실행해보세요.

    
    podman images
    podman run -d --name my-custom-web -p 8080:80 my-custom-nginx
    curl localhost:8080
    
    Podman 이미지 빌드 프로세스 다이어그램

    Podman을 이용한 이미지 빌드 과정입니다. Docker와 거의 동일한 명령어로 편리하게 이미지를 만들 수 있어요.

    Podman 운영 시 주의사항

    제가 Podman을 처음 접하면서 겪었던 몇 가지 ‘삽질’과 그 해결책을 공유합니다. 저처럼 헤매지 마시길 바라요! ㅎㅎ

    1. 낮은 포트(Low Port) 바인딩 문제: Rootless 컨테이너는 기본적으로 1024번 이하의 포트(예: 80, 443)를 직접 바인딩(Binding)할 수 없습니다. 이는 보안상의 이유로 일반 사용자에게 허용되지 않는 권한이거든요. 처음 Nginx를 80번 포트에 바로 연결하려고 했을 때, ‘왜 안 되지?’ 하면서 한참을 헤맸습니다. 😩
      해결책: 가장 쉬운 방법은 -p 8080:80처럼 1024번보다 높은 포트에 바인딩하는 것입니다. 아니면 Nginx나 Apache 같은 리버스 프록시(Reverse Proxy)를 호스트에 설정해서 80번 포트로 들어오는 트래픽을 컨테이너의 8080번 포트로 포워딩(Forwarding)하는 방법도 있어요. (sysctl net.ipv4.ip_unprivileged_port_start=80 같은 설정을 할 수도 있지만, 보안상 권장하지는 않습니다.)

    2. 볼륨 마운트(Volume Mount) 권한 문제: Rootless 컨테이너에서 호스트의 특정 디렉토리를 볼륨으로 마운트할 때 권한 문제가 발생할 수 있어요. 컨테이너 내부의 UID/GID(User ID/Group ID)가 호스트 시스템의 UID/GID와 매핑되는 방식 때문에 생기는 문제인데, /etc/subuid와 /etc/subgid 파일에 정의된 보조 UID/GID 범위가 적절히 설정되어 있지 않으면 문제가 됩니다.
      해결책: 이 파일들을 수동으로 편집하거나, Podman이 자동으로 설정하도록 합니다. 대부분의 경우 Podman 설치 시 자동으로 설정되지만, 가끔 문제가 생기면 이 파일을 확인해보세요. 저도 특정 디렉토리에 로그를 남기려다가 권한 문제로 고생 좀 했어요.

    3. systemd 통합: Podman은 systemd와 아주 잘 통합됩니다. podman generate systemd 명령어를 사용하면 실행 중인 컨테이너나 파드를 systemd 서비스로 등록할 수 있는 유닛(Unit) 파일을 자동으로 생성해줍니다. 이걸 몰랐을 때는 직접 유닛 파일을 만들었는데, 나중에 이 기능을 알고 얼마나 편했는지 몰라요! 🤩

    검증: Rootless 컨테이너로 더 안전한 운영

    Podman으로 Rootless 컨테이너를 운영하면서 제가 가장 크게 느낀 점은 ‘마음의 평화’입니다. 😅 호스트 시스템에 대한 잠재적인 보안 위협이 크게 줄어들기 때문에, 훨씬 안심하고 컨테이너를 돌릴 수 있게 되었거든요. podman ps, podman inspect 같은 명령어로 컨테이너 상태를 확인해보면, 도커와 거의 동일한 방식으로 작동한다는 걸 알 수 있습니다. 단지 실행 주체가 루트가 아닌 일반 사용자라는 것만 다를 뿐이죠.

    특히 ~/.local/share/containers 경로를 확인해보면, 모든 컨테이너 관련 파일들이 일반 사용자 홈 디렉토리 아래에 생성된 것을 볼 수 있어요. 이게 바로 Rootless 컨테이너의 핵심 증거입니다. 👍

    Rootless 컨테이너 보안 이점 다이어그램

    Rootless 컨테이너는 호스트 시스템의 루트 권한으로부터 격리되어 보안을 강화합니다.

    마무리: 언제 Podman을 써야 할까?

    오늘 Podman과 Docker의 차이점, 그리고 Rootless 컨테이너의 중요성에 대해 이야기해봤습니다. Podman은 다음과 같은 상황에서 특히 강력한 대안이 될 수 있다고 생각해요.

    • 보안이 최우선 고려사항일 때: 특히 다중 사용자 환경이나, 호스트 시스템의 보안을 최대한 강화하고 싶을 때 Rootless Podman은 탁월한 선택입니다.
    • 쿠버네티스 환경을 준비 중일 때: Podman은 쿠버네티스 파드 개념을 네이티브하게 지원하고, podman generate kube 같은 명령어로 쉽게 YAML 파일을 생성할 수 있어 쿠버네티스 학습 및 연동에 유리합니다.
    • 경량화된 컨테이너 환경이 필요할 때: 백그라운드 데몬 없이 필요한 시점에만 프로세스가 실행되므로, 리소스 효율성 측면에서도 장점이 있어요.

    물론 Docker도 여전히 막강한 생태계와 성숙한 도구들을 가지고 있습니다. Docker Compose나 Docker Swarm 같은 기능은 아직 Podman보다 더 안정적이고 편리한 측면이 많죠. 따라서 어떤 도구를 선택할지는 여러분의 특정 요구사항과 환경에 따라 달라질 겁니다.

    저의 13년차 경험상, ‘만능 도구’는 없습니다. 하지만 각 도구의 장단점을 정확히 알고 상황에 맞게 사용하는 것이 진정한 엔지니어의 자세라고 생각합니다. Podman은 컨테이너 보안과 효율성을 한 단계 끌어올릴 수 있는 정말 매력적인 도구임은 분명해요. 다음번에는 Podman Compose를 이용한 다중 컨테이너 애플리케이션 배포에 대해 더 깊이 다뤄보도록 하겠습니다. 긴 글 읽어주셔서 감사합니다! 👋