지난 주가 "모델을 어떻게 배포하느냐"였다면, 이번 주는 "그 배포를 어떻게 자동화하고 반복 가능하게 만드느냐" — 즉 MLOps다.
ML 워크플로를 위한 CI/CD 오케스트레이션 서비스. 각 작업을 "단계(step)"로 정의한다.
단계 간 데이터 의존성을 연결하면 SageMaker가 실행 순서를 가진 DAG(Directed Acyclic Graph)를 자동 구성한다.
한 단계의 출력(예: 전처리된 데이터 S3 경로)이 다음 단계 입력으로 전달되고, 의존성이 없는 단계는 병렬 실행된다.
노트북 셀을 사람이 매번 손으로 실행하는 방식은 재현성이 없고 실수를 부른다 — 그 자리를 대체하는 것이 목적이다.
특징
내용
재현성(reproducibility)
워크플로 정의가 코드와 JSON으로 남아 동일 절차를 반복 실행
계보 추적(lineage tracking)
어떤 데이터·코드·파라미터로 어떤 모델이 나왔는지 자동 기록
Studio 통합
DAG를 시각적으로 보고 실행(execution)별 단계 상태를 추적
파이썬 SDK 기반
별도 DSL 없이 SageMaker Python SDK로 정의
Registry 연동
RegisterModel 단계로 Model Registry에 바로 버전 등록
Pipelines가 만드는 것은 DAG다. DAG는 "방향이 있고(실행 순서) 순환이 없는(무한 루프 방지)" 그래프로, 데이터 의존성을 노드/엣지로 표현하는 워크플로 오케스트레이션의 표준 모델이다. Airflow, Step Functions도 같은 DAG 모델을 쓴다. 시험에서 "SageMaker 내부에서 학습→평가→배포를 자동화"라면 Pipelines, "여러 AWS 서비스를 폭넓게 오케스트레이션"이면 Step Functions로 갈린다.
평가도 학습이 아니라 ProcessingStep이다. "평가 단계"라는 별도 타입이 있다고 착각하기 쉽다.
짧은 커스텀 로직이면 LambdaStep, 파이프라인 밖 시스템의 응답을 기다려야 하면 CallbackStep으로 갈린다.
⚠️ 함정: "정확도가 기준 미달이면 등록하지 않는다"까지는 ConditionStep의 else_steps를 비워두면 끝난다. 하지만 "미달이면 파이프라인을 실패로 표시해 알림이 울려야 한다"까지 요구하면 else_steps=[FailStep(...)]이 필요하다. 조건이 거짓일 때 아무것도 안 하면 실행 상태는 그냥 Succeeded로 끝나 아무도 눈치채지 못한다.
파라미터는 값을 정의 시점에 고정하지 않고 실행 시점에 주입하게 해주는 변수다. 인스턴스 타입·개수, 학습 데이터 경로, 정확도 임계값 같은 값을 파라미터로 빼두면 같은 정의로 여러 환경을 돌릴 수 있다.
파라미터 타입
담는 값
예
ParameterString
문자열
S3 경로, 인스턴스 타입, 승인 상태
ParameterInteger
정수
인스턴스 개수, epoch 수
ParameterFloat
실수
정확도·AUC 임계값
ParameterBoolean
참/거짓
캐싱 사용 여부 같은 토글
from sagemaker.workflow.parameters import ( ParameterString, ParameterInteger, ParameterFloat,)# 실행 시점에 값을 주입할 수 있는 파라미터 선언input_data = ParameterString( name="InputData", default_value="s3://my-bucket/dataset/train.csv",)instance_count = ParameterInteger(name="TrainingInstanceCount", default_value=1)accuracy_threshold = ParameterFloat(name="AccuracyThreshold", default_value=0.85)
실행 시 기본값을 그대로 쓰거나 덮어쓸 수 있다.
# 기본값으로 실행pipeline.start()# 파라미터를 덮어써서 실행 — 같은 정의, 다른 입력pipeline.start(parameters={ "InputData": "s3://my-bucket/dataset/train_v2.csv", "AccuracyThreshold": 0.90,})
⚠️ 함정: 파라미터 객체는 그대로 전달해야 실행 시점 주입이 작동한다. instance_count.default_value처럼 값을 꺼내 쓰면 정의 시점의 숫자가 그대로 구워져 버리고, 실행할 때 아무리 덮어써도 반영되지 않는다. 파라미터를 문자열로 이어 붙이는 것도 같은 이유로 위험하다.
조건 단계는 평가 결과에 따라 파이프라인을 분기시킨다. 대표적인 패턴은 "모델 정확도가 임계값을 넘으면 등록, 아니면 등록하지 않음"이라는 품질 게이트다.
from sagemaker.workflow.conditions import ConditionGreaterThanOrEqualTofrom sagemaker.workflow.condition_step import ConditionStepfrom sagemaker.workflow.functions import JsonGet# 평가 단계가 출력한 evaluation.json에서 accuracy를 읽어 임계값과 비교cond_accuracy = ConditionGreaterThanOrEqualTo( left=JsonGet( step_name=step_eval.name, property_file=evaluation_report, json_path="metrics.accuracy.value", ), right=accuracy_threshold, # ParameterFloat)step_condition = ConditionStep( name="CheckAccuracy", conditions=[cond_accuracy], if_steps=[step_register], # 조건 충족 시: 모델 등록 else_steps=[], # 미충족 시: 아무것도 안 함)
conditions에 여러 조건을 넣으면 모두 참일 때만 if_steps가 실행된다(정확도 ≥ 0.85 그리고 지연 시간 ≤ 100ms 같은 복합 게이트).
JsonGet이 읽는 대상은 평가 단계가 PropertyFile로 선언해 둔 JSON 파일이다. 선언하지 않으면 읽을 수 없다.
조건 종류: ConditionGreaterThanOrEqualTo, ConditionLessThanOrEqualTo, ConditionEquals, ConditionIn, ConditionNot 등.
from sagemaker.workflow.properties import PropertyFile# 평가 단계가 어떤 파일을 "읽을 수 있는 속성 파일"로 내놓는지 선언evaluation_report = PropertyFile( name="EvaluationReport", output_name="evaluation", # ProcessingOutput의 output_name과 일치해야 함 path="evaluation.json", # 컨테이너가 그 경로에 쓴 파일명)
조건 단계로 만드는 품질 게이트는 MLOps의 핵심 사상인 "사람의 수동 판단을 자동화된 검증으로 대체"의 구현이다. 모델이 기준선(baseline)을 넘지 못하면 자동으로 배포 흐름이 멈추므로, 성능 저하 모델의 프로덕션 유입을 코드 레벨에서 차단한다. 시험에서 "정확도가 일정 수준 이상일 때만 등록/배포하도록 자동화"라는 문장이 나오면 ConditionStep이 답이다.
pipeline = Pipeline( name="MLA-DemoPipeline", parameters=[input_data, instance_count, accuracy_threshold], steps=[step_process, step_train, step_eval, step_condition], sagemaker_session=pipeline_session,)# 파이프라인 정의를 SageMaker에 생성 또는 갱신pipeline.upsert(role_arn=role)# 실행 시작execution = pipeline.start()execution.wait() # 완료까지 대기
각 실행은 고유한 execution ARN을 갖고, Studio의 Pipelines 탭에서 DAG와 단계별 상태(성공/실패/진행 중)를 시각적으로 추적한다.
실행 후 단계별 결과는 execution.list_steps()로 확인한다.
단계를 정의할 때는 일반 Session이 아니라 PipelineSession 을 쓴다. 그래야 estimator.fit()이 실제 학습을 즉시 실행하지 않고 "단계 인자"만 만들어 낸다.
파이프라인 디버깅은 "정의 문제인가 실행 문제인가"부터 갈라야 한다. DAG 모양이 이상하면 정의(참조·depends_on) 문제이고, 모양은 맞는데 특정 단계만 죽으면 그 단계가 실행하는 작업(권한·데이터·컨테이너) 문제다. 앞엣것은 pipeline.definition()으로 JSON을 뽑아 보면 바로 드러난다.
내일은 이 파이프라인이 등록한 모델을 버전으로 관리하고 승인을 거쳐 배포로 연결하는 Model Registry를 다룬다.