13년차의 서버실

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

[태그:] LLM 배포

  • [OpenStack] OpenStack에서 Ollama로 프라이빗 LLM 추론 환경 구축하기

    [OpenStack] OpenStack에서 Ollama로 프라이빗 LLM 추론 환경 구축하기

    [OpenStack] OpenStack과 Ollama로 프라이빗 LLM 추론 환경 구축하기

    OpenStack 인스턴스에 Ollama를 올려서 프라이빗 LLM 추론 환경을 만드는 이야기는 요즘 꽤 자주 나오더라고요. 저도 홈랩이랑 업무성 테스트 환경을 오가면서 이것저것 붙여봤는데, 공개 SaaS에 바로 데이터를 넣기 애매한 상황에서는 Ollama OpenStack 조합이 생각보다 실용적이었습니다. 특히 로그, 운영 문서, 내부 위키처럼 외부 반출이 조심스러운 데이터를 다룰 때는 더 그렇고요. 혹시 ‘GPU는 비싸고, 그렇다고 완전 관리형 서비스만 믿기엔 불안하다’ 같은 고민 해보신 적 있으신가요? 저는 딱 그 지점에서 이 구성을 꽤 오래 만지작거렸습니다.

    이번 글은 특정 벤더 홍보가 아니라, OpenStack AI 실험을 실제 인프라 관점에서 어떻게 굴려볼 수 있는지 정리한 사례입니다. 처음엔 이게 뭔가 싶었는데, 막상 해보니 구조는 단순합니다. OpenStack 가상머신 위에 Ollama를 올리고, 모델을 내려받고, 네트워크와 스토리지, 보안그룹만 제대로 잡아주면 됩니다. 다만 여기서 중요한 포인트가 몇 개 있습니다. CPU만으로도 테스트는 되지만, 추론 속도와 동시성은 기대치를 잘 관리해야 하거든요.

    Ollama OpenStack 기반 프라이빗 LLM 아키텍처 다이어그램

    OpenStack 기반 프라이빗 LLM 아키텍처를 한눈에 보여주는 개요 이미지입니다.

    1. 왜 굳이 OpenStack에 Ollama를 올렸을까

    쉽게 말해 Ollama는 로컬이나 서버에서 대형 언어 모델을 비교적 간단하게 실행하게 도와주는 런타임(runtime, 실행 환경)입니다. 반면 OpenStack은 가상머신, 네트워크, 볼륨 같은 인프라 자원을 묶어서 운영할 수 있게 해주는 IaaS(Infrastructure as a Service, 서비스형 인프라) 플랫폼이고요. 둘을 합치면 뭐가 좋으냐면, 프라이빗 LLM 실험 환경을 내가 통제하는 네트워크 안에서 만들 수 있습니다.

    제가 직접 해보니 이 조합의 장점은 아래처럼 정리되더라고요.

    • 데이터 통제: 추론 요청과 응답이 내부 네트워크에 머물 수 있습니다.
    • 배포 유연성: 테스트용 인스턴스와 운영성 인스턴스를 분리하기 쉽습니다.
    • 복제 가능성: 스냅샷(snapshot, 시점 복사)이나 이미지 기반으로 재현이 편합니다.
    • 네트워크 제어: 보안그룹(Security Group, 가상 방화벽)과 내부망만으로 노출 범위를 제한할 수 있습니다.
    • 확장 여지: 나중에 API Gateway, Reverse Proxy, 모니터링을 붙이기 좋습니다.

    반대로 단점도 있습니다. Ollama 자체는 비교적 쉽게 뜨는데, 모델 파일이 크고 디스크 I/O나 메모리 조건을 꽤 타는 편입니다. 그리고 OpenStack 쪽에서 Floating IP, 내부망, 볼륨 연결, 이미지 준비 같은 기본기가 안 되어 있으면 이상하게 자꾸 삽질하게 됩니다. 저도 처음엔 애플리케이션 문제가 아니라 네트워크 정책 때문에 응답이 안 와서 한참 헤맸거든요 ㅎㅎ

    2. Ollama OpenStack 구성 개념 쉽게 이해하기

    이 구성을 너무 어렵게 볼 필요는 없습니다. 전체 흐름은 이렇습니다.

    1. OpenStack에서 Ubuntu 계열 Linux 인스턴스를 하나 만듭니다.
    2. 필요하면 Block Storage(블록 스토리지) 볼륨을 따로 붙입니다.
    3. 서버에 Ollama를 설치합니다.
    4. 원하는 모델을 내려받아 로드합니다.
    5. 보안그룹과 Reverse Proxy(리버스 프록시, 요청 전달기)를 붙여 내부 사용자만 접근하게 합니다.
    6. curl이나 간단한 앱에서 API 호출로 검증합니다.

    여기서 핵심은 모델 실행 위치와 접근 제어입니다. 모델은 인스턴스 안에서 돌고, 사용자는 HTTP API로 붙습니다. 즉, AI 서비스처럼 보이지만 사실은 내부 애플리케이션 하나 더 배포하는 느낌에 가깝습니다. 그래서 인프라 엔지니어 입장에서는 웹 애플리케이션 운영하듯 접근하면 편합니다.

    구성 요소 역할 운영 포인트
    OpenStack Instance Ollama 실행 서버 vCPU, RAM, 디스크 여유 확인
    Volume 모델 파일 저장 루트 디스크와 분리 시 관리 편함
    Security Group 접근 제어 22, 11434 등 최소 포트만 허용
    Private Network 내부 통신 내부 서비스 전용망 권장
    Reverse Proxy TLS, 접근 경로 정리 Nginx 등으로 앞단 보호
    Ollama 모델 추론 런타임 모델 다운로드와 실행 담당

    3. 배포 전에 체크할 현실적인 준비 사항

    이 단계 무시하면 나중에 꼭 되돌아오게 됩니다. 실제로 써보니까 아래 세 가지가 제일 중요했습니다.

    3-1. 컴퓨트 리소스 계획

    정확한 수치는 환경마다 달라서 함부로 말하면 안 되지만, 적어도 메모리 여유는 넉넉하게 잡는 게 좋습니다. 작은 모델 테스트와 실서비스성 사용은 체감 차이가 큽니다. CPU-only 환경은 검증용으로는 괜찮아도, 응답 지연이 길어질 수 있습니다. GPU 패스스루(passthrough, 장치 직접 할당)나 vGPU를 쓰는 환경이라면 OpenStack 쪽 설정 난이도가 확 올라가니, 처음에는 CPU 기반 검증 후 확장하는 편이 안전합니다.

    3-2. 스토리지 분리

    모델 파일은 금방 용량을 먹습니다. 그래서 루트 디스크에 다 넣기보다 별도 볼륨을 붙여서 /var/lib/ollama 같은 경로를 분리하는 방식이 운영상 편했습니다. 백업, 확장, 재배포가 훨씬 수월하거든요.

    3-3. 보안 기준

    Ollama API를 외부에 바로 열어두는 건 추천하지 않습니다. 적어도 다음은 챙기세요.

    • 내부망 우선 배치
    • 보안그룹 최소 허용
    • SSH 키 기반 로그인
    • 필요 시 Nginx로 TLS 종료
    • 로그와 요청 이력 점검

    여기서 중요한 포인트! 프라이빗 LLM이라고 해서 자동으로 안전해지는 건 아닙니다. 외부 SaaS 대신 내부에 둔다는 의미일 뿐, 접근 제어를 대충 하면 오히려 더 위험해질 수 있습니다.

    4. 실전 구현: OpenStack 인스턴스에 Ollama 설치

    이제 본격적으로 해보겠습니다. 아래 예시는 Ubuntu 계열 Linux 인스턴스를 기준으로 정리했습니다. 저는 보통 먼저 인스턴스를 띄우고, 볼륨 붙이고, 방화벽부터 확인한 다음 애플리케이션을 올립니다. 순서를 바꾸면 나중에 원인 분석이 꼬이더라고요.

    4-1. 보안그룹과 인스턴스 준비

    필수 포트는 최소한으로만 엽니다. SSH용 22 포트, 그리고 내부 호출이 필요하면 Ollama 기본 포트로 알려진 11434를 내부 대역에만 허용하는 식이 무난합니다.

    # 예시: 서버 접속 후 기본 점검
    uname -a
    lsblk
    ip a
    sudo timedatectl set-timezone Asia/Seoul

    볼륨을 별도로 붙였다면 먼저 마운트합니다.

    sudo mkfs.ext4 /dev/vdb
    sudo mkdir -p /data/ollama
    sudo mount /dev/vdb /data/ollama
    sudo blkid /dev/vdb

    /etc/fstab에 UUID 기준으로 등록해두면 재부팅 후에도 안정적입니다.

    sudo cp /etc/fstab /etc/fstab.bak
    sudo editor /etc/fstab
    UUID=YOUR_VOLUME_UUID  /data/ollama  ext4  defaults,nofail  0  2

    4-2. Ollama 설치

    공식 설치 방식은 시점에 따라 바뀔 수 있으니 실제 배포 전에는 공식 문서를 꼭 같이 확인하시는 걸 권장합니다. 다만 큰 흐름은 비슷합니다. 서버에 패키지를 설치하고 서비스로 띄우는 구조입니다.

    curl -fsSL https://ollama.com/install.sh | sh
    sudo systemctl enable ollama
    sudo systemctl status ollama

    설치 후 서비스가 떠 있는지 먼저 확인하세요. 여기서 안 뜨면 모델 문제 보기 전에 서비스 로그부터 보는 게 맞습니다.

    sudo journalctl -u ollama -n 100 --no-pager
    OpenStack 인스턴스에서 Ollama 배포 구성을 설명하는 이미지

    배포 과정 중 네트워크와 스토리지, 서비스 구성을 설명하는 이미지입니다.

    4-3. 데이터 경로 분리

    모델 저장 경로를 별도 볼륨으로 빼고 싶다면 서비스 환경 변수를 조정하는 식으로 운영할 수 있습니다. 배포 방식에 따라 경로 정의가 다를 수 있어서 저는 서비스 오버라이드 방식으로 처리하는 편입니다.

    sudo mkdir -p /data/ollama/models
    sudo systemctl edit ollama
    [Service]
    Environment="OLLAMA_MODELS=/data/ollama/models"
    Environment="OLLAMA_HOST=0.0.0.0:11434"
    sudo systemctl daemon-reload
    sudo systemctl restart ollama
    sudo systemctl show ollama --property=Environment

    여기서 OLLAMA_HOST를 0.0.0.0으로 열었다면, 네트워크 레벨에서 반드시 접근 대역을 제한하세요. 애플리케이션이 열려 있다는 건 생각보다 금방 스캔됩니다.

    4-4. 모델 다운로드와 실행

    모델 이름은 시점마다 추가되거나 바뀔 수 있으니, 실제 사용 시에는 현재 지원 목록을 직접 확인하셔야 합니다. 이 글에서는 특정 최신 모델명을 무리하게 적기보다, 일반적인 사용 흐름 위주로 보겠습니다.

    ollama pull llama3
    ollama list
    ollama run llama3

    제가 직접 해보니 처음 실행은 모델 준비 때문에 시간이 걸릴 수 있습니다. 이때 ‘멈췄나?’ 싶어서 여러 번 다시 치면 오히려 꼬입니다. 디스크 사용량과 네트워크 다운로드 상태를 같이 보면서 기다리는 게 낫습니다.

    4-5. API 호출 테스트

    Ollama는 HTTP API 기반으로 붙이기 쉬운 편입니다. 간단한 curl 테스트부터 해보면 감이 금방 옵니다.

    curl http://127.0.0.1:11434/api/generate \
      -H "Content-Type: application/json" \
      -d '{
        "model": "llama3",
        "prompt": "OpenStack 환경에서 프라이빗 LLM을 운영할 때 주의할 점 3가지를 설명해줘.",
        "stream": false
      }'

    내부 다른 서버에서 붙일 거라면 127.0.0.1 대신 인스턴스의 프라이빗 IP를 사용하면 됩니다. 다만 이 경우 보안그룹과 OS 방화벽을 같이 확인하세요.

    5. Ollama와 Reverse Proxy로 운영 편의성 높이기

    실무 느낌으로 가려면 앞단에 Reverse Proxy를 두는 게 편합니다. Nginx를 붙이면 TLS 종료, 접근 경로 통합, 간단한 접근 제어가 가능하거든요. 나중에 인증 프록시나 API Gateway로 확장하기도 좋습니다.

    sudo apt update
    sudo apt install -y nginx
    server {
        listen 80;
        server_name ollama.internal;
    
        location / {
            proxy_pass http://127.0.0.1:11434;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
    sudo nginx -t
    sudo systemctl reload nginx

    여기까지 오면 내부 DNS나 /etc/hosts로 이름을 붙여서 접근하기도 편해집니다. 작은 차이 같아도 운영성은 꽤 올라갑니다. 특히 여러 추론 서버를 비교 테스트할 때요.

    6. ⚠️ 실제 겪었던 문제와 트러블슈팅

    이 섹션은 좀 현실적으로 적어볼게요. 저는 이 작업하면서 애플리케이션보다 인프라 쪽에서 더 많이 막혔습니다.

    6-1. 포트는 열었는데 접속이 안 되는 문제

    처음엔 분명 11434를 열었는데 외부에서 응답이 없었습니다. 알고 보니 셋 중 하나였습니다.

    • 서비스가 127.0.0.1에만 바인딩됨
    • OpenStack 보안그룹에는 열었지만 OS 방화벽이 차단
    • Floating IP가 아닌 내부망 전용 인스턴스라 접근 경로 자체가 다름

    해결은 단순합니다. ss -lntp, ufw status, 보안그룹 규칙을 순서대로 확인하면 됩니다.

    ss -lntp | grep 11434
    sudo ufw status
    ip route

    6-2. 디스크 공간 부족

    작은 테스트 VM에서 시작했다가 모델을 몇 개만 내려받아도 금방 공간이 줄어듭니다. 이건 진짜 많이 겪는 문제예요. 루트 디스크를 아끼겠다고 너무 작게 잡으면 결국 다시 옮기게 됩니다. 그래서 초반부터 모델 저장 경로를 분리하는 걸 추천드립니다.

    6-3. 응답 속도가 너무 느린 문제

    이건 대부분 버그가 아니라 리소스 문제입니다. CPU 기반에서는 특히 그렇습니다. 프롬프트가 길거나 동시에 여러 요청이 들어오면 체감이 확 느려집니다. 저도 처음엔 설정이 잘못된 줄 알았는데, 실제로는 기대치를 조정해야 하는 영역이더라고요. 검증 목적과 운영 목적을 구분해서 보셔야 합니다.

    6-4. 재부팅 후 경로가 꼬이는 문제

    볼륨 마운트를 수동으로만 해두면 재부팅 뒤 서비스가 이상한 위치를 바라보는 경우가 있습니다. 꼭 fstab와 systemd 서비스 환경 변수를 함께 확인하세요. 이거 한 번 놓치면 ‘어제 됐는데 왜 오늘 안 되지?’ 모드 들어갑니다 ㅎㅎ

    Ollama OpenStack API 테스트와 프록시 검증 운영 이미지

    API 테스트와 프록시 구성을 검증하는 실제 운영 흐름을 보여주는 이미지입니다.

    7. 검증과 결과 확인

    구축이 끝났으면 이제 ‘떠 있다’가 아니라 ‘쓸 수 있다’를 확인해야 합니다. 저는 보통 아래 순서로 검증합니다.

    1. 서비스 상태 확인
    2. 로컬 API 호출 확인
    3. 내부망 다른 서버에서 호출 확인
    4. 리소스 사용량 확인
    5. 로그 확인
    systemctl status ollama
    curl http://127.0.0.1:11434/api/tags
    curl http://YOUR_PRIVATE_IP:11434/api/tags
    free -h
    df -h
    sudo journalctl -u ollama -n 50 --no-pager

    응답이 정상이고, 모델 목록이 보이고, 간단한 프롬프트가 처리되면 1차 검증은 통과입니다. 여기서 한 걸음 더 나가면 Prometheus(프로메테우스, 메트릭 수집기)나 Grafana(그라파나, 대시보드 도구) 같은 모니터링 체계를 붙이는 것도 좋습니다. 아직 이 글에서는 거기까지 깊게 다루진 않지만, 다음 글에서 OpenStack AI 운영 관점의 모니터링도 다뤄볼 예정입니다.

    간단한 체크리스트도 남겨보겠습니다.

    • ✅ 인스턴스 재부팅 후 서비스 자동 시작 확인
    • ✅ 모델 저장 경로가 의도한 볼륨인지 확인
    • ✅ 내부 대역 외 접근 차단 확인
    • ✅ 테스트 프롬프트 응답 정상 확인
    • ✅ 로그에 반복 오류 없는지 확인

    드디어 됐다! 싶은 순간은 사실 OpenStack 인스턴스가 뜨는 순간이 아니라, 재부팅 후에도 똑같이 잘 동작할 때입니다. 이건 진짜 운영해보면 공감하실 겁니다.

    OpenStack AI 환경에서 Ollama 결과 검증을 보여주는 대시보드 이미지

    구축 완료 후 상태 점검과 결과 검증을 시각적으로 보여주는 이미지입니다.

    8. 어떤 환경에 특히 잘 맞는가

    모든 곳에 이 구성이 정답은 아닙니다. 하지만 아래 같은 경우에는 꽤 잘 맞습니다.

    사용 상황 적합도 이유
    내부 문서 요약/검색 보조 높음 데이터 외부 반출 부담을 줄이기 좋음
    개발팀 실험용 AI 추론 환경 높음 빠르게 띄우고 지우기 쉬움
    고성능 대규모 실시간 서비스 중간 리소스 계획과 확장 설계가 더 중요함
    완전 비관리형 개인 테스트 높음 홈랩과 사내 테스트베드에 잘 맞음

    사실 저는 이 구성을 ‘최종 답안’보다는 프라이빗 LLM 운영 감각을 익히는 출발점으로 봅니다. 나중에는 컨테이너 기반으로 옮기거나, 쿠버네티스 위에 올리거나, 인증 계층을 붙이거나, 모델 라우팅을 나누는 식으로 진화할 수 있거든요.

    9. 정리하며: OpenStack AI 첫걸음으로는 꽤 괜찮았습니다

    Ollama OpenStack 조합은 화려하진 않지만, 인프라 엔지니어 입장에서 이해하기 쉽고 제어하기 편한 방식입니다. 제가 직접 해보니 핵심은 세 가지였습니다. 리소스 계획, 스토리지 분리, 그리고 접근 제어입니다. 이 셋만 제대로 잡아도 절반은 성공입니다.

    처음엔 단순히 ‘내부망에서 LLM 한 번 돌려보자’ 정도로 시작했었는데, 실제로 써보니까 운영 포인트가 꽤 명확했습니다. 특히 OpenStack을 이미 쓰고 있는 조직이라면 기존 VM 운영 경험을 그대로 가져올 수 있다는 게 큽니다. 반대로, 모델 성능 자체를 극한까지 뽑아내는 목적이라면 별도 GPU 전략과 오케스트레이션 설계가 더 필요하겠죠.

    혹시 지금 AI 추론 환경을 사내에 조용히 검증해보고 싶으셨다면, 이 방식부터 시작해보셔도 좋겠습니다. 이전 글에서 다뤘던 스토리지와 네트워크 기본기와도 연결되는 내용이고, 다음 글에서는 인증 프록시를 붙여 여러 팀이 함께 쓰는 형태까지 이어서 정리해보겠습니다. 여기서 중요한 포인트는 거창한 아키텍처보다, 작게 시작해서 반복 가능하게 만드는 것입니다. 그게 결국 오래 가더라고요. 🎉

    프라이빗 LLM 구축 단계와 운영 체크포인트 요약 이미지

    구축 절차와 운영 체크포인트를 한 장으로 정리한 요약 이미지입니다.

  • [AI] vLLM 배포 시 흔히 겪는 메모리 및 GPU 활용 문제 해결 가이드

    [AI] vLLM 배포 시 흔히 겪는 메모리 및 GPU 활용 문제 해결 가이드

    vLLM 배포 시 흔히 겪는 메모리 및 GPU 활용 문제 해결 가이드

    안녕하세요, 13년차 서버실 지킴이입니다. 요즘 인프라 엔지니어들 사이에서 LLM(Large Language Model) 배포는 뜨거운 감자죠. 저도 홈랩에서 이것저것 시도해보다가 vLLM이라는 라이브러리를 써봤는데, 처음엔 정말 “이거 물건이다!” 싶었거든요. 그런데 막상 모델을 올리고 트래픽을 좀 주니 메모리는 터져나가고, GPU 활용률은 바닥을 기는 경험, 혹시 있으신가요? ⚠️ 제가 직접 겪었던 vLLM 메모리 최적화와 vLLM GPU 활용 문제, 그리고 그 해결 과정을 솔직하게 공유해볼까 합니다. LLM 추론 서버를 운영하시면서 비슷한 vLLM 문제 해결에 어려움을 겪는 분들께 멘토처럼 도움이 되었으면 좋겠습니다.

    vLLM 아키텍처 다이어그램: LLM 추론 서버에서 GPU 메모리, KV 캐시, PagedAttention의 관계

    vLLM이 어떻게 LLM 추론을 최적화하는지, 그리고 왜 메모리와 GPU 활용률 문제가 발생하는지 전체적인 그림을 먼저 보고 가시죠.

    vLLM, 왜 메모리와 GPU를 많이 쓸까요? (핵심 개념 파고들기)

    vLLM은 LLM 추론(Inference) 속도를 획기적으로 개선하기 위해 개발된 오픈소스 라이브러리입니다. 특히 두 가지 핵심 기술 덕분에 주목받고 있는데요.

    • PagedAttention (페이지드 어텐션): 기존 LLM 추론 방식은 각 시퀀스(Sequence)마다 어텐션 키(Key)와 밸류(Value) 캐시(Cache), 일명 KV Cache를 GPU 메모리에 통째로 할당했습니다. 이는 마치 OS의 페이지 메모리 관리와 비슷하게, KV Cache를 페이지(Page) 단위로 쪼개어 필요한 만큼만 동적으로 할당하고 해제하는 방식입니다. 덕분에 메모리 단편화(Fragmentation)를 줄이고, GPU 메모리 사용 효율을 극대화할 수 있죠.
    • Continuous Batching (연속 배치 처리): 일반적인 배치 처리(Batching)는 모든 요청이 완료될 때까지 기다렸다가 다음 배치를 처리합니다. 하지만 Continuous Batching은 GPU가 유휴 상태가 되지 않도록, 새로운 요청이 들어오면 현재 처리 중인 배치에 동적으로 추가하여 계속해서 GPU를 채워 넣는 방식입니다. 이 덕분에 GPU 활용률(Utilization)을 높이고, 전체 처리량(Throughput)을 증가시킬 수 있습니다.

    이렇게 들으면 만능 같죠? 그런데 이 두 가지 기술이 역설적으로 vLLM 메모리 문제와 vLLM GPU 활용 문제를 일으키는 주범이 되기도 합니다. PagedAttention은 KV Cache를 효율적으로 쓰지만, 모델 사이즈가 크고 동시 요청이 많아지면 결국 전체 KV Cache가 커질 수밖에 없어요. Continuous Batching은 GPU를 계속 돌리지만, 모델 로딩 자체에 필요한 메모리도 만만치 않거든요. 제가 처음 이걸 접했을 때 “분명 효율적이라고 했는데, 왜 내 서버는 빌빌거리지?” 싶었답니다. 😅

    vLLM 기본 배포: 첫걸음부터 문제 발생까지

    vLLM을 배포하는 방법은 정말 간단합니다. 저는 주로 Docker를 사용하거나, Python 환경에서 pip으로 설치해서 쓰곤 합니다. 여기서는 Python 코드를 통해 vLLM 서버를 띄우는 기본적인 예시를 보여드릴게요.

    1. vLLM 설치:
      pip install vllm
    2. vLLM 서버 실행 예제:

      간단한 Python 스크립트로 vLLM 서버를 실행하고, 모델을 로딩할 수 있습니다. 저는 주로 Hugging Face Hub에 있는 모델을 사용합니다.

      
      from vllm import LLM, SamplingParams
      
      # LLM 모델 로딩
      # 'meta-llama/Llama-2-7b-chat-hf' 와 같은 모델은 상당한 GPU 메모리를 요구합니다.
      # 실제 운영 환경에서는 더 작은 모델이나 양자화된 모델을 고려해야 합니다.
      llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0", # 예시로 작은 모델 사용
                dtype="auto", # bfloat16, float16 등 GPU에 따라 자동으로 선택
                gpu_memory_utilization=0.9 # 이 설정이 중요합니다! 뒤에서 자세히 다룹니다.
               )
      
      # 프롬프트 리스트
      prompts = [
          "Hello, my name is",
          "The president of the United States is",
          "The capital of France is",
          "What is the meaning of life?",
      ]
      
      # 샘플링 파라미터 설정
      sampling_params = SamplingParams(temperature=0.7, top_p=0.95, max_tokens=128)
      
      # 추론 실행
      outputs = llm.generate(prompts, sampling_params)
      
      # 결과 출력
      for prompt, output in zip(prompts, outputs):
          generated_text = output.outputs[0].text
          print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
      
      print("vLLM 서버가 성공적으로 모델을 로딩하고 추론을 수행했습니다.")
              
    vLLM 서버 모델 로딩 및 대기 상태를 보여주는 CLI 화면

    위 코드에서 모델을 로딩하는 <code>LLM(model=…) 부분이 핵심입니다. 이때 지정된 모델이 GPU 메모리에 올라가게 되죠. 처음엔 잘 작동하는 것 같다가도, 여러 요청이 몰리면 문제가 생기기 시작합니다. 바로 이 지점에서 vLLM 메모리 최적화의 필요성을 느끼게 됩니다.

    ⚠️ 제가 겪었던 vLLM 메모리 및 GPU 활용 문제와 해결책

    제가 vLLM을 사용하면서 가장 많이 부딪혔던 문제는 크게 두 가지였습니다. 바로 GPU 메모리 부족(OOM: Out Of Memory)과 기대보다 낮은 GPU 활용률이었죠. 마치 “야근은 했는데 성과가 없는” 상황이랄까요? 삽질 좀 했습니다 ㅎㅎ. 하나씩 해결 과정을 공유해 드릴게요.

    1. GPU 메모리 부족 (OOM) 문제 해결

    가장 흔한 문제입니다. 모델을 로딩하자마자 OOM이 나거나, 몇몇 요청 처리 후 서버가 죽는 경우죠. 이건 주로 KV Cache 때문에 발생합니다.

    1. gpu_memory_utilization 옵션 조절 (가장 중요!)

      이 옵션은 vLLM이 GPU 메모리 중 KV Cache에 할당할 최대 비율을 지정합니다. 기본값은 0.9 (90%)인데, 이 값을 잘 조절해야 합니다. 모델 자체를 로딩하는 데 필요한 메모리도 있기 때문에, 너무 높으면 OOM이 날 수 있어요. 그렇다고 너무 낮추면 KV Cache 공간이 부족해져서 동시 처리량이 줄어듭니다.

      
      llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
                gpu_memory_utilization=0.7 # 70%로 낮춰서 시도해보세요.
               )
              

      💡 팁: GPU 메모리가 넉넉하다면 0.8~0.9까지도 괜찮지만, 10GB 미만 GPU라면 0.6~0.7부터 시작해서 조금씩 올려보는 걸 추천합니다. vLLM 메모리 최적화의 핵심입니다.

    2. 모델 양자화 (Quantization) 적용

      모델 자체의 크기를 줄이는 가장 효과적인 방법입니다. LLM 양자화는 모델의 가중치(Weights)를 낮은 비트(예: FP32 → FP16 → INT8 → INT4)로 표현하여 메모리 사용량을 줄입니다. 정확도는 조금 희생될 수 있지만, 체감 성능 저하 없이 메모리를 크게 절약할 수 있어요.

      
      llm = LLM(model="TheBloke/Llama-2-7B-Chat-GPTQ", # GPTQ 양자화된 모델 사용 예시
                dtype="auto" # 양자화된 모델은 dtype을 auto로 두는 것이 일반적
               )
              

      저도 홈랩에서 8비트(INT8)나 4비트(INT4) 양자화(Quantization)된 모델을 많이 사용하는데, 특히 NVIDIA RTX 30/40 시리즈 같은 컨슈머 GPU에서는 필수적이라고 느껴지더라고요.

    3. 더 작은 모델 선택

      물론 가장 확실한 방법입니다. Llama-2-70B 같은 거대 모델 대신, Llama-2-7B나 TinyLlama 같은 작은 모델을 사용하는 거죠. 요구사항에 맞춰 모델을 선택하는 것이 LLM 추론 서버 운영의 기본입니다.

    4. max_model_len 및 max_num_seqs 조절

      max_model_len은 모델이 처리할 수 있는 최대 시퀀스 길이(토큰 수)를 의미하고, max_num_seqs는 한 번에 처리할 수 있는 최대 동시 시퀀스 수를 의미합니다. 이 값들이 너무 크면 KV Cache가 그만큼 더 많이 필요해집니다. 특히 max_model_len은 프롬프트 길이와 생성될 답변 길이를 모두 고려해야 해서 중요합니다.

      
      llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
                max_model_len=1024, # 최대 토큰 길이 조절
                max_num_seqs=256 # 최대 동시 시퀀스 수 조절
               )
              
    5. 메모리 스와핑 활용

      vLLM은 KV Cache가 GPU 메모리에 부족할 경우 CPU 메모리로 스와핑하는 기능을 제공합니다. 이는 OOM을 방지하는 최후의 보루가 될 수 있지만, 성능 저하가 발생할 수 있으니 주의해야 합니다. 최신 vLLM 버전의 경우 PagedAttention 자체가 이를 효율적으로 처리하므로, 명시적 설정이 필요한 경우는 많지 않습니다.

      
      llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
                # 최신 vLLM에서는 PagedAttention 자체가 메모리 관리를 효율적으로 처리합니다
                # 필요시 vLLM 버전에 따라 추가 옵션이 사용 가능할 수 있습니다
               )
              

      사실 vLLM의 PagedAttention은 이미 OS의 가상 메모리 관리처럼 동작해서, 필요한 경우 자동으로 메모리를 활용하는 경향이 있습니다. 버전에 따라 세부 옵션이 달라질 수 있으므로, 공식 문서를 참고하는 것이 좋습니다.

    2. 낮은 GPU 활용률 문제 해결

    Continuous Batching 덕분에 GPU 활용률이 높아야 하는데, 생각보다 낮게 나오는 경우가 있습니다. 이건 주로 요청이 너무 적거나, 모델의 추론 속도가 너무 빨라서 GPU가 대기하는 시간이 길어질 때 발생합니다.

    1. 동시 요청 늘리기

      가장 직접적인 방법입니다. vLLM은 Continuous Batching을 통해 동시 요청이 많을수록 GPU를 더 효율적으로 사용합니다. 테스트 환경이라면 벤치마크 툴로 요청 수를 늘려보세요.

    2. 배치 사이즈 최적화

      위에서 언급한 max_num_seqs와 max_model_len 값을 적절히 조절하는 것이 중요합니다. 너무 작으면 GPU가 놀고, 너무 크면 OOM이 나거나 지연 시간(Latency)이 길어질 수 있습니다. 여러 번 테스트하면서 최적값을 찾아야 합니다. “삽질 좀 해봐야 내 것이 되는 법”이거든요. 😉

    3. 모델 로딩 시 enforce_eager=True 옵션 (디버깅 목적)

      이 옵션은 디버깅 목적으로 GPU 활용률을 강제로 높여볼 때 사용해볼 수 있습니다. 하지만 실제 운영 환경에서는 오버헤드가 발생할 수 있으니 주의해야 합니다.

      
      llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
                enforce_eager=True # 디버깅 및 테스트 목적으로 사용
               )
              

      이 옵션은 주로 vLLM 내부 동작을 확인하거나 특정 문제를 디버깅할 때 유용하고, 일반적인 LLM 추론 서버 운영에는 권장되지 않습니다.

    nvidia-smi를 통해 확인한 vLLM GPU 메모리 및 활용률 개선 결과 스크린샷

    이렇게 여러 옵션을 조절해가면서 저의 홈랩 GPU 자원을 최대한 쥐어짜내는 데 성공했습니다! 물론 처음부터 완벽하게 되는 건 아니었고, nvidia-smi 명령어를 옆에 띄워놓고 메모리 사용량과 활용률을 실시간으로 보면서 값들을 바꿔가며 테스트했죠. vLLM 트러블슈팅은 인내심과의 싸움이더라고요.

    ✅ vLLM 최적화, 제대로 됐는지 확인해볼까요?

    이제 vLLM 메모리 최적화와 vLLM GPU 활용 개선을 위한 여러 방법을 적용해봤으니, 실제로 효과가 있는지 확인해봐야겠죠? 저는 주로 nvidia-smi와 vLLM의 로그 메시지를 활용해서 검증합니다.

    1. nvidia-smi로 GPU 상태 확인

      터미널에서 nvidia-smi를 주기적으로 실행하거나, watch -n 1 nvidia-smi 명령으로 1초마다 갱신되는 정보를 확인해보세요. 최적화 전후로 다음과 같은 변화를 볼 수 있을 겁니다.

      • GPU Memory Usage (메모리 사용량): OOM이 발생하지 않고, 설정한 gpu_memory_utilization에 맞춰 안정적으로 유지되는지 확인합니다.
      • GPU Utilization (GPU 활용률): 요청이 들어올 때 GPU-Util이 70~90% 이상으로 높게 유지되는지 확인합니다. 만약 낮다면 동시 요청 수를 늘리거나 배치 사이즈를 조절해봐야 합니다.
      
      watch -n 1 nvidia-smi
              

      저도 처음엔 GPU-Util이 20~30%에서 빌빌거리다가, 설정을 만지고 나니 80% 이상으로 치솟는 것을 보고 “드디어 됐다!” 하고 환호성을 질렀답니다. 🎉

    2. vLLM 로그 확인

      vLLM은 시작 시 모델 로딩 정보, KV Cache 할당 정보 등을 상세하게 로그로 남겨줍니다. 이 로그를 통해 내 설정이 제대로 적용되었는지 확인할 수 있습니다.

      • vLLM engine is initialized with model: ...
      • GPU memory usage: X.X GB (Y.Y%) for model weights, Z.Z GB for KV cache.

      특히 KV Cache에 할당된 메모리 양이 gpu_memory_utilization과 잘 맞아떨어지는지 확인하는 것이 중요합니다.

    3. 부하 테스트 (Load Testing)

      실제 서비스 환경을 시뮬레이션하기 위해 부하 테스트 툴(예: `locust`, `wrk`)을 사용해서 동시 요청을 발생시켜보고, 이때의 GPU 상태와 추론 지연 시간(Latency), 처리량(Throughput)을 측정해보는 것이 가장 확실한 방법입니다.

    마무리하며: 삽질은 경험이 되고, 경험은 노하우가 됩니다.

    오늘은 vLLM 배포 시 흔히 겪는 메모리 및 GPU 활용 문제 해결 가이드를 저의 경험을 토대로 이야기해봤습니다. 돌이켜보면 인프라 엔지니어의 삶은 끊임없는 트러블슈팅(Troubleshooting)의 연속인 것 같아요. 특히 LLM 추론 서버 같은 새로운 기술 스택을 다룰 때는 더욱 그렇죠.

    결론적으로 vLLM 최적화의 핵심은 다음과 같습니다.

    • gpu_memory_utilization 값을 적절히 조절하여 KV Cache 메모리를 효율적으로 관리하는 것.
    • 양자화(Quantization)된 모델을 활용하여 모델 자체의 메모리 점유율을 낮추는 것.
    • max_model_len과 max_num_seqs를 서비스 요구사항과 GPU 자원에 맞춰 최적화하는 것.
    • 끊임없이 nvidia-smi로 모니터링하며 실험하고, 최적의 파라미터를 찾아내는 인내심!
    vLLM 메모리 및 GPU 활용 최적화 핵심 팁 요약 인포그래픽

    제가 처음 vLLM을 만졌을 때 “이거 왜 이래?” 하면서 답답해했던 기억이 생생하네요. 하지만 이런 삽질 경험 하나하나가 쌓여서 결국 저만의 vLLM 트러블슈팅 노하우가 되더라고요. 혹시 더 궁금한 점이나 다른 vLLM 문제 해결 팁이 있다면 댓글로 알려주세요. 다음번에는 더 깊이 있는 LLM 배포 팁, 예를 들어 멀티 GPU 환경이나 분산 추론에 대해 다뤄볼 수도 있겠네요. 그때까지 여러분의 서버실도 평안하시기를 바랍니다!