
MLflow UI는 확인했고 그다음은 MySQL.
MySQL은 웹보다 까다롭다
데이터베이스의 보안 모델이 애플리케이션 포트보다 훨씬 엄격하기 때문
어떻게 알았냐고? 저도 알고싶지 않았습니다,,
MLflow 과정은 이중창이였다면 MySQL 과정은 3중창이라는 뜻
3개중 하나라도 닫혀있다면 바깥바람이 들어올 수 없음

Custom TCP 의 3306 포트를 모든 IPv4의 주소를 열어준다
이제 비유는 안들어도 알겠죠
인스턴스 내의 운영체제(Ubuntu 등)에 설치된 자체 방화벽이다. 1단계 방화벽을 통과한 트래픽이라도 OS 내부 방화벽에 의해 다시 한번 차단될 수 있습니다.

이걸 못찾아서 1시간 낭비했다


처절한 시도의 흔적
sudo ufw allow 3306/tcp
이 ufw 명령어를 통해 3306 포트를 열어줘야한다. 이건 AWS에 연다고 열리는게 아닌가보다.
이것이 MySQL 접속을 가장 복잡하게 만든 근본적인 이유. 웹 서버나 MLflow와 같은 일반 애플리케이션은 1, 2단계를 통과하면 접속이 완료되지만, 데이터베이스는 애플리케이션 자체가 또 다른 두 가지 보안 설정을 가지고 있습니다.
MySQL 서버 자체가 어떤 IP로부터의 접속을 들을지 설정합니다. 기본값은 127.0.0.1(로컬에서만 수신)이므로, 외부 IP 요청은 아예 무시됩니다.

etc/system/~mysqld.cnf 파일에서 bind-address를 #로 주석 처리하거나 0.0.0.0으로 변경 후 재시작한다.
sudo ss -tuln | grep 3306
ubuntu@ip-172-26-11-190:/etc/mysql/mysql.conf.d$ sudo ss -tuln | grep 3306
tcp LISTEN 0 151 0.0.0.0:3306 0.0.0.0:*
tcp LISTEN 0 70 127.0.0.1:33060 0.0.0.0:*
이러면 모든 주소의 3306 포트 접속을 허락하는 것 ! 하지만 여기서 끝이 아니다
서버가 접속을 수신하더라도, 접속을 시도하는 계정(mlops)이 원격 호스트(%)에서 접속할 수 있는 권한이 데이터베이스 내부에 정의되어 있어야 한다.
SELECT user, host FROM mysql.user;

CREATE USER 'mlops'@'%' IDENTIFIED BY '<본인 비밀번호>';
GRANT ALL PRIVILEGES ON *.* TO 'mlops'@'%';
FLUSH PRIVILEGES;

mlops에 host가 전체 주소인 것을 볼 수 있다.
이제 username mlops + 위에서 설정한 비밀번호로 접속

성공

MLOps 의 다양한 메타데이터들을 확인할 수 있게됨.(이 데이터는 mlflow ui test 때 보내졌던 데이터들)
초기 설정은 로컬 어딘가의 mlruns 파일에 저장하는 구조를 가지고 있다고 한다.
모델 가중치 파일 (.pt, .onnx, .h5)
학습 로그, 플롯, 체크포인트
데이터 스냅샷, 피클(.pkl) 파일 등
→ 크기가 크고, 보관 기간이 길며, 주로 파일 스토리지에 저장
- 추천 저장소
- 오브젝트 스토리지 (AWS S3, GCS, Azure Blob)
- 아티팩트 레지스트리 (MLflow Artifact Store, Weights & Biases artifact storage 등)
- 사내용이면 MinIO 같은 self-hosted object store도 많이 씀.
실험 이름, 실행 ID
학습 파라미터 (learning rate, batch size)
성능 지표 (accuracy, loss, F1-score)
태그, 실행 시간, 사용자 정보
→ 크기가 작고, 쿼리/검색이 중요 RDBMS 나 메타스토어에 저장
추천 저장소
- RDBMS (MySQL, PostgreSQL) → MLflow Tracking, Kubeflow Metadata 등
- NoSQL/Document DB (MongoDB, ElasticSearch) → 로그성/비정형 메타데이터 저장
- 필요시 Data Warehouse (BigQuery, Snowflake)로 집계/분석.
성격이 다르기 때문
메타데이터는 “빠른 조회”가 필요 → DB 적합
아티팩트는 “대용량 저장”이 필요 → 파일/오브젝트 스토리지 적합
Experiment = 실험 그룹 단위
1개의 Experiment ID 디렉토리 밑에 여러 개의 Run(실행)이 존재하게 된다.
ex)
Experiment ID = 6
Run1: lr=0.01, acc=0.91
Run2: lr=0.001, acc=0.93
Run3: lr=0.0001, acc=0.89
즉, 실험은 여러 Run을 묶는 상위 개념이에요.
(= "Test1 실험"이라는 폴더 안에 여러 학습 시도가 들어있는 느낌)
각 Run은 고유한 Run ID(UUID)를 가진다 (= 가장 작은 단위 , 한번의 코드 실행).

