🤖

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

⚠️

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

이미지 로딩 중...

TensorFlow Serving 완벽 가이드 - 모델 서빙 - 슬라이드 1/8
A

AI Generated

2025. 11. 30. · 72 Views

TensorFlow Serving 완벽 가이드 - 모델 서빙

머신러닝 모델을 실제 서비스에 배포하는 TensorFlow Serving의 모든 것을 다룹니다. 설치부터 Docker 컨테이너 설정, REST/gRPC API 서빙, 모델 버전 관리, 성능 최적화까지 초급 개발자도 이해할 수 있도록 쉽게 설명합니다.


목차

  1. TF_Serving_설치하기
  2. Docker_컨테이너_설정
  3. REST_gRPC_API_서빙
  4. SavedModel_포맷_이해
  5. 모델_버전_관리
  6. Batching으로_성능_향상
  7. GPU_활용_및_Kubernetes_배포

1. TF Serving 설치하기

김개발 씨는 드디어 첫 번째 딥러닝 모델을 완성했습니다. 그런데 선배 박시니어 씨가 물었습니다.

"모델은 잘 만들었네요. 그런데 이걸 어떻게 실제 서비스에 배포할 건가요?" 김개발 씨는 그제야 깨달았습니다.

모델을 만드는 것과 서비스하는 것은 완전히 다른 문제라는 것을.

TensorFlow Serving은 머신러닝 모델을 프로덕션 환경에 배포하기 위한 고성능 서빙 시스템입니다. 마치 레스토랑의 서빙 직원이 주방에서 만든 요리를 손님에게 전달하듯, TF Serving은 학습된 모델의 예측 결과를 사용자에게 전달합니다.

이를 통해 모델을 REST API나 gRPC로 쉽게 서비스할 수 있습니다.

다음 코드를 살펴봅시다.

# Ubuntu/Debian에서 TensorFlow Serving 설치
# 1. 패키지 저장소 추가
echo "deb [arch=amd64] http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal" | sudo tee /etc/apt/sources.list.d/tensorflow-serving.list

# 2. GPG 키 추가
curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | sudo apt-key add -

# 3. 패키지 설치
sudo apt-get update
sudo apt-get install tensorflow-model-server

# 4. 설치 확인
tensorflow_model_server --version

김개발 씨는 입사 6개월 차 머신러닝 엔지니어입니다. 회사에서 처음으로 이미지 분류 모델을 개발하는 임무를 맡았고, 밤낮으로 노력한 끝에 정확도 95%의 훌륭한 모델을 만들어냈습니다.

기쁜 마음으로 팀장님께 보고했더니 예상치 못한 질문이 돌아왔습니다. "모델 성능은 좋네요.

그런데 이 모델을 어떻게 실제 서비스에 올릴 건가요?" 김개발 씨는 당황했습니다. 주피터 노트북에서 모델이 잘 돌아가는 것과 실제 서비스에서 수천 명의 사용자 요청을 처리하는 것은 완전히 다른 문제였기 때문입니다.

선배 개발자 박시니어 씨가 다가와 말했습니다. "걱정 마세요.

TensorFlow Serving을 사용하면 됩니다." 그렇다면 TensorFlow Serving이란 정확히 무엇일까요? 쉽게 비유하자면, TensorFlow Serving은 마치 레스토랑의 서빙 직원과 같습니다.

주방에서 셰프가 아무리 맛있는 요리를 만들어도, 서빙 직원이 없으면 손님에게 전달할 수 없습니다. 마찬가지로 데이터 과학자가 아무리 뛰어난 모델을 만들어도, 서빙 시스템이 없으면 사용자에게 예측 결과를 전달할 수 없습니다.

TensorFlow Serving이 없던 시절에는 어땠을까요? 개발자들은 Flask나 Django로 직접 API 서버를 만들어야 했습니다.

모델 로딩, 요청 처리, 배치 처리, 버전 관리까지 모든 것을 직접 구현해야 했습니다. 코드가 복잡해지고, 성능 최적화도 어려웠습니다.

더 큰 문제는 트래픽이 몰릴 때 서버가 버티지 못하는 경우가 많았다는 것입니다. 바로 이런 문제를 해결하기 위해 Google이 TensorFlow Serving을 만들었습니다.

TensorFlow Serving을 사용하면 고성능 모델 서빙이 가능해집니다. C++로 작성되어 Python 기반 서버보다 훨씬 빠릅니다.

또한 자동 배치 처리로 여러 요청을 묶어서 효율적으로 처리합니다. 무엇보다 무중단 모델 업데이트가 가능하다는 큰 장점이 있습니다.

위의 설치 코드를 살펴보겠습니다. 먼저 TensorFlow Serving의 apt 저장소를 시스템에 추가합니다.

이 저장소에서 최신 버전의 TF Serving 패키지를 받아올 수 있습니다. 다음으로 GPG 키를 추가하여 패키지의 진위를 검증합니다.

마지막으로 apt-get install 명령으로 실제 설치를 진행합니다. 실제 현업에서는 대부분 Docker를 사용하여 TF Serving을 실행합니다.

