프로젝트 간단 개요
이번 프로젝트는 2024년 9월 26일부터 2024년 10월 11일까지 약 2주간 다섯 명이 함께 진행한 MLOps 프로젝트다. 목표는 모델링뿐만 아니라 MLflow와 Airflow를 활용해 자동화된 플로우를 구축하는 것이었다.
데이터 설명
우리가 사용한 데이터는 Kaggle에서 제공하는 spotify_songs.csv 파일로, 약 30,000곡의 데이터와 음악의 다양한 피처가 포함된 데이터셋이다.
이 데이터를 기반으로 최초 모델을 학습시켜, 군집화(clustering) 알고리즘을 통해 유사한 음악을 추천하는 모델을 개발하기로 했다.
Clustering Algorithm 사용
추천 시스템에서 보편적으로 사용하는 알고리즘에는 필터링(Filtering)과 군집화(Clustering)가 있는데, 우리는 Clustering Algorithm을 선택했다. 그 이유는 다음과 같다:
- 여러 특성을 동시에 고려해 데이터 포인트를 자동으로 그룹화할 수 있기 때문이다.
- 우리가 다룬 데이터셋은 약 30,000곡의 데이터와 10개 이상의 피처가 있었기 때문에, 이러한 데이터를 빠르고 효율적으로 처리할 수 있는 알고리즘이 필요했다.
- 또한, 군집화 알고리즘이 직관적이고, 대규모 데이터를 처리하는 데 적합하다고 판단했다.
팀 내에서 DS를 전공한 분이 모델링을 주로 담당해 주셨고, 나는 MLflow와 Airflow를 통한 자동화 작업에 집중할 수 있었다.
그리고 Data Drift 데이터 드리프트 현상 을 방지하기 위해서 매주 spotify에서 신곡을 크롤링해 와서 신곡 데이터도 주기적으로 수집해 오는 api도 개발하려고 했다. 이 부분은 다른 팀원분이 맡아서 진행해 주셨다.
MLFlow 적용
MLflow를 적용하는 것이 이번 프로젝트의 첫 번째 핵심이었다. 실제 모델링 부분은 팀원이 담당했으므로, 나는 학습 코드 대신 가짜 코드로 구조를 만들고, MLflow로 모델 학습 과정을 추적하는 데 집중했다.
처음에는 Jupyter 노트북에서 MLflow를 적용해 전체 플로우를 만든 후, 실제 모델링 코드에 적용했다. 이 과정에서 여러 파이썬 파일에 나뉘어 있는 코드라 하더라도, 하나의 run 안에 있으면 모든 파일에서 mlflow.log_param을 통해 추적이 가능하다는 사실을 확인할 수 있었다.
문제 발생 1 - Docker로 MLFlow 실행 오류
프로젝트 초기에 로컬에서 MLflow UI를 실행하려고 했을 때 문제가 발생했다. 명령어를 입력했음에도 실행되지 않아, 이를 해결하기 위해 MLflow를 Docker로 실행하기로 했다. 이에 대한 문제는 이전 글에서 상세히 다루었으니 참고하면 좋을 것 같다. 결국 MLflow를 Docker로 실행하는 데 성공했고, 이를 통해 모델 추적이 가능해졌다.
Docker로 mlflow 실행할 때 OSError: [Errno 30] Read-only file system: '/mlflow' 에러 발생
이렇게 mlflow를 도커로 실행하기에 성공했다!!
Airflow 적용
MLflow 적용 이후, Airflow를 통해 자동화 플로우를 구축했다. 수업에서 배운 방법을 바탕으로 Airflow를 Docker로 실행하고, MLflow와 연동하여 자동화 환경을 설정했다.
최초 DAG(Task)은 다음과 같이 구성했다:
- Task 1: 팀원이 진행한 신곡 크롤링 및 기존 데이터셋에 추가하는 작업
- Task 2: 신곡이 추가된 데이터셋을 기반으로 모델을 재학습하는 작업
이 DAG를 실행하려고 했을 때 외부 파이썬 코드 접근 문제가 발생했다.
문제발생 2 - airflow에서 외부 파이썬 함수 호출
우선 Dag 파일을 만들고 scripts 디렉터리에 있는 기존 파이썬 코드들의 함수를 불러와서 실행하려고 했다.
일단 프로젝트 구조는 다음과 같다.
.
├── docker-compose.yml
├── Dockerfile.airflow
├── Dockerfile.mlflow
├── README.md
├── dags
│ ├── model_pipeline_dag.py
├── scripts
│ ├── load_model_data.py (학습 완료된 모델과 데이터셋 로드)
│ ├── model.py (모델 호출)
│ ├── preprocess.py (데이터셋 전처리)
│ ├── train_model.py (모델 학습)
│ ├── spotify_api.py (spotify api 호출해서 신곡 데이터 추가)
├── data
│ ├── spotify_songs.csv (raw data인 데이터)
│ ├── train.csv (모델 학습할때 전처리 완료된 데이터셋)
├── model
│ ├── kmeans_model.pkl
├── mlartifacts
├── mlflow
├── mlruns
├── tmp
사실상 dags 폴더만 더 추가해 줌으로써 airflow 구조를 만들었다고 생각했다.
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
from datetime import datetime
from scripts.load_model import load_or_train_model
from scripts.spotify_api import fetch_new_song()
def train_model():
# 모델 훈련 및 MLflow 로깅
load_or_train_model()
def fetch_new_data():
fetch_new_song()
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime(2023, 10, 1),
'retries': 1,
}
with DAG(
'model_pipeline',
default_args=default_args,
description='A simple model training and serving DAG',
schedule_interval='@daily',
catchup=False,
) as dag:
t1 = PythonOperator(
task_id='train_model',
python_callable=train_model,
)
t2 = PythonOperator(
task_id='fetch_new_data',
python_callable=fetch_new_data,
)
t1 >> t2
이렇게 프로젝트 트리를 만들고 실행하니까 Broken DAG 에러가 났다.
Broken DAG: [/usr/local/airflow/dags/model_pipeline_dag.py]
Traceback (most recent call last):
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/usr/local/airflow/dags/model_pipeline_dag.py", line 4, in <module>
from scripts.load_model import load_or_train_model
ModuleNotFoundError: No module named 'scripts'
뭐가 문제인지 몰라서 이것저것 찾아보니까 Airflow가 scripts 디렉토리 안의 모듈을 찾지 못해서 발생한 이슈였다.
Airflow의 기본 Python 환경에서는 scripts가 현재 작업 디렉토리에 포함되어 있지 않기 때문에, Airflow가 해당 모듈을 찾을 수 없다. Airflow에서 DAG이 동작하는 기본 경로는 보통 dags 폴더이기 때문에, dags 폴더 외부에 있는 파일을 참조하려면 다른 설정을 해줘야 한다.
✅ 여기서 말하는 기본 경로라 하면, Dockerfile에 정의된 ENV AIRFLOW_HOME=/usr/local/airflow 이 경로를 뜻한다!
이걸 해결하는 방법은 여러 가지가 있었다.
- PYTHONPATH 설정
- sys.path 수정
- Airflow 플러그인으로 구성
등 여러가지 방법이 있었는데 쉽게 해결되지 않았다. ㅜㅜㅜ 그래서 일단은 가장 간단한 해결방법인 scripts, data, model 디렉토리를 dags 안으로 옮기는 방법이었다. 이렇게 하니까 적용은 되었다.
그런데 이 방법 말고 더 근본적인 해결방법이 있지 않을까 하다가 DockerOperator라는 걸 알게 되었다.
(어차피 우리는 spotify_api를 호출해서 신곡을 크롤링하고 데이터셋을 업데이트하는 전 과정을 도커로 올리려고 했었다.)
✅ 그리고 이 뒤에 여러 가지 이유로 Dockerfile에서 ENV AIRFLOW_HOME=/app으로 수정해 줬다. 이후 코드에서 이 부분을 감안해 주면 좋을 것 같다.
DockerOperator
DockerOperator는 Airflow에서 도커 컨테이너를 이용해 작업을 실행할 수 있도록 도와주는 도구다. 이 방법을 사용하면 각 작업을 독립된 도커 컨테이너에서 실행할 수 있어 환경 설정 문제나 의존성 문제를 격리할 수 있다. 특히 외부 Python 스크립트를 실행할 때, 복잡한 환경 설정을 해결하는 데 유리하다.
from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.providers.docker.operators.docker import DockerOperator
from datetime import datetime
from scripts.train_model import train_model
def retrain_model():
# 모델 훈련 및 MLflow 로깅
train_model()
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime(2023, 10, 1),
'retries': 1,
}
with DAG(
'model_pipeline',
default_args=default_args,
description='A simple model training and serving DAG',
schedule_interval='@weekly',
catchup=False,
max_active_runs=1,
) as dag:
t1 = DockerOperator(
task_id='fetch_spotify_data',
image='ml-project-mlops_5-spotify_api', # 빌드된 도커 이미지 이름
auto_remove=True,
command="python /app/dags/scripts/spotify_api.py", # 스크립트를 실행하는 명령어
docker_url='unix://var/run/docker.sock',
network_mode='bridge',
mount_tmp_dir=False
)
t2 = PythonOperator(
task_id='retrain_model',
python_callable=retrain_model,
)
t1 >> t2
Airflow에서 DockerOperator를 설정하려면 먼저 도커 데몬에 접근할 수 있어야 한다. 이를 위해 docker-compose.yml에 다음과 같은 설정을 추가해줘야 한다.
- services:
airflow:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
이 설정은 도커 컨테이너 내에서 호스트의 도커 데몬에 접근할 수 있도록 해준다. 이렇게 해야 Airflow가 도커 컨테이너를 생성하고 실행할 수 있다.
문제발생 3 - Airflow에만 모델, 데이터셋 저장
근데 Airflow와 Spotify API가 각각 도커 컨테이너에서 실행되었고, Spotify API에서 수집된 데이터가 Airflow 도커 내에만 저장되는 문제가 발생했다. 이로 인해 FastAPI나 로컬 시스템에서도 데이터를 사용할 수 있도록 경로 문제를 해결해야 했다.
이를 해결하기 위해 로컬 경로와 도커 볼륨을 연결하는 방법을 사용했다. 모든 컨테이너가 동일한 로컬 디렉토리를 공유하도록 설정해, 수집된 데이터와 학습된 모델이 FastAPI에서도 접근 가능하도록 만들었다.
Airflow, Spotify API, FastAPI 컨테이너가 모두 동일한 로컬 디렉토리(./dags/data, ./mlartifacts 등)를 공유할 수 있도록 설정되어 있다. 이를 위해 docker-compose.yml 파일에서 각각의 서비스가 동일한 로컬 디렉토리를 마운트 하도록 설정했다.
services:
airflow:
build:
context: .
dockerfile: Dockerfile.airflow
container_name: airflow
ports:
- "8080:8080"
volumes:
- ./dags:/app/dags
- ./tmp:/tmp
- /var/run/docker.sock:/var/run/docker.sock
environment:
- AIRFLOW__CORE__LOAD_EXAMPLES=False
networks:
- app_network
command: >
bash -c "airflow db migrate && sleep 5 && airflow users create --username admin --firstname cho --lastname sungsu --role Admin --email ski06043@gmail.com --password 1234 && airflow webserver & airflow scheduler"
mlflow:
build:
context: .
dockerfile: Dockerfile.mlflow
ports:
- "5000:5000"
volumes:
- ./mlflow:/mlflow
- ./mlartifacts:/mlartifacts
environment:
- MLFLOW_TRACKING_URI=http://0.0.0.0:5000
- MLFLOW_BACKEND_STORE_URI=sqlite:///mlflow/db/mlflow.db
command: >
mlflow server --backend-store-uri sqlite:///mlflow.db --default-artifact-root ./mlartifacts --host 0.0.0.0 --port 5000
networks:
- app_network
spotify_api:
build:
context: .
dockerfile: Dockerfile.spotify_api
container_name: spotify_api
volumes:
- ./dags/data:/app/dags/data
environment:
- SPOTIFY_CLIENT_ID=your_client_id
- SPOTIFY_CLIENT_SECRET=your_client_secret
- SPOTIFY_REDIRECT_URI=http://localhost:8000/callback
command: >
/bin/bash -c "while true; do sleep 1; done"
networks:
- app_network
networks:
app_network:
driver: bridge
이렇게 하니까 로컬 경로에도 모델과 데이터셋이 잘 저장되는 걸 확인할 수 있었다!!
FastAPI, Streamlit 실행
Airflow, MLflow, Spotify API와 더불어 FastAPI와 Streamlit도 도커로 실행하기로 결정했다. 각 서비스가 /app/dags 디렉토리를 공유하도록 설정해, FastAPI와 Streamlit에서도 데이터를 접근할 수 있도록 했다. 이를 통해 FastAPI와 Streamlit 모두 원활하게 작동하는 것을 확인했다.
이렇게 전부 도커로 띄우고 /app/dags 디렉토리도 전부 공유하게 해 줬다.
이렇게 Fastapi도 잘 확인했고, Streamlit도 잘 적용이 된 걸 확인했다!
결론
이 프로젝트를 진행하면서 아쉬웠던 부분은 MLflow에서도 직접 모델을 저장할 수 있는데, 이 과정에서 에러가 발생하여 현재는 모델을 직접 .pkl 파일로 로컬에 저장하는 방식으로 관리하고 있다는 점이다. 이 문제를 해결하면 다음 글에서 이를 정리해보려고 한다.
또한, MLflow를 사용해 모델을 배포할 때 staging, production, archived 등의 태그로 모델을 관리할 수 있다. 하지만 이번 Airflow 자동화 단계에서는 이를 적용하지 못한 점이 아쉬웠다. 추후 이 기능을 포함해 더 완벽한 모델 관리 체계를 구축할 계획이다.
또한, 다섯 개의 서비스를 모두 도커로 실행하다 보니 각각의 컨테이너에서 경로 문제도 자주 발생했다. 나중에 도커 환경에 더 익숙해지면 이러한 문제를 더 빠르게 해결할 수 있을 거라고 생각한다.
이번 프로젝트는 도커와 Airflow를 사용한 자동화 과정에서 많은 부분을 배우고 개선할 기회가 있었으며, 다음 단계에서는 MLflow와 모델 배포 관련 부분을 더욱 심도 있게 다룰 예정이다.
'MLOps' 카테고리의 다른 글
쿠버네티스 환경 구축 및 주요 구성 (0) | 2024.11.19 |
---|---|
[MLflow] 로컬 파일 시스템에 기록된 실험 데이터 확인 방법 (0) | 2024.11.02 |
Docker로 mlflow 실행할때 OSError: [Errno 30] Read-only file system: '/mlflow' 에러 발생 (5) | 2024.10.09 |
Docker 환경에서 Airflow의 DAGs 테스트 (파이썬 코드, Slack Webhook, MLFlow) (3) | 2024.09.26 |
FastAPI와 Docker를 활용한 S3 모델 서빙 및 배포 방법 (5) | 2024.09.24 |