[MLOps ์šด์˜ ๊ณ ๋„ํ™” - 2๋‹จ๊ณ„: Slack Alert ํ†ตํ•ฉ (FastAPIยทAirflow ๊ณต์šฉ)]

์ด ๊ธ€์—์„œ ๋‹ค๋ฃจ๋Š” ๊ฒƒ FastAPI์™€ Airflow๊ฐ€ ํฉ์–ด์ ธ ๋ณด๋‚ด๋˜ ์•Œ๋ฆผ์„ ํ•˜๋‚˜์˜ Slack ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํ†ตํ•ฉํ•˜๊ณ , SealedSecret ๊ธฐ๋ฐ˜์œผ๋กœ Webhook URL์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฃผ์ž…ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์„ ์ˆ˜์ง€์‹ MLOps ์šด์˜ ๊ณ ๋„ํ™” 1๋‹จ๊ณ„: ํ•ซ์Šค์™‘ ๊ณ ๋„ํ™” โ€” /reload ๋ณด์•ˆ ๊ฐ•ํ™”์™€ DAG ์ž๋™ํ™” ์ด ๋‹จ๊ณ„์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๊ฐ€์žฅ ๋ฌด์„œ์šด ๊ฑด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋Š”๋ฐ ์•„๋ฌด๋„ ๋ชจ๋ฅด๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์ด๋‹ค. FastAPI์™€ Airflow๊ฐ€ ๊ฐ์ž ํฉ์–ด์ ธ ๋ณด๋‚ด๋˜ ์•Œ๋ฆผ์„ ํ•˜๋‚˜์˜ Slack ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์™„์ „ํžˆ ํ†ตํ•ฉํ•ด์„œ, ๋ชจ๋ธ ํ•™์Šตยท๋“ฑ๋กยท์„ผ์„œยทํ•ซ์Šค์™‘ยทํ—ฌ์Šค์ฒดํฌ๊นŒ์ง€ ์–ด๋””์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๋„ ์ฆ‰์‹œ ๋ˆˆ์— ๋“ค์–ด์˜ค๋„๋ก ๋งŒ๋“ ๋‹ค. ...

July 26, 2025 ยท 4 min

[MLOps ์šด์˜ ๊ณ ๋„ํ™” - 1๋‹จ๊ณ„: ํ•ซ์Šค์™‘ ๊ณ ๋„ํ™” (/reload ๋ณด์•ˆยทDAG ์ž๋™ํ™”)]

์ด ๊ธ€์—์„œ ๋‹ค๋ฃจ๋Š” ๊ฒƒ /reload ์—”๋“œํฌ์ธํŠธ์— 3์ค‘ ๋ณด์•ˆ(ํ† ํฐยทIP ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธยทTLS)์„ ์ ์šฉํ•˜๊ณ , Airflow DAG์œผ๋กœ ํ•™์Šตโ†’๋“ฑ๋กโ†’๊ฐ์‹œโ†’ํ•ซ์Šค์™‘ ์ „์ฒด ์‹œํ€€์Šค๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” ๊ณผ์ •์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์„ ์ˆ˜์ง€์‹ MLOps ์šด์˜ ๊ณ ๋„ํ™” 0๋‹จ๊ณ„: FastAPI A/BยทCanaryยทBlue-Green ์„œ๋น™ ๋ฒ ์ด์Šค โ€” ๋ฉ€ํ‹ฐ ๋ชจ๋ธ ๋ผ์šฐํŒ… ๊ธฐ๋ฐ˜ ์„ค๊ณ„ ์ด ๋‹จ๊ณ„์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ ๋ชจ๋ธ์„ ์ž˜ ํ•™์Šต์‹œํ‚ค๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ์ž˜๋ชป๋œ ๋ชจ๋ธ์ด ์‹ค์ˆ˜๋กœ ํ•ซ์Šค์™‘๋˜๋Š” ์ˆœ๊ฐ„์ด ๋” ์น˜๋ช…์ ์ด๋‹ค. /reload๋ฅผ ํ™•์‹คํ•˜๊ฒŒ ์ž ๊ทธ๊ณ , ํ•™์Šตโ†’๋“ฑ๋กโ†’๊ฐ์‹œโ†’ํ•ซ์Šค์™‘๊นŒ์ง€๊ฐ€ Airflow์—์„œ ์ž๋™์œผ๋กœ ํ˜๋Ÿฌ๊ฐ€๋„๋ก ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ์ด ๋‹จ๊ณ„๊ฐ€ ์™„์„ฑ๋ผ์•ผ ์ดํ›„ ๋‹จ๊ณ„์—์„œ ์•ˆ์ „ํ•œ ์ž๋™ ์šด์˜(MLOps)์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ...

