본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 01. 07:46

Pi에 생명력을 불어넣기: Raspberry Pi 5에 Gemma 4 2B 배포하기

요약

Raspberry Pi 5 4GB 모델에서 Gemma 4 2B 모델을 구동하기 위한 로컬 AI 서버 구축 튜토리얼입니다. 리소스 제약을 극복하기 위해 8-bit 양자화 모델과 NVMe SSD, Raspberry Pi OS Lite를 활용한 최적화 방법을 다룹니다.

핵심 포인트

  • Raspberry Pi 5 4GB 환경에서 Gemma 4 2B 모델 구동 시도
  • 메모리 부족 해결을 위해 8-bit 양자화 모델 사용
  • 성능 극대화를 위해 NVMe SSD 및 Active Cooler 장착 권장
  • 리소스 최적화를 위한 Raspberry Pi OS Lite(64-bit) 활용

안녕하세요 여러분,
새로운 프로젝트와 함께 돌아왔습니다. 이 프로젝트는 오랫동안 준비해 온 것입니다.

한동안 저에게는 자신만의 전용 로컬 AI 서버를 구축하고 싶다는 지속적인 욕구가 있었습니다. 하지만 대규모 언어 모델 (LLM)을 로컬에서 실행해 본 적이 있다면, 모든 개발자가 거의 즉시 맞닥뜨리는 공통적인 장애물을 이미 알고 계실 것입니다. 바로 리소스 제약 (resource constraints)입니다. 몇 달 전, 저는 메인 워크스테이션에서 Gemma 3 1B 모델을 실험하고 있었습니다. 솔직히 말씀드리면, 정말 인상적이었습니다. 일상적인 텍스트 기반 작업과 일반적인 브레인스토밍을 위해, 그 작은 모델은 체급을 훨씬 뛰어넘는 성능을 보여주었습니다. 제 메인 머신은 꽤 괜찮은 하드웨어 구성을 갖추고 있었기에, Gemma 3는 지연 (lag) 없이 완전히 로컬 상태로 완벽하게 실행되었습니다.

하지만 그 경험은 저에게 여운이 남는 질문을 던졌습니다. 이 최적화를 한 단계 더 발전시킬 수 있을까요? 이 워크로드 (workload)를 메인 워크스테이션에서 완전히 분리하여 작고 저전력인 싱글 보드 컴퓨터 (single-board computer)로 옮길 수 있을까요?

저는 아직 Raspberry Pi 5에서 LLM을 테스트해 본 적이 없습니다. 그래서 이번에는 초경량 모델을 건너뛰고 하드웨어를 극한까지 밀어붙이기로 했습니다. 이 튜토리얼에서는 단 4GB의 RAM을 가진 Raspberry Pi 5에서 최신 Gemma 4 2B (8-bit Quantized) 모델을 구동해 보려고 합니다. 4GB 메모리를 가진 신용카드 크기의 보드가 실제로 프로덕션 수준의 현대적인 20억 파라미터 (2-billion parameter) AI 서버를 호스팅할 수 있을까요? 함께 알아봅시다.

이 프로젝트를 위해 저는 아래와 같은 하드웨어를 사용했습니다.

  1. Active Cooler가 설치된 Raspberry Pi 5 4GB
  2. SD 카드 대신 NVMe 500GB

솔직히 말해서, 제가 이 냉각 팬(cooling fan)과 빠른 SSD 저장 장치를 갖추고 있기 때문에 위험을 감수하고 4GB RAM 보드에서 이 모델을 실행해 보려는 것입니다. 이러한 업그레이드가 없다면, 이렇게 작은 보드에서 2B 모델을 실행하려는 시도는 엄청난 고전이 될 것이며, 제한된 메모리 때문에 작동조차 하지 않을 수도 있습니다.

하지만 하드웨어 준비가 완료되었으므로, 이제 드디어 소프트웨어 측면을 다룰 준비가 되었습니다. OS를 부팅하고 최대 속도를 위해 시스템을 정리해 봅시다.

로컬 RPi AI 서버 구축 단계

1. RPi OS lite 부팅:

우리는 매우 타이트한 하드웨어 리소스 제약 조건 하에서 작동하고 있으므로, 시스템 아키텍처(system architecture)는 가능한 한 최적화되고 경량화되어야 합니다. 이를 달성하기 위해, 우리는 표준 데스크톱 에디션 대신 공식 Raspberry Pi OS Lite (64-bit) 버전을 선택합니다.

