🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

MLflow 프로덕션 운영 실전 가이드 - 슬라이드 1/7
A

AI Generated

2025. 11. 30. · 17 Views

MLflow 프로덕션 운영 실전 가이드

MLflow를 활용하여 프로덕션 환경에서 ML 모델을 안정적으로 운영하는 방법을 다룹니다. Model Registry부터 Kubernetes 배포, 롤백 전략까지 실무에서 바로 적용할 수 있는 내용을 담았습니다.


목차

  1. Model_Registry_워크플로우
  2. 모델_버전_관리_전략
  3. AB_테스팅_구현하기
  4. 롤백_전략_수립하기
  5. Docker_Kubernetes_통합
  6. 모델_승인_프로세스_설계

1. Model Registry 워크플로우

김개발 씨는 데이터 과학팀에서 개발한 추천 모델을 프로덕션에 배포하려고 합니다. 그런데 문제가 생겼습니다.

어제 학습한 모델은 어디 있고, 지난주에 배포했던 모델은 또 어디에 있는 걸까요? 모델 파일들이 여기저기 흩어져 있어서 도무지 정리가 되지 않습니다.

Model Registry는 한마디로 ML 모델들의 중앙 저장소입니다. 마치 도서관에서 모든 책을 체계적으로 분류하고 관리하듯이, 학습된 모델들을 한곳에서 버전별로 관리할 수 있게 해줍니다.

이것을 제대로 활용하면 모델의 생명주기 전체를 투명하게 추적할 수 있습니다.

다음 코드를 살펴봅시다.

import mlflow
from mlflow.tracking import MlflowClient

# MLflow 트래킹 서버 설정
mlflow.set_tracking_uri("http://mlflow-server:5000")
client = MlflowClient()

# 모델 학습 후 Registry에 등록
with mlflow.start_run() as run:
    # 모델 학습 로직
    model = train_recommendation_model(data)

    # 모델을 Registry에 등록
    mlflow.sklearn.log_model(
        model,
        "recommendation_model",
        registered_model_name="ProductRecommender"
    )

# 등록된 모델 조회
model_versions = client.search_model_versions("name='ProductRecommender'")
for mv in model_versions:
    print(f"Version: {mv.version}, Stage: {mv.current_stage}")

김개발 씨는 입사 6개월 차 ML 엔지니어입니다. 오늘도 열심히 추천 모델을 개선하던 중, 팀장님이 다가와 물었습니다.

"김개발 씨, 지난달에 배포했던 모델 성능이 더 좋았던 것 같은데, 그 모델 파일 어디 있어요?" 김개발 씨는 당황했습니다. 분명히 어딘가에 저장해 뒀는데, model_v1.pkl인지 model_final.pkl인지 model_really_final.pkl인지 기억이 나지 않습니다.

로컬 폴더를 뒤지고, 공유 드라이브를 찾아보고, 심지어 슬랙 메시지까지 뒤져봐야 했습니다. 선배 개발자 박시니어 씨가 이 모습을 보고 다가왔습니다.

"아직도 그렇게 모델 관리하고 있었어요? MLflow Model Registry를 써야죠." 그렇다면 Model Registry란 정확히 무엇일까요?

쉽게 비유하자면, Model Registry는 마치 잘 정리된 도서관과 같습니다. 도서관에서는 모든 책이 고유한 분류 번호를 가지고 있고, 언제 입고되었는지, 현재 대출 중인지 아닌지를 한눈에 파악할 수 있습니다.

똑같은 책이라도 초판과 개정판이 구분되어 있죠. Model Registry도 마찬가지로 모든 모델에 고유한 이름과 버전을 부여하고, 각 버전의 상태를 명확하게 추적합니다.

Model Registry가 없던 시절에는 어땠을까요? 개발자들은 모델 파일을 직접 폴더에 저장하고, 이름에 날짜나 버전 번호를 붙여서 관리했습니다.

model_20231015.pkl, model_v2_final.pkl 같은 파일명이 넘쳐났습니다. 더 큰 문제는 어떤 모델이 어떤 데이터로 학습되었는지, 하이퍼파라미터는 무엇이었는지 추적하기가 거의 불가능했다는 점입니다.

바로 이런 문제를 해결하기 위해 MLflow Model Registry가 등장했습니다. Model Registry를 사용하면 모든 모델이 중앙 서버에 체계적으로 저장됩니다.

