Kubernetes에 Ollama(LLM REST API)를 배포
배경
비즈니스와 실제 시나리오에서 서비스의 확장성과 고가용성은 매우 중요하다. Kubernetes는 이러한 작업을 조율하는 도구로 각광받고 있다. 여러 모델을 선택할 수 있는 LLM을 REST API로 배포하고 이를 확장할 수 있다면 어떨까? 하는 생각에 시도해보았다.
환경
- Firebat AK2 Plus
- CPU : Intel N100
- RAM : 16GB
- storage : 512GB
- OS : Rocky Linux 9
배포
namespace
ollama_ns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ollama
$ kubectl apply -f ollama_ns.yaml
deployment
ollama_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ollama
namespace: ollama
spec:
selector:
matchLabels:
name: ollama
template:
metadata:
labels:
name: ollama
spec:
containers:
- name: ollama
image: ollama/ollama:latest
ports:
- name: ollamaport
containerPort: 11434
protocol: TCP
$ kubectl apply -f ollama_deploy.yaml
service
ollama_service.yaml
apiVersion: v1
kind: Service
metadata:
name: ollama
namespace: ollama
spec:
type: ClusterIP
selector:
name: ollama
ports:
- port: 11434
name: ollamaport
targetPort: ollamaport
protocol: TCP
$ kubectl apply -f ollama_service.yaml
기본적으로 네임스페이스, 디플로이먼트, 서비스를 올렸다.
배포가 정상적으로 되면 테스트해보자.
포트포워딩
$ kubectl -n ollama port-forward service/ollama 11434:11434 &
[1] 1980276
Forwarding from 127.0.0.1:11434 -> 11434
Forwarding from [::1]:11434 -> 11434
포트포워딩은 끄면 더 이상 동작하지 않기 때문에, 백그라운드로 실행해둔다.
테스트
테스트용으로 업로드한 EEVE-Korean-Instruct-10.8B-v1.0-Q4_0 모델을 실행한다.
커스텀 모델 업로드하는 방법은 다른 글에서 다룰 예정
$ curl http://localhost:11434/api/generate -d '{
"model": "EEVE-Korean-Instruct-10.8B-v1.0-Q4_0",
"prompt": "오늘 하루의 응원 문구를 하나 추천해줘"
}'
응답은 아래와 같다.
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:55.627097996Z","response":"\"","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:56.159037675Z","response":"당","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:56.694890053Z","response":"신의","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:57.235140309Z","response":" 꿈을","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:57.786867691Z","response":" 향해","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:58.326435132Z","response":" 나아가","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:58.864553471Z","response":"세요","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:59.409821047Z","response":"!","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:40:59.965482751Z","response":" 하늘","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:41:00.502485428Z","response":"은","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:41:01.058521242Z","response":" 한계가","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:41:01.622901471Z","response":" 아","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:41:02.18777015Z","response":"닙","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:41:02.751312498Z","response":"니다","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:41:03.319721477Z","response":"!\"","done":false}
{"model":"EEVE-Korean-Instruct-10.8B-v1.0-Q4_0","created_at":"2024-07-04T12:41:03.880403883Z","response":"","done":true,
"done_reason":"stop",
"context":[28705,13,1,330,10706,1444,264,13903,2188,304,396,18278,10895,13892,28723,415,13892,5212,10865,28725,10537,28725,304,27057,11194,298,272,2188,28742,28713,4224,28723,32321,35118,29538,36190,28732,28796,431,276,28731,32004,35118,29426,38146,28723,2,28705,13,1,10649,28747,13,35553,33186,29187,35999,32094,33557,32234,33260,29426,38146,2,28705,13,1,21631,28747,13,28739,30287,34677,35697,34680,34997,32649,28808,34199,29538,37579,32014,40063,32012,2781],
"total_duration":33565547597,
"load_duration":1534955218,
"prompt_eval_count":71,
"prompt_eval_duration":23734876000,
"eval_count":16,
"eval_duration":8253303000}
응답이 한 번에 생성되어 돌아오는 것이 아닌,
ChatGPT처럼 생성되는 대로 온다.
사용성 측면에서 더 나을 것으로 보인다.
시간 측정
응답에 duration들이 포함되어있지만, time 명령어를 사용하여 시간을 측정해보자.
$ time curl -o /dev/null -s -w "%{time_total}\n" http://localhost:11434/api/generate -d '{
"model": "EEVE-Korean-Instruct-10.8B-v1.0-Q4_0",
"prompt": "오늘 하루의 응원 문구를 하나 추천해줘"
}'
33.662070
real 0m33.668s
user 0m0.000s
sys 0m0.008s
해당 환경에선 약 30초 정도 소요된다.