- Published on
Triton Inference Server 모델 핫스왑 배포·롤백 실전
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서빙 중인 모델을 바꾸는 순간이 가장 위험합니다. 새 모델이 느려지거나, 출력 스키마가 달라지거나, 특정 입력에서만 크래시가 나면 트래픽이 그대로 장애로 이어집니다. Triton Inference Server는 모델 리포지토리 기반 로딩/언로딩과 버전 디렉터리를 제공해 “프로세스 재시작 없이” 모델 교체(핫스왑)가 가능하지만, 운영에서 중요한 건 기능 자체보다 배포·검증·롤백 절차를 어떻게 표준화하느냐입니다.
이 글에서는 Triton의 모델 로딩 메커니즘을 바탕으로,
- 무중단(또는 최소 다운타임) 핫스왑 배포 패턴
- 실패를 빠르게 감지하는 검증(health/readiness + 샘플 추론)
- 안전한 롤백(버전 고정, 원자적 스위치, 트래픽 분리)
- Kubernetes에서의 적용 포인트
를 한 번에 정리합니다.
Triton의 모델 로딩 개념 정리
Triton은 기본적으로 모델 리포지토리(model repository) 디렉터리를 감시하거나(폴링), 관리 API로 로드/언로드를 수행합니다. 모델은 일반적으로 아래 형태로 배치합니다.
model_repository/{model_name}/{version}/model.*model_repository/{model_name}/config.pbtxt
핵심은 버전 디렉터리입니다. Triton은 같은 모델 이름 아래 여러 버전을 둘 수 있고, 정책에 따라 “어떤 버전을 서빙할지”를 결정합니다.
모델 컨트롤 모드: 배포 전략을 좌우하는 옵션
Triton 실행 시 --model-control-mode가 중요합니다.
none: 시작 시 로드된 모델이 고정. 운영 중 변경 불가에 가깝습니다.poll: 리포지토리 변경을 주기적으로 감지해 로드/리로드.explicit: 관리 API로load/unload를 명시적으로 호출.
운영 관점에서 가장 예측 가능한 방식은 explicit 입니다. 파일이 반쯤 복사된 상태를 Triton이 감지해 로딩하다 실패하는 류의 사고를 줄이고, “검증 후 로드” 같은 절차를 만들기 쉽습니다.
핫스왑 배포의 목표: 원자성, 검증, 되돌리기
핫스왑을 안정적으로 만들려면 다음 3가지를 동시에 만족해야 합니다.
- 원자성: 새 버전이 “완전히 준비된 상태”로만 노출되어야 함
- 검증: 로드 성공뿐 아니라 성능/출력 계약을 확인해야 함
- 롤백: 문제가 생기면 즉시 이전 버전으로 되돌릴 수 있어야 함
Triton은 “로드 성공”은 보장해주지만, “출력 품질”과 “p99 지연”은 보장해주지 않습니다. 그래서 배포 파이프라인이 필요합니다.
모델 리포지토리 버저닝 규칙(권장)
운영에서 가장 흔한 실수는 버전을 덮어쓰는 것입니다. 예를 들어 1/ 디렉터리를 덮어쓰면, 캐시/로드 타이밍/동시성에 따라 예측 불가능한 상태가 됩니다.
권장 규칙:
- 버전 디렉터리는 불변(immutable): 한 번 배포한
42/는 절대 수정하지 않기 - 새 배포는 항상 새 버전 번호로:
43/추가 - 롤백은 “이전 버전으로 트래픽을 되돌리는 것”이지 “파일을 되돌리는 것”이 아님
추가로, 모델 아티팩트는 가능하면 콘텐츠 해시를 남겨 추적성을 확보합니다.
- 예:
models/my_model/43/에SHA256SUMS파일 저장 - 예: CI에서
git commit sha와 매핑되는 메타데이터를 별도 저장
배포 방식 1: explicit 모드 + 관리 API로 안전하게 로드
explicit 모드에서는 아래 흐름이 가장 깔끔합니다.
- 새 버전 디렉터리를 리포지토리에 업로드(아직 로드하지 않음)
- 관리 API로 새 버전
load - readiness + 샘플 추론으로 검증
- 트래픽 스위칭(클라이언트/게이트웨이/라우팅 레벨)
- 안정화 후 구버전
unload또는 유지
관리 API 호출 예시
Triton은 HTTP 관리 포트를 통해 모델 로드/언로드를 제공합니다(환경에 따라 포트/경로는 다를 수 있음).
# 모델 로드
curl -X POST \
http://triton.example.com:8000/v2/repository/models/my_model/load
# 모델 언로드
curl -X POST \
http://triton.example.com:8000/v2/repository/models/my_model/unload
특정 버전을 직접 지정하는 방식은 설정/버전 정책에 따라 달라질 수 있으므로, 운영에서는 보통 모델 이름은 고정하고 “어떤 버전을 기본으로 쓸지”를 config.pbtxt의 버전 정책으로 제어하거나, 아예 모델 이름 자체를 버전으로 분리해 라우팅으로 전환하는 패턴을 씁니다.
배포 방식 2: 모델 이름을 분리해 블루/그린 스위칭
가장 단순하면서 강력한 방법은 “같은 모델 이름 아래 버전 디렉터리”에만 의존하지 않고, 모델 이름을 아예 분리하는 것입니다.
reco_model_bluereco_model_green
그리고 게이트웨이(Envoy/Nginx)나 애플리케이션 레벨에서 호출 모델명을 바꾸거나, 라우팅 규칙으로 트래픽을 전환합니다.
장점:
- 새 모델이 완전히 준비되기 전까지 기존 모델에 영향이 없음
- 롤백이 매우 빠름(라우팅만 되돌리면 됨)
- A/B, 카나리 배포가 쉬움
단점:
- 모델이 2벌 떠 있어 GPU 메모리/CPU 메모리 사용량이 증가
GPU 메모리가 빠듯한 환경이라면 카나리 비율을 낮게 시작하거나, 시간대별로 전환하는 식의 운영 절충이 필요합니다.
“원자적 배포”를 위한 파일 배치 요령
poll 모드를 쓰거나, 리포지토리를 공유 볼륨으로 마운트하는 경우 “복사 중인 파일을 Triton이 읽는” 문제가 생길 수 있습니다. 이를 피하려면 스테이징 디렉터리를 두고, 마지막에 원자적으로 스위치합니다.
예시(동일 파일시스템에서 rename이 원자적이라는 가정):
set -euo pipefail
REPO=/models
MODEL=my_model
VER=43
STAGE=${REPO}/.staging/${MODEL}/${VER}
FINAL=${REPO}/${MODEL}/${VER}
mkdir -p "${STAGE}"
# 1) 스테이징에 업로드
rsync -a --delete ./artifacts/ "${STAGE}/"
# 2) 무결성 체크(예: sha256)
( cd "${STAGE}" && sha256sum -c SHA256SUMS )
# 3) 최종 경로로 원자적 이동
mkdir -p "${REPO}/${MODEL}"
mv "${STAGE}" "${FINAL}"
이 패턴을 쓰면 Triton이 디렉터리를 보더라도 “완성된 버전 디렉터리”만 보게 됩니다.
검증(Validation): 로드 성공만으로는 부족하다
핫스왑에서 중요한 건 “서버가 모델을 로드했다”가 아니라 “모델이 SLA와 계약을 만족한다”입니다.
권장 검증 체크리스트:
ready상태 확인- 메타데이터(입출력 텐서 이름/shape/dtype) 확인
- 대표 입력으로 샘플 추론(정상 응답, 범위 체크)
- 지연 측정(p50/p95/p99)
- 오류율(특정 입력에서만 터지는지)
readiness / health 확인 예시
# 서버 readiness
curl -sf http://triton.example.com:8000/v2/health/ready
# 모델 readiness (환경에 따라 endpoint 제공)
curl -sf http://triton.example.com:8000/v2/models/my_model/ready
샘플 추론 검증(파이썬) 예시
아래는 Triton HTTP 클라이언트를 쓴 “배포 후 자동 검증”의 최소 형태입니다.
import numpy as np
import tritonclient.http as httpclient
TRITON_URL = "triton.example.com:8000"
MODEL_NAME = "my_model"
client = httpclient.InferenceServerClient(url=TRITON_URL, verbose=False)
# 1) 서버/모델 준비 확인
assert client.is_server_live()
assert client.is_server_ready()
assert client.is_model_ready(MODEL_NAME)
# 2) 대표 입력 1건으로 스모크 테스트
x = np.random.rand(1, 128).astype(np.float32)
inputs = [httpclient.InferInput("INPUT__0", x.shape, "FP32")]
inputs[0].set_data_from_numpy(x)
outputs = [httpclient.InferRequestedOutput("OUTPUT__0")]
resp = client.infer(model_name=MODEL_NAME, inputs=inputs, outputs=outputs)
y = resp.as_numpy("OUTPUT__0")
assert y is not None
assert np.isfinite(y).all()
print("ok", y.shape)
운영에서는 여기에 “정답 라벨이 있는 골든셋”을 붙이거나, 이전 버전과의 출력 차이를 비교해 회귀(regression) 를 잡는 게 효과적입니다.
롤백 전략: 되돌릴 대상은 파일이 아니라 트래픽
롤백을 빠르게 만드는 핵심은 “모델 파일을 다시 복사”가 아니라, 어떤 모델(또는 버전)로 요청을 보낼지를 즉시 되돌리는 것입니다.
롤백 패턴 A: 라우팅 기반(권장)
- 블루/그린 모델명을 유지
- 게이트웨이/클라이언트 설정에서 라우팅만 변경
이 방식은 수 초 내 롤백이 가능하고, 파일 조작이 없어 안전합니다.
롤백 패턴 B: 버전 정책 기반
같은 모델 이름 아래 여러 버전이 있을 때, 기본 서빙 버전을 바꾸는 방식입니다. 이 경우 “어떤 버전이 기본인지”를 바꾸는 메커니즘을 명확히 해야 합니다.
실무 팁:
- “현재 프로덕션 버전”을 별도 설정 저장소(예: ConfigMap, Consul, DB)에 기록
- 변경 시에는 항상 “이전 값”을 남겨 즉시 복구 가능하게 구성
Git을 운영 설정의 소스로 쓰는 팀이라면, 설정 변경이 꼬였을 때 복구 루트가 필요합니다. 강제 푸시나 리베이스 이후 히스토리가 꼬여도 reflog로 되살릴 수 있는 패턴은 배포 운영에서 꽤 유용합니다: Git rebase 후 강제푸시 충돌? reflog로 복구하기
Kubernetes에서의 무중단 배포 포인트
Triton을 K8s에서 운영하면 “모델 핫스왑”과 “파드 롤링 업데이트”가 겹칩니다. 둘을 섞으면 복잡도가 커지므로, 다음 중 하나로 의도를 분리하는 게 좋습니다.
- 파드는 고정(서버는 유지) + 모델만 핫스왑
- 모델은 고정 + 파드를 롤링 업데이트(이미지에 모델 포함)
- 둘 다 하되, 항상 한 번에 하나만 바꾸기
(1) Readiness Probe를 엄격하게
롤링 업데이트를 쓰든, 핫스왑을 하든, readiness가 느슨하면 장애가 증폭됩니다.
예시:
readinessProbe:
httpGet:
path: /v2/health/ready
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
httpGet:
path: /v2/health/live
port: 8000
initialDelaySeconds: 20
periodSeconds: 10
다만 모델 로딩이 무거운 경우 readiness가 오래 걸릴 수 있으니, 초기 지연/실패 임계치를 모델 크기에 맞춰 조정해야 합니다.
(2) 모델 로딩 실패가 CrashLoop로 번지지 않게
모델 하나가 깨졌다고 파드가 계속 재시작하면, 롤백 전에 서비스가 더 불안정해집니다. 로딩 실패를 “모델만 실패”로 격리하거나, 최소한 원인을 빠르게 파악할 수 있어야 합니다.
CrashLoopBackOff 디버깅은 Triton 자체 이슈뿐 아니라 볼륨 마운트, 권한, OOM 등 인프라 요인이 섞이는 경우가 많습니다. 재현/분류 방법은 이 글의 체크리스트가 도움이 됩니다: Kubernetes CrashLoopBackOff 원인 7가지와 재현·해결
(3) 트래픽 급증 시 네트워크/리소스 한계도 같이 본다
모델을 바꾸면 지연이 늘어 HPA가 스케일아웃하고, 그 과정에서 노드 IP가 부족해지는 등 “서빙 외부”에서 병목이 터질 수 있습니다. 특히 EKS에서 CNI IP가 타이트하면 배포/스케일링 시점에 증상이 두드러집니다: EKS CNI Prefix Delegation인데도 IP 부족할 때
운영 체크리스트: 배포 전/중/후
핫스왑을 반복 가능한 절차로 만들기 위해, 아래를 런북으로 고정하는 것을 권합니다.
배포 전
- 새 버전 디렉터리는 불변으로 생성(덮어쓰기 금지)
config.pbtxt의 입출력 계약 변경 여부 확인- 골든 입력/회귀 테스트 준비
- GPU 메모리 여유 확인(블루/그린이면 2벌 기준)
배포 중
- 스테이징 업로드 후 무결성 체크
explicit모드면 관리 API로 로드- readiness 확인 후 샘플 추론
- 카나리 트래픽으로 p95/p99, 오류율 관찰
배포 후
- 안정화 시간 동안 구버전 유지(즉시 언로드하지 않기)
- 지표 기반 자동 롤백 조건 정의(오류율, p99, OOM)
- 문제 발생 시 “트래픽 라우팅 먼저 롤백” 후 원인 분석
마무리: 핫스왑은 기능이 아니라 절차다
Triton은 모델을 런타임에 교체할 수 있는 강력한 기반을 제공합니다. 하지만 운영에서 무중단을 만드는 건 poll이냐 explicit이냐보다,
- 버전 디렉터리 불변 규칙
- 원자적 배치
- 로드 후 검증 자동화
- 라우팅 기반의 즉시 롤백
같은 절차의 표준화입니다.
가장 추천하는 조합은 “모델 이름 분리(블루/그린) + explicit 로드 + 카나리 라우팅 + 지표 기반 자동 롤백”입니다. GPU 리소스가 허락하지 않는다면, 최소한 “불변 버저닝 + 원자적 배치 + 샘플 추론 검증 + 트래픽 스위치 우선 롤백”만이라도 갖추면 핫스왑의 실패 비용을 크게 줄일 수 있습니다.