13년차의 서버실

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

[태그:] 로컬LLM

  • 로컬 LLM을 n8n 자동화에 연결하기 — llama-server API로 로그 분류기 만들기 (홈랩 CPU)

    1편에서 GPU 없이 로컬 LLM을 띄웠고, 2편에서 한국어엔 Qwen2.5-3B가 낫다는 걸 실측으로 확인했습니다. 이제 마지막 단계 — 이 로컬 LLM을 실제 자동화에 연결합니다. llama.cpp를 OpenAI 호환 API 서버로 띄우고, n8n 워크플로우에 붙여서 일을 시켜 봤습니다. 결론부터: n8n이 로컬 LLM을 호출해 nginx 에러 로그를 “ERROR”로 정확히 분류하는 데까지 성공했습니다. API 요금 0원, 전부 홈랩 안에서.

    1. llama-server로 OpenAI 호환 API 띄우기

    llama.cpp에는 llama-server라는 실행 파일이 함께 빌드됩니다(1편 참고). 이걸 띄우면 OpenAI의 /v1/chat/completions와 똑같은 형식의 API가 생깁니다.

    ./build/bin/llama-server \
      -m /root/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf \
      -c 4096 -t 4 \
      --host 0.0.0.0 --port 8080

    여기서 OpenAI 호환이라는 점이 핵심입니다. 세상의 수많은 도구·라이브러리·자동화 노드가 이미 “OpenAI API 형식”을 표준으로 지원하니까, URL만 내 홈랩 서버로 바꾸면 그대로 붙습니다. 외부에 돈 내고 부르던 걸 로컬로 갈아끼우는 셈이죠. --host 0.0.0.0은 같은 네트워크의 다른 기기(n8n 등)에서 접근하게 열어 주는 옵션입니다.

    2. API가 진짜 되는지 먼저 확인

    워크플로우에 붙이기 전에 curl로 직접 찔러 봤습니다.

    curl http://127.0.0.1:8080/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "qwen2.5-3b",
        "messages": [{"role":"user","content":"홈랩 자동화에 로컬 LLM을 쓰면 좋은 점 한 문장."}],
        "temperature": 0.3
      }'

    실제 응답(발췌)입니다.

    "content": "로컬 LLM은 ... 홈랩 로그의 특징을 정확하게 분류하는 데 유용할 수 있습니다.",
    "usage": { "prompt_tokens": 60, "completion_tokens": 50, "total_tokens": 110 }

    토큰 사용량까지 OpenAI와 똑같은 스키마로 돌아옵니다. 이제 이걸 자동화 도구에 물릴 차례입니다.

    3. n8n 설치 — 여기서 함정을 밟았다

    자동화 도구로는 오픈소스 워크플로우 툴 n8n을 골랐습니다. 그런데 설치가 한 번에 되지 않았습니다. 최신 Debian(13)에서 npm install -g n8n이 네이티브 모듈(isolated-vm) 빌드 단계에서 죽더군요.

    ModuleNotFoundError: No module named 'distutils'
    gyp ERR! configure error
    npm ERR! ... isolated-vm ... not ok  (exit 127)

    원인은 Python 3.12에서 distutils가 표준 라이브러리에서 제거됐기 때문입니다. 기본 저장소의 구버전 Node/node-gyp가 이걸 아직 참조해서 빌드가 깨진 거죠. 해결은 최신 Node로 교체하는 것이었습니다.

    # NodeSource로 Node 22 설치 (npm 10 / 최신 node-gyp 포함)
    curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
    apt-get install -y nodejs
    npm install -g n8n

    Node 22로 바꾸니 네이티브 모듈이 정상 빌드되고 n8n(2.35)이 깔렸습니다. 스펙표엔 안 나오는, 직접 깔아 봐야 아는 함정이라 그대로 남겨 둡니다 — 같은 에러를 만날 분이 분명 있을 테니까요.

    4. n8n 워크플로우 구성 — 로그 분류기

    실용적인 예로 “로그 한 줄을 INFO / WARN / ERROR로 분류”하는 워크플로우를 만들었습니다. 구조는 단순합니다.

    • Manual Trigger — 실행 시작
    • HTTP Request — 로컬 LLM 호출

    HTTP Request 노드 설정이 전부입니다.

    필드 값
    Method POST
    URL http://<llama-server-IP>:8080/v1/chat/completions
    Body JSON (아래)
    {
      "model": "qwen2.5-3b",
      "messages": [
        { "role": "system", "content": "너는 로그 분류기다. 입력 로그를 INFO/WARN/ERROR 중 하나로만 답하라." },
        { "role": "user", "content": "nginx: upstream timed out (110: Connection timed out) while reading response header" }
      ],
      "temperature": 0
    }

    n8n과 llama-server가 같은 호스트면 127.0.0.1, 다른 기기면 서버의 홈랩 IP를 넣으면 됩니다. system 메시지로 “분류기” 역할을 못 박고 temperature: 0으로 답을 고정한 게 포인트입니다.

    5. 실행 결과 — 로컬 LLM이 정확히 판단했다

    워크플로우를 실행했습니다. n8n의 실제 실행 결과(요약)입니다.

    status: "success"
    Local LLM 노드 → message.content: "ERROR"
    timings: prompt 46 tok/s, generation 13.65 tok/s (노드 실행 1.4초)

    nginx의 upstream 타임아웃 로그를 로컬 LLM이 정확히 “ERROR”로 분류했습니다. GPU도, 외부 API도, 요금도 없이 내 홈랩 안에서 자동화 파이프라인의 부품으로 로컬 LLM이 동작한 순간입니다. 이제 이 노드 앞뒤에 트리거·알림만 붙이면 실제 운영 자동화가 됩니다.

    6. 이걸로 뭘 할 수 있나

    • 로그·알림 분류: 서버 로그를 등급으로 분류해 ERROR만 텔레그램으로 즉시 알림
    • 요약: 긴 문서·메일·뉴스를 로컬에서 요약(데이터가 외부로 안 나감)
    • 정형화: 자유 텍스트를 JSON·태그로 구조화

    핵심 이점은 비용과 프라이버시입니다. 외부 LLM API는 호출마다 돈이 들고 데이터가 밖으로 나가지만, 로컬 LLM은 몇 번을 부르든 0원이고 데이터가 홈랩을 벗어나지 않습니다. 실시간 초고성능이 필요한 게 아니라 배치·자동화 용도라면, GPU 없는 홈랩 CPU로도 충분히 실전에서 굴릴 수 있습니다.

    시리즈를 마치며

    1편 설치 → 2편 모델 선택 → 3편 자동화 연동까지, GPU 없는 i5 미니 PC 한 대로 로컬 LLM을 실전에 투입하는 전 과정을 직접 해봤습니다. “로컬 AI는 그래픽카드 있어야 한다”는 편견은, 적어도 3B급 자동화 용도에선 사실이 아닙니다. 여러분의 홈랩에도 하나 올려 보시길 권합니다.

  • 홈랩 CPU에서 로컬 LLM 3종 실측 비교 — Qwen2.5 vs Gemma 2 vs Llama 3.2 (한국어·속도)

    지난 1편에서 GPU 없는 i5 홈랩에 llama.cpp를 올리고 Qwen2.5-3B 하나를 돌려봤습니다. 그러자 당연한 질문이 남습니다 — “그럼 다른 모델은? 어떤 게 제일 나은데?” 그래서 소형 모델 3종을 완전히 같은 조건에서 돌려 속도와 한국어 품질을 실측했습니다. 결론부터 말하면, 한국어로 쓸 거면 Qwen2.5-3B가 1순위입니다. 그런데 그 과정에서 예상 못 한 함정도 하나 밟았습니다(마지막에).

    1. 실험 조건 — 공정하게 맞췄다

    비교가 의미 있으려면 변수를 통제해야 합니다. 세 모델 모두 아래 조건으로 통일했습니다.

    • 같은 컨테이너: LXC 4코어 / 8GB (1편과 동일)
    • 같은 양자화: Q4_K_M (4비트)
    • 같은 스레드: -t 4
    • 속도는 llama-bench, 품질은 동일 프롬프트를 llama-cli로

    대상 모델은 홈랩 CPU에서 현실적인 2~3B급으로 골랐습니다.

    모델 파라미터 파일 크기(Q4_K_M)
    Qwen2.5-3B-Instruct 3.09B 1.79 GiB
    Gemma 2 2B-it 2.61B 1.59 GiB
    Llama 3.2 3B-Instruct 3.21B 1.87 GiB

    2. 속도 실측 — Gemma가 가장 빠르다

    ./build/bin/llama-bench \
      -m /root/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf \
      -m /root/models/gemma-2-2b-it-Q4_K_M.gguf \
      -m /root/models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
      -t 4 -p 512 -n 128
    모델 프롬프트 처리 (pp512) 생성 (tg128)
    Qwen2.5-3B 58.7 tok/s 13.83 tok/s
    Gemma 2 2B 79.9 tok/s 15.07 tok/s
    Llama 3.2 3B 58.5 tok/s 13.35 tok/s

    예상대로 Gemma 2 2B가 가장 빠릅니다. 파라미터가 2.6B로 가장 작으니 당연한 결과죠. Qwen과 Llama는 둘 다 3B급이라 속도는 사실상 동률(생성 13토큰대)입니다. 즉 속도만 보면 크기 순서 그대로이고, 셋 다 CPU에서 실사용 가능한 범위입니다. 그럼 승부는 품질에서 갈립니다.

    3. 한국어 품질 — 여기서 갈린다

    세 모델에 똑같은 질문을 던졌습니다.

    홈 서버 초보자에게 SSH 키 인증이 비밀번호 방식보다
    안전한 이유를 3문장으로 설명해줘.

    아래는 각 모델의 실제 출력입니다(발췌, 가공 없음).

    Qwen2.5-3B — 자연스럽고 정확

    SSH 키 인증은 비밀번호 방식보다 보안이 더 강화되어 있으며, 키는 암호화되어 있어 단방향으로 전송되어야 하므로 공유되는 데이터가 아닌 개인의 비밀로 보호됩니다. 이는 공유된 컴퓨터 환경에서 특히 중요한 요소이며, 악의적인 공격자가 비밀번호를 해독하는 것은 더 어렵습니다.

    문장이 매끄럽고 내용도 맞습니다. 3B 소형 모델 치고 한국어 완성도가 확실히 높습니다.

    Gemma 2 2B — 무난하고 가볍다

    SSH 키 인증은 비밀번호 방식보다 더 안전하며, 이유는 첫째, SSH 키는 개인이 직접 생성하고 유지하며, 둘째, SSH 키는 서버와 사용자 간의 연결을 위한 신뢰도를 높이고, 셋째, 키는 암호화 방식으로 사용하여 비밀을 유지한다는 점 때문이다.

    2.6B밖에 안 되는데 한국어가 깔끔합니다. 요청한 “첫째·둘째·셋째” 구조도 잘 지켰고요. 내용 깊이는 Qwen보다 살짝 얕지만, 가장 빠르면서도 한국어가 무너지지 않는다는 게 강점입니다.

    Llama 3.2 3B — 한국어가 무너진다

    1. mật secret.key는 해킹자가 쉽게 obten할 수 있는 sensitive information이 아니기 때문입니다. …
    2. secret.key는 해킹자가 얻어도 사용할 수 있는 information이기 때문에 … (2·3번 문장 거의 동일하게 반복)

    가장 크고(3.21B) 유명한 모델인데, 한국어는 셋 중 최악이었습니다. 외국어(“mật”, “obten”, “information”)가 뒤섞이고, 같은 문장을 반복하고, 내용도 부정확했습니다. Llama 3.2는 영어 중심으로 학습돼 소형 버전의 한국어가 약합니다. 영어로 쓸 거면 몰라도, 한국어 용도로는 비추천입니다.

    4. 내가 밟은 함정 — Llama 3.2가 메모리를 터뜨렸다

    사실 Llama 3.2는 처음에 실행하자마자 죽었습니다. 로그를 보니 Killed, 종료 코드 137(OOM). 8GB나 비어 있는데 왜?

    원인은 기본 컨텍스트 길이였습니다. Llama 3.2는 학습 컨텍스트가 128K 토큰이라, 옵션을 안 주면 llama.cpp가 그 거대한 KV 캐시를 통째로 잡으려다 메모리를 초과합니다. Qwen·Gemma는 기본값이 작아 문제없었던 것이죠. 해결은 간단합니다 — 컨텍스트를 명시적으로 제한하면 됩니다.

    # -c 로 컨텍스트를 4096으로 제한하면 정상 동작
    ./build/bin/llama-cli \
      -m /root/models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
      -c 4096 -p "..." -n 200 -t 4 -st

    홈랩처럼 RAM이 넉넉하지 않은 환경에서 큰 컨텍스트 모델을 돌릴 땐 -c를 습관처럼 붙이는 게 안전합니다. 이건 스펙표만 봐선 절대 모르고, 직접 돌려봐야 아는 함정입니다.

    5. 결론 — 용도별 추천

    우선순위 추천 모델 이유
    한국어 품질 Qwen2.5-3B 가장 자연스럽고 정확. 홈랩 한국어 1순위
    속도·초경량 Gemma 2 2B 가장 빠르고 가벼우면서 한국어도 무난
    영어 전용 Llama 3.2 3B 한국어는 약함. 영어 작업엔 선택지

    제 결론은 “한국어 홈랩 CPU 추론이면 Qwen2.5-3B를 기본으로, 더 가볍고 빠르게 가고 싶으면 Gemma 2 2B”입니다. 파라미터 수나 유명세가 아니라, 같은 조건에서 직접 돌려본 결과가 이렇게 갈립니다.

    다음 편 예고

    이제 쓸 만한 모델을 골랐으니, 다음 편에서는 llama-server로 OpenAI 호환 API를 띄우고, 이걸 n8n 자동화에 연결해 실제로 일을 시키는 과정을 정리하겠습니다. 로컬 LLM이 장난감을 넘어 파이프라인의 부품이 되는 단계입니다.

  • GPU 없이 로컬 LLM 돌리기 — i5 홈랩 + llama.cpp 실전 (설치부터 첫 구동, 실측 13토큰/초)

    “로컬 LLM 돌리려면 그래픽카드부터 사야지” — 대부분 이렇게 알고 계실 겁니다. 그런데 제 홈랩엔 외장 그래픽카드가 없습니다. Intel i5-8500에 내장그래픽(UHD 630)뿐이라 CUDA는 애초에 못 씁니다. 그래서 직접 해봤습니다 — GPU 없이 CPU만으로 로컬 LLM을. 결론부터 숫자로 말하면, 3B 모델이 초당 13.85토큰으로 돌아갑니다. 읽는 속도보다 빠릅니다. 설치부터 첫 구동, 실측까지 그대로 공개합니다.

    1. 왜 CPU인가 — GPU 없는 홈랩의 현실

    홈랩용 미니 PC나 중고 서버엔 외장 GPU가 없는 경우가 훨씬 많습니다. 전력·발열·가격 때문이죠. 제 환경도 그렇습니다.

    • CPU: Intel i5-8500 (6코어) — AVX2 지원
    • GPU: Intel UHD 630 내장그래픽뿐 → NVIDIA/CUDA 경로 불가

    많은 “로컬 LLM 설치” 글이 NVIDIA Driver + CUDA 설치부터 시작하는데, 그래픽카드가 없으면 그 글은 무용지물입니다. 다행히 llama.cpp는 순수 CPU 추론을 제대로 지원하고, 소형 양자화 모델이라면 CPU로도 충분히 쓸 만합니다. 이 글은 정확히 그 경우를 다룹니다.

    2. 실제 구성 — LXC 컨테이너로 격리

    호스트에 직접 깔지 않고 Proxmox LXC 컨테이너 하나를 새로 만들어 그 안에 구축했습니다. 이유는 단순합니다 — 빌드 도구·모델 파일이 호스트를 어지럽히지 않게 격리하고, 실험이 꼬이면 컨테이너째 버리면 되니까요.

    항목 할당 비고
    OS Debian 13 (trixie) gcc 14.2 / cmake 3.31
    vCPU 4코어 추론 스레드 4개
    RAM 8GB 3B 모델(1.8GB) + 여유 충분
    디스크 24GB 빌드 + 모델 몇 개

    3B 모델은 8GB면 넉넉합니다. 오히려 CPU 추론에서 진짜 자원은 메모리 대역폭이라, 코어를 무작정 늘린다고 비례해서 빨라지지 않습니다(뒤에서 실측).

    3. llama.cpp 빌드 — AVX2 네이티브가 핵심

    먼저 빌드 도구를 설치합니다.

    apt-get update
    apt-get install -y build-essential cmake git libcurl4-openssl-dev ccache wget

    소스를 받아 빌드합니다. CPU 추론 속도를 좌우하는 건 -DGGML_NATIVE=ON입니다. 이 옵션이 현재 CPU의 명령어 확장(AVX2 등)을 최대한 활용하도록 컴파일해 줍니다. 이걸 빼면 체감 속도가 뚝 떨어집니다.

    cd /root
    git clone --depth 1 https://github.com/ggml-org/llama.cpp
    cd llama.cpp
    cmake -B build -DGGML_NATIVE=ON -DLLAMA_CURL=ON -DCMAKE_BUILD_TYPE=Release
    cmake --build build -j4 --config Release

    4코어에서 몇 분이면 끝납니다. 빌드가 끝나면 build/bin/ 아래에 실행 파일이 생깁니다. 우리가 쓸 건 세 개입니다.

    • llama-cli — 실제 대화·생성
    • llama-bench — 성능 측정 전용
    • llama-server — OpenAI 호환 API 서버(다음 편에서 활용)

    4. 모델 선택 — 왜 Qwen2.5-3B Q4_K_M인가

    CPU·소형 환경에서 한국어까지 쓸 만한 모델로 Qwen2.5-3B-Instruct를 골랐습니다. 파일명 뒤의 Q4_K_M이 중요한데, 이건 양자화(quantization) 방식입니다.

    • 원본 모델은 파라미터를 16비트로 저장 → 3B라도 6GB가 넘습니다.
    • Q4_K_M은 4비트로 압축한 버전 → 같은 모델이 1.8GB로 줄어듭니다.
    • 품질 손실은 체감상 미미하면서 CPU·메모리 부담을 크게 줄여, 홈랩 CPU 추론의 사실상 표준입니다.
    mkdir -p /root/models && cd /root/models
    wget -O Qwen2.5-3B-Instruct-Q4_K_M.gguf \
      "https://huggingface.co/bartowski/Qwen2.5-3B-Instruct-GGUF/resolve/main/Qwen2.5-3B-Instruct-Q4_K_M.gguf"

    5. 첫 구동 — 진짜 한국어가 나온다

    바로 물어봤습니다.

    ./build/bin/llama-cli \
      -m /root/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf \
      -p "홈랩에서 GPU 없이 CPU만으로 로컬 LLM을 돌릴 때의 장점 3가지를 간단히 알려줘." \
      -n 220 -t 4 -st

    실제로 나온 답변입니다(가공 없음).

    GPU 없이 CPU만으로 로컬 LLM을 실행할 때의 장점은 다음과 같습니다:

    1. 비용 절감: GPU가 필요하지 않아 소프트웨어 구동에 필요한 비용을 줄일 수 있습니다.
    2. 컴퓨팅 능력: CPU는 보통 GPU보다 더 저렴한 가격과 더 적은 부피를 가지고 있지만, 특정 작업에는 충분한 컴퓨팅 능력을 제공할 수 있습니다.
    3. 편리성: CPU가 설치된 컴퓨터를 사용하면, 별도의 GPU 장치 구매나 설치 없이도 로컬 LLM을 실행할 수 있습니다.

    마크다운 서식까지 알아서 잡아 줍니다. 3B 소형 모델이라 아주 복잡한 추론은 무리지만, 요약·번역·분류·간단한 글쓰기 같은 실무 자동화용으로는 충분한 품질입니다.

    6. 실측 성능 — 초당 몇 토큰?

    체감이 아니라 숫자로 재려고 llama-bench를 돌렸습니다.

    ./build/bin/llama-bench \
      -m /root/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf \
      -t 4 -p 512 -n 128
    테스트 속도 의미
    pp512 (프롬프트 처리) 58.8 tok/s 입력을 읽어들이는 속도
    tg128 (텍스트 생성) 13.85 tok/s 답변을 뱉는 속도

    핵심은 생성 속도 13.85 tok/s입니다. 한국어 기준 사람이 눈으로 읽는 속도가 대략 초당 7~10토큰 남짓이니, 읽는 것보다 빠르게 답이 흘러나옵니다. 대화형으로 써도 답답하지 않고, 모델 파일이 1.8GB라 8GB 컨테이너에 메모리도 여유롭습니다. “GPU 없으면 로컬 LLM은 못 쓴다”는 편견이 3B급에서는 사실이 아닙니다.

    7. CPU-only의 한계 — 정직하게

    • 큰 모델은 느립니다. 14B·32B급으로 가면 CPU에선 초당 1~3토큰으로 뚝 떨어져 실시간 대화엔 부적합합니다. CPU의 스윗스팟은 3B~8B입니다.
    • 코어 늘린다고 비례하지 않습니다. CPU 추론은 메모리 대역폭에 묶여서, 스레드를 물리 코어 이상으로 올리면 오히려 손해입니다. 코어 수보다 -DGGML_NATIVE=ON 빌드가 더 큰 차이를 냅니다.
    • 진짜 큰 모델·낮은 지연이 필요하면 그때 GPU를 고민하면 됩니다. 하지만 요약·번역·자동화 파이프라인 용도라면 3B CPU로 충분합니다.

    다음 편 예고

    1편은 “일단 돌린다”였습니다. 다음 편에서는 모델별 실측 비교(Qwen2.5-3B vs Gemma 2 vs Llama 3.2, 속도·한국어 품질)를 다루고, 그다음 편에서는 llama-server로 API를 띄워 n8n·자동화에 로컬 LLM을 연동하는 실전을 정리하겠습니다. GPU 없는 홈랩에서도 충분히 갈 수 있습니다.