July 22, 2025 ยท 4 min

[MLOps ์šด์˜ ๊ณ ๋„ํ™” - 0๋‹จ๊ณ„: FastAPI A/BยทCanaryยทBlue-Green ์„œ๋น™ ๋ฒ ์ด์Šค]

์ด ๊ธ€์—์„œ ๋‹ค๋ฃจ๋Š” ๊ฒƒ MLflow Alias ๊ธฐ๋ฐ˜์œผ๋กœ A/BยทCanaryยทBlue-Green ์„ธ ๊ฐ€์ง€ ์„œ๋น™ ์ „๋žต์„ ํ•˜๋‚˜์˜ FastAPI ์•ฑ์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๊ณ , ์ดํ›„ ์šด์˜ ์ž๋™ํ™”์˜ ๊ณตํ†ต ๋ฒ ์ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์„ ์ˆ˜์ง€์‹ [TS] Airflow ๊ธฐ์ดˆ ์ž๋™ํ™” ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… โ€” Airflow โ†’ MLflow โ†’ FastAPI ์—ฐ๋™ ๊ธฐ๋ณธ ํ๋ฆ„ Level 3์€ ์ด ๊ธ€๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋ชจ๋ธ์€ ์–ธ์ œ๋“  ๊ต์ฒด๋  ์ˆ˜ ์žˆ๊ณ , ๊ทธ ์ˆœ๊ฐ„์ด ์„œ๋น„์Šค ํ’ˆ์งˆ์ด ๊ฐ€์žฅ ํฌ๊ฒŒ ํ”๋“ค๋ฆฌ๋Š” ์œ„ํ—˜ ๊ตฌ๊ฐ„์ด๋‹ค. ์ดํ›„ ๋ชจ๋“  ์ž๋™ํ™”(ํ•™์Šตยท๋“ฑ๋กยทํ•ซ์Šค์™‘ยท๋กค๋ฐฑ)์˜ ๊ธฐ๋ฐ˜์ด ๋˜๋Š” A/BยทCanaryยทBlue-Green ์ „๋žต์„ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” FastAPI ์„œ๋น™ ๊ตฌ์กฐ๋ถ€ํ„ฐ ์žก์•„์•ผ ํ•œ๋‹ค. ์ด ๋ผˆ๋Œ€๊ฐ€ ์™„์„ฑ๋ผ์•ผ Airflow, MLflow, ArgoCD์™€ ์—ฐ๊ฒฐ๋œ ์šด์˜ํ˜• MLOps ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ...

July 18, 2025 ยท 5 min

[MLOps ํ”Œ๋žซํผ ๊ตฌ์ถ• : Airflow-MLflow-FastAPI (Helm)]

