우와한 개발자

[Spring AI] On-Premise LLM 구현하기 — Ollama, Docker, Llama

by 우와한개발자

1. On-Premise란?

  • 클라우드 서버가 아닌 자체 서버나 로컬 환경에 직접 설치해서 운영하는 방식
  • OpenAI API처럼 외부 서버에 요청을 보내는 것이 아니라, 내 서버 안에서 LLM을 직접 실행한다.
구분 클라우드 LLM (OpenAI 등) On-Premise LLM
실행 위치 외부 서버 (OpenAI 서버) 내 서버 / 로컬
인터넷 연결 필수 불필요
데이터 보안 외부로 데이터 전송됨 외부로 나가지 않음
비용 토큰당 과금 서버 비용만 발생
성능 높음 서버 사양에 따라 다름
사용 상황 일반적인 AI 서비스 보안이 중요한 환경, 오프라인 환경

 

2. On-Premise LLM을 쓰는 이유

  • 보안 : 민감한 데이터(의료, 금융, 법률 등)를 외부로 보내지 않아야 할 때
  • 비용 : API 호출 비용 없이 자체 서버에서 무제한 사용 가능
  • 오프라인 : 인터넷이 없는 폐쇄망 환경에서도 사용 가능
  • 커스터마이징 : 모델을 직접 파인튜닝하거나 수정 가능

 

3. 주요 On-Premise LLM 모델

모델 제공사 특징
Llama Meta 가장 널리 사용되는 오픈소스 모델
Mistral Mistral AI 경량화되어 성능 대비 빠름
Gemma Google 경량 모델. 로컬 실행에 적합
Phi Microsoft 초경량 모델. 저사양 환경에 적합
Qwen Alibaba 한국어 등 다국어 지원 강점
DeepSeek DeepSeek 코딩 특화 모델
CodeLlama Meta 코드 생성 특화

 

4. Ollama란?

  • On-Premise LLM을 쉽게 실행할 수 있게 해주는 툴
  • 복잡한 설정 없이 명령어 하나로 다양한 LLM 모델을 로컬에서 실행할 수 있다.
  • OpenAI API와 호환되는 REST API를 제공하므로 기존 코드를 거의 수정하지 않고 교체 가능하다.
개발자 → Ollama → LLM 모델 (Llama, Mistral 등)
          ↑
     REST API 제공 (localhost:11434)

 

5. Docker로 Ollama 실행

1) Ollama 컨테이너 실행

# Ollama 컨테이너 실행
docker run -d \\
  --name ollama \\
  -p 11434:11434 \\
  -v ollama:/root/.ollama \\
  ollama/ollama
옵션 설명
-d 백그라운드 실행
--name ollama 컨테이너 이름 지정
-p 11434:11434 포트 연결 (호스트:컨테이너)
-v ollama:/root/.ollama 모델 데이터 볼륨 마운트 (재시작해도 모델 유지)

2) 모델 다운로드

# 컨테이너 안에서 모델 다운로드
docker exec -it ollama ollama pull llama3.2

3) 실행 확인

# 설치된 모델 목록 확인
docker exec -it ollama ollama list

# API 직접 호출 테스트
curl <http://localhost:11434/api/generate> -d '{
  "model": "llama3.2",
  "prompt": "안녕하세요"
}'

 

6. 구현

1) build.gradle

ext { set('springAiVersion', "1.1.7") }

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.ai:spring-ai-starter-model-ollama'  // openai 대신 ollama
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'io.projectreactor:reactor-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.ai:spring-ai-bom:${springAiVersion}"
    }
}

2) application.properties

# Ollama 서버 주소 (Docker로 실행 시 localhost:11434)
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.options.model=llama3.2

3) Service

@Service
@RequiredArgsConstructor
public class OllamaService {

    private final ChatClient chatClient;

    // 일반 호출
    public String chat(String userInput) {
        return chatClient.prompt()
                .user(userInput)
                .call()
                .content();
    }

    // 스트림 호출
    public Flux<String> stream(String userInput) {
        return chatClient.prompt()
                .user(userInput)
                .stream()
                .content()
                .map(this::preserveSseLeadingSpace)
                .concatWith(Flux.just("[[END]]"));
    }

    // data: 뒤에 오는 공백은 브라우저가 자동으로 제거하므로 보정
    private String preserveSseLeadingSpace(String chunk) {
        if (chunk == null) return "";
        return chunk.startsWith(" ") ? " " + chunk : chunk;
    }
}

4) Controller

@RestController
@RequiredArgsConstructor
public class OllamaController {

    private final OllamaService ollamaService;

    // 일반 호출
    @GetMapping("/ollama/chat")
    public String chat(@RequestParam("q") String q) {
        return ollamaService.chat(q);
    }

    // produces 없으면 Spring이 Flux를 JSON 배열로 모아서 한번에 응답함
    @GetMapping(value = "/ollama/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> stream(@RequestParam("q") String q) {
        return ollamaService.stream(q)
                .map(chunk -> ServerSentEvent.builder(chunk).build());
    }
}

 

7. OpenAI vs Ollama 비교

항목 OpenAI Ollama
코드 구조 동일 동일
의존성 spring-ai-starter-model-openai spring-ai-starter-model-ollama
API 키 필요 불필요
응답 속도 빠름 서버 사양에 따라 다름
비용 토큰당 과금 무료
데이터 보안 외부 전송 외부 전송 없음
모델 교체 application.properties만 변경 application.properties만 변경

 

8. 주의사항

항목 내용
GPU 권장 CPU만으로는 응답이 매우 느림. GPU 있으면 훨씬 빠름
모델 용량 llama3.2 기준 약 2GB. 디스크 여유 공간 필요
첫 응답 느림 모델 로딩 시간이 있어 첫 요청은 느릴 수 있음
Docker 재시작 -v 옵션으로 볼륨 마운트 안 하면 재시작 시 모델 재다운로드 필요

블로그의 정보

우와한개발자 님의 블로그

우와한개발자

활동하기