각 모델은 고유한 이름을 가지고, 버전이 자동으로 관리됩니다. 무엇보다 Staging, Production, Archived 같은 스테이지를 통해 모델의 현재 상태를 명확하게 표현할 수 있습니다.

위의 코드를 살펴보겠습니다. 먼저 mlflow.set_tracking_uri로 MLflow 서버 주소를 설정합니다.

이렇게 하면 모든 실험 기록이 중앙 서버에 저장됩니다. mlflow.start_run 컨텍스트 안에서 모델을 학습하고, log_model 함수로 모델을 저장합니다.

이때 registered_model_name 파라미터를 지정하면 자동으로 Registry에 등록됩니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 이커머스 회사에서 상품 추천 모델을 운영한다고 가정해봅시다. 매주 새로운 데이터로 모델을 재학습하는데, Model Registry를 사용하면 모든 버전이 자동으로 기록됩니다.

특정 버전에서 문제가 발생하면 이전 버전으로 즉시 롤백할 수 있고, 어떤 버전이 현재 프로덕션에서 동작 중인지도 한눈에 파악할 수 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 모든 실험 모델을 Registry에 등록하는 것입니다. Registry는 어느 정도 검증된, 배포 후보가 될 만한 모델만 등록하는 것이 좋습니다.

그렇지 않으면 Registry가 쓸모없는 모델들로 가득 차버립니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 조언을 듣고 Model Registry를 도입한 김개발 씨는 더 이상 모델 파일을 찾아 헤매지 않습니다. 팀장님이 "지난달 모델 어디 있어요?"라고 물으면, Registry에서 버전 기록을 확인하고 바로 찾아드릴 수 있게 되었습니다.

실전 팁

💡 - 모델 이름은 팀 내에서 일관된 명명 규칙을 정해두세요

  • 모든 실험 모델이 아닌, 배포 후보 모델만 Registry에 등록하세요
  • 모델 등록 시 description 필드에 주요 변경사항을 기록해두면 나중에 추적이 쉽습니다

2. 모델 버전 관리 전략

김개발 씨가 Model Registry를 도입한 지 한 달이 지났습니다. 그런데 새로운 고민이 생겼습니다.

벌써 모델 버전이 47개나 쌓였는데, 어떤 버전이 현재 서비스 중이고, 어떤 버전이 테스트 중인지 구분하기가 어렵습니다. 버전 관리 전략이 필요해 보입니다.

모델 버전 관리 전략은 수많은 모델 버전들 사이에서 질서를 유지하는 방법입니다. 마치 소프트웨어의 Git 브랜치 전략처럼, ML 모델도 체계적인 버전 관리가 필요합니다.

MLflow는 None, Staging, Production, Archived 네 가지 스테이지를 제공하여 이를 쉽게 구현할 수 있게 해줍니다.

다음 코드를 살펴봅시다.

from mlflow.tracking import MlflowClient

client = MlflowClient()
model_name = "ProductRecommender"

# 새 버전을 Staging으로 전환
client.transition_model_version_stage(
    name=model_name,
    version=5,
    stage="Staging"
)

# Staging에서 검증 완료 후 Production으로 승격
client.transition_model_version_stage(
    name=model_name,
    version=5,
    stage="Production",
    archive_existing_versions=True  # 기존 Production 버전 자동 아카이브
)

# 특정 스테이지의 모델 로드
staging_model = mlflow.pyfunc.load_model(
    model_uri=f"models:/{model_name}/Staging"
)
prod_model = mlflow.pyfunc.load_model(
    model_uri=f"models:/{model_name}/Production"
)

김개발 씨네 팀에서 운영하는 추천 모델은 매주 새로운 버전이 만들어집니다. 그러다 보니 어느새 버전이 47개까지 늘어났습니다.

"버전 32가 현재 프로덕션이고, 버전 45가 테스트 중이래." "아니야, 버전 38이 프로덕션이야." 팀원들 사이에서 혼란이 생기기 시작했습니다. 박시니어 씨가 팀 회의에서 제안했습니다.

"우리도 소프트웨어 개발처럼 체계적인 버전 관리 전략을 세워야 할 것 같아요." 그렇다면 모델 버전 관리 전략이란 무엇일까요? 쉽게 비유하자면, 이것은 마치 신약 개발 과정과 같습니다.

