본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 7. · 18 Views
ML CI/CD 파이프라인 완벽 가이드
머신러닝 모델을 자동으로 테스트하고 배포하는 CI/CD 파이프라인 구축 방법을 알아봅니다. GitHub Actions를 활용한 실무 중심의 MLOps 가이드입니다.
목차
1. ML CI/CD 특수성
김개발 씨는 처음으로 머신러닝 모델을 운영 환경에 배포하게 되었습니다. 웹 애플리케이션 배포 경험은 있었지만, 모델 배포는 왠지 다르게 느껴졌습니다.
"코드만 배포하면 되는 거 아닌가요?" 선배 박시니어 씨가 고개를 저었습니다. "ML은 조금 다르답니다."
ML CI/CD는 일반적인 소프트웨어 CI/CD와 근본적으로 다른 점이 있습니다. 코드뿐만 아니라 데이터와 모델이라는 두 가지 요소가 추가되기 때문입니다.
마치 요리사가 레시피(코드)뿐 아니라 재료(데이터)와 완성된 요리(모델)까지 모두 관리해야 하는 것과 같습니다.
다음 코드를 살펴봅시다.
# ML 프로젝트의 세 가지 핵심 아티팩트
ml_artifacts = {
"code": "model_training.py", # 학습 코드
"data": "training_data.csv", # 학습 데이터
"model": "model_v1.pkl" # 학습된 모델
}
# 각 아티팩트의 버전 관리가 필요합니다
versioning = {
"code": "git", # Git으로 코드 버전 관리
"data": "dvc", # DVC로 데이터 버전 관리
"model": "mlflow" # MLflow로 모델 버전 관리
}
# 세 요소가 함께 변경될 때 재현 가능해야 합니다
def ensure_reproducibility(code_version, data_version, model_version):
return f"실험 재현: {code_version}-{data_version}-{model_version}"
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 웹 서비스 개발팀에서 일하다가 최근 ML 엔지니어링 팀으로 이동했습니다.
첫 번째 미션은 추천 모델을 운영 환경에 배포하는 것이었습니다. "선배님, 저 웹 서비스 배포할 때처럼 Docker 이미지 만들어서 배포하면 되는 거 아닌가요?" 박시니어 씨가 화이트보드 앞으로 다가갔습니다.
"일반 소프트웨어와 ML의 가장 큰 차이가 뭔지 알아요?" 김개발 씨가 잠시 생각했습니다. "음...
모델 파일이 추가된다는 건가요?" "맞아요, 하지만 그게 다가 아닙니다." 박시니어 씨가 그림을 그리기 시작했습니다. 일반 소프트웨어는 코드라는 하나의 축만 관리하면 됩니다.
코드가 바뀌면 새 버전을 배포하고, 문제가 생기면 이전 버전으로 롤백합니다. 단순명료합니다.
하지만 ML 시스템은 다릅니다. 코드, 데이터, 모델이라는 세 개의 축이 존재합니다.
코드가 같아도 데이터가 바뀌면 모델이 달라집니다. 모델이 같아도 코드가 바뀌면 추론 결과가 달라질 수 있습니다.
쉽게 비유하자면, 일반 소프트웨어 배포는 마치 완성된 레시피 책을 출판하는 것과 같습니다. 레시피만 관리하면 됩니다.
반면 ML 배포는 레스토랑을 운영하는 것과 비슷합니다. 레시피(코드)뿐 아니라 재료(데이터)의 신선도, 완성된 요리(모델)의 품질까지 모두 신경 써야 합니다.
"그럼 세 가지를 어떻게 관리해요?" 박시니어 씨가 답했습니다. "각각 다른 도구를 사용합니다.
코드는 Git으로, 데이터는 DVC나 Delta Lake로, 모델은 MLflow나 Weights & Biases로 관리하죠." 여기서 중요한 개념이 등장합니다. 바로 재현 가능성입니다.
3개월 전에 배포한 모델에 문제가 발견되었다고 가정해 봅시다. 그 모델이 어떤 코드, 어떤 데이터로 학습되었는지 정확히 알 수 있어야 합니다.
그래야 문제를 분석하고 수정할 수 있습니다. 일반 CI/CD 파이프라인은 코드 변경을 감지하고 자동으로 빌드, 테스트, 배포합니다.
ML CI/CD는 여기에 모델 성능 검증이라는 단계가 추가됩니다. 새 모델이 기존 모델보다 성능이 좋은지, 편향은 없는지, 추론 속도는 적절한지 자동으로 확인해야 합니다.
김개발 씨가 고개를 끄덕였습니다. "생각보다 복잡하네요.
그런데 이걸 수동으로 하면 안 되나요?" 박시니어 씨가 웃으며 답했습니다. "한두 번은 괜찮아요.
하지만 모델을 매주 재학습하고, 여러 실험을 동시에 진행하고, 여러 환경에 배포해야 한다면요? 자동화 없이는 불가능합니다." 이것이 바로 MLOps가 탄생한 이유입니다.
DevOps가 개발과 운영의 간극을 메웠듯이, MLOps는 ML 개발과 운영의 간극을 메웁니다. 그리고 CI/CD는 MLOps의 핵심 축입니다.
실전 팁
💡 - 코드, 데이터, 모델의 버전을 항상 함께 기록하여 재현 가능성을 확보하세요
- 처음에는 간단한 파이프라인부터 시작하고, 필요에 따라 점진적으로 복잡도를 높이세요
2. GitHub Actions 기초
박시니어 씨의 설명을 들은 김개발 씨가 물었습니다. "그럼 CI/CD 파이프라인을 어떤 도구로 만들어야 하나요?" 선배가 답했습니다.
"우리 팀은 GitHub Actions를 사용해요. 저장소와 통합되어 있어서 설정이 간편하거든요."
GitHub Actions는 GitHub에 내장된 CI/CD 플랫폼입니다. YAML 파일로 워크플로우를 정의하면, 특정 이벤트가 발생할 때 자동으로 작업이 실행됩니다.
마치 집에 설치한 스마트 홈 시스템처럼, 특정 조건이 충족되면 미리 정해둔 동작이 자동으로 수행됩니다.
다음 코드를 살펴봅시다.
# .github/workflows/ml-pipeline.yml
name: ML Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest tests/ -v
김개발 씨가 GitHub Actions 문서를 펼쳤습니다. 처음 보는 개념들이 쏟아졌습니다.
Workflow, Job, Step, Runner... 머리가 어지러웠습니다.
박시니어 씨가 다가와 설명을 시작했습니다. "복잡하게 생각할 것 없어요.
하나씩 이해해 봅시다." Workflow는 자동화의 전체 과정을 의미합니다. 하나의 YAML 파일이 하나의 워크플로우입니다.
마치 공장의 생산 라인 전체를 가리키는 것과 같습니다. Job은 워크플로우 안의 독립적인 작업 단위입니다.
하나의 러너(가상 머신)에서 실행됩니다. 생산 라인에서 각 부서가 담당하는 작업이라고 생각하면 됩니다.
조립부, 도색부, 포장부처럼요. Step은 Job 안의 개별 단계입니다.
명령어를 실행하거나 다른 사람이 만든 액션을 사용할 수 있습니다. 각 부서 안에서 수행하는 세부 작업이라고 보면 됩니다.
Runner는 워크플로우가 실행되는 서버입니다. GitHub에서 제공하는 서버를 사용하거나, 직접 서버를 등록할 수도 있습니다.
"이제 코드를 보면서 이해해 볼까요?" 위의 YAML 파일에서 on 섹션은 워크플로우가 언제 실행될지 정의합니다. main 브랜치에 push하거나 PR을 생성하면 자동으로 실행됩니다.
마치 "초인종이 울리면 현관문 카메라를 켜라"라는 규칙과 같습니다. jobs 섹션에는 실행할 작업들을 정의합니다.
여기서는 test라는 이름의 Job 하나만 있습니다. runs-on은 어떤 환경에서 실행할지 지정합니다.
ubuntu-latest는 최신 우분투 서버를 의미합니다. steps 아래에 실제 수행할 작업들이 나열됩니다.
첫 번째로 actions/checkout@v4를 사용해 코드를 가져옵니다. 이건 GitHub에서 공식 제공하는 액션입니다.
저장소의 코드를 러너에 복사합니다. 두 번째로 Python 환경을 설정합니다.
actions/setup-python@v5 액션을 사용하고, with 아래에 원하는 Python 버전을 지정합니다. 세 번째로 의존성을 설치합니다.
run 키워드 뒤에 실행할 명령어를 작성합니다. 일반 터미널에서 입력하는 것과 동일합니다.
마지막으로 pytest를 실행해 테스트를 수행합니다. 테스트가 실패하면 워크플로우 전체가 실패로 표시됩니다.
김개발 씨가 질문했습니다. "파일은 어디에 저장해야 하나요?" "저장소의 .github/workflows 폴더 안에 넣으면 됩니다.
GitHub가 자동으로 인식해요." 김개발 씨가 파일을 만들고 push했습니다. GitHub의 Actions 탭에 들어가니 워크플로우가 실행되고 있었습니다.
초록색 체크 표시가 나타났습니다. 성공입니다.
"생각보다 간단하네요!" 박시니어 씨가 고개를 끄덕였습니다. "기본은 간단해요.
이제 ML에 특화된 기능들을 추가해 봅시다."
실전 팁
💡 - 워크플로우 파일은 반드시 .github/workflows 폴더에 위치해야 합니다
- 처음에는 단순한 워크플로우로 시작하고, 동작을 확인한 후 점진적으로 기능을 추가하세요
3. 모델 테스트 자동화
기본 CI 파이프라인이 동작하는 것을 확인한 김개발 씨가 물었습니다. "일반 테스트는 알겠는데, 모델 테스트는 어떻게 하나요?
정확도 같은 걸 매번 확인해야 하지 않나요?" 박시니어 씨가 고개를 끄덕였습니다. "좋은 질문이에요.
ML 테스트는 조금 특별합니다."
모델 테스트 자동화는 모델의 품질을 코드 수준에서 검증하는 과정입니다. 단순히 에러가 없는지 확인하는 것을 넘어, 모델 성능이 기준치를 만족하는지, 입출력 형식이 올바른지, 추론 속도가 적절한지까지 자동으로 검사합니다.
마치 자동차 출고 전 품질 검사처럼, 모든 항목을 체크리스트로 확인하는 것입니다.
다음 코드를 살펴봅시다.
# tests/test_model.py
import pytest
import numpy as np
from model import load_model, predict
class TestModelQuality:
@pytest.fixture
def model(self):
return load_model("models/model_v1.pkl")
def test_model_accuracy(self, model):
# 검증 데이터셋으로 정확도 테스트
X_val, y_val = load_validation_data()
predictions = predict(model, X_val)
accuracy = np.mean(predictions == y_val)
assert accuracy >= 0.85, f"정확도 {accuracy}가 기준치 0.85 미만입니다"
def test_inference_time(self, model):
# 추론 속도 테스트
import time
X_sample = np.random.randn(1, 10)
start = time.time()
predict(model, X_sample)
elapsed = time.time() - start
assert elapsed < 0.1, f"추론 시간 {elapsed}초가 0.1초를 초과합니다"
def test_output_shape(self, model):
# 출력 형식 테스트
X_sample = np.random.randn(5, 10)
output = predict(model, X_sample)
assert output.shape == (5,), "출력 shape이 예상과 다릅니다"
박시니어 씨가 화이트보드에 크게 세 가지를 적었습니다. 정확도, 속도, 안정성.
"ML 모델 테스트는 크게 이 세 가지를 확인해야 합니다." 첫 번째는 정확도 테스트입니다. 새로 학습한 모델이 기존 모델보다 성능이 떨어지면 안 됩니다.
검증 데이터셋을 사용해 정확도, F1 스코어, AUC 등의 지표를 측정하고, 미리 정해둔 기준치와 비교합니다. 김개발 씨가 걱정스럽게 물었습니다.
"그런데 검증 데이터셋을 CI 환경에서 사용해도 괜찮나요? 보안 문제는 없나요?" 좋은 지적입니다.
실제 운영 데이터를 CI 환경에 올리는 것은 위험할 수 있습니다. 그래서 보통 두 가지 방법을 사용합니다.
하나는 샘플 데이터셋을 만들어 저장소에 포함시키는 것입니다. 크기가 작고 민감하지 않은 데이터를 사용합니다.
다른 방법은 GitHub Secrets에 데이터 저장소 접근 정보를 넣고, CI에서 안전하게 가져오는 것입니다. 두 번째는 속도 테스트입니다.
아무리 정확도가 높아도 추론에 10초가 걸린다면 서비스에 사용할 수 없습니다. 단일 요청의 추론 시간, 배치 처리 속도 등을 측정하고 기준치와 비교합니다.
"추론 시간은 서버 환경에 따라 다르지 않나요?" 맞습니다. GitHub Actions 러너와 실제 운영 서버의 성능은 다릅니다.
그래서 CI에서는 상대적인 기준을 사용하거나, 심각한 성능 저하만 감지하는 용도로 활용합니다. 정확한 성능 측정은 스테이징 환경에서 별도로 수행합니다.
세 번째는 안정성 테스트입니다. 모델이 예상치 못한 입력에도 에러 없이 동작하는지 확인합니다.
빈 입력, 이상한 값, 형식이 다른 데이터 등을 넣어보고 적절히 처리하는지 검사합니다. 위의 코드에서 pytest.fixture는 테스트에 필요한 공통 자원을 준비합니다.
여러 테스트 함수에서 같은 모델을 사용해야 하므로, fixture로 한 번만 로드합니다. test_model_accuracy 함수는 검증 데이터로 예측을 수행하고 정확도를 계산합니다.
assert 문으로 기준치 0.85 이상인지 확인합니다. 미달이면 테스트가 실패하고 CI 파이프라인도 중단됩니다.
test_inference_time 함수는 단일 샘플의 추론 시간을 측정합니다. 0.1초를 초과하면 실패입니다.
이런 테스트가 있으면 실수로 느린 코드를 배포하는 것을 방지할 수 있습니다. test_output_shape 함수는 출력의 형태가 예상과 일치하는지 확인합니다.
모델 구조를 변경했을 때 API 호환성이 깨지는 것을 조기에 발견할 수 있습니다. 김개발 씨가 감탄했습니다.
"이런 테스트가 자동으로 돌아가면 정말 안심이 되겠네요." 박시니어 씨가 덧붙였습니다. "처음에는 기본적인 테스트만 넣고, 문제가 발생할 때마다 해당 케이스를 테스트로 추가하세요.
시간이 지나면 견고한 테스트 스위트가 만들어집니다."
실전 팁
💡 - 모든 테스트를 통과해야 배포가 진행되도록 CI를 설정하세요
- 실패한 테스트는 즉시 알림을 받을 수 있도록 Slack이나 이메일 연동을 추가하세요
4. 이미지 빌드 자동화
테스트 자동화를 마친 김개발 씨가 다음 단계를 물었습니다. "테스트를 통과하면 바로 배포하면 되나요?" 박시니어 씨가 답했습니다.
"그 전에 Docker 이미지를 만들어야 해요. 모델과 코드를 하나의 패키지로 묶는 거죠."
Docker 이미지 빌드 자동화는 모델 서빙에 필요한 모든 것을 하나의 컨테이너 이미지로 패키징하는 과정입니다. 코드, 모델 파일, 의존성 라이브러리를 모두 포함하여 어느 환경에서든 동일하게 실행될 수 있도록 합니다.
마치 이사할 때 모든 짐을 박스에 담아 어디서든 다시 꺼내 쓸 수 있게 하는 것과 같습니다.
다음 코드를 살펴봅시다.
# .github/workflows/build.yml
name: Build and Push Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
박시니어 씨가 질문을 던졌습니다. "김개발 씨, 왜 Docker를 사용해야 할까요?" 김개발 씨가 대답했습니다.
"음... 환경을 통일하기 위해서요?" "맞아요.
하지만 ML에서는 더 중요한 이유가 있어요." ML 모델은 특정 버전의 라이브러리에 의존합니다. TensorFlow 2.10에서 학습한 모델이 TensorFlow 2.15에서 제대로 동작하지 않을 수 있습니다.
Python 버전, NumPy 버전, CUDA 버전까지 모든 것이 맞아야 합니다. Docker는 이런 의존성 지옥을 해결합니다.
이미지 안에 모든 라이브러리를 정확한 버전으로 고정하기 때문입니다. 한 번 만든 이미지는 어디서 실행해도 동일하게 동작합니다.
위의 워크플로우를 살펴보겠습니다. docker/setup-buildx-action은 Docker Buildx를 설정합니다.
Buildx는 Docker의 확장 빌드 도구로, 멀티 플랫폼 빌드와 캐싱 기능을 제공합니다. docker/login-action은 컨테이너 레지스트리에 로그인합니다.
여기서는 GitHub Container Registry(ghcr.io)를 사용합니다. secrets.GITHUB_TOKEN은 GitHub에서 자동으로 제공하는 토큰으로, 별도 설정 없이 사용할 수 있습니다.
docker/build-push-action이 실제 빌드와 푸시를 수행합니다. context는 Dockerfile이 있는 경로입니다.
tags에 이미지 이름과 태그를 지정합니다. github.sha를 태그로 사용하면 각 커밋마다 고유한 이미지가 생성됩니다.
cache-from과 cache-to 옵션이 중요합니다. ML 이미지는 라이브러리가 많아서 빌드에 시간이 오래 걸립니다.
캐싱을 사용하면 변경되지 않은 레이어는 재사용하여 빌드 시간을 크게 줄일 수 있습니다. 김개발 씨가 Dockerfile도 보고 싶어했습니다.
기본적인 ML 서빙용 Dockerfile은 다음과 같은 구조를 가집니다. 베이스 이미지로 Python을 선택하고, 의존성을 설치하고, 모델 파일과 서빙 코드를 복사하고, 서버를 실행합니다.
"이미지 태그로 커밋 해시를 사용하는 이유가 있나요?" 박시니어 씨가 설명했습니다. "추적 가능성 때문이에요.
운영 중인 이미지가 어떤 코드에서 만들어졌는지 바로 알 수 있거든요. 문제가 생기면 해당 커밋을 확인하면 됩니다." 또한 latest 태그만 사용하면 위험합니다.
어떤 버전이 실제로 배포되어 있는지 알기 어렵기 때문입니다. 커밋 해시 태그와 함께 버전 태그(v1.0.0)를 병행하는 것이 좋습니다.
김개발 씨가 워크플로우를 실행해 보았습니다. 몇 분 후 GitHub Packages에 새 이미지가 등록되어 있었습니다.
이제 이 이미지를 배포하면 됩니다.
실전 팁
💡 - 멀티 스테이지 빌드를 사용하면 이미지 크기를 크게 줄일 수 있습니다
- 모델 파일이 큰 경우 별도의 스토리지에서 런타임에 다운로드하는 방식도 고려하세요
5. 배포 파이프라인 구성
이미지 빌드까지 성공한 김개발 씨가 드디어 마지막 단계에 도달했습니다. "이제 진짜 배포만 남았네요!" 박시니어 씨가 고개를 끄덕이며 말했습니다.
"배포는 단계별로 진행해야 해요. 한 번에 모든 사용자에게 배포하면 위험하거든요."
배포 파이프라인은 빌드된 이미지를 실제 운영 환경에 배포하는 자동화된 프로세스입니다. 스테이징 환경에서 먼저 테스트하고, 문제가 없으면 프로덕션에 점진적으로 배포합니다.
마치 신약을 출시할 때 임상 시험을 거치고, 소수에게 먼저 처방하고, 문제가 없으면 전체에 공개하는 것과 같습니다.
다음 코드를 살펴봅시다.
# .github/workflows/deploy.yml
name: Deploy to Production
on:
workflow_run:
workflows: ["Build and Push Image"]
types: [completed]
branches: [main]
jobs:
deploy-staging:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to Staging
run: |
kubectl set image deployment/ml-model \
ml-model=ghcr.io/${{ github.repository }}:${{ github.sha }} \
--namespace=staging
- name: Run smoke tests
run: |
sleep 30
curl -f https://staging.example.com/health || exit 1
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to Production
run: |
kubectl set image deployment/ml-model \
ml-model=ghcr.io/${{ github.repository }}:${{ github.sha }} \
--namespace=production
박시니어 씨가 배포 전략에 대해 설명을 시작했습니다. "배포에는 여러 전략이 있어요.
가장 기본적인 것부터 알아봅시다." 롤링 배포는 기존 인스턴스를 하나씩 새 버전으로 교체하는 방식입니다. 5개의 인스턴스가 있다면, 하나를 새 버전으로 바꾸고, 정상 동작하면 다음 것을 바꾸고, 이런 식으로 진행합니다.
Kubernetes의 기본 배포 방식입니다. 블루-그린 배포는 두 개의 동일한 환경을 유지합니다.
블루가 현재 운영 중이라면, 그린에 새 버전을 배포합니다. 테스트가 완료되면 트래픽을 그린으로 전환합니다.
문제가 생기면 다시 블루로 돌리면 됩니다. 카나리 배포는 새 버전을 일부 사용자에게만 먼저 배포합니다.
전체 트래픽의 10%만 새 버전으로 보내고, 문제가 없으면 점차 비율을 높입니다. ML 모델에서 특히 유용한 전략입니다.
위의 워크플로우를 살펴보겠습니다. workflow_run 트리거는 다른 워크플로우가 완료되면 실행됩니다.
Build and Push Image 워크플로우가 성공하면 배포 워크플로우가 시작됩니다. environment 키워드가 중요합니다.
GitHub에서 환경을 설정하면 배포 전 승인을 요구할 수 있습니다. 프로덕션 환경은 팀장의 승인이 필요하도록 설정하면, 실수로 바로 배포되는 것을 방지할 수 있습니다.
deploy-staging 잡은 먼저 스테이징 환경에 배포합니다. kubectl set image 명령으로 Kubernetes 디플로이먼트의 이미지를 업데이트합니다.
그 후 smoke test를 실행하여 서비스가 정상적으로 응답하는지 확인합니다. 스테이징에서 문제가 발견되면 워크플로우가 실패하고 프로덕션 배포는 진행되지 않습니다.
이것이 게이트 역할을 합니다. deploy-production 잡은 needs: deploy-staging으로 인해 스테이징 배포가 성공해야만 실행됩니다.
프로덕션 환경에도 동일한 방식으로 이미지를 업데이트합니다. 김개발 씨가 질문했습니다.
"kubectl을 사용하려면 Kubernetes 클러스터 접근 정보가 필요하지 않나요?" 맞습니다. GitHub Secrets에 kubeconfig 파일이나 서비스 계정 토큰을 저장해야 합니다.
보안을 위해 각 환경별로 별도의 시크릿을 사용하는 것이 좋습니다. 박시니어 씨가 덧붙였습니다.
"ML 모델 배포에서는 A/B 테스트도 자주 사용해요. 새 모델과 기존 모델을 동시에 운영하면서 어떤 것이 더 좋은 성과를 내는지 비교하는 거죠." 이를 위해서는 트래픽을 분할하는 인프라가 필요합니다.
Istio 같은 서비스 메시를 사용하면 비율에 따라 트래픽을 라우팅할 수 있습니다.
실전 팁
💡 - 프로덕션 배포 전 반드시 스테이징에서 충분히 테스트하세요
- environment 설정으로 승인 절차를 추가하면 실수를 방지할 수 있습니다
6. 롤백 전략
배포를 마친 김개발 씨가 안도의 한숨을 쉬었습니다. 그런데 박시니어 씨가 진지한 표정으로 말했습니다.
"배포보다 더 중요한 게 롤백이에요. 문제가 생겼을 때 빠르게 이전 버전으로 돌아갈 수 있어야 합니다."
롤백 전략은 배포 후 문제가 발생했을 때 신속하게 이전 안정 버전으로 되돌리는 방법입니다. 자동 롤백은 모니터링 지표가 임계값을 초과하면 자동으로 실행되고, 수동 롤백은 운영자가 직접 트리거합니다.
마치 자동차의 에어백처럼, 평소에는 필요 없지만 사고가 났을 때 생명을 구하는 안전장치입니다.
다음 코드를 살펴봅시다.
# .github/workflows/rollback.yml
name: Rollback Deployment
on:
workflow_dispatch:
inputs:
target_version:
description: '롤백할 이미지 태그'
required: true
environment:
description: '환경 선택'
required: true
type: choice
options:
- staging
- production
jobs:
rollback:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}
steps:
- name: Rollback deployment
run: |
echo "Rolling back to version: ${{ github.event.inputs.target_version }}"
kubectl set image deployment/ml-model \
ml-model=ghcr.io/${{ github.repository }}:${{ inputs.target_version }} \
--namespace=${{ inputs.environment }}
- name: Verify rollback
run: |
kubectl rollout status deployment/ml-model \
--namespace=${{ inputs.environment }} \
--timeout=300s
- name: Notify team
run: |
echo "롤백 완료: ${{ inputs.environment }} -> ${{ inputs.target_version }}"
박시니어 씨가 과거 경험담을 들려주었습니다. "예전에 금요일 오후에 배포했다가 큰일 날 뻔했어요.
새 모델이 특정 케이스에서 완전히 엉뚱한 예측을 했거든요." "어떻게 해결하셨어요?" "다행히 롤백 파이프라인이 준비되어 있었어요. 5분 만에 이전 버전으로 돌아갔죠." 롤백이 중요한 이유는 ML 모델의 불확실성 때문입니다.
코드는 테스트로 어느 정도 검증할 수 있지만, 모델은 실제 트래픽에서 예상치 못한 동작을 할 수 있습니다. 학습 데이터에 없던 패턴이 들어오면 이상한 결과를 낼 수 있습니다.
위의 워크플로우를 살펴보겠습니다. workflow_dispatch는 수동으로 워크플로우를 실행할 수 있게 합니다.
GitHub Actions 탭에서 Run workflow 버튼을 클릭하면 됩니다. inputs로 필요한 정보를 입력받습니다.
target_version은 롤백할 이미지 태그입니다. 이전에 빌드된 이미지 중 안정적인 버전을 선택합니다.
environment는 롤백할 환경을 선택합니다. 롤백 단계는 배포와 동일합니다.
kubectl set image로 이미지를 변경합니다. 다만 새 이미지가 아니라 이전 이미지로 변경한다는 점이 다릅니다.
kubectl rollout status는 롤아웃이 완료될 때까지 기다립니다. 타임아웃을 설정하여 무한 대기를 방지합니다.
김개발 씨가 질문했습니다. "자동 롤백은 어떻게 구현하나요?" 자동 롤백은 모니터링 시스템과 연동해야 합니다.
Prometheus로 지표를 수집하고, 에러율이나 지연 시간이 임계값을 초과하면 알람을 발생시킵니다. 이 알람이 롤백 워크플로우를 트리거하도록 설정합니다.
Kubernetes의 자동 롤백 기능도 활용할 수 있습니다. 디플로이먼트에 readinessProbe와 livenessProbe를 설정하면, 새 파드가 정상 동작하지 않을 경우 자동으로 이전 버전을 유지합니다.
박시니어 씨가 중요한 조언을 했습니다. "롤백은 빠를수록 좋아요.
30초 안에 롤백할 수 있도록 준비해 두세요. 그리고 롤백 훈련을 정기적으로 하세요.
실제 상황에서 당황하지 않으려면 미리 연습해야 합니다." 또한 이전 버전 보관도 중요합니다. 컨테이너 레지스트리에서 이미지를 너무 빨리 삭제하면 롤백할 수 없습니다.
최소 30일 이상의 이미지를 보관하는 정책을 세우세요. 김개발 씨가 고개를 끄덕였습니다.
"배포보다 롤백이 더 중요하다는 말씀이 이해됐어요. 잘 준비해 둬야겠네요." 박시니어 씨가 마무리했습니다.
"CI/CD 파이프라인은 한 번 만들고 끝이 아니에요. 계속 개선하고, 문제가 생기면 보완하고, 더 나은 방법을 찾아가는 과정입니다."
실전 팁
💡 - 롤백 워크플로우는 반드시 사전에 테스트해두세요. 실제 장애 상황에서 처음 사용하면 안 됩니다
- 최근 배포 이력과 각 버전의 이미지 태그를 쉽게 조회할 수 있도록 문서화하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Spring Boot 상품 서비스 구축 완벽 가이드
실무 RESTful API 설계부터 테스트, 배포까지 Spring Boot로 상품 서비스를 만드는 전 과정을 다룹니다. JPA 엔티티 설계, OpenAPI 문서화, Docker Compose 배포 전략을 초급 개발자도 쉽게 따라할 수 있도록 스토리텔링으로 풀어냅니다.
Docker로 컨테이너화 완벽 가이드
Spring Boot 애플리케이션을 Docker로 컨테이너화하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 중심으로 설명합니다. Dockerfile 작성부터 멀티스테이지 빌드, 이미지 최적화, Spring Boot의 Buildpacks까지 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.