# RayService 업데이트가 클러스터를 통째로 갈아엎는 조건

RayService로 Ray Serve 애플리케이션을 운영하다 보면, 별것 아닌 값 하나를 고쳤는데 클러스터 전체가 재생성되는 일이 있습니다. 반대로 큰 변경인 줄 알았는데 무중단으로 조용히 반영되기도 합니다. 이 차이는 임의적이지 않습니다. 매니페스트의 어느 필드를 건드렸는가로 결정됩니다.

GPU 워커가 붙은 클러스터라면 재생성 비용이 작지 않습니다. 이 글에서는 두 갱신 경로를 가르는 기준, 그 경계에 뚫려 있는 예외, 그리고 GitOps로 관리할 때 생기는 상호작용을 정리합니다.

# 1. 두 갈래로 갈리는 갱신

RayService가 관리하는 것은 둘입니다. Kubernetes 리소스를 담당하는 RayCluster, 그리고 그 위에서 도는 Serve 애플리케이션입니다. 어느 쪽을 고쳤느냐에 따라 처리 방식이 달라집니다.

serveConfigV2를 고치면 제자리에서 반영됩니다.

reapplies the new configurations to the existing RayCluster instead of creating a new RayCluster

spec.rayClusterConfig를 고치면 클러스터를 새로 만듭니다.

temporarily creates a new RayCluster and waits for it to be ready, then switches traffic to the new cluster and terminates the old one

기본 업그레이드 전략이 NewCluster라는 점이 핵심입니다. 무중단을 위해 구·신 클러스터가 한동안 공존합니다.

[기존 RayCluster]  ← 트래픽
        │
        │  rayClusterConfig 변경 감지
        ▼
[기존] + [신규 RayCluster 생성 중]     ← 이 구간 동안 자원 2배
        │
        │  신규 Ready
        ▼
[기존 종료]  [신규] ← 트래픽 전환 완료
변경 대상 처리 실무적 의미
serveConfigV2 (배포 수, 라우팅, 인자) 제자리 재적용 빠르고 저렴
spec.rayClusterConfig (이미지, 자원, 워커 그룹) 새 클러스터 생성 후 전환 느리고 자원 2배 필요

# 2. 이 구분이 만드는 실제 비용

GPU 워커 8대짜리 클러스터에서 워커 이미지 태그 한 글자를 바꾸면 rayClusterConfig 변경입니다. 전환이 끝날 때까지 GPU 16대분의 자원이 동시에 필요합니다. 클러스터에 그만한 여유가 없으면 신규 클러스터의 워커가 Pending에 걸리고, 전환은 완료되지 않은 채 기존 클러스터도 계속 살아 있습니다. 자원은 자원대로 묶이고 배포는 끝나지 않습니다.

그래서 결정해야 할 것이 생깁니다.

  • 여유 자원을 상시 확보할 것인가 - 가장 단순하지만 유휴 비용이 상시 발생합니다.
  • 노드 오토스케일링에 맡길 것인가 - 전환 시점에 노드를 늘립니다. 노드 프로비저닝 시간만큼 전환이 길어지고, 그동안 기존 클러스터도 유지됩니다.
  • 변경을 묶을 것인가 - 이미지·자원·워커 그룹 변경을 한 번에 반영해 재생성 횟수 자체를 줄입니다.

세 번째가 대개 가장 효과적입니다. rayClusterConfig를 건드리는 변경은 모아서 한 번에 내보내고, 일상적인 조정은 serveConfigV2 안에서 끝내도록 매니페스트를 설계하는 편이 좋습니다. 배포 수, 동시 요청 상한, 애플리케이션 인자처럼 자주 만지는 값은 전부 이쪽에 있습니다.

# 3. 경계에 난 구멍 - 오토스케일러가 쓰는 필드

여기서 미묘한 예외가 등장합니다. rayClusterConfig 하위인데도 재생성을 유발하지 않는 필드가 있습니다.

Fields managed by Ray autoscaler-replicas, minReplicas, maxReplicas, and scaleStrategy.workersToDelete-don't trigger a zero downtime upgrade.

이 예외가 없으면 오토스케일러가 워커를 하나 늘릴 때마다 클러스터가 통째로 재생성될 테니, 반드시 있어야 하는 규칙입니다. 다만 결과적으로 같은 블록 안의 필드들이 서로 다르게 취급된다는 점은 기억해야 합니다.

spec:
  rayClusterConfig:
    workerGroupSpecs:
      - groupName: gpu-workers
        replicas: 4          # 바꿔도 재생성 없음
        minReplicas: 0       # 바꿔도 재생성 없음
        maxReplicas: 16      # 바꿔도 재생성 없음
        template:
          spec:
            containers:
              - image: ...   # 바꾸면 재생성
                resources:   # 바꾸면 재생성