새로 개발된 약은 바로 환자에게 투여되지 않습니다. 먼저 실험실에서 테스트하고(None), 임상 시험을 거치고(Staging), 최종 승인을 받아야(Production) 시장에 출시됩니다.

문제가 발견된 약은 회수됩니다(Archived). ML 모델도 이와 똑같은 과정을 거쳐야 합니다.

MLflow는 네 가지 스테이지를 제공합니다. None은 막 등록된 상태입니다.

아직 어떤 검증도 거치지 않은 신생아 같은 모델이죠. Staging은 테스트 환경에서 검증 중인 상태입니다.

실제 트래픽의 일부를 받아서 성능을 확인하는 단계입니다. Production은 실제 서비스에 투입된 모델입니다.

가장 중요한 스테이지죠. Archived는 더 이상 사용하지 않는 모델입니다.

삭제하지는 않고 보관만 합니다. 위의 코드를 살펴보겠습니다.

transition_model_version_stage 함수가 핵심입니다. 이 함수로 모델 버전의 스테이지를 변경할 수 있습니다.

특히 archive_existing_versions=True 옵션이 중요한데, 새 버전을 Production으로 올릴 때 기존 Production 버전을 자동으로 Archived로 옮겨줍니다. 이렇게 하면 항상 Production 스테이지에는 하나의 버전만 존재하게 됩니다.

실제 현업에서는 이 스테이지들을 CI/CD 파이프라인과 연동합니다. 예를 들어 모델 학습이 완료되면 자동으로 None 스테이지에 등록됩니다.

자동화된 테스트(정확도, 지연시간 등)를 통과하면 Staging으로 승격됩니다. Staging에서 일정 기간 모니터링 후 문제가 없으면 Production으로 최종 승격됩니다.

이 모든 과정이 코드로 자동화되어 있으면 인간의 실수를 줄일 수 있습니다. 주의할 점이 있습니다.

스테이지 전환 권한을 아무나 가지면 안 됩니다. 특히 Production 스테이지로의 전환은 팀 리드나 지정된 담당자만 할 수 있도록 제한해야 합니다.

MLflow 자체적으로는 이런 권한 관리 기능이 약하므로, 회사의 인증 시스템과 연동하거나 별도의 승인 프로세스를 구축해야 합니다. 김개발 씨 팀은 이제 명확한 규칙을 세웠습니다.

새 모델은 무조건 None에서 시작하고, 오프라인 테스트 통과 후 Staging으로, 3일간의 A/B 테스트 후 문제없으면 Production으로 승격합니다. 덕분에 "지금 프로덕션 모델이 뭐야?"라는 질문에는 누구나 명확하게 답할 수 있게 되었습니다.

실전 팁

💡 - Production 스테이지에는 항상 하나의 버전만 유지하세요

  • 스테이지 전환 시 반드시 로그를 남기고 알림을 보내세요
  • Archived 모델도 일정 기간 보관 후 정리하는 정책을 세우세요

3. AB 테스팅 구현하기

김개발 씨가 새로운 추천 모델을 만들었습니다. 오프라인 테스트에서는 기존 모델보다 정확도가 5% 높았습니다.

하지만 팀장님은 신중합니다. "오프라인 성능이 좋다고 실제 서비스에서도 좋을까요?

일단 조금만 트래픽을 주고 테스트해봅시다."

A/B 테스팅은 두 개 이상의 모델을 동시에 운영하면서 실제 성능을 비교하는 방법입니다. 마치 음식점에서 신메뉴를 출시하기 전에 일부 고객에게만 먼저 제공해보는 것과 같습니다.

MLflow와 함께 트래픽 라우팅을 구현하면 안전하게 새 모델을 검증할 수 있습니다.

다음 코드를 살펴봅시다.

import mlflow
import random
from typing import Dict, Any

class ABTestingRouter:
    def __init__(self, model_name: str, traffic_split: float = 0.1):
        self.model_name = model_name
        self.traffic_split = traffic_split  # 새 모델로 보낼 트래픽 비율

        # Production(기존)과 Staging(신규) 모델 로드
        self.prod_model = mlflow.pyfunc.load_model(
            f"models:/{model_name}/Production"
        )
        self.staging_model = mlflow.pyfunc.load_model(
            f"models:/{model_name}/Staging"
        )

    def predict(self, features: Dict[str, Any], user_id: str) -> Dict:
        # 사용자별 일관된 할당을 위해 user_id 기반 해싱
        use_staging = (hash(user_id) % 100) < (self.traffic_split * 100)

        if use_staging:
            prediction = self.staging_model.predict(features)
            model_version = "staging"
        else:
            prediction = self.prod_model.predict(features)
            model_version = "production"

        # 로깅 (나중에 분석용)
        self._log_prediction(user_id, model_version, prediction)
        return {"prediction": prediction, "model": model_version}