๐Ÿงฉ ์‹ค์ „ ์‹œ๋‚˜๋ฆฌ์˜ค ๊ธฐ๋ฐ˜ ๊ตฌ์„ฑ ๋ฐฐ๊ฒฝ ์ด ํ”„๋กœ์ ํŠธ๋Š” ๋‹จ์ˆœ ์‹ค์Šต์„ ๋„˜์–ด์„œ, ์‹ค์ œ ๋ฐœ์ƒํ•˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ MLOps ์ธํ”„๋ผ ๊ตฌ์ถ•์„ ๋ชฉํ‘œ๋กœ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ชจ๋ธ ์‹คํ—˜ ๊ฒฐ๊ณผ๊ฐ€ ๋’ค์„ž์—ฌ ์ถ”์ ์ด ์–ด๋ ค์šด ๋ฌธ์ œ โ†’ MLflow Tracking ์„œ๋ฒ„ + PostgreSQL ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ ๊ตฌ์„ฑ ๋ชจ๋ธ ํŒŒ์ผ ๋ฐ ๋กœ๊ทธ๊ฐ€ ๋กœ์ปฌ์—๋งŒ ์ €์žฅ๋˜์–ด ํ˜‘์—… ๋ฐ ์žฌํ˜„์„ฑ์ด ๋–จ์–ด์ง€๋Š” ๋ฌธ์ œ โ†’ S3 ๊ธฐ๋ฐ˜ artifact store ๊ตฌ์„ฑ + pyfunc ๊ธฐ๋ฐ˜ ๋ชจ๋ธ ์„œ๋น™ ๊ตฌ์กฐ ์„ค๊ณ„ ์ˆ˜์ž‘์—… DAG ๋“ฑ๋ก, ๋ชจ๋ธ ๋ฐฐํฌ ๋“ฑ์˜ ๋น„ํšจ์œจ์  ์šด์˜ ๋ฌธ์ œ โ†’ Airflow + GitSync ์—ฐ๋™์œผ๋กœ ํŒŒ์ดํ”„๋ผ์ธ ์ž๋™ํ™” ๋ฐ ๋ฒ„์ „ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ ...

July 15, 2025 ยท 3 min

[MLOps ํ”Œ๋žซํผ ๊ตฌ์ถ• - 6๋‹จ๊ณ„: ์‹ค์‹œ๊ฐ„ ๋ชจ๋ธ ํ•ซ์Šค์™‘ ๊ตฌ์กฐ ์‹คํ—˜]

์ด ๊ธ€์—์„œ ๋‹ค๋ฃจ๋Š” ๊ฒƒ Airflow DAG์—์„œ ์กฐ๊ฑด๋ถ€ ๋ชจ๋ธ ๋“ฑ๋ก ํ›„ FastAPI ํ•ซ์Šค์™‘๊นŒ์ง€ E2E ์ž๋™ํ™” ํ๋ฆ„์„ ์‹คํ—˜ํ•ฉ๋‹ˆ๋‹ค. ์„ ์ˆ˜์ง€์‹ MLOps ํ”Œ๋žซํผ ๊ตฌ์ถ• 5๋‹จ๊ณ„: FastAPI ์„œ๋น™ ๋ฐ ํ•ซ์Šค์™‘ ๊ตฌ์กฐ ๊ตฌ์ถ• โ€” FastAPI ๋ชจ๋ธ ๋กœ๋”ฉ๊ณผ /reload API ๊ตฌ์กฐ ์ด ๋‹จ๊ณ„์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ ๊ฐœ๋ณ„ ์ปดํฌ๋„ŒํŠธ(Airflow, MLflow, FastAPI)๋ฅผ ๊ฐ๊ฐ ๊ตฌ์„ฑํ–ˆ์ง€๋งŒ, ํ•™์Šต๋ถ€ํ„ฐ ์„œ๋น™๊นŒ์ง€์˜ ์ž๋™ํ™” ํ๋ฆ„์„ E2E๋กœ ๊ฒ€์ฆํ•ด์•ผ ์‹ค์ œ ์šด์˜ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ๋Š” Airflow DAG์—์„œ ๋ชจ๋ธ ํ•™์Šต โ†’ ์„ฑ๋Šฅ ๊ธฐ์ค€ ๋ถ„๊ธฐ โ†’ MLflow ๋“ฑ๋ก โ†’ FastAPI ํ•ซ์Šค์™‘๊นŒ์ง€ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์„ ์‹คํ—˜ํ•ฉ๋‹ˆ๋‹ค. ...

July 10, 2025 ยท 2 min

[MLOps ํ”Œ๋žซํผ ๊ตฌ์ถ• - 5๋‹จ๊ณ„: FastAPI ์„œ๋น™: MLflow ๋ชจ๋ธ ์—ฐ๋™ ๋ฐ ํ•ซ์Šค์™‘ ๊ตฌ์กฐ ๊ตฌ์ถ•]