하지만 로컬 개발 환경에서 테스트할 때는 직접 설치하는 방식이 더 편리할 수 있습니다. 하지만 주의할 점도 있습니다.

TensorFlow Serving은 현재 Linux 환경만 공식 지원합니다. Windows나 macOS에서 개발한다면 Docker를 사용하는 것이 좋습니다.

또한 GPU 버전을 사용하려면 CUDA와 cuDNN이 미리 설치되어 있어야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 바로 TensorFlow Serving 설치를 시작했습니다. 생각보다 간단한 설치 과정에 안도의 한숨을 쉬었습니다.

실전 팁

💡 - 프로덕션 환경에서는 직접 설치보다 Docker 사용을 권장합니다

  • GPU를 사용할 경우 tensorflow-model-server-gpu 패키지를 설치하세요
  • 버전 호환성을 위해 TensorFlow와 TF Serving 버전을 맞추는 것이 좋습니다

2. Docker 컨테이너 설정

김개발 씨가 로컬에서 TF Serving 테스트를 마치고 서버에 배포하려고 하자 문제가 생겼습니다. 개발 환경과 서버 환경이 달라서 똑같은 설정이 작동하지 않았던 것입니다.

박시니어 씨가 말했습니다. "Docker를 사용하면 그런 문제가 없어요."

Docker를 사용하면 TensorFlow Serving을 어떤 환경에서든 동일하게 실행할 수 있습니다. 마치 이사할 때 가구를 그대로 컨테이너에 담아 옮기는 것처럼, Docker 컨테이너 안에 TF Serving과 모든 의존성을 담아서 어디서든 실행할 수 있습니다.

이는 개발 환경과 프로덕션 환경의 차이로 인한 문제를 완전히 해결해줍니다.

다음 코드를 살펴봅시다.

# TensorFlow Serving Docker 이미지 다운로드
docker pull tensorflow/serving

# 기본 실행 - 모델 디렉토리 마운트
docker run -p 8501:8501 \
  --name tf_serving \
  -v "/path/to/my_model:/models/my_model" \
  -e MODEL_NAME=my_model \
  tensorflow/serving

# GPU 버전 실행
docker run --gpus all -p 8501:8501 \
  --name tf_serving_gpu \
  -v "/path/to/my_model:/models/my_model" \
  -e MODEL_NAME=my_model \
  tensorflow/serving:latest-gpu

# 설정 파일과 함께 실행
docker run -p 8501:8501 -p 8500:8500 \
  -v "/path/to/models:/models" \
  -v "/path/to/config:/config" \
  tensorflow/serving \
  --model_config_file=/config/models.config

김개발 씨는 로컬 컴퓨터에서 TensorFlow Serving을 성공적으로 테스트했습니다. 모델이 잘 서빙되는 것을 확인하고 기쁜 마음으로 AWS 서버에 배포를 시도했습니다.

그런데 이상한 일이 벌어졌습니다. 분명히 같은 명령어를 입력했는데 서버에서는 에러가 발생했습니다.

라이브러리 버전이 다르다느니, 의존성이 맞지 않는다느니 하는 오류 메시지가 쏟아졌습니다. 박시니어 씨가 김개발 씨의 모니터를 보며 말했습니다.

"이런 문제를 환경 불일치라고 해요. 개발자들 사이에서 유명한 농담이 있죠.

'제 컴퓨터에서는 되는데요?' 이 문제를 해결하려면 Docker를 사용해야 합니다." 그렇다면 Docker란 무엇이고, 왜 이 문제를 해결해줄까요? 쉽게 비유하자면, Docker는 마치 이사 전문 업체의 컨테이너와 같습니다.

이사할 때 가구를 하나하나 옮기면 깨지거나 잃어버리기 쉽습니다. 하지만 컨테이너에 모든 것을 담아서 통째로 옮기면 안전합니다.

Docker도 마찬가지입니다. TF Serving과 모든 의존성을 컨테이너에 담아서 어디서든 똑같이 실행할 수 있습니다.

Docker 없이 배포하던 시절에는 정말 힘들었습니다. 서버마다 운영체제 버전이 다르고, 설치된 라이브러리도 제각각이었습니다.

개발자는 각 서버 환경에 맞춰 설정을 일일이 수정해야 했습니다. 하나의 서버에서 여러 서비스를 운영하면 라이브러리 충돌 문제도 발생했습니다.

Docker를 사용하면 이 모든 문제가 해결됩니다. 위의 코드를 살펴보겠습니다.

첫 번째 명령어 docker pull은 TensorFlow Serving의 공식 이미지를 다운로드합니다. 이 이미지 안에는 TF Serving 실행에 필요한 모든 것이 포함되어 있습니다.

두 번째 명령어가 핵심입니다. -p 8501:8501은 컨테이너의 8501 포트를 호스트의 8501 포트에 연결합니다.

이 포트로 REST API 요청을 받습니다. -v 옵션은 호스트의 모델 디렉토리를 컨테이너 안에 마운트합니다.

-e MODEL_NAME은 서빙할 모델의 이름을 지정합니다. GPU를 사용하고 싶다면 --gpus all 옵션과 함께 GPU 버전 이미지를 사용하면 됩니다.