김개발 씨는 신이 났습니다. 새로 만든 모델의 오프라인 정확도가 무려 5%나 향상되었기 때문입니다.

당장 프로덕션에 배포하고 싶었지만, 박시니어 씨가 말렸습니다. "오프라인 지표만 믿으면 안 돼요.

실제 사용자 반응은 다를 수 있어요." 이게 무슨 말일까요? 오프라인에서 좋으면 온라인에서도 좋은 거 아닌가요?

안타깝게도 그렇지 않습니다. 오프라인 테스트는 과거 데이터로 평가하지만, 실제 서비스에서는 실시간으로 변하는 사용자 행동을 다루어야 합니다.

또한 모델의 예측 결과가 사용자 행동에 영향을 주기도 합니다. 추천 시스템의 경우, 추천 결과가 달라지면 사용자의 클릭 패턴도 달라지니까요.

A/B 테스팅은 이 문제를 해결하는 황금률입니다. 쉽게 비유하자면, A/B 테스팅은 마치 신약의 임상 시험과 같습니다.

환자 그룹을 둘로 나누어 한쪽에는 새 약을, 다른 쪽에는 기존 약을 투여하고 효과를 비교합니다. ML 모델도 마찬가지입니다.

사용자 그룹을 나누어 각각 다른 모델의 예측을 받게 하고, 실제 결과(클릭률, 구매율 등)를 비교하는 것입니다. 위 코드에서 핵심은 ABTestingRouter 클래스입니다.

이 클래스는 Production 모델과 Staging 모델을 동시에 로드합니다. traffic_split 파라미터로 새 모델에 보낼 트래픽 비율을 설정합니다.

처음에는 10% 정도로 시작하는 것이 안전합니다. 문제가 없으면 점점 비율을 늘려갑니다.

여기서 중요한 점은 사용자 ID 기반 해싱입니다. 단순히 random.random()을 쓰면 같은 사용자가 요청할 때마다 다른 모델의 결과를 받게 됩니다.

이러면 사용자 경험이 일관되지 않고, 정확한 비교도 어렵습니다. 사용자 ID를 해싱하면 같은 사용자는 항상 같은 모델의 결과를 받게 됩니다.

실무에서는 더 정교한 A/B 테스팅 프레임워크를 사용합니다. 예를 들어 특정 사용자 세그먼트(신규 사용자, VIP 고객 등)별로 다른 비율을 적용하거나, 시간대별로 다르게 설정하기도 합니다.

또한 통계적으로 유의미한 결과를 얻기 위해 필요한 샘플 크기를 미리 계산하고, 그 수치에 도달할 때까지 테스트를 진행합니다. A/B 테스팅에서 흔히 하는 실수가 있습니다.

첫째, 테스트 기간이 너무 짧은 경우입니다. 최소 일주일 이상은 돌려야 요일별 패턴까지 파악할 수 있습니다.

둘째, 여러 변수를 동시에 테스트하는 경우입니다. 모델도 바꾸고 UI도 바꾸면 어떤 변화가 효과를 낸 건지 알 수 없습니다.

한 번에 하나의 변수만 테스트해야 합니다. 김개발 씨는 10%의 트래픽으로 A/B 테스트를 시작했습니다.

일주일 후 결과가 나왔는데, 오프라인에서 5% 향상이었던 정확도가 실제 클릭률에서는 2% 향상에 그쳤습니다. 하지만 이것도 충분히 의미 있는 개선이었고, 통계적으로 유의미했기에 김개발 씨는 자신 있게 프로덕션 승격을 요청할 수 있었습니다.

실전 팁

💡 - A/B 테스트 시작 전에 성공 기준(예: 클릭률 1% 이상 향상)을 미리 정의하세요

  • 최소 일주일 이상 테스트하여 요일별 패턴을 반영하세요
  • 사용자 ID 기반 할당으로 일관된 경험을 제공하세요

4. 롤백 전략 수립하기

새벽 2시, 김개발 씨의 핸드폰이 울렸습니다. 온콜 알람입니다.