완전한 헤드리스 모드(headless mode), 즉 그래픽 사용자 인터페이스 (GUI)가 없는 상태로 작동함으로써 순수한 명령줄 환경(command-line environment)을 확보할 수 있습니다. 이를 통해 Raspberry Pi 5의 모든 RAM 1MB와 모든 여유 CPU 사이클을 로컬 LLM 워크로드(workload)를 실행하는 데 온전히 보존할 수 있습니다.

먼저 패키지를 업데이트해 봅시다.

Linux 패키지 업데이트

sudo apt update && sudo apt upgrade -y

Raspberry Pi OS Lite는 최적화를 위해 군더더기를 제거했기 때문에, 전체 Vim 텍스트 에디터가 사전 설치되어 있지 않습니다.
수동으로 설치할 수 있습니다.

vim 설치

sudo apt install vim -y

2. Podman 설정:

네, 이번 빌드에서는 Docker 대신 Podman을 사용합니다.

이러한 선택의 주요 이유는 최적화에 있습니다. Podman은 완전히 루트리스(rootless)이며 데몬리스(daemonless)입니다. 시스템 메모리에서 지속적으로 실행되는 무거운 백그라운드 서비스가 필요한 Docker와 달리, Podman은 호스트에서 유휴 상태의 백그라운드 RAM 소비가 전혀 없습니다. 컨테이너가 활발하게 작동하지 않을 때는 오버헤드(overhead)가 발생하지 않습니다.

Podman을 사용하는 데에는 이 프로젝트에 완벽하게 부합하는 또 다른 엄청난 장점이 있지만, 그 놀라움은 나중에 이 블로그의 서비스(Services) 섹션에서 공개하겠습니다!

podman 설치

패키지 관리자를 사용하여 Podman을 설치해 보겠습니다:

sudo apt -y install podman

3. Hugging Face에서 Gemma 4 2B 모델 다운로드:

우리는 GGML 팀이 GGUF 형식으로 패키징한 Gemma 4 E2B 모델의 Q8_0 (8-bit quantized, 8비트 양자화) 변형을 선택합니다. 이를 통해 놀라운 균형을 얻을 수 있습니다. 모델의 고유한 지능과 추론 정확도를 거의 모두 보존하면서도 RAM 요구 사항을 획기적으로 낮추어, 시스템에 과부하를 주지 않고 Raspberry Pi 5에서 완벽하게 실행될 수 있게 합니다. 여기서 Llama.cpp를 사용하기 때문에, GGUF는 로컬 추론 (local inference)을 실행하기 위해 실제로 사용할 수 있는 유일한 형식입니다.

우리는 헤드리스 OS (headless OS) 모드를 사용하고 있으므로, wget 리눅스 명령어를 사용하여 모델을 설치하겠습니다.

명령어 구조 :
wget -c -P /path/to/download/dir url-to-model-file

-c (Continue, 계속하기): 만약 SSH 세션이 끊기거나 이 무거운 멀티 기가바이트 다운로드 도중에 인터넷 연결이 불안정해지더라도, 이 플래그를 추가하면 wget이 처음부터 다시 시작하는 대신 중단된 지점부터 자동으로 재개할 수 있게 해줍니다.

-P (Prefix Directory, 접두사 디렉토리): 이 옵션은 들어오는 다운로드 스트림을 우리가 새로 만든 ~/models/ 디렉토리로 바로 라우팅하여 홈 디렉토리를 깔끔하게 유지해 줍니다.
만약 ~/models/ 디렉토리가 존재하지 않는다면, wget 명령어가 우리를 위해 새 디렉토리를 생성합니다.

Gemma 4 2B 모델 다운로드 :

wget -c -P ~/models/ https://huggingface.co/ggml-org/gemma-4-E2B-it-GGUF/resolve/main/gemma-4-E2B-it-Q8_0.gguf

4. Llama 서버 컨테이너 :

우리는 프로덕션(production) 준비 단계에 가까운 서버를 구축하고 있습니다. 따라서 프로덕션 서버에 권장되는 llama 서버 컨테이너 이미지를 사용할 것입니다. 이 이미지는 우리의 gemma 모델을 실행하는 데 필요한 도구들만 포함하고 있습니다. 추가적인 패키지나 도구는 포함되지 않았습니다. 최적화된 시스템을 염두에 두고 있기 때문에, 이 컨테이너 이미지는 그러한 이념에 부합하여 도움이 될 것입니다.