이렇게 하면 GPU 가속을 활용한 빠른 추론이 가능합니다. 실제 현업에서는 여러 모델을 동시에 서빙하는 경우가 많습니다.

이럴 때는 설정 파일을 만들어서 사용합니다. 설정 파일에 여러 모델의 경로와 이름을 지정하면 하나의 TF Serving 인스턴스로 여러 모델을 서빙할 수 있습니다.

주의할 점이 있습니다. 볼륨 마운트 경로를 잘못 지정하면 모델을 찾을 수 없다는 에러가 발생합니다.

경로가 정확한지 항상 확인하세요. 또한 컨테이너를 중지하면 내부 데이터가 사라지므로, 로그나 중요한 데이터는 반드시 볼륨으로 마운트해야 합니다.

김개발 씨는 Docker를 사용하여 배포를 다시 시도했습니다. 이번에는 로컬과 서버에서 완벽히 동일하게 동작했습니다.

"정말 마법 같네요!" 김개발 씨가 감탄했습니다.

실전 팁

💡 - docker-compose를 사용하면 여러 컨테이너를 쉽게 관리할 수 있습니다

  • 프로덕션에서는 --restart=always 옵션으로 자동 재시작을 설정하세요
  • 메모리 제한이 필요하면 --memory 옵션을 사용하세요

3. REST gRPC API 서빙

Docker로 TF Serving을 실행한 김개발 씨는 이제 실제로 모델에 요청을 보내볼 차례입니다. 그런데 API 문서를 보니 REST와 gRPC 두 가지 방식이 있었습니다.

"둘 다 API인데 뭐가 다른 거죠?" 김개발 씨의 질문에 박시니어 씨가 웃으며 설명을 시작했습니다.

TensorFlow Serving은 REST APIgRPC API 두 가지 방식으로 모델을 서빙합니다. REST는 HTTP 기반으로 어떤 언어에서든 쉽게 호출할 수 있고, gRPC는 Protocol Buffers 기반으로 더 빠른 성능을 제공합니다.

마치 일반 우편과 퀵서비스의 차이와 같습니다. 상황에 따라 적절한 방식을 선택하면 됩니다.

다음 코드를 살펴봅시다.

import requests
import json
import numpy as np

# REST API로 예측 요청 (포트 8501)
def predict_rest(image_data):
    url = "http://localhost:8501/v1/models/my_model:predict"
    payload = {
        "instances": image_data.tolist()
    }
    response = requests.post(url, json=payload)
    return response.json()["predictions"]

# gRPC API로 예측 요청 (포트 8500)
import grpc
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc

def predict_grpc(image_data):
    channel = grpc.insecure_channel("localhost:8500")
    stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)

    request = predict_pb2.PredictRequest()
    request.model_spec.name = "my_model"
    request.inputs["input"].CopyFrom(
        tf.make_tensor_proto(image_data)
    )
    result = stub.Predict(request)
    return result.outputs["output"].float_val

김개발 씨는 TF Serving 컨테이너를 실행하고 터미널에서 로그가 올라오는 것을 확인했습니다. "서버가 시작됐다!"라는 메시지를 보고 기뻤지만, 다음 순간 막막해졌습니다.

이 서버에 어떻게 요청을 보내야 할까요? API 문서를 열어보니 REST와 gRPC 두 가지 방식이 나와 있었습니다.

김개발 씨는 고개를 갸웃했습니다. 박시니어 씨가 화이트보드 앞으로 걸어갔습니다.

"쉽게 설명해줄게요. REST와 gRPC의 차이는 일반 우편과 퀵서비스의 차이와 비슷해요." REST API는 일반 우편과 같습니다.

누구나 쉽게 보낼 수 있고, 특별한 장비가 필요 없습니다. HTTP라는 표준 프로토콜을 사용하기 때문에 어떤 프로그래밍 언어에서든 쉽게 호출할 수 있습니다.

웹 브라우저에서 직접 테스트해볼 수도 있습니다. 반면 gRPC는 퀵서비스와 같습니다.

더 빠르고 효율적이지만, 전용 클라이언트가 필요합니다. Protocol Buffers라는 바이너리 형식으로 데이터를 주고받기 때문에 JSON보다 훨씬 작고 빠릅니다.

위의 코드를 자세히 살펴보겠습니다. REST API 함수를 먼저 보세요.

requests 라이브러리로 HTTP POST 요청을 보냅니다. URL은 정해진 형식을 따릅니다.

/v1/models/모델이름:predict 형태입니다. 데이터는 JSON 형식으로 instances 키 안에 넣어서 보냅니다.

gRPC 함수는 조금 더 복잡합니다. 먼저 gRPC 채널을 만들고, Stub이라는 클라이언트 객체를 생성합니다.

요청 객체를 만들어서 모델 이름과 입력 데이터를 설정한 후 전송합니다. 코드가 길지만, 성능은 REST보다 2-3배 빠릅니다.

그렇다면 언제 어떤 것을 선택해야 할까요? REST API는 다음 상황에서 추천합니다.