"추천 시스템 클릭률이 급락하고 있습니다." 어제 배포한 새 모델에 문제가 생긴 것 같습니다. 심장이 쿵쾅거립니다.

어떻게 해야 할까요?

롤백 전략은 배포한 모델에 문제가 생겼을 때 이전 버전으로 신속하게 되돌리는 계획입니다. 마치 비행기의 비상 착륙 절차처럼, 미리 준비해두지 않으면 실제 상황에서 패닉에 빠지게 됩니다.

MLflow의 Model Registry를 활용하면 몇 줄의 코드로 안전하게 롤백할 수 있습니다.

다음 코드를 살펴봅시다.

from mlflow.tracking import MlflowClient
from datetime import datetime
import logging

class ModelRollbackManager:
    def __init__(self, model_name: str):
        self.client = MlflowClient()
        self.model_name = model_name
        self.logger = logging.getLogger(__name__)

    def rollback_to_previous(self) -> dict:
        # 현재 Production 버전 찾기
        prod_versions = self.client.get_latest_versions(
            self.model_name, stages=["Production"]
        )
        # Archived에서 가장 최근 버전 찾기 (이전 Production)
        archived = self.client.get_latest_versions(
            self.model_name, stages=["Archived"]
        )

        if not archived:
            raise ValueError("롤백할 이전 버전이 없습니다")

        prev_version = archived[0].version
        curr_version = prod_versions[0].version if prod_versions else None

        # 롤백 실행
        self.client.transition_model_version_stage(
            name=self.model_name, version=prev_version, stage="Production"
        )
        self.logger.warning(f"롤백 완료: v{curr_version} -> v{prev_version}")

        return {"rolled_back_from": curr_version, "rolled_back_to": prev_version}

새벽 2시의 알람. 모든 ML 엔지니어가 두려워하는 순간입니다.

김개발 씨는 침대에서 벌떡 일어나 노트북을 켰습니다. 대시보드를 확인하니 클릭률이 평소의 절반으로 떨어져 있었습니다.

어제 자신 있게 배포한 새 모델이 원인인 것 같습니다. 이런 상황에서 롤백 전략이 없다면 어떻게 될까요?

먼저 이전 모델 파일을 찾아야 합니다. 어디에 저장했더라?

찾았다면 배포 스크립트를 수정해야 합니다. 새벽에 잠이 덜 깬 상태로 코드를 수정하다가 오타를 내면 더 큰 장애로 이어질 수 있습니다.

시간은 계속 흘러가고, 매 순간 회사는 돈을 잃고 있습니다. 롤백 전략은 이런 패닉 상황을 대비한 비상 계획입니다.

쉽게 비유하자면, 롤백은 게임의 세이브 포인트와 같습니다. 보스전에서 실패하면 세이브 포인트로 돌아가서 다시 도전하죠.

ML 모델도 마찬가지입니다. 새 버전에서 문제가 생기면 검증된 이전 버전으로 돌아가는 것입니다.

단, 게임과 달리 실제 서비스는 롤백하는 동안에도 사용자들이 계속 접속하고 있다는 점을 명심해야 합니다. 위 코드의 ModelRollbackManager 클래스가 이 역할을 합니다.

핵심 로직은 간단합니다. 먼저 현재 Production 버전을 확인합니다.

그리고 Archived 스테이지에서 가장 최근 버전을 찾습니다. 이 버전이 바로 직전에 Production이었던 모델입니다.

이 버전을 다시 Production으로 전환하면 롤백이 완료됩니다. 하지만 이것만으로는 부족합니다.

롤백을 위한 인프라도 준비되어 있어야 합니다. 서비스 코드가 항상 models:/ModelName/Production처럼 스테이지 기반으로 모델을 로드해야 합니다.

특정 버전(예: models:/ModelName/5)을 하드코딩하면 스테이지를 변경해도 서비스에 반영되지 않습니다. 또한 모델 로딩 캐시가 있다면 캐시 무효화 로직도 필요합니다.

롤백 후에도 해야 할 일이 있습니다. 문제를 일으킨 모델의 원인을 분석해야 합니다.

왜 오프라인 테스트에서는 좋았는데 실제 서비스에서는 문제가 생겼을까요? 데이터 분포가 달랐을 수도 있고, 특정 엣지 케이스를 처리하지 못했을 수도 있습니다.