์ด ๊ธ€์—์„œ ๋‹ค๋ฃจ๋Š” ๊ฒƒ MLflow์— ๋“ฑ๋ก๋œ ๋ชจ๋ธ์„ FastAPI๋กœ ์„œ๋น™ํ•˜๊ณ , Stage ๊ธฐ๋ฐ˜ ํ•ซ์Šค์™‘๊ณผ ๋ชจ๋ธ ์ •๋ณด ์กฐํšŒ API๋ฅผ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค. ์„ ์ˆ˜์ง€์‹ MLOps ํ”Œ๋žซํผ ๊ตฌ์ถ• 4๋‹จ๊ณ„: Airflow GitSync + Secret ์—ฐ๋™ โ€” Helm ๋ฐฐํฌ์™€ Secret ๋งˆ์šดํŠธ ํŒจํ„ด ์ด ๋‹จ๊ณ„์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ ๋ชจ๋ธ์„ ์ˆ˜๋™์œผ๋กœ ์„œ๋น™ ์„œ๋ฒ„์— ๋ณต์‚ฌํ•˜๋ฉด ๋ฐฐํฌ ์‹ค์ˆ˜์™€ ๋ฒ„์ „ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. MLflow Registry์—์„œ Stage๋ณ„ ๋ชจ๋ธ์„ ์ž๋™ ๋กœ๋”ฉํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด, ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด ๋ชจ๋ธ์„ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ“ ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์„ฑ๋„ ๐Ÿณ FastAPI ์ปค์Šคํ…€ ์ด๋ฏธ์ง€ Dockerfile FROM python:3.12 WORKDIR /app COPY app /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] requirements.txt fastapi==0.110.2 uvicorn==0.29.0 mlflow==2.13.0 pandas==2.1.4 scikit-learn==1.6.1 pydantic==2.7.1 boto3==1.34.113 numpy==1.26.4 packaging==24.2 psutil==7.0.0 scipy==1.15.3 setuptools==69.5.1 ๋นŒ๋“œ & ํ‘ธ์‹œ docker build -t ghcr.io/hoizz/fastapi-ml:mlflow-model-info . docker push ghcr.io/hoizz/fastapi-ml:mlflow-model-info ๐Ÿ“„ app/main.py (ํ•ต์‹ฌ ๋ถ€๋ถ„) app = FastAPI() model = None model_info = {} def load_model_from_mlflow(): global model, model_info tracking_uri = os.environ.get("MLFLOW_TRACKING_URI") model_name = os.environ.get("MODEL_NAME") model_stage = os.environ.get("MODEL_STAGE", "Production") mlflow.set_tracking_uri(tracking_uri) model_uri = f"models:/{model_name}/{model_stage}" model = mlflow.pyfunc.load_model(model_uri) client = MlflowClient() latest = client.get_latest_versions(name=model_name, stages=[model_stage])[0] model_info = { "model_name": model_name, "stage": model_stage, "version": latest.version, "run_id": latest.run_id, "model_uri": model_uri, } @app.on_event("startup") def startup_event(): load_model_from_mlflow() @app.get("/model-info") def get_model_info(): return model_info @app.post("/predict") async def predict(request: Request): input_data = await request.json() prediction = model.predict(input_data) return {"prediction": prediction.tolist()} ์ „์ฒด ์ฝ”๋“œ: GitHub (main.py) ...

July 7, 2025 ยท 2 min

[Airflow ๊ธฐ์ดˆ ์ž๋™ํ™” - Airflow โ†’ MLflow โ†’ FastAPI]

