프로젝트 2주차 요약
Mlflow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
apiVersion: apps/v1
kind: Deployment
metadata:
name: mlflow
spec:
replicas: 1
selector:
matchLabels:
app: mlflow
template:
metadata:
labels:
app: mlflow
spec:
nodeSelector:
type: "mlops" # 여기서 레이블 지정
containers:
- name: mlflow
image: bitnami/mlflow:latest
ports:
- containerPort: 8080
command: ["mlflow", "server",
#"--backend-store-uri", "postgresql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@postgres:5432/mlops", # PostgreSQL URI, 실험, 모델, 매트릭 저장
"--default-artifact-root", "$(MLFLOW_S3_ARTIFACT_ROOT)", # S3 버킷 경로
"--host", "0.0.0.0",
"--port", "8080"] # MLflow 서버 내부 컨테이너 연결 포트
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: workflow
key: POSTGRES_USER # PostgreSQL 사용자
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: workflow
key: POSTGRES_PASSWORD # PostgreSQL 비밀번호
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: workflow
key: AWS_ACCESS_KEY_ID
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: workflow
key: AWS_SECRET_ACCESS_KEY
- name: MLFLOW_S3_ARTIFACT_ROOT
valueFrom:
secretKeyRef:
name: workflow
key: MLFLOW_S3_ARTIFACT_ROOT
resources: # 제한 안하면 주황줄 끄임
requests:
memory: "512Mi" # 요청 메모리
cpu: "500m" # 요청 CPU
limits:
memory: "1Gi" # 제한 메모리
cpu: "1" # 제한 CPU
---
apiVersion: v1
kind: Service
metadata:
name: mlflow
spec:
type: NodePort
ports:
- port: 8080 # 클러스터 내부 포트
targetPort: 8080
nodePort: 30003 # 클러스터 외부 접근 포트
selector:
app: mlflow
S3 연결
export MLFLOW_S3_ARTIFACT_ROOT=s3://team06-mlflow-feature(연결할 버킷 이름)
쿠버 삭제하고 다시 설치하면 사라지는 설정이므로 다시 입력이 필요함
S3 연결을 위해 AWS 액세서 ID, key 포함한 secret 생성
1
kubectl create secret generic 지정한이름 --from-env-file=.env
DB(Postgresql) 연결
1
"--backend-store-uri", "postgresql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@postgres:5432/mlops"
postgresql:// : postgre 쓰겠다고 명시하는 부분
/mlops : DB 이름 지정
Mlflow 시각화, 성능 테스트, 모델 레지스트리 및 배포 자동화
Tracking API
mlflow.start_run(): 이 함수를 통해 새로운 실행(run)을 함. 훈련 중에 사용한 하이퍼파라미터, 훈련 데이터, 모델 아키텍처 등의 정보를 기록 가능함.
mlflow.log_metric(): 훈련 중에 얻은 성능 지표(예: 정확도, 손실 값)를 기록. 여러 지표를 기록해서 모델의 성능을 종합적으로 평가할 수 있음.
mlflow.log_artifact(): 훈련된 모델 파일, 그래프, 데이터 시각화등을 이 함수로 저장 가능함.
모델 레지스트리
1
2
3
# 모델 레지스트리에 등록
model_uri = f"runs:/{mlflow.active_run().info.run_id}/{model_path}"
mlflow.register_model(model_uri, "IrisModel")
테스트용으로 만든 간단한 모델의 코드 일부.
같은 이름으로 모델을 지정하면 mlflow가 자동으로 버저닝을 해준다.
FastAPI
- mlflow: 10.103.73.87
mlflow service에서 지정된 클러스터 ip. mlflow service를 삭제하지 않는 이상 ip는 고정됨.
아래는 FastAPI앱 코드 전문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import mlflow
import mlflow.pyfunc
import numpy as np
app = FastAPI()
# MLflow tracking URI 및 실험 설정
mlflow.set_tracking_uri("http://10.103.73.87:8080") # service cluster ip
mlflow.set_experiment("testjun") # 실험 이름으로 변경
class InputData(BaseModel):
model_name: str # 모델 이름 추가
version: int # 모델 버전 추가
features: list
@app.post("/predict")
def predict(data: InputData):
try:
# 모델 URI 설정 (모델 이름과 버전)
model_uri = f"models:/{data.model_name}/{data.version}" # 동적으로 모델 URI 생성
model = mlflow.pyfunc.load_model(model_uri) # 모델 로드
# 입력 데이터 형태 변경
features = np.array(data.features).reshape(1, -1)
# 예측 수행
prediction = model.predict(features)
# 결과 반환
return {
"prediction": int(prediction[0])
}
except ValueError:
raise HTTPException(status_code=400, detail="잘못된 입력 데이터입니다. 입력을 확인하세요.")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
모델 URI 설정 할 때 주의사항
설정 방법
1
model_uri = f"models:/{data.model_name}/{data.version}"
웹에서의 url은 아래와 같은데
1
http://3.39.68.171:30003/#/models/IrisModel/versions/2
모델 이름과 버전 숫자 사이에 versions라는 경로가 존재하지만 API 호출 할 때는 넣으면 경로 오류가 생긴다.
이유는 mlflow 모델 URI가 아래 형식을 따르기 때문.
1
models:/<model_name>/<version>
고로 url에서 보이는 경로는 모델을 보여주기 위한 ui 요소일 뿐 API 호출과는 별개의 것이다.
실행 명령어 및 사용법
서버 실행 명령어
1
2
3
4
uvicorn test_api:app --host 0.0.0.0 --port 8000 --reload
# 백그라운드 실행
nohup uvicorn test_api:app --host 0.0.0.0 --port 8000 --reload &
접속 url
1
2
#인스턴스1 공인 아이피
http://3.39.68.171:8000/docs
사용법
1
2
3
4
5
6
7
{
"model_name": "모델 이름",
"version": 버전,
"features": [
넣을 값
]
}
인스턴스 내부 python 라이브러리 목록 자동갱신
코드 전문
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
from importlib.metadata import distributions import sys # 현재 파이썬이 설치된 경로 sys.path.append('/home/ubuntu/.pyenv/shims/python3') # 현재 설치된 패키지 메타데이터를 반환 패키지 이름을 키로, 버전을 값으로 가지게 됨 installed_packages = {dist.metadata['Name']: dist.version for dist in distributions()} # requirements.txt 파일 경로를 설정 requirements_file = '/home/ubuntu/yeardream-miniproject/instance1/crontab/requirements.txt' # w 쓰기 모드로 설치된 버전을 설정한 형식으로 저장 try: with open(requirements_file, 'w') as f: for package, version in installed_packages.items(): f.write(f"{package}=={version}\n") print(f"{requirements_file}가 성공적으로 업데이트되었습니다.") except Exception as e: print(f"오류 발생: {e}")
importlib.metadata: 이 모듈은 Python 패키지의 메타데이터에 접근할 수 있게 해준다.
crontab을 이용한 라이브러리 목록 업데이트 자동화
1
30 17 * * * /home/ubuntu/.pyenv/shims/python3 /home/ubuntu/yeardream-miniproject/instance1/crontab/update_requirements.py >> /home/ubuntu/yeardream-miniproject/instance1/crontab/logs/requirements/req_log_$(date +%Y-%m-%d).log 2>&1
하루에 한 번만 오후 5시 30분에 실행하게 설정.
파이썬 파일로 작성했기 때문에 실행하기 위해 파이썬 인터프리터 경로를 지정해줌.
나머지는 지정한 경로에 그날 날짜를 이름으로 한 log 파일이 생성되게 설정함.
쿠버네티스 마스터 노드에 워커 join 오류
1
2
3
4
error execution phase preflight: [preflight] Some fatal errors occurred:
[ERROR FileAvailable--etc-kubernetes-kubelet.conf]: /etc/kubernetes/kubelet.conf already exists
[ERROR Port-10250]: Port 10250 is in use
[ERROR FileAvailable--etc-kubernetes-pki-ca.crt]: /etc/kubernetes/pki/ca.crt already exists
파일 다 삭제해주고 kubelt이 사용중인 포트를 내려도 해결이 안됨.
이 오류가 일어나는 경우는 크게 두 가지다.
이전에 쿠버네티스 클러스터의 일부로 설정되다 제거된 노드를 재추가하려 할 때.
노드에 쿠버네티스를 설치하는 과정에서 실패하거나 중단된 후 재시도하려 할 때.
여러가지 해결책이 존재했지만 실제로 유효했던건 아래 명령어다.
1
sudo kubeadm reset
현 노드의 쿠버 관련 설정을 모두 지워주는 명령어다.
이후 다시 join을 시도하면 정상적으로 실행된다.
에러 핸들링 추가