이 분석 없이는 같은 실수를 반복하게 됩니다. 주의할 점이 있습니다.

롤백은 최후의 수단입니다. 모든 문제를 롤백으로 해결하려 하면 안 됩니다.

경미한 성능 저하는 수정 배포로 해결하는 것이 낫습니다. 또한 롤백 권한은 제한적으로 부여해야 합니다.

실수로 잘못된 버전을 Production으로 올리면 더 큰 문제가 생깁니다. 김개발 씨는 미리 준비해둔 롤백 스크립트를 실행했습니다.

단 30초 만에 이전 버전으로 돌아갔고, 클릭률이 서서히 정상으로 회복되기 시작했습니다. 다음 날 원인을 분석해보니, 새 모델이 특정 카테고리의 상품을 과도하게 추천하는 버그가 있었습니다.

김개발 씨는 버그를 수정하고, 더 철저한 테스트를 거친 후 다시 배포할 수 있었습니다.

실전 팁

💡 - 롤백 스크립트는 미리 작성하고 테스트해두세요

  • 롤백 실행 시 자동으로 팀에 알림이 가도록 설정하세요
  • 롤백 후에는 반드시 원인 분석과 사후 회의(postmortem)를 진행하세요

5. Docker Kubernetes 통합

김개발 씨네 팀의 추천 서비스가 인기를 끌면서 트래픽이 10배로 늘었습니다. 단일 서버로는 감당이 안 됩니다.

박시니어 씨가 말합니다. "이제 Kubernetes로 모델 서빙을 확장해야 할 때가 됐어요."

Docker/Kubernetes 통합은 MLflow 모델을 컨테이너화하여 확장 가능한 인프라에서 서빙하는 방법입니다. 마치 프랜차이즈 식당처럼, 잘 만든 레시피(모델)를 여러 지점(컨테이너)에서 동일하게 제공할 수 있게 됩니다.

MLflow는 모델을 Docker 이미지로 자동 변환하는 기능을 제공합니다.

다음 코드를 살펴봅시다.

# Dockerfile for MLflow model serving
FROM python:3.9-slim

# MLflow와 의존성 설치
RUN pip install mlflow boto3 scikit-learn

# 환경 변수 설정
ENV MLFLOW_TRACKING_URI=http://mlflow-server:5000
ENV MODEL_NAME=ProductRecommender
ENV MODEL_STAGE=Production

# 모델 서빙 스크립트
COPY serve_model.py /app/serve_model.py
WORKDIR /app

# 모델 로드 및 서빙 시작
CMD ["python", "serve_model.py"]

---
# kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: recommender-model
spec:
  replicas: 3
  selector:
    matchLabels:
      app: recommender
  template:
    spec:
      containers:
      - name: model-server
        image: my-registry/recommender:latest
        resources:
          requests:
            memory: "2Gi"
            cpu: "1"

김개발 씨네 추천 서비스가 대박이 났습니다. 처음에는 하루 10만 건의 요청이었는데, 어느새 100만 건을 넘어섰습니다.

단일 서버에서 Flask로 모델을 서빙하던 기존 방식으로는 더 이상 버틸 수가 없었습니다. 응답 시간이 느려지고, 가끔 서버가 다운되기도 했습니다.

팀장님이 결단을 내렸습니다. "이제 Kubernetes로 가야 해요." Docker는 애플리케이션을 컨테이너라는 독립된 환경에 패키징하는 기술입니다.

쉽게 비유하면 이사할 때 사용하는 표준화된 이삿짐 박스와 같습니다. 박스에 물건을 담아두면 어느 집으로 이사해도 그대로 꺼내 쓸 수 있습니다.

Docker 컨테이너에 모델과 필요한 라이브러리를 모두 담아두면, 어떤 서버에서든 동일하게 동작합니다. Kubernetes는 이런 컨테이너들을 대규모로 관리하는 오케스트레이션 시스템입니다.

프랜차이즈 본사를 떠올려 보세요. 본사에서 레시피를 만들고, 각 지점에 전달하면, 어느 지점에서나 동일한 맛의 음식을 제공합니다.

손님이 많은 지점에는 직원을 더 배치하고, 한가한 지점에는 줄입니다. Kubernetes도 마찬가지입니다.

트래픽이 늘면 컨테이너를 더 띄우고, 줄면 줄입니다. 위 코드에서 Dockerfile은 모델 서빙 환경을 정의합니다.

