Agent Server를 확장(scale)하도록 구성하기
Agent Server를 확장(scale)하도록 구성하기
자체 호스팅 배포를 위해 Agent Server를 조정해 볼게요 — 쓰기 부하, 읽기 부하, 그리고 다양한 부하 패턴에 대한 예시 Helm 구성까지 다룹니다. LangSmith Agent Server의 기본 구성은 다양한 워크로드에 걸쳐 상당한 읽기/쓰기 부하를 처리하도록 설계되어 있어요. 아래 모범 사례를 따르면 특정 워크로드에 최적화된 성능으로 Agent Server를 조정할 수 있습니다.
출처: 문서
본문
LangSmith Agent Server의 기본 구성은 다양한 워크로드에 걸쳐 상당한 읽기/쓰기 부하를 처리하도록 설계되었습니다. 아래 설명된 모범 사례를 따르면 특정 워크로드에 대해 최적으로 수행되도록 Agent Server를 조정할 수 있습니다. 이 페이지는 자체 호스팅 배포에서 Agent Server의 확장 고려 사항을 설명하고 예시 구성을 제공합니다.
팁: API 서버와 큐 워커가 컨테이너 수준에서 어떻게 작동하는지 아직 잘 모르겠다면 먼저 런타임 아키텍처 개요를 읽어보세요.
Cloud의 경우 플랫폼이 자동 스케일링하므로 아래 Helm 구성은 적용되지 않습니다.
요청 대비 런 동시성
Agent Server가 어떻게 스케일링되는지 결정하는 두 가지 독립적인 종류의 동시성이 있으며, 서로 별도로 제어됩니다:
- 요청 동시성(Request concurrency) 은 배포가 동시에 처리하는 API 요청(런 생성, 스레드 상태 읽기, 결과 스트리밍) 수입니다. API 서버는 요청을 비동기적으로 처리하며, 요청 동시성은 API 서버 복제본 수에 따라 수평 스케일링됩니다.
- 런 동시성(Run concurrency) 은 한 번에 실행되는 런 수입니다. 단일 큐 워커는 최대
N_JOBS_PER_WORKER개의 런을 동시에 실행합니다(기본 10). 런 동시성은 큐 워커 수 곱하기N_JOBS_PER_WORKER로 상한이 정해집니다.
런 생성은 빠른 쓰기 요청입니다: API 서버는 보류(pending) 런을 영속화하고 런 실행을 기다리지 않고 즉시 반환합니다. 모든 런 슬롯이 사용 중이면 추가 런은 큐에서 슬롯이 비워질 때까지 기다립니다. N_JOBS_PER_WORKER를 올리거나 큐 워커를 추가하면 런 처리량이 증가합니다. 배포가 동시에 처리할 수 있는 요청 수는 변경되지 않습니다.
쓰기 부하
쓰기 부하는 주로 다음 요인에 의해 발생합니다:
쓰기 부하를 처리하는 주요 컴포넌트:
- API 서버: 초기 요청과 데이터의 데이터베이스 영속화를 처리합니다.
- 큐 워커: 런 실행을 처리합니다.
- Redis: 진행 중인 런에 대한 임시 데이터 저장을 처리합니다.
- Postgres: 런, 스레드, 어시스턴트, cron 작업, 체크포인팅, 장기 메모리를 포함한 모든 데이터 저장을 처리합니다.
어시스턴트 특성에 따른 N_JOBS_PER_WORKER 조정
N_JOBS_PER_WORKER의 기본값은 10입니다. 어시스턴트 특성에 따라 단일 큐 워커가 한 번에 실행할 수 있는 최대 런 수를 조정하도록 이 값을 변경할 수 있습니다.
N_JOBS_PER_WORKER 변경에 대한 일반적인 지침:
- 어시스턴트가 CPU 바운드라면 기본값 10이 충분할 가능성이 높습니다. 큐 워커에서 과도한 CPU 사용 또는 런 실행 지연이 보이면
N_JOBS_PER_WORKER를 낮출 수 있습니다. - 어시스턴트가 메모리 바운드이거나 큐 워커가 메모리 한도에 근접하면, 워커당 동시 런 수를 줄이도록
N_JOBS_PER_WORKER를 낮추세요. - 어시스턴트가 IO 바운드라면, 워커당 더 많은 동시 런을 처리하도록
N_JOBS_PER_WORKER를 올리세요.
N_JOBS_PER_WORKER에는 상한이 없습니다. 그러나 큐 워커는 새 런을 가져올 때 탐욕스러워서, 사용 가능한 작업 수만큼 많은 런을 집어 들고 즉시 실행을 시작하려 합니다. 버스트 트래픽이 있는 환경에서 N_JOBS_PER_WORKER를 너무 높게 설정하면 워커 활용이 고르지 않고, 런 실행 시간이 늘어나며, 큐 워커의 메모리 사용이 높아질 수 있습니다.
동기 블로킹 작업 회피
코드에서 동기 블로킹 작업을 피하고 비동기 작업을 선호하세요. 긴 동기 작업은 메인 이벤트 루프를 블로킹해 요청 및 런 실행 시간을 늘리고 잠재적 타임아웃을 유발할 수 있습니다.
예를 들어 1초 동안 sleep이 필요한 애플리케이션을 생각해 봅시다. 다음과 같은 동기 코드 대신:
import time
def my_function():
time.sleep(1)
이런 비동기 코드를 선호하세요:
import asyncio
async def my_function():
await asyncio.sleep(1)
어시스턴트가 동기 블로킹 작업을 필요로 하면, 이를 asyncio.to_thread() 또는 이에 상응하는 곳에서 실행하세요.
중복 체크포인팅 최소화
durability를 데이터 내구성에 필요한 최소값으로 설정해 중복 체크포인팅을 최소화하세요.
기본 durability 모드는 "async"로, 각 단계 후 체크포인트가 비동기적으로 기록됩니다. 어시스턴트가 런의 최종 상태만 영속화하면 된다면 durability를 "exit"로 설정할 수 있으며, 이는 런의 최종 상태만 저장합니다. 이는 런 생성 시 설정할 수 있습니다:
from langgraph_sdk import get_client
client = get_client(url=<DEPLOYMENT_URL>)
thread = await client.threads.create()
run = await client.runs.create(
thread_id=thread["thread_id"],
assistant_id="agent",
durability="exit"
)
큐 워커 활성화
기본적으로 API 서버가 큐를 관리하며 큐 워커를 사용하지 않습니다. queue.enabled를 true로 설정해 큐 워커를 활성화하세요:
queue:
enabled: true
이렇게 하면 큐 관리가 API 서버에서 전용 큐 워커로 오프로드되어 API 서버의 부하를 줄이고 요청 처리에 집중할 수 있게 합니다.
예상 처리량에 맞게 jobs 크기 조정
이 섹션은 런 실행 용량(큐 워커)의 크기를 정하며, 이는 요청 처리 용량(API 서버 복제본)과 별개입니다. 자세한 내용은 요청 대비 런 동시성을 참고하세요.
병렬로 실행하는 런이 많을수록 부하를 처리할 jobs가 더 많이 필요합니다. 사용 가능한 jobs를 확장하는 두 가지 주요 매개변수가 있습니다:
number_of_queue_workers: 프로비저닝된 큐 워커 수.N_JOBS_PER_WORKER: 단일 큐 워커가 한 번에 실행할 수 있는 런 수. 기본값 10.
다음 방정식으로 사용 가능한 jobs를 계산할 수 있습니다:
available_jobs = number_of_queue_workers * N_JOBS_PER_WORKER
처리량은 사용 가능한 jobs가 초당 실행할 수 있는 런 수입니다:
throughput_per_second = available_jobs / average_run_execution_time_seconds
따라서 예상 정상 상태 처리량을 지원하기 위해 프로비저닝해야 하는 최소 큐 워커 수는:
number_of_queue_workers = throughput_per_second * average_run_execution_time_seconds / N_JOBS_PER_WORKER
버스트 쓰기 워크로드에 대한 자동 스케일링 구성
자동 스케일링은 기본적으로 비활성화되어 있지만, 버스트 워크로드에 대해 구성해야 합니다. 이전 섹션과 동일한 계산을 사용하여 최대 예상 처리량에 기반해 자동 스케일러가 스케일링하도록 허용할 최대 큐 워커 수를 결정할 수 있습니다.
읽기 부하
읽기 부하는 주로 다음 요인에 의해 발생합니다:
읽기 부하를 처리하는 주요 컴포넌트:
- API 서버: 요청과 데이터베이스에서의 직접 검색을 처리합니다.
- Postgres: 런, 스레드, 어시스턴트, cron 작업, 체크포인팅, 장기 메모리를 포함한 모든 데이터 저장을 처리합니다.
- Redis: 진행 중인 런에 대한 임시 데이터 저장을 처리하며, 큐 워커에서 API 서버로의 스트리밍 메시지를 포함합니다.
필터링으로 요청당 결과 줄이기
Agent Server는 각 리소스 유형에 대해 검색 API를 제공합니다. 이 API는 기본적으로 페이지네이션을 구현하고 많은 필터링 옵션을 제공합니다. 필터링을 사용해 요청당 반환되는 리소스 수를 줄이고 성능을 개선하세요.
TTL로 오래된 데이터 자동 삭제
스레드에 TTL을 설정해 오래된 데이터를 자동으로 정리합니다. 연결된 스레드가 삭제되면 런과 체크포인트가 자동으로 삭제됩니다.
폴링 회피; /join으로 런 모니터링
폴링으로 런 상태를 확인하는 대신 /join API 엔드포인트를 사용하세요. 이 메서드는 런이 완료되면 최종 상태를 반환합니다.
런의 출력을 실시간으로 모니터링해야 한다면 /stream API 엔드포인트를 사용하세요. 이 메서드는 런의 최종 상태를 포함한 런 출력을 스트리밍합니다.
버스트 읽기 워크로드에 대한 자동 스케일링 구성
자동 스케일링은 기본적으로 비활성화되어 있지만, 버스트 워크로드에 대해 구성해야 합니다. 최대 예상 처리량에 기반해 자동 스케일러가 스케일링하도록 허용할 최대 API 서버 수를 결정하세요.
예시 구성
참고: 정확한 최적 구성은 애플리케이션 복잡성, 요청 패턴, 데이터 요구 사항에 따라 다릅니다. 다음 예시를 이전 섹션의 정보 및 특정 사용과 결합해 필요에 따라 배포 구성을 업데이트하세요. 질문이 있으면 support.langchain.com을 통해 지원팀에 문의하세요.
다음 표는 다양한 부하 패턴(초당 읽기 요청 / 초당 쓰기 요청)과 표준 어시스턴트 특성(평균 런 실행 시간 1초, 중간 정도의 CPU 및 메모리 사용량)에 대한 서로 다른 Agent Server 구성의 개요를 제공합니다. 요청 속도는 필요한 정상 상태 런 처리량을 결정하며, 이는 큐 워커와 N_JOBS_PER_WORKER를 통해 크기가 정해집니다. 반면 API 서버 복제본은 요청 규모 자체를 처리하도록 크기가 정해집니다:
| Low / low | Low / high | High / low | Medium / medium | High / high | |
|---|---|---|---|---|---|
| 초당 쓰기 요청 | 5 | 5 | 500 | 50 | 500 |
| 초당 읽기 요청 | 5 | 500 | 5 | 50 | 500 |
| API 서버 (서버당 1 CPU, 2Gi) |
1 (기본) | 6 | 10 | 3 | 15 |
| 큐 워커 (워커당 1 CPU, 2Gi) |
1 (기본) | 10 | 1 (기본) | 5 | 10 |
N_JOBS_PER_WORKER |
10 (기본) | 50 | 10 | 10 | 50 |
| Redis 리소스 | 2 Gi (기본) | 2 Gi (기본) | 2 Gi (기본) | 2 Gi (기본) | 2 Gi (기본) |
| Postgres 리소스 | 2 CPU 8 Gi (기본) |
4 CPU 16 Gi 메모리 |
4 CPU 16 Gi |
4 CPU 16 Gi 메모리 |
8 CPU 32 Gi 메모리 |
예시의 부하 수준은 다음과 같이 정의됩니다:
- Low는 초당 약 5 요청을 의미합니다.
- Medium은 초당 약 50 요청을 의미합니다.
- High는 초당 약 500 요청을 의미합니다.
낮은 읽기, 낮은 쓰기
기본 LangSmith Deployment 구성이 이 부하를 처리합니다. 여기서 커스텀 리소스 구성은 필요하지 않습니다.
낮은 읽기, 높은 쓰기
배포가 대량의 쓰기 요청(초당 500개)을 처리하지만 상대적으로 읽기 요청이 적습니다(초당 5개).
이 경우 다음 구성을 권장합니다:
# Example configuration for low reads, high writes (5 read/500 write requests per second)
api:
replicas: 6
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
queue:
replicas: 10
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
config:
numberOfJobsPerWorker: 50
redis:
resources:
requests:
memory: "2Gi"
limits:
memory: "2Gi"
postgres:
resources:
requests:
cpu: "4"
memory: "16Gi"
limits:
cpu: "8"
memory: "32Gi"
높은 읽기, 낮은 쓰기
대량의 읽기 요청(초당 500개)을 처리하지만 쓰기 요청은 비교적 적습니다(초당 5개).
이 경우 다음 구성을 권장합니다:
# Example configuration for high reads, low writes (500 read/5 write requests per second)
api:
replicas: 10
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
queue:
replicas: 1 # Default, minimal write load
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
redis:
resources:
requests:
memory: "2Gi"
limits:
memory: "2Gi"
postgres:
resources:
requests:
cpu: "4"
memory: "16Gi"
limits:
cpu: "8"
memory: "32Gi"
# Consider read replicas for high read scenarios
readReplicas: 2
중간 읽기, 중간 쓰기
이것은 적당한 읽기/쓰기 부하(초당 50 읽기/50 쓰기 요청)를 처리해야 하는 균형 잡힌 구성입니다.
이 경우 다음 구성을 권장합니다:
# Example configuration for medium reads, medium writes (50 read/50 write requests per second)
api:
replicas: 3
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
queue:
replicas: 5
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
redis:
resources:
requests:
memory: "2Gi"
limits:
memory: "2Gi"
postgres:
resources:
requests:
cpu: "4"
memory: "16Gi"
limits:
cpu: "8"
memory: "32Gi"
높은 읽기, 높은 쓰기
읽기와 쓰기 요청이 모두 많습니다(초당 500 읽기/500 쓰기 요청).
이 경우 다음 구성을 권장합니다:
# Example configuration for high reads, high writes (500 read/500 write requests per second)
api:
replicas: 15
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
queue:
replicas: 10
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
config:
numberOfJobsPerWorker: 50
redis:
resources:
requests:
memory: "2Gi"
limits:
memory: "2Gi"
postgres:
resources:
requests:
cpu: "8"
memory: "32Gi"
limits:
cpu: "16"
memory: "64Gi"
자동 스케일링
배포가 버스트 트래픽을 경험하면 자동 스케일링을 활성화해 API 서버와 큐 워커 수를 확장해 부하를 처리할 수 있습니다.
높은 읽기와 높은 쓰기에 대한 자동 스케일링 샘플 구성은 다음과 같습니다:
api:
autoscaling:
enabled: true
minReplicas: 15
maxReplicas: 25
queue:
autoscaling:
enabled: true
minReplicas: 10
maxReplicas: 20
참고: 배포 환경에 권장 크기로 확장하기에 충분한 리소스가 있는지 확인하세요. 최적의 성능을 보장하려면 애플리케이션과 인프라를 모니터링하세요. 리소스 사용량과 애플리케이션 성능을 추적하는 모니터링 및 경고를 구현하는 것을 고려하세요.
더 알아보기
- 런타임 아키텍처에 대한 자세한 내용은 Agent Server 문서를 참고하세요.
- 환경 변수 레퍼런스는 Environment variables 문서를 확인해 보세요.