๐Ÿงญ ์ „์ฒด ํ๋ฆ„ ์˜ˆ์‹œ [AIRFLOW DAG ์‹คํ–‰] โ†“ [train_mlflow.py] - iris ๋ชจ๋ธ ํ•™์Šต - ํŒŒ๋ผ๋ฏธํ„ฐ/๋ฉ”ํŠธ๋ฆญ ๋กœ๊น… - ๋ชจ๋ธ Registry ๋“ฑ๋ก โ†“ [promote_mlflow.py] - ์ตœ์‹  ๋ชจ๋ธ์„ Production์œผ๋กœ ์ „ํ™˜ โ†“ [FastAPI] - models:/IrisModel/Production โ†’ ์‹ค์‹œ๊ฐ„ ์˜ˆ์ธก ๐Ÿ‘‰ ์‹ค์Šต ์ฝ”๋“œ๋Š” ๐Ÿ”— GitHub (Airflow + MLflow + FastAPI) โœ… [1๋‹จ๊ณ„] ํ”„๋กœ์ ํŠธ ๊ธฐ๋ณธ ํด๋” ๊ตฌ์กฐ ์„ค๊ณ„ ๐Ÿ“ 1. ์ „์ฒด ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์„ฑ๋„ mlops_project/ โ”œโ”€โ”€ airflow/ ๐Ÿ›ซ Airflow ์„ค์ • ๋ฐ DAG ์Šค์ผ€์ค„๋Ÿฌ โ”‚ โ”œโ”€โ”€ dags/ โ† DAG ์ •์˜ ๋””๋ ‰ํ† ๋ฆฌ โ”‚ โ”‚ โ””โ”€โ”€ train_with_mlflow.py โ† ํ•™์Šต DAG (MLflow ์—ฐ๋™) โ”‚ โ”œโ”€โ”€ Dockerfile.airflow โ† Airflow์šฉ Dockerfile โ”‚ โ”œโ”€โ”€ requirements.txt โ† Airflow ์˜์กด์„ฑ โ”‚ โ””โ”€โ”€ .dockerignore โ”‚ โ”œโ”€โ”€ fastapi/ โšก FastAPI ์˜ˆ์ธก API ์„œ๋ฒ„ โ”‚ โ”œโ”€โ”€ app/ โ”‚ โ”‚ โ””โ”€โ”€ main.py โ† ๋ชจ๋ธ ์„œ๋น™ ์—”๋“œํฌ์ธํŠธ โ”‚ โ”œโ”€โ”€ Dockerfile.api โ† FastAPI์šฉ Dockerfile โ”‚ โ”œโ”€โ”€ requirements.txt โ† FastAPI ์˜์กด์„ฑ โ”‚ โ””โ”€โ”€ .dockerignore โ”‚ โ”œโ”€โ”€ ml_code/ ๐Ÿง  ML ํ•™์Šต ๋ฐ ํ”„๋กœ๋ชจ์…˜ ์ฝ”๋“œ โ”‚ โ”œโ”€โ”€ train_mlflow.py โ† ๋ชจ๋ธ ํ•™์Šต ๋ฐ MLflow ๋กœ๊น… โ”‚ โ””โ”€โ”€ promote_mlflow.py โ† ๋ชจ๋ธ ํ”„๋กœ๋ชจ์…˜ (Staging โ†’ Production) โ”‚ โ”œโ”€โ”€ mlflow_store/ ๐Ÿ—‚๏ธ MLflow ์ €์žฅ์†Œ ๊ฒฝ๋กœ (๋ณผ๋ฅจ) โ”‚ โ”œโ”€โ”€ Dockerfile.mlflow โ† MLflow ์„œ๋ฒ„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• โ”‚ โ”œโ”€โ”€ mlflow.db โ† Model Registry DB (sqlite) โ”‚ โ”œโ”€โ”€ mlruns/ โ† ์‹คํ—˜ ๋กœ๊ทธ ๋””๋ ‰ํ† ๋ฆฌ โ”‚ โ”œโ”€โ”€ artifacts/ โ† ๋ชจ๋ธ ์•„ํ‹ฐํŒฉํŠธ ์ €์žฅ์†Œ โ”‚ โ””โ”€โ”€ .dockerignore โ”‚ โ”œโ”€โ”€ docker-compose.yaml ๐Ÿงฉ ์ „์ฒด ์„œ๋น„์Šค ๊ตฌ์„ฑ ์ •์˜ โ”œโ”€โ”€ .env ๐Ÿ” ๋ฏผ๊ฐ ์ •๋ณด (.env๋กœ ๋ถ„๋ฆฌ) โ”œโ”€โ”€ README.md ๐Ÿ“ ์ „์ฒด ํ”„๋กœ์ ํŠธ ๋ฌธ์„œํ™” โ”œโ”€โ”€ .gitignore โ””โ”€โ”€ .dockerignore โœ… [2๋‹จ๊ณ„] docker-compose.yaml ํ†ตํ•ฉ ๊ตฌ์„ฑ ๐Ÿงญ ๊ตฌ์„ฑ ๋ชฉํ‘œ ์„œ๋น„์Šค๋ช… ์„ค๋ช… ํฌํŠธ airflow DAG ์‹คํ–‰ ํ™˜๊ฒฝ (webserver/scheduler) 8080 postgres Airflow ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ์šฉ DB ๋‚ด๋ถ€ ํ†ต์‹  mlflow MLflow UI + Registry ๊ธฐ๋Šฅ 5000 fastapi ์ถ”๋ก  API ์„œ๋ฒ„ (๋ชจ๋ธ ๋กœ๋”ฉ) 8000 ์ด๋ฏธ์ง€ ์‚ฌ์šฉ์‹œ ์ฃผ์˜ (UI๋งŒ ์ œ๊ณตํ•˜๋Š” ์ด๋ฏธ์ง€ ์กด์žฌ) ๐Ÿ“„ docker-compose.yaml ์ „์ฒด ์˜ˆ์‹œ version: '3.8' services: # ๐Ÿ“ฆ PostgreSQL: Airflow ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ์šฉ DB postgres: image: postgres:13 container_name: postgres env_file: - .env # โ† ๋ฏผ๊ฐ์ •๋ณด ๋ถ„๋ฆฌ (์•„์ด๋””/๋น„๋ฒˆ) environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} volumes: # โ† ์ฝ”๋“œ/๋ฐ์ดํ„ฐ ๊ณต์œ  ๋ฐ ์˜์†์„ฑ ๋ณด์žฅ - postgres_data:/var/lib/postgresql/data # โ† DB ๋ฐ์ดํ„ฐ ์œ ์ง€ (์žฌ์‹œ์ž‘ ๋Œ€๋น„) # ๐Ÿ›ซ Airflow: DAG ์Šค์ผ€์ค„๋Ÿฌ ๋ฐ ํƒœ์Šคํฌ ์‹คํ–‰ airflow: build: context: ./airflow # โ†’ Airflow ์ „์šฉ Dockerfile ๊ฒฝ๋กœ dockerfile: Dockerfile.airflow container_name: airflow command: standalone # โ†’ ๋กœ์ปฌ ํ…Œ์ŠคํŠธ์šฉ ๊ฐ„๋‹จ ์‹คํ–‰ ๋ช…๋ น # (- Scheduler + Webserver + DB ์ดˆ๊ธฐํ™”๊นŒ์ง€ ์ž๋™์œผ๋กœ ํ•œ๋ฒˆ์— ์‹คํ–‰) # (- ์‹ค๋ฌด/์šด์˜์—์„œ๋Š” airflow-webserver, airflow-scheduler ํ•„๋“œ ๋ถ„๋ฆฌ) ports: - "8080:8080" # โ†’ Airflow ์›น UI (localhost:8080) depends_on: - postgres # โ†’ DB๊ฐ€ ๋จผ์ € ์˜ฌ๋ผ์™€์•ผ Airflow ์‹œ์ž‘ ๊ฐ€๋Šฅ env_file: - .env environment: # Airflow ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ DB ์—ฐ๊ฒฐ ์ฃผ์†Œ AIRFLOW__CORE__SQL_ALCHEMY_CONN: ${AIRFLOW__CORE__SQL_ALCHEMY_CONN} # Airflow ์˜ˆ์ œ DAG ๋ถˆ๋Ÿฌ์˜ฌ์ง€ ์—ฌ๋ถ€ AIRFLOW__CORE__LOAD_EXAMPLES: ${AIRFLOW__CORE__LOAD_EXAMPLES} MLFLOW_TRACKING_URI: http://mlflow:5000 # โ†’ DAG ์ฝ”๋“œ์—์„œ MLflow ์—ฐ๋™ volumes: - ./airflow/dags:/opt/airflow/dags # DAG ํŒŒ์ผ mount - ./ml_code:/opt/airflow/ml_code # ํ•™์Šต ์ฝ”๋“œ ๊ณต์œ  - ./mlflow_store:/mlflow # ๋ชจ๋ธ ์ €์žฅ์†Œ ๊ณต์œ  # ๐Ÿ”ฌ MLflow: ์‹คํ—˜ ์ถ”์  + ๋ชจ๋ธ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์„œ๋ฒ„ mlflow: build: context: ./mlflow_store # ์ปค์Šคํ…€ Dockerfile ์œ„์น˜ dockerfile: Dockerfile.mlflow ports: - "5000:5000" # โ†’ MLflow UI (localhost:5000) volumes: - ./mlflow_store:/mlflow # ์‹คํ—˜ ๋กœ๊ทธ + DB + artifacts ์ €์žฅ environment: - MLFLOW_TRACKING_URI=http://0.0.0.0:5000 # ๋‚ด๋ถ€ ์ปจํ…Œ์ด๋„ˆ ๊ธฐ์ค€ URI # โšก FastAPI: ๋ชจ๋ธ ์„œ๋น™ API fastapi: build: context: ./fastapi dockerfile: Dockerfile.api container_name: fastapi ports: - "8000:8000" # โ†’ ์˜ˆ์ธก API ์—”๋“œํฌ์ธํŠธ (localhost:8000) volumes: - ./fastapi/app:/app/app # FastAPI app ๋””๋ ‰ํ† ๋ฆฌ mount - ./ml_code:/app/ml_code # ํ•™์Šต/๋ชจ๋ธ ์ฝ”๋“œ ๊ณต์œ  - ./mlflow_store:/mlflow # ์ €์žฅ๋œ ๋ชจ๋ธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•œ mount # ๐Ÿ—‚๏ธ ๋ณผ๋ฅจ ์ •์˜ (Postgres DB ์˜์†์„ฑ ์œ ์ง€) volumes: postgres_data: ๐ŸŽ ์ถ”๊ฐ€๋กœ ํ•ด์•ผ ํ•  ๊ฒƒ Airflow ์ฒซ ์‹คํ–‰ ํ›„์—” ๋ณดํ†ต ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ƒ์„ฑ๋„ ํ•ด์ค˜์•ผ ํ•จ: # airflow ์ปจํ…Œ์ด๋„ˆ ์ ‘์† docker exec -it airflow bash # ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ƒ์„ฑ airflow users create \ --username airflow \ --password airflow \ --firstname Keoho \ --lastname Ban \ --role Admin \ --email airflow@example.com ๐Ÿ” [๊ตฌ์ถ• Tip] Airflow, FastAPI, MLflow ๊ฐ„ ๊ณต์œ  ๋ณผ๋ฅจ ๊ตฌ์กฐ ํ™•์ธ ๊ณต์œ  ๋ฆฌ์†Œ์Šค ์„ค๋ช… ./mlflow_store:/mlflow (MLflow) MLflow ์„œ๋ฒ„๊ฐ€ ์“ฐ๋Š” ๋กœ๊ทธ/๋ชจ๋ธ ์ €์žฅ์†Œ ./mlflow_store:/mlflow (Airflow) ํ•™์Šต ํ›„ ๋ชจ๋ธ ์ €์žฅ ์œ„์น˜ ๊ณต์œ  ./mlflow_store:/mlflow (FastAPI) ๋ชจ๋ธ ์ถ”๋ก  ์‹œ ๋กœ๋“œ ๊ฒฝ๋กœ ๊ณต์œ  โžก ๊ฒฝ๋กœ ํ†ต์ผ์„ฑ์ด ์ค‘์š”ํ•จ! ์ง€๊ธˆ์€ ๋ชจ๋‘ ./mlflow๋กœ ๊ณต์œ  (./mlflow ํ•˜์œ„์— /mlruns ์กด์žฌ) ...