Python 이미지를 기반으로 MLflow와 필요한 라이브러리를 설치합니다. 환경 변수로 MLflow 서버 주소와 모델 정보를 지정합니다.

컨테이너가 시작되면 serve_model.py 스크립트가 실행되어 모델을 로드하고 요청을 처리합니다. Kubernetes Deployment 설정도 살펴봅시다.

replicas: 3은 동일한 컨테이너를 3개 띄우겠다는 의미입니다. 트래픽이 늘면 이 숫자를 10개, 100개로 늘릴 수 있습니다.

resources 섹션에서는 각 컨테이너가 사용할 메모리와 CPU를 지정합니다. ML 모델은 보통 메모리를 많이 사용하므로 넉넉하게 설정하는 것이 좋습니다.

실제 운영에서는 몇 가지 더 고려해야 할 점이 있습니다. 먼저 Health Check입니다.

Kubernetes가 컨테이너가 정상인지 확인할 수 있도록 헬스체크 엔드포인트를 만들어야 합니다. 또한 모델 캐싱도 중요합니다.

컨테이너가 새로 뜰 때마다 MLflow 서버에서 모델을 다운로드하면 시간이 오래 걸립니다. 모델을 이미지에 포함시키거나, 별도의 볼륨에 캐시해두는 방식을 고려해야 합니다.

주의할 점도 있습니다. 컨테이너는 기본적으로 상태가 없는(stateless) 것으로 설계해야 합니다.

요청 간에 데이터를 공유해야 한다면 외부 저장소(Redis, 데이터베이스 등)를 사용해야 합니다. 또한 모델 버전 업데이트 시 Rolling Update를 사용하여 무중단으로 배포해야 합니다.

김개발 씨 팀은 Kubernetes 환경을 구축하고 나서 한숨 돌릴 수 있었습니다. 트래픽 피크 시간에는 자동으로 컨테이너가 10개까지 늘어났다가, 새벽에는 2개로 줄어듭니다.

비용도 절감되고, 안정성도 높아졌습니다. "역시 확장 가능한 인프라가 답이었어요." 박시니어 씨가 흐뭇하게 웃었습니다.

실전 팁

💡 - 모델 로딩 시간을 줄이기 위해 컨테이너 이미지에 모델을 포함시키거나 캐싱을 사용하세요

  • Horizontal Pod Autoscaler를 설정하여 트래픽에 따라 자동 스케일링되게 하세요
  • 모델 업데이트 시 Rolling Update로 무중단 배포를 구현하세요

6. 모델 승인 프로세스 설계

김개발 씨가 만든 새 모델이 A/B 테스트를 통과했습니다. 이제 프로덕션에 배포할 차례인데, 팀장님이 말합니다.

"잠깐, 그 모델 누가 검토했어요? 승인 기록은 있나요?" 김개발 씨는 깨달았습니다.

배포 전 승인 프로세스가 필요하다는 것을.

모델 승인 프로세스는 모델이 프로덕션에 배포되기 전에 거쳐야 할 검토와 승인 단계를 정의한 것입니다. 마치 은행에서 큰 금액을 송금할 때 여러 단계의 승인이 필요한 것처럼, 프로덕션 모델도 체계적인 검토를 거쳐야 합니다.

이를 통해 품질을 보장하고 규제 준수를 증명할 수 있습니다.

다음 코드를 살펴봅시다.

from mlflow.tracking import MlflowClient
from datetime import datetime
from enum import Enum