Podman 테스트 실행

모든 과정을 자동화하기 전에, Podman이 이미지를 다운로드하고, 모델 파일을 찾고, 서버를 올바르게 시작할 수 있는지 확인하기 위해 백그라운드에서 간단한 수동 테스트를 실행해 보겠습니다.

podman run -d \
  --name llama-server \
  -p 8080:8080 \
...

이 Podman 명령은 설명을 위해 두 부분으로 나눌 수 있습니다.

파트 1: Podman 설정 플래그 (Configuration Flags)

이 설정들은 Podman이 Raspberry Pi에서 컨테이너를 어떻게 생성하고 격리할지 지시합니다.

podman run: Podman에게 컨테이너 이미지(아직 없는 경우)를 가져오고(pull down) 시작하도록 명령하는 핵심 명령어입니다.

-d (Detached): 컨테이너를 백그라운드에서 조용히 실행합니다. 이를 통해 터미널을 열어둔 채로 계속 작업을 이어갈 수 있습니다.

--name llama-server: 컨테이너에 친숙한 사용자 정의 이름을 할당합니다. 이렇게 하면 나중에 무작위 ID 문자열을 입력할 필요 없이 컨테이너를 쉽게 시작, 중지하거나 로그를 확인할 수 있습니다.

-p 8080:8080 (포트 매핑 (Port Mapping)): 실제 Raspberry Pi와 컨테이너 사이에 브릿지를 엽니다. Pi의 8080 포트로 들어오는 트래픽을 컨테이너의 8080 포트로 직접 전달합니다.

-v $HOME/models:/models:Z (볼륨 마운트 (Volume Mount)): Pi의 고속 NVMe 드라이브에 있는 ~/models 폴더를 컨테이너 내부의 /models라는 이름의 폴더와 연결합니다. 여기서 :Z 플래그는 매우 중요합니다. 이는 Podman에게 SELinux 보안 권한을 구성하도록 알려주어, 루트리스 (rootless) 컨테이너가 모델 파일을 읽을 수 있도록 허용합니다.

ghcr.io/ggml-org/llama.cpp:server: 이는 GitHub Container Registry에 호스팅된 공식 경량 컨테이너 이미지의 정확한 주소입니다.

파트 2: Llama 서버 내부 인자 (Internal Arguments)

이미지 이름 뒤에 작성된 모든 내용은 컨테이너 내부에서 실행되는 AI 애플리케이션으로 직접 전달됩니다.

-m /models/gemma-4-E2B-it-Q8_0.gguf (모델 경로 (Model Path)): 서버에 공유 폴더로부터 어떤 모델 파일을 메모리에 로드할지 정확히 알려줍니다.

-c 1024 (Context Size (컨텍스트 크기)): 모델이 단일 대화에서 처리할 수 있는 최대 토큰 제한(단어 및 공백)을 설정합니다. 이를 1024로 제한함으로써 RAM 사용량을 낮게 유지하고 4GB 모델의 Raspberry Pi에 최적화합니다.
컨텍스트 크기를 AI의 단기 기억이라고 생각하면 쉽습니다.
Gemma 모델의 경우 이 숫자를 최대 8192까지 조정할 수 있지만, 메모리가 커질수록 훨씬 더 많은 RAM이 필요합니다. 저의 Raspberry Pi 5 설정에서는 이 값을 2048까지 높일 수 있었습니다. 하지만 이 숫자를 높이면 시스템이 NVMe 드라이브의 스왑 파티션 (Swap Partition)을 사용해야 할 가능성이 높다는 점을 유념하세요. 스왑 공간에 의존하면 AI의 응답 속도가 약간 느려질 수 있지만, 확실히 작동은 합니다!

-t 4 (Threads (스레드)): AI 응답을 계산하기 위해 정확히 4개의 CPU 스레드를 할당합니다. 이는 과열 없이 최대 속도를 내기 위해 Raspberry Pi 5의 4개 물리 코어와 완벽하게 일치합니다.

--host 0.0.0.0: AI 서버가 Pi 자체로만 제한되지 않고, 로컬 네트워크의 모든 IP 주소로부터의 연결을 수락하도록 설정합니다. 저는 이 LLM 서버를 제 실험실에 배포하고 LAN을 통해 메인 데스크톱이나 노트북에서 쉽게 액세스하고 싶기 때문에, 이 플래그 (Flag)는 절대적으로 필수적입니다.