웹 프론트엔드에서 직접 호출할 때, 빠른 프로토타이핑이 필요할 때, 여러 언어의 클라이언트가 접근할 때 좋습니다. 디버깅도 쉽습니다.

gRPC는 다음 상황에서 추천합니다. 높은 처리량이 필요할 때, 지연시간이 중요할 때, 마이크로서비스 간 통신에 적합합니다.

특히 대용량 데이터를 주고받을 때 효과적입니다. 실제 현업에서는 어떨까요?

많은 회사에서 외부 클라이언트에는 REST API를, 내부 서비스 간 통신에는 gRPC를 사용합니다. 예를 들어 모바일 앱에서 오는 요청은 REST로 받고, 추천 시스템이 여러 ML 모델을 호출할 때는 gRPC를 사용하는 식입니다.

주의할 점이 있습니다. gRPC를 사용하려면 tensorflow-serving-api 패키지를 설치해야 합니다.

또한 방화벽 설정에서 8500(gRPC)과 8501(REST) 포트를 모두 열어야 합니다. REST 요청 시 입력 데이터의 shape가 모델이 기대하는 것과 정확히 일치하는지 확인하세요.

김개발 씨는 먼저 REST API로 테스트를 시작했습니다. curl 명령어로 간단히 요청을 보내보니 예측 결과가 바로 돌아왔습니다.

"오, 생각보다 쉽네요!" 그리고 나서 gRPC도 시도해봤는데, 확실히 응답 속도가 빨랐습니다.

실전 팁

💡 - 개발 단계에서는 REST로 시작하고, 성능이 중요해지면 gRPC로 전환하세요

  • gRPC 사용 시 연결 풀링을 적용하면 성능이 더 향상됩니다
  • 두 API 모두 SSL/TLS 암호화를 지원합니다

4. SavedModel 포맷 이해

김개발 씨가 TF Serving에 모델을 올리려고 하자 에러가 발생했습니다. "SavedModel 포맷이 아닙니다"라는 메시지였습니다.

그동안 .h5 파일로만 모델을 저장해왔던 김개발 씨는 당황했습니다. SavedModel이란 대체 무엇일까요?

SavedModel은 TensorFlow의 표준 모델 저장 포맷입니다. 모델의 구조, 가중치, 연산 그래프, 그리고 서빙에 필요한 메타데이터까지 모두 포함하는 완전한 패키지입니다.

마치 가구를 배송할 때 조립 설명서와 모든 부품을 함께 포장하는 것과 같습니다. TF Serving은 오직 SavedModel 포맷만 지원합니다.

다음 코드를 살펴봅시다.

import tensorflow as tf

# Keras 모델을 SavedModel로 저장
model = tf.keras.models.load_model("my_model.h5")

# SavedModel 포맷으로 내보내기
export_path = "/models/my_model/1"  # 버전 번호 포함
tf.saved_model.save(model, export_path)

# 또는 Keras의 save 메서드 사용
model.save(export_path, save_format="tf")

# SavedModel 구조 확인
# /models/my_model/1/
#   ├── saved_model.pb          # 모델 구조와 그래프
#   ├── variables/
#   │   ├── variables.data-*    # 가중치 데이터
#   │   └── variables.index     # 가중치 인덱스
#   └── assets/                 # 추가 파일 (어휘 사전 등)

# 저장된 모델 정보 확인
saved_model_cli show --dir /models/my_model/1 --all

김개발 씨는 Keras로 만든 모델을 my_model.h5 파일로 저장해두었습니다. 주피터 노트북에서는 이 파일을 불러와서 잘 사용하고 있었습니다.

그런데 TF Serving에 올리려고 하자 문제가 생겼습니다. "SavedModel format not found"라는 에러 메시지가 화면에 떴습니다.

박시니어 씨가 설명을 시작했습니다. "TF Serving은 SavedModel이라는 특별한 포맷만 받아들여요.

.h5 파일은 Keras 전용 포맷이라서 변환이 필요합니다." SavedModel이란 무엇일까요? 쉽게 비유하자면, SavedModel은 완제품 가구 배송 패키지와 같습니다.

IKEA에서 가구를 사면 조립 설명서, 모든 부품, 나사까지 한 박스에 들어 있죠. SavedModel도 마찬가지입니다.

모델 구조, 가중치, 연산 그래프, 입출력 정보까지 모든 것이 하나의 디렉토리에 담겨 있습니다. 반면 .h5 파일은 가중치 위주로 저장됩니다.

모델 구조 정보가 부족할 수 있고, 서빙에 필요한 메타데이터가 없습니다. 그래서 TF Serving에서는 사용할 수 없는 것입니다.

SavedModel의 디렉토리 구조를 살펴보겠습니다. saved_model.pb 파일은 모델의 전체 구조를 담고 있습니다.

어떤 연산들이 어떤 순서로 실행되는지, 입력과 출력의 형태가 어떤지 등의 정보가 들어 있습니다. variables 디렉토리에는 학습된 가중치가 저장됩니다.

이 가중치들이 모델이 "학습한 지식"입니다. assets 디렉토리는 선택적입니다.

자연어 처리 모델의 경우 어휘 사전 파일 같은 추가 자료를 여기에 저장합니다. 특히 중요한 것은 버전 번호입니다.

