Ray Serve 자동 스케일링
Ray Serve 자동 스케일링
Ray Serve deployment는 기본적으로 레플리카가 하나라서 트래픽이 늘면 단일 레플리카가 과부하될 수 있어요. **자동 스케일링(autoscaling)**을 설정하면 큐 크기를 모니터링하며 들어오는 트래픽에 맞춰 레플리카 수를 자동으로 늘리거나 줄여요. 이 가이드에선 수동 스케일링부터 num_replicas="auto" 설정, 핵심 파라미터 튜닝까지 다뤄요. LLM 서빙처럼 GPU 레플리카를 쓰는 워크로드에서 특히 비용과 성능을 맞추는 데 중요해요.
수동 스케일링
자동 스케일링으로 넘어가기 전에, 더 단순한 대안인 수동 스케일링을 먼저 고려해 볼 수 있어요. deployment 옵션의 num_replicas 값을 in-place 업데이트로 높이면 레플리카를 늘릴 수 있어요. 기본 값은 num_replicas가 1이에요. 레플리카 수를 늘리면 deployment가 수평 스케일아웃돼 증가한 트래픽 수준에서 지연 시간과 처리량이 개선돼요.
# Deploy with a single replica
deployments:
- name: Model
num_replicas: 1
# Scale up to 10 replicas
deployments:
- name: Model
num_replicas: 10
자동 스케일링 기본 구성
deployment에 고정 레플리카 수를 설정하고 수동으로 갱신하는 대신, 들어오는 트래픽에 따라 자동으로 스케일링되게 구성할 수 있어요. Serve 자동 스케일러는 큐 크기를 모니터링하고 레플리카를 추가·제거하는 스케일링 결정을 내려 트래픽 급증에 반응해요. deployment에 num_replicas="auto"를 설정하면 자동 스케일링이 켜져요. deployment 옵션의 autoscaling_config로 더 세부 설정을 할 수 있어요.
다음 구성은 다음 섹션 예시에서 쓸 설정이에요.
- name: Model
num_replicas: auto
num_replicas="auto"를 설정하는 것은 다음 deployment 구성과 동일해요.
- name: Model
max_ongoing_requests: 5
autoscaling_config:
target_ongoing_requests: 2
min_replicas: 1
max_replicas: 100
:::note 참고
num_replicas="auto"를 설정하면 위 기본값들이 적용돼요, 여기엔 max_replicas: 100도 포함돼요. 하지만 num_replicas="auto" 없이 자동 스케일링을 수동으로 설정하면 max_replicas 기본값이 1이라, 명시적으로 더 높은 값을 설정하지 않으면 스케일링이 일어나지 않아요. num_replicas="auto"를 쓰더라도 autoscaling_config를 지정해 이 기본값들을 덮어쓸 수 있어요.
:::
각 파라미터가 하는 일을 살펴볼게요.
- target_ongoing_requests — Serve 자동 스케일러가 유지하려는 레플리카당 평균 진행 중 요청 수예요. 요청 처리 길이(요청이 길수록 이 값은 작아야 함)와 지연 목표(지연을 짧게 원할수록 이 값은 작아야 함)에 맞춰 조정할 수 있어요.
- max_ongoing_requests — 레플리카가 허용하는 최대 진행 중 요청 수예요. 이 파라미터는 모든 deployment에 관련돼 있어서 자동 스케일링 구성의 일부는 아니지만, deployment에 자동 스케일링을 켜면 target 값과 상대적으로 설정하는 게 중요해요.
- min_replicas — deployment의 최소 레플리카 수예요. 오래 트래픽이 없어도 괜찮고 스케일업 중 약간의 꼬리 지연을 허용한다면 0으로 설정하세요. 그렇지 않으면 저트래픽에 필요한 값으로 설정하면 돼요.
- max_replicas — deployment의 최대 레플리카 수예요. 피크 트래픽에 필요한 값보다 약 20% 높게 설정하세요.
이 가이드라인은 좋은 출발점이에요. 자동 스케일링 설정을 더 튜닝하려면 Advanced Ray Serve Autoscaling을 참고하세요.
기본 예시
이 예시는 ResNet50을 실행하는 동기 워크로드예요. application 코드와 자동 스케일링 구성은 아래와 같아요.
import requests
from io import BytesIO
from PIL import Image
import starlette.requests
import torch
from torchvision import transforms
import torchvision.models as models
from torchvision.models import ResNet50_Weights
from ray import serve
@serve.deployment(
ray_actor_options={"num_cpus": 1},
num_replicas="auto",
)
class Model:
def __init__(self):
self.resnet50 = (
models.resnet50(weights=ResNet50_Weights.DEFAULT).eval().to("cpu")
)
self.preprocess = transforms.Compose(
[
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
),
]
)
resp = requests.get(
"https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt"
)
self.categories = resp.content.decode("utf-8").split("\n")
async def __call__(self, request: starlette.requests.Request) -> str:
uri = (await request.json())["uri"]
image_bytes = requests.get(uri).content
image = Image.open(BytesIO(image_bytes)).convert("RGB")
# Batch size is 1
input_tensor = torch.cat([self.preprocess(image).unsqueeze(0)]).to("cpu")
with torch.no_grad():
output = self.resnet50(input_tensor)
sm_output = torch.nn.functional.softmax(output[0], dim=0)
ind = torch.argmax(sm_output)
return self.categories[ind]
app = Model.bind()
applications:
- name: default
import_path: resnet:app
deployments:
- name: Model
num_replicas: auto
이 예시는 Locust로 이 application을 대상으로 부하 테스트를 돌려요. Locust 부하 테스트는 상수 대기 시간(0)으로 ResNet50 서비스에 ping 하는 특정 수의 'users'를 실행해요. 각 사용자는 요청을 보내고 응답을 기다린 뒤 즉시 다음 요청을 보내는 걸 반복해요.
부하 테스트 결과에서 다음을 관찰할 수 있어요:
- 각 Locust 사용자가 단일 요청을 계속 보내고 응답을 기다리므로, 시간이 흐르면서 자동 스케일링된 레플리카 수는
target_ongoing_requests=2설정을 충족하려는 Serve 동작으로 Locust 사용자 수의 대략 절반이 돼요. - 시스템 처리량은 사용자와 레플리카 수가 늘어남에 따라 증가해요.
- 지연 시간은 트래픽이 증가할 때 짧게 튀지만, 그 외에는 비교적 안정적이에요.
Ray Serve Autoscaler vs Ray Autoscaler
Ray Serve Autoscaler는 Ray Autoscaler 위에 놓이는 애플리케이션 레벨 자동 스케일러예요. 구체적으로는 Ray Serve 자동 스케일러가 요청 수요에 따라 일정 수의 레플리카 Actor를 시작하도록 Ray에 요청해요. Ray Autoscaler가 이 Actor들을 배치할 충분한 리소스(CPU, GPU 등)가 없다고 판단하면 더 많은 Ray 노드를 요청하고, 기본 클라우드 프로바이더가 노드를 추가해요. 마찬가지로 Ray Serve가 스케일다운하며 레플리카 Actor를 종료할 때는 가능한 한 많은 노드를 유휴 상태로 만들어 Ray Autoscaler가 제거하도록 해요. Ray Serve 자동 스케일링의 기반 아키텍처를 더 보려면 Ray Serve Autoscaling Architecture를 참고하세요.