--port 8080: 컨테이너 내부의 웹 서버가 8080 포트에서 요청을 대기하도록 구성합니다.

Podman 명령어를 실행하면, AI 서버 컨테이너가 백그라운드에서 구동됩니다. 새로운 LLM과 대화를 시도하기 전에, 서버가 올바르게 초기화되었는지와 Gemma 모델이 메모리에 성공적으로 로드되었는지 확인해야 합니다.

내장된 헬스 엔드포인트 (Health Endpoint)에 핑을 보내는 간단한 curl 명령어를 사용하여 서버의 실시간 상태를 쉽게 확인할 수 있습니다:

curl http://localhost:8080/health

출력 결과에서 기대할 수 있는 사항:
{"status": "ok"}: 이 메시지와 함께 200 OK 응답을 받았다면 축하합니다! 여러분의 경량 서버가 완전히 정상이며 프롬프트(prompt)를 처리할 준비가 되었습니다.

{"status": "loading model"}: 503 상태 코드를 받더라도 당황하지 마세요. 이는 서버가 아직 NVMe 드라이브에서 2B 모델 파일을 읽고 있다는 의미일 뿐입니다. 몇 초만 기다렸다가 다시 시도하세요.

Podman Quadlets를 이용한 자동화

수동 테스트는 완벽하게 작동했으며 서버도 정상입니다. 하지만 솔직해집시다. 명령어를 수동으로 실행하는 것은 장기적으로 좋은 해결책이 아닙니다.

만약 Raspberry Pi가 무거운 작업을 수행하는 동안 4GB 제한을 초과하면 어떤 일이 벌어질지 생각해 보세요. Linux 커널이 자신을 보호하기 위해 갑작스러운 재부팅을 트리거할 수도 있습니다. 또는, 단순히 밤사이에 Pi를 종료했다가 다음 날 아침에 AI 서버를 사용하고 싶다면 어떻게 될까요? 다시 로그인하여 그 거대한 podman run 명령어를 처음부터 끝까지 다시 입력해야 할 것입니다. 이는 매우 답답한 병목 현상(bottleneck)처럼 느껴집니다.

개발자로서 우리는 Raspberry Pi가 부팅될 때마다 AI 서버가 자동으로 시작되도록 이 과정을 자동화하고 싶어 합니다.

이 지점에서 Podman의 강력한 장점이 발휘됩니다. Docker와 달리, Podman은 Quadlets라는 도구를 사용하여 Linux systemd 서비스와 네이티브하게 통합됩니다. 컨테이너를 관리하기 위해 지저분한 스크립트를 작성하는 대신, 단 하나의 깔끔한 .container 파일만 생성하면 됩니다. Podman의 내장 엔진이 이 파일을 읽어 자동으로 백그라운드 Linux 서비스로 변환해 줍니다.

설정 파일 생성하기
먼저, Podman이 이러한 자동화 파일을 찾는 특정 디렉토리를 생성해야 합니다:

mkdir -p $HOME/.config/containers/systemd/

다음으로, Vim을 사용하여 해당 폴더 안에 llama-server.container라는 이름의 새 파일을 엽니다:

vim $HOME/.config/containers/systemd/llama-server.container
[Unit]
Description=Llama server for Gemma 4-2BQ Model
After=network-online.target
...

이 설정이 작동하는 방식:

전체 설정은 앞서 사용했던 podman run 명령과 동일한 작업을 수행하지만, 한 가지 차이점이 있습니다. Raspberry Pi가 부팅될 때마다 우리의 개입 없이도 LLM 서버가 자동으로 시작됩니다.

After=network-online.target: 이 설정은 AI 서버를 시작하기 전에 Raspberry Pi가 네트워크에 완전히 연결될 때까지 시스템이 기다리도록 지시합니다.

[Container]: 이 부분이 바로 Quadlets가 빛을 발하는 지점입니다. 길고 혼란스러운 명령줄 대신, 컨테이너 설정을 한 줄씩 명확하게 나열할 수 있습니다.

Volume=%h/models:/models:Z: systemd 설정에서 %h는 사용자의 홈 디렉터리 경로를 자동으로 가져오는 유용한 단축키입니다. 이를 통해 우리의 NVMe 저장소 폴더를 안전하게 연결합니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0