위 코드에서 저장 경로가 /models/my_model/1인 것을 주목하세요. 마지막 숫자 1이 버전 번호입니다.

TF Serving은 이 버전 번호를 보고 어떤 모델을 서빙할지 결정합니다. 새 버전을 배포하고 싶으면 /models/my_model/2 디렉토리에 저장하면 됩니다.

위의 코드를 실행하는 방법을 설명하겠습니다. 먼저 기존 .h5 파일을 tf.keras.models.load_model로 불러옵니다.

그다음 tf.saved_model.save 함수로 SavedModel 포맷으로 변환하여 저장합니다. 또는 Keras 모델의 save 메서드에 save_format="tf" 옵션을 주어도 됩니다.

저장 후에는 saved_model_cli 도구로 모델 정보를 확인할 수 있습니다. 이 도구는 모델의 입력/출력 텐서 이름과 shape 등을 보여줍니다.

API 호출 시 이 정보가 필요하므로 꼭 확인해두세요. 주의할 점이 있습니다.

SavedModel로 변환할 때 커스텀 레이어나 함수가 있다면 추가 작업이 필요합니다. @tf.function 데코레이터로 함수를 정의하거나, 커스텀 객체를 등록해야 합니다.

또한 모델의 input_signature를 명시적으로 지정하면 서빙 성능이 향상됩니다. 김개발 씨는 바로 모델 변환 작업을 시작했습니다.

tf.saved_model.save 함수 한 줄로 변환이 완료되었고, 이번에는 TF Serving이 모델을 제대로 로드했습니다.

실전 팁

💡 - 모델 저장 시 항상 버전 번호 디렉토리를 사용하세요

  • saved_model_cli로 모델 정보를 미리 확인하면 API 호출이 쉬워집니다
  • TensorFlow 2.x에서는 tf.saved_model.save를 권장합니다

5. 모델 버전 관리

모델을 업데이트해야 하는 상황이 왔습니다. 새로 학습한 모델의 성능이 더 좋아졌거든요.

그런데 김개발 씨는 걱정이 됐습니다. "모델을 교체하는 동안 서비스가 중단되면 어떡하죠?" 박시니어 씨가 미소 지었습니다.

"TF Serving은 무중단 모델 업데이트를 지원해요."

TensorFlow Serving의 모델 버전 관리 기능은 서비스 중단 없이 모델을 업데이트할 수 있게 해줍니다. 마치 고속도로에서 달리는 자동차의 타이어를 교체하는 것과 같습니다.

새 버전의 모델을 디렉토리에 추가하면 TF Serving이 자동으로 감지하여 로드하고, 이전 버전은 안전하게 언로드합니다.

다음 코드를 살펴봅시다.

# 모델 디렉토리 구조 (버전별 관리)
# /models/my_model/
#   ├── 1/           # 버전 1 (이전 버전)
#   │   └── saved_model.pb
#   ├── 2/           # 버전 2 (최신 버전)
#   │   └── saved_model.pb
#   └── 3/           # 버전 3 (새로 배포)
#       └── saved_model.pb

# models.config 설정 파일
model_config_list {
  config {
    name: 'my_model'
    base_path: '/models/my_model'
    model_platform: 'tensorflow'
    model_version_policy {
      specific {
        versions: 2
        versions: 3
      }
    }
  }
}

# 특정 버전으로 예측 요청
# REST API - 최신 버전 (기본)
curl http://localhost:8501/v1/models/my_model:predict

# REST API - 특정 버전 지정
curl http://localhost:8501/v1/models/my_model/versions/2:predict

# 모델 상태 확인
curl http://localhost:8501/v1/models/my_model

김개발 씨의 이미지 분류 모델이 서비스에 올라간 지 한 달이 지났습니다. 그동안 새로운 데이터가 쌓였고, 이를 활용해 더 정확한 모델을 학습시켰습니다.

정확도가 95%에서 97%로 올랐습니다. 이제 새 모델로 교체해야 합니다.

그런데 문제가 있었습니다. "지금 서비스가 운영 중인데, 모델을 교체하려면 서버를 내려야 하나요?" 김개발 씨가 걱정스럽게 물었습니다.

박시니어 씨가 고개를 저었습니다. "TF Serving의 핫 스왑 기능을 사용하면 서비스 중단 없이 모델을 교체할 수 있어요." 핫 스왑이란 무엇일까요?

쉽게 비유하자면, 마치 주유 중인 비행기에 연료를 보급하는 것과 같습니다. 비행기가 하늘에 떠 있는 상태에서도 연료를 보급할 수 있듯이, TF Serving은 서비스가 돌아가는 중에도 모델을 교체할 수 있습니다.

작동 원리는 간단합니다. TF Serving은 지정된 모델 디렉토리를 주기적으로 모니터링합니다.

새로운 버전 디렉토리가 감지되면 자동으로 해당 모델을 로드합니다. 새 모델 로드가 완료되면 요청을 새 모델로 전환하고, 이전 버전은 안전하게 언로드합니다.

위의 디렉토리 구조를 살펴보세요. /models/my_model/ 아래에 1, 2, 3이라는 버전 디렉토리가 있습니다.

