LLM 엔지니어링14장 · Streamlit 화면과 Docker 컨테이너12 / 22← 이전목차다음 →MetaContents

14장. Streamlit 화면과 Docker 컨테이너

API 아키텍처

큰 식당은 홀, 주방, 창고, 계산대가 나뉘어 있고, 손님이 늘면 주방장을 더 둡니다. 서비스도 화면, API, 모델 서버, 데이터를 나누면 바쁜 곳만 늘리고, 한 곳을 고칠 때 다른 곳은 그대로 둘 수 있습니다.

사용자의 화면, 인증과 검사를 하는 API, 모델 서버, 벡터DB 와 기록 같은 데이터의 네 층을 차례로 이은 흐름도

1번 화면은 사용자와 만납니다. 2번 API 는 인증, 입력 검사, 기록을 맡고 일을 나눠 맡깁니다. 3번 모델 서버(Ollama, 이후 장의 vLLM)는 계산이 무거운 추론만 합니다. 4번 데이터에는 벡터DB, 문서, 기록이 있습니다. 화면은 API 만 알고, 모델 서버와 데이터는 API 뒤에 숨깁니다. 여기서 홀이 화면, 계산대와 주문 관리가 API, 주방이 모델 서버, 창고가 데이터입니다.

나누는 까닭과 늘리는 법

층무거운 것사용자가 늘면
화면가볍다화면 서버를 여러 개로
API가볍다(인증, 검사)여러 개를 띄우고 앞에 부하 분산기를 둔다
모델 서버GPU 와 메모리GPU 를 늘리거나, 묶어서 처리(16장의 vLLM)
데이터저장 공간벡터DB 를 서버형으로(11장)

오래 걸리는 일(문서 수백 개 요약 같은)은 API 가 바로 답하지 않고 작업 번호를 주고, 뒤에서 처리한 뒤 결과를 따로 받게 하기도 합니다(작업 큐). 대화처럼 바로 답해야 하는 일은 지금처럼 요청을 받으면 바로 답합니다.

여러 API 서버에 나눠 보내기

Python
import itertools

replicas = ["http://api-1:8013", "http://api-2:8013", "http://api-3:8013"]   # 같은 API 를 셋 띄웠다
next_server = itertools.cycle(replicas)               # 돌아가며 하나씩(라운드 로빈)
for request_id in range(5):
    print(request_id, "→", next(next_server))

코드에 나오는 함수

코드쉬운 뜻
import itertools반복(iteration)을 돕는 도구(tools) 모음이다
itertools.cycle(replicas)목록을 끝까지 돌면 다시 처음으로 돌아가며 끝없이 하나씩 내준다. cycle 은 "돌고 돈다"는 뜻이다
next(next_server)다음 차례 하나를 꺼낸다
replicasreplica(복제본)들이다. 같은 API 를 여러 개 띄운 것을 가리킨다

이 코드가 뜻하는 것

은행 창구가 셋일 때 번호표를 1번 창구, 2번 창구, 3번 창구, 다시 1번 창구 순으로 나눠 주는 것과 같습니다. 여기서 창구가 API 서버 하나, 번호표를 나눠 주는 사람이 부하 분산기입니다.

줄하는 일쉽게 말하면
replicas = [...]같은 API 를 셋 띄웠다고 적는다창구 셋
next_server = itertools.cycle(replicas)셋을 돌아가며 내주는 줄을 만든다번호표를 차례로 나눠 줄 준비
for request_id in range(5):요청 다섯 개를 받는다손님 다섯 명
next(next_server)이번 요청을 받을 서버를 고른다이번 손님을 몇 번 창구로 보낼지

결과는 0, 1, 2 번 요청이 api-1, api-2, api-3 으로, 3, 4 번 요청이 다시 api-1, api-2 로 갑니다. 비유와 다른 점은, 진짜 은행은 빈 창구로 보내지만 라운드 로빈은 서버가 바쁜지 보지 않고 차례만 따진다는 것입니다.

작게 줄여서 눈으로 보기

요청이 많아지면 서버마다 몇 개씩 가는지 세어 봅니다.

Python
import itertools
from collections import Counter

replicas = ["api-1", "api-2", "api-3"]
next_server = itertools.cycle(replicas)
sent = Counter(next(next_server) for _ in range(10))   # 요청 10개를 돌아가며 보낸다
print(sent)
출력
Counter({'api-1': 4, 'api-2': 3, 'api-3': 3})
결과읽는 법
'api-1': 410은 3으로 나누어떨어지지 않아, 처음 차례인 api-1 이 하나 더 받았다
'api-2': 3, 'api-3': 3나머지는 셋씩 받았다. 요청이 많을수록 거의 고르게 나뉜다

Counter 는 같은 것이 몇 번 나왔는지 세어 주는 도구입니다. counter 는 "세는 것"이라는 뜻입니다.

주의할 점

  • 나눌수록 고장 날 곳과 지켜볼 곳이 늘어납니다. 처음에는 화면과 API 둘로 시작하고, 필요할 때 나눕니다.
  • 층마다 /health 같은 상태 확인 주소를 두면, compose 나 감시 도구가 죽은 서비스를 알아채고 다시 띄울 수 있습니다(20장).
← 이전 절14장 · docker compose 로 여러 서비스 띄우기다음 절 →14장 · 실습 지도