class ApprovalStatus(Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"

class ModelApprovalWorkflow:
    def __init__(self, model_name: str):
        self.client = MlflowClient()
        self.model_name = model_name

    def submit_for_review(self, version: str, submitter: str) -> dict:
        # 모델 버전에 메타데이터 태깅
        self.client.set_model_version_tag(
            self.model_name, version, "approval_status", ApprovalStatus.PENDING.value
        )
        self.client.set_model_version_tag(
            self.model_name, version, "submitted_by", submitter
        )
        self.client.set_model_version_tag(
            self.model_name, version, "submitted_at", datetime.now().isoformat()
        )
        return {"status": "submitted", "version": version}

    def approve_model(self, version: str, approver: str, comments: str) -> dict:
        # 승인 처리
        self.client.set_model_version_tag(
            self.model_name, version, "approval_status", ApprovalStatus.APPROVED.value
        )
        self.client.set_model_version_tag(
            self.model_name, version, "approved_by", approver
        )
        self.client.set_model_version_tag(
            self.model_name, version, "approval_comments", comments
        )
        # Production으로 스테이지 전환
        self.client.transition_model_version_stage(
            self.model_name, version, "Production", archive_existing_versions=True
        )
        return {"status": "approved", "version": version}

김개발 씨 팀에서 만든 추천 모델이 어느 날 이상한 결과를 내놓기 시작했습니다. 알고 보니 인턴이 실수로 테스트 모델을 프로덕션에 배포한 것이었습니다.

다행히 빨리 발견해서 큰 피해는 없었지만, 팀장님은 심각한 표정으로 말했습니다. "우리 팀에 승인 프로세스가 필요해요.

아무나 프로덕션에 배포할 수 있으면 안 됩니다." 모델 승인 프로세스란 무엇일까요? 쉽게 비유하자면, 이것은 마치 회사의 결재 시스템과 같습니다.

사원이 보고서를 작성하면 과장이 검토하고, 부장이 최종 승인합니다. 각 단계에서 문제가 있으면 반려되고, 수정 후 다시 올립니다.

ML 모델도 마찬가지입니다. 개발자가 모델을 만들면, 시니어 엔지니어가 코드를 검토하고, 팀 리드가 성능을 확인한 뒤, 최종 승인권자가 프로덕션 배포를 승인합니다.

왜 이런 프로세스가 필요할까요? 첫째, 품질 보장입니다.

여러 사람의 눈을 거치면 혼자서는 놓칠 수 있는 문제를 발견할 수 있습니다. 둘째, 규제 준수입니다.

금융이나 의료 분야에서는 AI 모델의 의사결정 과정을 추적하고 기록해야 하는 법적 요구사항이 있습니다. 승인 기록이 없으면 규제 위반으로 큰 벌금을 물 수 있습니다.

셋째, 책임 소재 명확화입니다. 문제가 생겼을 때 누가 언제 승인했는지 기록이 있어야 원인을 파악하고 재발을 방지할 수 있습니다.

위 코드의 ModelApprovalWorkflow 클래스를 살펴봅시다. submit_for_review 함수는 모델을 검토 대기 상태로 만듭니다.

MLflow의 태그 기능을 활용하여 승인 상태, 제출자, 제출 시간을 기록합니다. approve_model 함수는 승인을 처리합니다.

승인자와 코멘트를 기록하고, 모델을 Production 스테이지로 전환합니다. 실제 운영에서는 더 복잡한 워크플로우가 필요할 수 있습니다.

예를 들어 다단계 승인이 필요할 수 있습니다. 1차 검토는 동료 개발자, 2차 검토는 시니어 엔지니어, 최종 승인은 팀 리드 식으로요.

또한 특정 조건(정확도 95% 이상, 지연시간 100ms 이하 등)을 자동으로 검증하는 게이트를 추가할 수도 있습니다. 주의할 점도 있습니다.

승인 프로세스가 너무 복잡하면 배포 속도가 느려집니다. 긴급한 버그 수정도 3일씩 걸린다면 문제가 됩니다.

따라서 긴급 배포를 위한 패스트트랙 프로세스도 함께 준비해야 합니다. 다만 패스트트랙을 남용하면 안 되므로, 사후 검토를 의무화하는 등의 보완 장치가 필요합니다.

김개발 씨 팀은 승인 프로세스를 도입한 뒤, 더 이상 실수로 인한 사고가 발생하지 않았습니다. 처음에는 "귀찮다"는 불만도 있었지만, 몇 달 후 팀원들은 오히려 안심하고 일할 수 있게 되었다고 말합니다.

"누군가 내 모델을 검토해준다는 게 이렇게 마음 편한 거였네요." 인턴 이신입 씨의 말입니다. 프로세스는 문화입니다.

처음에는 불편해도, 익숙해지면 당연한 것이 됩니다. 그리고 그 당연함이 팀을 사고로부터 지켜줍니다.

실전 팁

💡 - 승인 권한은 역할별로 명확히 정의하세요

  • 긴급 배포를 위한 패스트트랙 프로세스도 함께 준비하세요
  • 모든 승인 기록은 감사 추적을 위해 영구 보관하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#MLflow#ModelRegistry#MLOps#Kubernetes#ABTesting#MLOps,MLflow,Production

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.