각 디렉토리 안에는 해당 버전의 SavedModel이 저장되어 있습니다. TF Serving은 기본적으로 가장 높은 버전 번호의 모델을 서빙합니다.

새 모델을 배포하려면 어떻게 해야 할까요? 그냥 새 버전 디렉토리에 모델을 복사하면 됩니다.

예를 들어 버전 4를 배포하고 싶다면 /models/my_model/4/ 디렉토리를 만들고 SavedModel을 저장합니다. TF Serving이 자동으로 감지하여 로드합니다.

models.config 파일을 사용하면 더 세밀한 제어가 가능합니다. model_version_policy를 설정하면 어떤 버전들을 동시에 서빙할지 지정할 수 있습니다.

위 예시에서는 버전 2와 3을 동시에 서빙하도록 설정했습니다. 이렇게 하면 A/B 테스트가 가능합니다.

일부 사용자에게는 버전 2를, 나머지에게는 버전 3을 제공할 수 있습니다. API 호출 시 특정 버전을 지정할 수도 있습니다.

기본 URL로 요청하면 최신 버전이 응답합니다. 하지만 /versions/2를 URL에 추가하면 정확히 버전 2가 응답합니다.

이 기능은 새 버전에 문제가 생겼을 때 롤백하는 데 유용합니다. 실제 현업에서의 활용 사례를 소개하겠습니다.

많은 회사에서 카나리 배포 전략을 사용합니다. 새 모델을 전체 트래픽의 5%에만 적용하고, 문제가 없으면 점진적으로 늘립니다.

TF Serving의 버전 관리 기능이 이를 가능하게 합니다. 주의할 점도 있습니다.

버전 디렉토리에 불완전한 모델이 있으면 에러가 발생합니다. 모델을 복사할 때는 임시 디렉토리에 먼저 복사한 후, 완료되면 버전 디렉토리로 이름을 변경하는 것이 안전합니다.

또한 오래된 버전이 계속 쌓이면 디스크 공간과 메모리를 낭비하므로, 사용하지 않는 버전은 정기적으로 삭제하세요. 김개발 씨는 새 모델을 버전 2 디렉토리에 복사했습니다.

로그를 확인하니 TF Serving이 새 모델을 자동으로 감지하여 로드하는 것을 볼 수 있었습니다. 서비스는 단 한 순간도 중단되지 않았습니다.

실전 팁

💡 - 프로덕션에서는 항상 버전 관리를 사용하세요

  • 문제 발생 시 빠른 롤백을 위해 최근 2-3개 버전은 유지하세요
  • 모델 복사는 atomic operation으로 처리하여 불완전한 로드를 방지하세요

6. Batching으로 성능 향상

서비스가 성장하면서 요청량이 급증했습니다. 김개발 씨의 모델 서버는 초당 100개의 요청을 처리해야 했는데, 점점 응답 시간이 느려지기 시작했습니다.

"더 좋은 서버로 업그레이드해야 할까요?" 박시니어 씨가 대답했습니다. "잠깐, 먼저 배치 처리를 시도해보죠."

**Batching(배치 처리)**은 여러 개의 요청을 모아서 한 번에 처리하는 기법입니다. 마치 엘리베이터가 여러 층의 승객을 한 번에 태워서 이동하는 것처럼, GPU나 CPU의 병렬 처리 능력을 최대한 활용합니다.

개별 요청을 하나씩 처리하는 것보다 전체 처리량이 크게 향상됩니다.

다음 코드를 살펴봅시다.

# batching_config.txt 설정 파일
max_batch_size { value: 32 }
batch_timeout_micros { value: 10000 }
max_enqueued_batches { value: 100 }
num_batch_threads { value: 4 }
pad_variable_length_inputs: true

# Docker 실행 시 배치 설정 적용
docker run -p 8501:8501 \
  -v "/models:/models" \
  -v "/config/batching_config.txt:/config/batching.txt" \
  tensorflow/serving \
  --model_name=my_model \
  --model_base_path=/models/my_model \
  --enable_batching=true \
  --batching_parameters_file=/config/batching.txt

# Python에서 배치 요청 보내기
import requests
import numpy as np

# 여러 이미지를 한 번에 요청
images = np.random.rand(32, 224, 224, 3)  # 32개 이미지
payload = {"instances": images.tolist()}
response = requests.post(
    "http://localhost:8501/v1/models/my_model:predict",
    json=payload
)
predictions = response.json()["predictions"]

김개발 씨의 이미지 분류 서비스가 인기를 끌면서 트래픽이 10배나 증가했습니다. 처음에는 초당 10개의 요청을 처리하면 됐는데, 이제는 초당 100개를 처리해야 합니다.

서버 응답 시간이 점점 느려지기 시작했습니다. 처음에는 50ms였던 응답 시간이 500ms까지 늘어났습니다.

사용자들의 불만이 쏟아지기 시작했습니다. 김개발 씨는 서버를 더 좋은 것으로 업그레이드하려고 견적을 받아봤습니다.

GPU 서버 비용이 만만치 않았습니다. 박시니어 씨가 조언했습니다.