June 13, 2025 ยท 8 min

[MLflow : Tracking + FastAPI ์—ฐ๋™]

๋ชฉํ‘œ MLflow Tracking Server ๊ตฌ์„ฑ ์‹คํ—˜(Experiment), ํŒŒ๋ผ๋ฏธํ„ฐ, ๋ฉ”ํŠธ๋ฆญ, ์•„ํ‹ฐํŒฉํŠธ ๊ธฐ๋ก ๋ชจ๋ธ ๋“ฑ๋ก โ†’ Stage ์ด๋™ โ†’ API ์—ฐ๋™๊นŒ์ง€ ๐Ÿ‘‰ ์‹ค์Šต ์ฝ”๋“œ๋Š” ๐Ÿ”— GitHub (Mlflow - Tracking + FastAPI) ๐Ÿงญ ์‹ค์Šต ์ „์ฒด ํ๋ฆ„ ์š”์•ฝ [1๋‹จ๊ณ„] MLflow Tracking Server ๊ตฌ์„ฑ (๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰) [2๋‹จ๊ณ„] ์‹คํ—˜ ์‹คํ–‰ (train.py) โ†’ ๋ชจ๋ธ ํ•™์Šต, ๊ธฐ๋ก [3๋‹จ๊ณ„] ๋ชจ๋ธ ๋“ฑ๋ก ๋ฐ Stage ์„ค์ • (Production ์ด๋™) [4๋‹จ๊ณ„] FastAPI ์—ฐ๋™ โ†’ ์˜ˆ์ธก API ์„œ๋น„์Šค ๐Ÿงฉ ์‹ค์Šต ๋””๋ ‰ํ† ๋ฆฌ ์˜ˆ์‹œ mlops-mlflow/ โ”œโ”€โ”€ app/ โ”‚ โ”œโ”€โ”€ train.py # ๋ชจ๋ธ ํ›ˆ๋ จ ๋ฐ ์‹คํ—˜ ๊ธฐ๋ก โ”‚ โ””โ”€โ”€ model.pkl # ์ €์žฅ๋œ ๋ชจ๋ธ โ”œโ”€โ”€ mlruns/ # ์‹คํ—˜ ๋ฐ์ดํ„ฐ ์ž๋™ ์ƒ์„ฑ โ”œโ”€โ”€ fastapi_app/ โ”‚ โ””โ”€โ”€ app.py # FastAPI ์˜ˆ์ธก API โ”œโ”€โ”€ Dockerfile (์„ ํƒ) โ””โ”€โ”€ README.md โœ… [1๋‹จ๊ณ„] MLflow ์„ค์น˜ & ์‹คํ–‰ ๐Ÿ› ๏ธ ๊ฐ€์ƒ ํ™˜๊ฒฝ ์„ค์ • # 1. venv ์„ค์น˜ sudo apt install python3-venv -y # 2. ๊ฐ€์ƒํ™˜๊ฒฝ ์ƒ์„ฑ python3 -m venv .venv # 3. ๊ฐ€์ƒํ™˜๊ฒฝ ํ™œ์„ฑํ™” source .venv/bin/activate # 4. ํŒจํ‚ค์ง€ ์„ค์น˜ pip install mlflow scikit-learn pandas fastapi uvicorn # 5. ๋‚˜๊ฐˆ ๋•Œ deactivate ๐Ÿ”ง MLflow ์„œ๋ฒ„ ์‹คํ–‰ mlflow ui --port 5000 # http://localhost:5000 ์—์„œ UI ํ™•์ธ ๐Ÿงช [2๋‹จ๊ณ„] ์‹คํ—˜ ์‹คํ–‰ (train.py) # app/train.py import mlflow import mlflow.sklearn from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # MLflow ์„ค์ • mlflow.set_tracking_uri("http://localhost:5000") mlflow.set_experiment("iris-rf-exp") with mlflow.start_run(): iris = load_iris() X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2) clf = RandomForestClassifier(n_estimators=100, max_depth=3) clf.fit(X_train, y_train) acc = clf.score(X_test, y_test) mlflow.log_param("n_estimators", 100) mlflow.log_param("max_depth", 3) mlflow.log_metric("accuracy", acc) mlflow.sklearn.log_model(clf, "model") # ์‹คํ—˜ ์‹คํ–‰ python app/train.py ์‹คํ—˜์ด ๋๋‚˜๋ฉด mlruns/ ํด๋”์— ์‹คํ—˜ ๊ธฐ๋ก ๋ฐ ๋ชจ๋ธ์ด ์ €์žฅ ...

June 6, 2025 ยท 3 min