minReplicasmaxReplicas가 예외 목록에 있다는 사실은 운영상 반가운 부분입니다. 용량 정책을 조정하는 것만으로 서비스가 재생성되지는 않습니다.

# 4. GitOps와 함께 쓸 때

배포 도구가 매니페스트를 계속 되돌리는 환경에서는 두 가지가 겹칩니다.

첫째, replicas 소유권 문제. 오토스케일러가 4로 올려놓은 값을 Git의 0으로 되돌리면 축소가 강제됩니다. 이 필드는 동기화 대상에서 빼야 합니다. 판정만 제외할지 실제 적용까지 제외할지 구분이 필요한데, 그 차이는 ArgoCD ignoreDifferences가 동작하지 않는 세 가지 이유 (opens new window)에 정리했습니다.

spec:
  ignoreDifferences:
    - group: ray.io
      kind: RayService
      jqPathExpressions:
        - .spec.rayClusterConfig.workerGroupSpecs[].replicas
  syncPolicy:
    syncOptions:
      - RespectIgnoreDifferences=true

둘째, 자동 동기화의 타이밍. rayClusterConfig를 건드리는 커밋이 자동 동기화로 즉시 반영되면, 사람이 자원 여유를 확인하기 전에 재생성이 시작됩니다. 여러 환경의 앱이 같은 브랜치를 바라보고 있으면 동시에 시작됩니다. 재생성을 유발하는 변경은 동기화 창을 통제하거나 수동 승인 단계를 두는 편이 안전합니다.

# 5. 전환이 끝나지 않을 때

RayService의 준비 판정은 Serve 엔드포인트가 실제로 노출되는지와 배포 상태 전이(UPDATINGHEALTHY/RUNNING)로 이뤄집니다. 전환이 멈췄다면 신규 클러스터가 어느 단계에서 걸렸는지부터 좁혀야 합니다.

# RayCluster가 몇 개 떠 있는가 - 2개면 전환 중이다
kubectl get raycluster -l ray.io/originated-from-cr-name=<rayservice-name>

kubectl get rayservice <name> -o jsonpath='{.status}' | jq
kubectl get pods -l ray.io/cluster=<new-cluster> -o wide     # Pending 여부
kubectl logs <new-head-pod> -c ray-head --tail=100           # Serve 배포 실패 여부
증상 원인 조치
RayCluster가 2개인 채로 멈춤 신규 워커 Pending (자원 부족) 자원 확보 또는 워커 요구량 축소
신규 클러스터는 Ready인데 전환 안 됨 Serve 애플리케이션이 HEALTHY에 도달 못 함 애플리케이션 임포트·초기화 오류 확인
사소한 변경인데 재생성됨 rayClusterConfig 하위를 건드림 변경 대상 필드 확인, serveConfigV2로 이동 가능한지 검토
배포할 때마다 워커가 0으로 돌아감 replicas 동기화 충돌 무시 규칙 + 적용 단계까지 제외
전환 중 요청 실패 애플리케이션 기동 시간이 준비 판정보다 김 초기화 로직과 준비 신호 정합

# 6. 설계에서 미리 나눠 둘 것

정리하면, 매니페스트를 처음 짤 때 자주 바뀌는 값을 serveConfigV2 쪽으로 몰아 두는 것이 이후 운영 비용을 결정합니다.

값의 성격 두어야 할 위치 이유
배포 수, 동시 요청 상한, 라우팅 serveConfigV2 제자리 반영, 즉시 적용
모델 경로·하이퍼파라미터 같은 인자 serveConfigV2 애플리케이션 인자 이미지 재빌드·재생성 회피
런타임 이미지, 컨테이너 자원 rayClusterConfig (불가피) 변경을 모아서 반영
용량 상·하한 rayClusterConfig 예외 필드 재생성 없이 조정 가능

모델 파일 경로를 이미지에 굽지 말고 인자로 빼는 것만으로도, 모델 교체가 클러스터 재생성에서 설정 반영으로 내려옵니다. GPU 클러스터에서는 이 하나로도 체감 차이가 큽니다.

# 7. 마무리

  • RayService의 갱신은 두 갈래입니다. serveConfigV2는 제자리 반영, rayClusterConfig는 새 클러스터 생성 후 전환입니다.
  • 기본 전략이 NewCluster이므로 전환 구간에 자원이 두 배로 필요합니다. GPU 클러스터에서는 이것이 배포 실패의 직접 원인이 됩니다.
  • replicas, minReplicas, maxReplicas, workersToDelete는 예외적으로 재생성을 유발하지 않습니다.
  • 자주 바뀌는 값을 serveConfigV2로 몰아 두는 설계가 나중의 배포 비용을 결정합니다.

# 참고