"서버를 업그레이드하기 전에 배치 처리를 적용해보세요. 같은 하드웨어로도 처리량을 몇 배로 늘릴 수 있어요." 배치 처리란 무엇일까요?

쉽게 비유하자면, 엘리베이터와 같습니다. 만약 엘리베이터가 한 사람씩만 태울 수 있다면 100명을 옮기는 데 100번을 왕복해야 합니다.

하지만 10명씩 태울 수 있다면 10번만 왕복하면 됩니다. 총 이동 시간이 크게 줄어들죠.

GPU도 마찬가지입니다. GPU는 수천 개의 코어가 병렬로 연산을 수행합니다.

이미지 1장을 처리하나 32장을 처리하나 걸리는 시간이 비슷합니다. 그래서 여러 요청을 모아서 한 번에 처리하면 전체 처리량이 크게 향상됩니다.

위의 설정 파일을 살펴보겠습니다. max_batch_size는 한 번에 모아서 처리할 최대 요청 수입니다.

32로 설정하면 최대 32개의 요청을 모읍니다. batch_timeout_micros는 배치를 기다리는 최대 시간입니다.

10000마이크로초, 즉 10밀리초입니다. 요청이 적을 때는 32개가 모일 때까지 기다리지 않고, 10ms가 지나면 현재 모인 요청들로 배치를 만듭니다.

num_batch_threads는 배치 처리를 담당하는 스레드 수입니다. CPU 코어 수에 맞춰서 설정하면 됩니다.

배치 처리의 트레이드오프를 이해해야 합니다. 배치 처리는 처리량은 높이지만 지연시간이 약간 증가할 수 있습니다.

첫 번째 요청은 배치가 모일 때까지 기다려야 하기 때문입니다. batch_timeout_micros 값으로 이 균형을 조절합니다.

값이 작으면 지연시간은 줄지만 배치 효율이 떨어지고, 값이 크면 그 반대입니다. 실제 현업에서는 어떻게 활용할까요?

트래픽이 많은 서비스에서는 거의 필수적으로 배치 처리를 사용합니다. 특히 GPU를 사용할 때 효과가 극대화됩니다.

배치 크기를 적절히 조절하면 같은 GPU로 2-5배 더 많은 요청을 처리할 수 있습니다. 클라이언트 측에서도 배치 요청을 보낼 수 있습니다.

위 Python 코드처럼 여러 이미지를 instances 배열에 담아서 한 번에 요청하면, 네트워크 오버헤드도 줄일 수 있습니다. 주의할 점이 있습니다.

max_batch_size를 너무 크게 설정하면 GPU 메모리가 부족해질 수 있습니다. 특히 대형 모델의 경우 배치 크기에 따라 메모리 사용량이 급격히 증가합니다.

테스트를 통해 적절한 값을 찾아야 합니다. 또한 가변 길이 입력을 처리할 때는 pad_variable_length_inputs 옵션을 활성화해야 합니다.

자연어 처리 모델처럼 입력 길이가 다양할 때 필요합니다. 김개발 씨는 배치 설정을 적용한 후 성능 테스트를 진행했습니다.

놀랍게도 같은 서버에서 처리량이 3배나 늘어났습니다. 서버 업그레이드 비용을 절약할 수 있었습니다.

실전 팁

💡 - GPU 사용 시 배치 크기를 GPU 메모리에 맞게 조절하세요

  • batch_timeout_micros는 서비스의 지연시간 요구사항에 맞춰 설정하세요
  • 트래픽 패턴에 따라 설정값을 튜닝하면 최적의 성능을 얻을 수 있습니다

7. GPU 활용 및 Kubernetes 배포

서비스가 대성공을 거두면서 더 큰 규모의 인프라가 필요해졌습니다. 김개발 씨는 이제 여러 대의 서버를 관리해야 하고, GPU도 효율적으로 활용해야 합니다.

"이걸 어떻게 관리하죠?" 박시니어 씨가 말했습니다. "이제 Kubernetes를 배워야 할 때가 됐네요."

GPU 활용Kubernetes 배포는 대규모 ML 서비스의 핵심입니다. GPU는 모델 추론 속도를 10배 이상 높여주고, Kubernetes는 수십 대의 서버를 자동으로 관리해줍니다.

마치 자동 항법 시스템이 여러 대의 비행기를 동시에 관제하는 것처럼, Kubernetes가 ML 서비스의 배포, 확장, 복구를 자동화합니다.

다음 코드를 살펴봅시다.

# Kubernetes Deployment YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tf-serving
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tf-serving
  template:
    metadata:
      labels:
        app: tf-serving
    spec:
      containers:
      - name: tf-serving
        image: tensorflow/serving:latest-gpu
        ports:
        - containerPort: 8500
        - containerPort: 8501
        resources:
          limits:
            nvidia.com/gpu: 1
        volumeMounts:
        - name: model-volume
          mountPath: /models
        env:
        - name: MODEL_NAME
          value: "my_model"
      volumes:
      - name: model-volume
        persistentVolumeClaim:
          claimName: model-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: tf-serving-service