위에있던 사진
Run 안에서메타데이터 (파라미터, 메트릭, 태그) , 아티팩트 (모델 파일, 로그 등) 가 지정한대로 발생하고 저장된다.
👉 하나의 Run 안에서 mlflow.log_artifact(), mlflow.sklearn.log_model() 같은 걸 하면 해당 Run 폴더에 저장된다.
여기서 “버전(Version)” 개념이 등장합니다.
특정 Run에서 학습된 모델을 Model Registry에 등록(register)하면:
MyModel v1 → Run2에서 나온 모델
MyModel v2 → Run5에서 나온 모델
MyModel v3 → Run9에서 나온 모델
그리고 각각의 버전은 Staging, Production, Archived 같은 상태(State)를 가질 수 있다.
✅ MLOps 기본 저장소 3요소
모델 파일, 체크포인트, 결과 데이터, 파이프라인 산출물 보관
예: S3, GCS, MinIO, MLflow Artifact Store (= MLflow에 연결된 저장소)
진짜 “모델 파일”이 물리적으로 들어가는 곳. 대용량의 파일을 저장하기 위한 저장소
실험 파라미터, metric, 실행 로그, 모델 lineage 관리
예: MLflow Tracking (MySQL/Postgres), Kubeflow Metadata
-> 겨우 요거 하나 함
학습/서빙 시점 모두에서 동일한 feature 제공
예: Feast, Hopsworks, Tecton
장점: feature 재사용, training/serving skew 방지
온라인 저장소를 필요로 함
원천 데이터나 대규모 전처리 결과 저장
Spark, Trino, Iceberg, Delta Lake 같은 포맷/엔진과 연결
ETL/ELT 파이프라인의 기반
모델 서비스 후 latency, drift, error rate, business KPI 로그 수집
Prometheus, ELK Stack, OpenTelemetry + Grafana
모델 레지스트리는 아티팩트 저장소 내부에 존재하는 것은 아님
MyModel:v1, MyModel:v2)Staging, Production, Archived)정리표
| 구분 | Artifact Store | Model Registry | Metadata Store | 겹침 여부 |
|---|---|---|---|---|
| 모델 파일 (.pt, .onnx, .pkl 등) | 저장 O | 직접 저장 X → 경로만 참조 | 저장 X | 겹침 X |
| 체크포인트 | 저장 O | 저장 X | 저장 X | 겹침 X |
| 실험 파라미터 | 저장 X | 저장 일부(O, 주로 summary만) | 저장 O | 겹침 △ (Registry는 핵심만, Metadata Store는 전체) |
| 성능 지표 (accuracy, AUC 등) | 저장 X | 저장 O | 저장 O | 겹침 O |
| 모델 버전/상태 (v1, Staging/Prod 등) | 저장 X | 저장 O | 저장 일부(링크 형태) | 겹침 △ |
| 데이터셋 버전/스키마 | 저장 X | 저장 X | 저장 O | 겹침 X |
| 실험 실행 로그 | 저장 O (로그 파일 형태) | 저장 X | 저장 O (구조화된 메타데이터) | 겹침 △ |
| 파이프라인 실행 lineage | 저장 X | 저장 X | 저장 O | 겹침 X |
| 시각화 결과/이미지 | 저장 O | 저장 X | 저장 X | 겹침 X |