spec:
  selector:
    app: tf-serving
  ports:
  - name: grpc
    port: 8500
  - name: rest
    port: 8501
  type: LoadBalancer

김개발 씨의 이미지 분류 서비스는 이제 하루에 수백만 건의 요청을 처리합니다. 처음에는 서버 한 대로 시작했지만, 지금은 수십 대의 서버가 필요합니다.

문제는 이 많은 서버를 어떻게 관리하느냐는 것이었습니다. 서버가 다운되면 누군가 새 서버를 띄워야 합니다.

트래픽이 몰리면 서버를 추가해야 합니다. 새 모델을 배포하면 모든 서버에 적용해야 합니다.

이 모든 것을 수동으로 하기에는 너무 힘들었습니다. 박시니어 씨가 말했습니다.

"Kubernetes를 사용하면 이 모든 것이 자동화됩니다." Kubernetes란 무엇일까요? 쉽게 비유하자면, Kubernetes는 항공 관제 시스템과 같습니다.

여러 대의 비행기가 동시에 이착륙할 때, 관제사가 각 비행기의 위치를 파악하고 충돌을 방지합니다. Kubernetes도 마찬가지로 수십 대의 서버 컨테이너를 관리하고, 문제가 생기면 자동으로 복구합니다.

먼저 GPU 활용에 대해 알아보겠습니다. GPU는 딥러닝 추론 속도를 크게 향상시킵니다.

CPU로 100ms 걸리던 추론이 GPU로는 10ms에 끝날 수 있습니다. TF Serving의 GPU 버전 이미지(tensorflow/serving:latest-gpu)를 사용하면 됩니다.

위의 Kubernetes YAML 파일을 살펴보겠습니다. Deployment 섹션은 어떤 컨테이너를 몇 개 실행할지 정의합니다.

replicas: 3은 동일한 TF Serving 컨테이너 3개를 실행한다는 의미입니다. 하나가 죽으면 Kubernetes가 자동으로 새 컨테이너를 띄웁니다.

resources.limits에서 nvidia.com/gpu: 1은 각 컨테이너가 GPU 1개를 사용한다는 의미입니다. Kubernetes의 NVIDIA 플러그인이 GPU를 컨테이너에 할당해줍니다.

volumeMounts는 모델 파일이 저장된 스토리지를 컨테이너에 연결합니다. PersistentVolumeClaim을 사용하면 여러 컨테이너가 동일한 모델 파일을 공유할 수 있습니다.

Service 섹션은 외부에서 접근할 수 있는 엔드포인트를 만듭니다. LoadBalancer 타입은 클라우드 제공자의 로드밸런서를 자동으로 생성하여 트래픽을 여러 컨테이너에 분산합니다.

자동 확장 기능도 중요합니다. Kubernetes의 **Horizontal Pod Autoscaler(HPA)**를 설정하면 트래픽에 따라 자동으로 컨테이너 수를 조절합니다.

CPU 사용률이 80%를 넘으면 컨테이너를 추가하고, 트래픽이 줄면 컨테이너를 줄입니다. 비용을 절약하면서도 성능을 유지할 수 있습니다.

실제 현업에서의 아키텍처를 소개하겠습니다. 대규모 ML 서비스는 보통 다음과 같은 구조를 가집니다.

사용자 요청은 로드밸런서를 통해 들어옵니다. 로드밸런서가 여러 TF Serving 파드에 요청을 분산합니다.

각 파드는 GPU를 사용하여 빠르게 추론합니다. 모델은 공유 스토리지(S3, GCS 등)에 저장되어 모든 파드가 접근할 수 있습니다.

주의할 점이 있습니다. GPU 서버는 비쌉니다.

클라우드에서 GPU 인스턴스를 사용할 때는 비용을 주의 깊게 모니터링해야 합니다. 또한 Kubernetes 클러스터 설정은 복잡하므로, AWS EKS, Google GKE, Azure AKS 같은 관리형 서비스를 사용하는 것이 좋습니다.

GPU 메모리도 주의해야 합니다. 여러 모델을 하나의 GPU에서 서빙하면 메모리 부족 에러가 발생할 수 있습니다.

CUDA_VISIBLE_DEVICES 환경변수나 TF Serving의 per_process_gpu_memory_fraction 옵션으로 GPU 메모리 사용량을 제어할 수 있습니다. 김개발 씨는 Kubernetes 배포를 완료하고 감탄했습니다.

트래픽이 갑자기 늘어도 자동으로 서버가 추가되고, 서버에 문제가 생겨도 자동으로 복구됩니다. 이제 인프라 걱정 없이 모델 개선에 집중할 수 있게 되었습니다.

실전 팁

💡 - 프로덕션에서는 관리형 Kubernetes 서비스(EKS, GKE, AKS)를 권장합니다

  • GPU 비용을 절약하려면 Spot/Preemptible 인스턴스를 고려하세요
  • 모델 파일은 S3나 GCS 같은 객체 스토리지에 저장하고 initContainer로 다운로드하는 패턴이 일반적입니다

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

#TensorFlow#TFServing#ModelServing#Docker#Kubernetes#MLOps,TensorFlow,Serving

댓글 (0)

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

함께 보면 좋은 카드 뉴스