Update mlflow REAMD.md
This commit is contained in:
@@ -0,0 +1,84 @@
|
|||||||
|
# mlflow 버전 갱신 가이드
|
||||||
|
|
||||||
|
> **카탈로그 업데이트 방식**: 기존 버전 디렉토리는 유지하고, 신규 버전 디렉토리를 새로 생성한다.
|
||||||
|
> `manifests/helm/mlflow/<new-version>/` 디렉토리를 직접 추가하는 방식으로 관리한다.
|
||||||
|
|
||||||
|
## 1. git 작업 환경 구성
|
||||||
|
|
||||||
|
- dip-catalog git 다운로드
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/paasup/dip-catalog.git
|
||||||
|
```
|
||||||
|
|
||||||
|
- 작업 브랜치로 체크아웃
|
||||||
|
```sh
|
||||||
|
git checkout -b update-mlflow/<new-version>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. helm chart 업데이트
|
||||||
|
|
||||||
|
### 1) 기존 버전 디렉토리 복사
|
||||||
|
|
||||||
|
신규 버전 디렉토리를 기존 버전에서 복사하여 시작한다.
|
||||||
|
`BUILD-README.md`, `CUSTOM-README.md`, `custom-values.yaml`, `README.md`가 함께 복사된다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd ~/dip-catalog/manifests/helm/mlflow
|
||||||
|
|
||||||
|
# 기존 버전에서 신규 버전 디렉토리 복사
|
||||||
|
cp -r 1.9.0 <new-version>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2) 업스트림 차트 파일 업데이트
|
||||||
|
|
||||||
|
신규 버전 디렉토리에서 업스트림 차트 파일만 교체한다.
|
||||||
|
`BUILD-README.md`, `CUSTOM-README.md`, `custom-values.yaml`, `README.md`는 유지한다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd ~/dip-catalog/manifests/helm/mlflow
|
||||||
|
|
||||||
|
# helm repo 추가
|
||||||
|
helm repo add community-charts https://community-charts.github.io/helm-charts
|
||||||
|
helm repo update
|
||||||
|
|
||||||
|
# 신규 버전 차트 다운로드 후 압축 해제
|
||||||
|
helm pull community-charts/mlflow --version="<new-version>"
|
||||||
|
tar xzvf mlflow-<new-version>.tgz -C <new-version> --strip-components=1
|
||||||
|
|
||||||
|
# 불필요한 파일 삭제
|
||||||
|
rm mlflow-<new-version>.tgz
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. git push 및 tag 추가
|
||||||
|
|
||||||
|
- 갱신 작업 진행 후 commit
|
||||||
|
```sh
|
||||||
|
git add .
|
||||||
|
git commit -m "update mlflow/1.9.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
- main 브랜치에 체크아웃 후 merge
|
||||||
|
```sh
|
||||||
|
git checkout main
|
||||||
|
git merge update-mlflow/1.9.0
|
||||||
|
```
|
||||||
|
|
||||||
|
- git에 push 후 작업 브랜치 삭제
|
||||||
|
```sh
|
||||||
|
git push -u origin main
|
||||||
|
git branch -d update-mlflow/1.9.0
|
||||||
|
```
|
||||||
|
|
||||||
|
- git tag 추가 후 push
|
||||||
|
```sh
|
||||||
|
git tag mlflow/1.9.0
|
||||||
|
git push origin mlflow/1.9.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 차트 버전 정보
|
||||||
|
|
||||||
|
- mlflow/1.9.0
|
||||||
|
- Chart version: 1.9.0
|
||||||
|
- App version: 3.11.1
|
||||||
|
- 커스텀 이미지: `paasup/mlflow:v3.11.1-oidc` (mlflow-oidc-auth v7.0.3 포함)
|
||||||
|
- 업스트림: https://github.com/community-charts/helm-charts
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
# MLflow 배포 (OIDC Auth + Multi-Tenant Workspace)
|
||||||
|
|
||||||
|
MLflow 3.11.1 + mlflow-oidc-auth v7.0.3을 Keycloak과 연동하고, 팀별 workspace로 멀티테넌시를 구성하는 가이드이다.
|
||||||
|
커스텀 이미지 `paasup/mlflow:v3.11.1-oidc`에 OIDC 플러그인이 포함되어 있다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 사전 준비
|
||||||
|
|
||||||
|
### 1.1 Keycloak 설정
|
||||||
|
|
||||||
|
#### Client 생성
|
||||||
|
|
||||||
|
- Realm: `paasup`, Client ID: `mlflow`, Protocol: `openid-connect`
|
||||||
|
- Access Type: `confidential`
|
||||||
|
- Valid Redirect URIs: `https://mlflow.example.org/*`
|
||||||
|
- Web Origins: `https://mlflow.example.org`
|
||||||
|
|
||||||
|
#### Groups Mapper 추가
|
||||||
|
|
||||||
|
Client → Mappers → Create:
|
||||||
|
|
||||||
|
| 항목 | 값 |
|
||||||
|
|------|----|
|
||||||
|
| Mapper type | `Group Membership` |
|
||||||
|
| Token Claim Name | `groups` |
|
||||||
|
| Full group path | `false` |
|
||||||
|
| Add to ID token | `true` |
|
||||||
|
| Add to access token | `true` |
|
||||||
|
| Add to userinfo | `true` ← 반드시 true |
|
||||||
|
|
||||||
|
#### Groups 생성
|
||||||
|
|
||||||
|
| 그룹명 | 역할 |
|
||||||
|
|--------|------|
|
||||||
|
| `mlflow` | **모든 사용자 필수 소속** — 미소속 시 로그인 거부 |
|
||||||
|
| `mlflow-admin` | 관리자 권한 |
|
||||||
|
| `mlflow-team-<팀명>` | workspace 매핑용 팀 그룹 (예: `mlflow-team-ds`, `mlflow-team-mlops`) |
|
||||||
|
|
||||||
|
사용자는 반드시 `mlflow` 그룹과 소속 팀 그룹 **모두에 추가**해야 한다.
|
||||||
|
|
||||||
|
```
|
||||||
|
예시:
|
||||||
|
mlflow-test → mlflow, mlflow-team-ds
|
||||||
|
mlflow-ops-test → mlflow, mlflow-team-mlops
|
||||||
|
mlflow-admin → mlflow, mlflow-admin
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.2 Keycloak CA 인증서 ConfigMap 생성
|
||||||
|
|
||||||
|
Keycloak이 사설 CA 인증서를 사용하는 경우 필수이다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Keycloak TLS secret에서 CA 인증서 추출
|
||||||
|
kubectl get secret keycloak.example.org-tls -n platform \
|
||||||
|
-o jsonpath='{.data.ca\.crt}' | base64 -d > /tmp/keycloak-ca.crt
|
||||||
|
|
||||||
|
# ConfigMap 생성
|
||||||
|
kubectl create configmap keycloak-ca-cert -n mlflow \
|
||||||
|
--from-file=ca.crt=/tmp/keycloak-ca.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.3 OIDC Secret 생성
|
||||||
|
|
||||||
|
`SECRET_KEY`는 uvicorn 멀티워커 환경에서 세션 공유를 위해 반드시 포함해야 한다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
FERNET_KEY=$(python3 -c "import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())")
|
||||||
|
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
|
||||||
|
|
||||||
|
kubectl create secret generic mlflow-oidc-secret -n mlflow \
|
||||||
|
--from-literal=OIDC_CLIENT_SECRET="<keycloak-client-secret>" \
|
||||||
|
--from-literal=MLFLOW_WEBHOOK_SECRET_ENCRYPTION_KEY="$FERNET_KEY" \
|
||||||
|
--from-literal=SECRET_KEY="$SECRET_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
Keycloak Client Secret은 Keycloak Admin Console → Client → Credentials 탭에서 확인한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.4 Workspace Detection Plugin ConfigMap 생성
|
||||||
|
|
||||||
|
팀 그룹(`mlflow-team-*`)을 workspace 이름으로 변환하는 Python 플러그인을 ConfigMap으로 배포한다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# mlflow_workspace_detector.py
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
|
||||||
|
def get_user_workspaces(access_token: str) -> list:
|
||||||
|
"""
|
||||||
|
JWT access_token의 groups 클레임에서 mlflow-team-* 그룹을 읽어
|
||||||
|
workspace 이름 목록을 반환한다.
|
||||||
|
|
||||||
|
예: ["mlflow-team-ds", "mlflow"] → ["team-ds"]
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
payload = access_token.split(".")[1]
|
||||||
|
payload += "=" * (4 - len(payload) % 4)
|
||||||
|
claims = json.loads(base64.b64decode(payload))
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
groups = claims.get("groups", [])
|
||||||
|
return [
|
||||||
|
g[len("mlflow-"):]
|
||||||
|
for g in groups
|
||||||
|
if g.startswith("mlflow-team-")
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl create configmap mlflow-workspace-plugin -n mlflow \
|
||||||
|
--from-file=mlflow_workspace_detector.py=mlflow_workspace_detector.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 배포 방법
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/paasup/dip-catalog.git
|
||||||
|
cd dip-catalog
|
||||||
|
|
||||||
|
# 신규 설치
|
||||||
|
helm install mlflow manifests/helm/mlflow/1.9.0/ \
|
||||||
|
-f manifests/helm/mlflow/1.9.0/custom-values.yaml \
|
||||||
|
-n mlflow --create-namespace
|
||||||
|
|
||||||
|
# 업그레이드
|
||||||
|
helm upgrade mlflow manifests/helm/mlflow/1.9.0/ \
|
||||||
|
-f manifests/helm/mlflow/1.9.0/custom-values.yaml \
|
||||||
|
-n mlflow
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. custom-values.yaml 설명
|
||||||
|
|
||||||
|
### 3.1 이미지 설정
|
||||||
|
|
||||||
|
OIDC 플러그인이 포함된 커스텀 이미지를 사용한다.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image:
|
||||||
|
repository: paasup/mlflow
|
||||||
|
tag: "v3.11.1-oidc"
|
||||||
|
|
||||||
|
initImages:
|
||||||
|
mlflowDbMigration:
|
||||||
|
repository: paasup/mlflow
|
||||||
|
tag: "v3.11.1-oidc"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 OIDC 환경변수
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extraEnvVars:
|
||||||
|
# --- 기본 설정 ---
|
||||||
|
MLFLOW_S3_ENDPOINT_URL: "http://minio.minio.svc.cluster.local:9000"
|
||||||
|
MLFLOW_S3_IGNORE_TLS: "true"
|
||||||
|
SSL_CERT_FILE: "/etc/ssl/certs/custom-ca.crt"
|
||||||
|
|
||||||
|
# --- OIDC 설정 ---
|
||||||
|
OIDC_CLIENT_ID: "mlflow"
|
||||||
|
OIDC_DISCOVERY_URL: "https://keycloak.example.org/realms/paasup/.well-known/openid-configuration"
|
||||||
|
OIDC_REDIRECT_URI: "https://mlflow.example.org/callback"
|
||||||
|
OIDC_SCOPE: "openid email profile"
|
||||||
|
OIDC_GROUPS_ATTRIBUTE: "groups"
|
||||||
|
OIDC_GROUP_NAME: "mlflow"
|
||||||
|
OIDC_ADMIN_GROUP_NAME: "mlflow-admin"
|
||||||
|
OIDC_USERS_DB_URI: "postgresql://mlflow:mlflow1234@mlflow-postgresql:5432/mlflow"
|
||||||
|
DEFAULT_MLFLOW_PERMISSION: "READ"
|
||||||
|
AUTOMATIC_LOGIN_REDIRECT: "true"
|
||||||
|
OIDC_ALEMBIC_VERSION_TABLE: "mlflow_oidc_alembic_version"
|
||||||
|
```
|
||||||
|
|
||||||
|
**주의사항**
|
||||||
|
|
||||||
|
| 항목 | 올바른 값 | 잘못된 값 | 이유 |
|
||||||
|
|------|-----------|-----------|------|
|
||||||
|
| `SSL_CERT_FILE` | `SSL_CERT_FILE` | `REQUESTS_CA_BUNDLE` | mlflow-oidc-auth는 httpx를 사용하며 httpx는 `REQUESTS_CA_BUNDLE`을 인식하지 않음 |
|
||||||
|
| `OIDC_SCOPE` | `"openid email profile"` | `"openid,email,profile"` | OAuth2 RFC 6749 표준: 스코프는 공백으로 구분 |
|
||||||
|
| `OIDC_REDIRECT_URI` | `.../callback` | `.../oidc/callback` | auth_router에 prefix가 없어 실제 경로는 `/callback` |
|
||||||
|
| `OIDC_ALEMBIC_VERSION_TABLE` | `"mlflow_oidc_alembic_version"` | 기본값(`alembic_version`) | MLflow와 mlflow-oidc-auth가 동일한 테이블 사용 시 마이그레이션 충돌 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 Workspace 환경변수
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extraEnvVars:
|
||||||
|
# --- Workspace 설정 ---
|
||||||
|
MLFLOW_ENABLE_WORKSPACES: "true"
|
||||||
|
OIDC_WORKSPACE_DEFAULT_PERMISSION: "EDIT"
|
||||||
|
OIDC_WORKSPACE_DETECTION_PLUGIN: "mlflow_workspace_detector"
|
||||||
|
PYTHONPATH: "/opt/mlflow-plugins"
|
||||||
|
WORKSPACE_CACHE_MAX_SIZE: "1024"
|
||||||
|
WORKSPACE_CACHE_TTL_SECONDS: "300"
|
||||||
|
PERMISSION_SOURCE_ORDER: "user,group,regex,group-regex"
|
||||||
|
```
|
||||||
|
|
||||||
|
| 항목 | 설명 |
|
||||||
|
|------|------|
|
||||||
|
| `MLFLOW_ENABLE_WORKSPACES` | workspace 기능 활성화 |
|
||||||
|
| `OIDC_WORKSPACE_DEFAULT_PERMISSION` | workspace 첫 접근 시 자동 부여 권한. `EDIT` 설정 시 팀원 전원 편집 가능 |
|
||||||
|
| `OIDC_WORKSPACE_DETECTION_PLUGIN` | workspace 감지 플러그인 모듈명 (함수명 미포함, 모듈명만 지정) |
|
||||||
|
| `PYTHONPATH` | ConfigMap으로 마운트된 플러그인 경로를 Python import 경로에 추가 |
|
||||||
|
| `WORKSPACE_CACHE_MAX_SIZE` | workspace 목록 캐시 최대 항목 수 |
|
||||||
|
| `WORKSPACE_CACHE_TTL_SECONDS` | workspace 목록 캐시 TTL (초) |
|
||||||
|
| `PERMISSION_SOURCE_ORDER` | 권한 판단 순서: 개인 → 그룹 → 정규식 → 그룹-정규식 |
|
||||||
|
|
||||||
|
**플러그인 주의사항**
|
||||||
|
|
||||||
|
- `OIDC_WORKSPACE_DETECTION_PLUGIN`에는 **모듈명만** 지정한다. (`mlflow_workspace_detector.get_user_workspaces` 형태 불가)
|
||||||
|
- 호출되는 함수 이름은 `get_user_workspaces`로 고정되어 있다.
|
||||||
|
- 함수 인자로 전달되는 `access_token`은 **JWT 문자열**이며, 플러그인 내부에서 base64 디코딩이 필요하다.
|
||||||
|
- 플러그인 오류 발생 시 exception이 조용히 처리되어 workspace 목록이 빈 배열로 반환된다. 로그에서 `WARNING` 레벨로 확인 가능하다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4 OIDC App 활성화
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extraArgs:
|
||||||
|
appName: "oidc-auth"
|
||||||
|
uvicornOpts: "--timeout-keep-alive 600"
|
||||||
|
allowedHosts: "mlflow.example.org"
|
||||||
|
|
||||||
|
log:
|
||||||
|
enabled: false # uvicornOpts 사용 시 반드시 false (gunicorn/uvicorn 충돌 방지)
|
||||||
|
|
||||||
|
auth:
|
||||||
|
enabled: false # mlflow-oidc-auth가 자체 인증 처리
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.5 Secret 참조
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extraSecretNamesForEnvFrom:
|
||||||
|
- mlflow-oidc-secret # OIDC_CLIENT_SECRET, SECRET_KEY, MLFLOW_WEBHOOK_SECRET_ENCRYPTION_KEY 포함
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.6 볼륨 마운트
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extraVolumes:
|
||||||
|
- name: keycloak-ca-cert
|
||||||
|
configMap:
|
||||||
|
name: keycloak-ca-cert
|
||||||
|
- name: workspace-plugin
|
||||||
|
configMap:
|
||||||
|
name: mlflow-workspace-plugin
|
||||||
|
|
||||||
|
extraVolumeMounts:
|
||||||
|
- name: keycloak-ca-cert
|
||||||
|
mountPath: /etc/ssl/certs/custom-ca.crt
|
||||||
|
subPath: ca.crt
|
||||||
|
readOnly: true
|
||||||
|
- name: workspace-plugin
|
||||||
|
mountPath: /opt/mlflow-plugins
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.7 OIDC Auth Middleware 패치
|
||||||
|
|
||||||
|
`mlflow-oidc-auth` 플러그인의 `auth_middleware.py`를 차트에 포함된 버전으로 교체한다.
|
||||||
|
워크스페이스 지원(`x-mlflow-workspace` 헤더 처리) 등 업스트림 수정 사항을 반영한다.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
oidcAuthPatch:
|
||||||
|
enabled: true
|
||||||
|
mountPath: "/usr/local/lib/python3.11/site-packages/mlflow_oidc_auth/middleware/auth_middleware.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
파일 소스: `files/auth_middleware.py`
|
||||||
|
|
||||||
|
> **Python 버전 확인**: 컨테이너 이미지의 Python 버전이 다를 경우 `mountPath`를 수정한다.
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> kubectl exec -n mlflow <pod> -- python -c \
|
||||||
|
> "import mlflow_oidc_auth.middleware.auth_middleware as m; print(m.__file__)"
|
||||||
|
> ```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.8 Ingress 설정
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: "kong"
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: "selfsigned-issuer"
|
||||||
|
cert-manager.io/duration: 8760h
|
||||||
|
cert-manager.io/renew-before: 720h
|
||||||
|
hosts:
|
||||||
|
- host: mlflow.example.org
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
tls:
|
||||||
|
- secretName: mlflow-tls-secret
|
||||||
|
hosts:
|
||||||
|
- mlflow.example.org
|
||||||
|
```
|
||||||
|
|
||||||
|
`mlflow.example.org`를 실제 도메인으로 변경한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.9 PostgreSQL 설정
|
||||||
|
|
||||||
|
내장 PostgreSQL을 사용한다.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
postgresql:
|
||||||
|
enabled: true
|
||||||
|
auth:
|
||||||
|
username: mlflow
|
||||||
|
password: mlflow1234 # 변경 권장
|
||||||
|
database: mlflow
|
||||||
|
image:
|
||||||
|
repository: bitnamilegacy/postgresql
|
||||||
|
primary:
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
```
|
||||||
|
|
||||||
|
`OIDC_USERS_DB_URI`도 동일한 접속 정보를 사용한다.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extraEnvVars:
|
||||||
|
OIDC_USERS_DB_URI: "postgresql://mlflow:mlflow1234@mlflow-postgresql:5432/mlflow"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.10 S3 (MinIO) 설정
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
artifactRoot:
|
||||||
|
proxiedArtifactStorage: true
|
||||||
|
defaultArtifactsDestination: "s3://mlflow/artifacts"
|
||||||
|
s3:
|
||||||
|
enabled: true
|
||||||
|
bucket: mlflow
|
||||||
|
path: artifacts
|
||||||
|
awsAccessKeyId: "adminuser" # MinIO access key
|
||||||
|
awsSecretAccessKey: "adminuser" # MinIO secret key
|
||||||
|
|
||||||
|
extraEnvVars:
|
||||||
|
MLFLOW_S3_ENDPOINT_URL: "http://minio.minio.svc.cluster.local:9000"
|
||||||
|
MLFLOW_S3_IGNORE_TLS: "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
외부 MinIO 사용 시 `MLFLOW_S3_ENDPOINT_URL`을 해당 엔드포인트로 변경한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 배포 검증
|
||||||
|
|
||||||
|
### 4.1 OIDC 인증 검증
|
||||||
|
|
||||||
|
1. `https://mlflow.example.org` 접속 → Keycloak 로그인 페이지로 자동 리다이렉트 확인
|
||||||
|
2. `mlflow` 그룹 사용자로 로그인 → MLflow UI 정상 진입 확인
|
||||||
|
3. `mlflow-admin` 그룹 사용자로 로그인 → 관리자 메뉴 접근 확인
|
||||||
|
4. `mlflow` 그룹 미소속 사용자 로그인 → 접근 거부 확인
|
||||||
|
|
||||||
|
### 4.2 Workspace 격리 검증
|
||||||
|
|
||||||
|
| 사용자 | 소속 그룹 | 기대 workspace |
|
||||||
|
|--------|-----------|----------------|
|
||||||
|
| `mlflow-test` | `mlflow`, `mlflow-team-ds` | `team-ds` |
|
||||||
|
| `mlflow-ops-test` | `mlflow`, `mlflow-team-mlops` | `team-mlops` |
|
||||||
|
|
||||||
|
1. `mlflow-test`로 로그인 → `team-ds` workspace 자동 생성 확인
|
||||||
|
2. `mlflow-ops-test`로 로그인 → `team-mlops` workspace 자동 생성 확인
|
||||||
|
3. 각 사용자가 상대방 workspace의 실험·모델에 접근 불가 확인
|
||||||
@@ -1,390 +1,114 @@
|
|||||||
# MLflow 배포 (OIDC Auth + Multi-Tenant Workspace)
|
# MLflow Helm Chart
|
||||||
|
|
||||||
MLflow 3.11.1 + mlflow-oidc-auth v7.0.3을 Keycloak과 연동하고, 팀별 workspace로 멀티테넌시를 구성하는 가이드이다.
|
MLflow는 머신러닝 실험 추적, 모델 패키징, 배포를 위한 오픈소스 플랫폼이다.
|
||||||
커스텀 이미지 `paasup/mlflow:v3.11.1-oidc`에 OIDC 플러그인이 포함되어 있다.
|
|
||||||
|
- **Chart version**: 1.9.0
|
||||||
|
- **App version**: 3.11.1
|
||||||
|
- **Upstream**: [community-charts/helm-charts](https://github.com/community-charts/helm-charts)
|
||||||
|
- **PaaSup 커스텀 이미지**: `paasup/mlflow:v3.11.1-oidc` (mlflow-oidc-auth v7.0.3 포함)
|
||||||
|
|
||||||
|
> 실제 배포 방법 및 custom-values.yaml 설명은 [CUSTOM-README.md](CUSTOM-README.md)를 참고한다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. 사전 준비
|
## 주요 파라미터
|
||||||
|
|
||||||
### 1.1 Keycloak 설정
|
### 이미지
|
||||||
|
|
||||||
#### Client 생성
|
| 파라미터 | 설명 | 기본값 |
|
||||||
|
|---------|------|--------|
|
||||||
- Realm: `paasup`, Client ID: `mlflow`, Protocol: `openid-connect`
|
| `image.repository` | MLflow 이미지 저장소 | `paasup/mlflow` |
|
||||||
- Access Type: `confidential`
|
| `image.tag` | 이미지 태그 | `v3.11.1-oidc` |
|
||||||
- Valid Redirect URIs: `https://mlflow.example.org/*`
|
| `image.pullPolicy` | 이미지 pull 정책 | `IfNotPresent` |
|
||||||
- Web Origins: `https://mlflow.example.org`
|
| `initImages.dbchecker.tag` | DB 연결 확인 init 컨테이너 태그 | `1.37` |
|
||||||
|
| `initImages.mlflowDbMigration.repository` | DB 마이그레이션 init 컨테이너 이미지 | `paasup/mlflow` |
|
||||||
#### Groups Mapper 추가
|
| `initImages.iniFileInitializer.tag` | ini 파일 초기화 init 컨테이너 태그 | `1.37` |
|
||||||
|
| `replicaCount` | Pod 복제 수 | `1` |
|
||||||
Client → Mappers → Create:
|
|
||||||
|
|
||||||
| 항목 | 값 |
|
|
||||||
|------|----|
|
|
||||||
| Mapper type | `Group Membership` |
|
|
||||||
| Token Claim Name | `groups` |
|
|
||||||
| Full group path | `false` |
|
|
||||||
| Add to ID token | `true` |
|
|
||||||
| Add to access token | `true` |
|
|
||||||
| Add to userinfo | `true` ← 반드시 true |
|
|
||||||
|
|
||||||
#### Groups 생성
|
|
||||||
|
|
||||||
| 그룹명 | 역할 |
|
|
||||||
|--------|------|
|
|
||||||
| `mlflow` | **모든 사용자 필수 소속** — 미소속 시 로그인 거부 |
|
|
||||||
| `mlflow-admin` | 관리자 권한 |
|
|
||||||
| `mlflow-team-<팀명>` | workspace 매핑용 팀 그룹 (예: `mlflow-team-ds`, `mlflow-team-mlops`) |
|
|
||||||
|
|
||||||
사용자는 반드시 `mlflow` 그룹과 소속 팀 그룹 **모두에 추가**해야 한다.
|
|
||||||
|
|
||||||
```
|
|
||||||
예시:
|
|
||||||
mlflow-test → mlflow, mlflow-team-ds
|
|
||||||
mlflow-ops-test → mlflow, mlflow-team-mlops
|
|
||||||
mlflow-admin → mlflow, mlflow-admin
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 1.2 Keycloak CA 인증서 ConfigMap 생성
|
### 백엔드 스토어
|
||||||
|
|
||||||
Keycloak이 사설 CA 인증서를 사용하는 경우 필수이다.
|
| 파라미터 | 설명 | 기본값 |
|
||||||
|
|---------|------|--------|
|
||||||
```sh
|
| `backendStore.databaseMigration` | 시작 시 DB 마이그레이션 실행 여부 | `false` |
|
||||||
# Keycloak TLS secret에서 CA 인증서 추출
|
| `backendStore.databaseConnectionCheck` | DB 연결 확인 init 컨테이너 활성화 | `false` |
|
||||||
kubectl get secret keycloak.example.org-tls -n platform \
|
| `backendStore.postgres.enabled` | 외부 PostgreSQL 사용 여부 | `false` |
|
||||||
-o jsonpath='{.data.ca\.crt}' | base64 -d > /tmp/keycloak-ca.crt
|
| `backendStore.mysql.enabled` | 외부 MySQL 사용 여부 | `false` |
|
||||||
|
| `postgresql.enabled` | 내장 Bitnami PostgreSQL 배포 여부 | `false` |
|
||||||
# ConfigMap 생성
|
| `postgresql.auth.database` | PostgreSQL 데이터베이스 이름 | `mlflow` |
|
||||||
kubectl create configmap keycloak-ca-cert -n mlflow \
|
|
||||||
--from-file=ca.crt=/tmp/keycloak-ca.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 1.3 OIDC Secret 생성
|
### Artifact 스토리지
|
||||||
|
|
||||||
`SECRET_KEY`는 uvicorn 멀티워커 환경에서 세션 공유를 위해 반드시 포함해야 한다.
|
| 파라미터 | 설명 | 기본값 |
|
||||||
|
|---------|------|--------|
|
||||||
```sh
|
| `artifactRoot.proxiedArtifactStorage` | MLflow 서버를 통한 프록시 아티팩트 접근 활성화 | `false` |
|
||||||
FERNET_KEY=$(python3 -c "import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())")
|
| `artifactRoot.defaultArtifactsDestination` | 기본 아티팩트 저장 경로 | `./mlartifacts` |
|
||||||
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
|
| `artifactRoot.s3.enabled` | S3 아티팩트 스토리지 사용 여부 | `false` |
|
||||||
|
| `artifactRoot.s3.bucket` | S3 버킷 이름 | `""` |
|
||||||
kubectl create secret generic mlflow-oidc-secret -n mlflow \
|
| `artifactRoot.s3.awsAccessKeyId` | S3 액세스 키 | `""` |
|
||||||
--from-literal=OIDC_CLIENT_SECRET="<keycloak-client-secret>" \
|
| `artifactRoot.s3.awsSecretAccessKey` | S3 시크릿 키 | `""` |
|
||||||
--from-literal=MLFLOW_WEBHOOK_SECRET_ENCRYPTION_KEY="$FERNET_KEY" \
|
| `artifactRoot.azureBlob.enabled` | Azure Blob Storage 사용 여부 | `false` |
|
||||||
--from-literal=SECRET_KEY="$SECRET_KEY"
|
| `artifactRoot.gcs.enabled` | Google Cloud Storage 사용 여부 | `false` |
|
||||||
```
|
|
||||||
|
|
||||||
Keycloak Client Secret은 Keycloak Admin Console → Client → Credentials 탭에서 확인한다.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 1.4 Workspace Detection Plugin ConfigMap 생성
|
### 인증
|
||||||
|
|
||||||
팀 그룹(`mlflow-team-*`)을 workspace 이름으로 변환하는 Python 플러그인을 ConfigMap으로 배포한다.
|
| 파라미터 | 설명 | 기본값 |
|
||||||
|
|---------|------|--------|
|
||||||
```python
|
| `auth.enabled` | MLflow 기본 인증 활성화 | `false` |
|
||||||
# mlflow_workspace_detector.py
|
| `auth.adminUsername` | 기본 관리자 계정명 | `admin` |
|
||||||
import base64
|
| `auth.adminPassword` | 기본 관리자 비밀번호 | `password` |
|
||||||
import json
|
| `extraArgs.appName` | MLflow 앱 플러그인 이름 (`oidc-auth` 설정 시 OIDC 활성화) | `""` |
|
||||||
|
| `extraArgs.uvicornOpts` | uvicorn 추가 옵션 | `""` |
|
||||||
def get_user_workspaces(access_token: str) -> list:
|
| `extraArgs.allowedHosts` | 허용할 호스트명 | `""` |
|
||||||
"""
|
|
||||||
JWT access_token의 groups 클레임에서 mlflow-team-* 그룹을 읽어
|
|
||||||
workspace 이름 목록을 반환한다.
|
|
||||||
|
|
||||||
예: ["mlflow-team-ds", "mlflow"] → ["team-ds"]
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
payload = access_token.split(".")[1]
|
|
||||||
payload += "=" * (4 - len(payload) % 4)
|
|
||||||
claims = json.loads(base64.b64decode(payload))
|
|
||||||
except Exception:
|
|
||||||
return []
|
|
||||||
groups = claims.get("groups", [])
|
|
||||||
return [
|
|
||||||
g[len("mlflow-"):]
|
|
||||||
for g in groups
|
|
||||||
if g.startswith("mlflow-team-")
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
kubectl create configmap mlflow-workspace-plugin -n mlflow \
|
|
||||||
--from-file=mlflow_workspace_detector.py=mlflow_workspace_detector.py
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 배포 방법
|
### 로깅
|
||||||
|
|
||||||
```sh
|
| 파라미터 | 설명 | 기본값 |
|
||||||
git clone https://github.com/paasup/dip-catalog.git
|
|---------|------|--------|
|
||||||
cd dip-catalog
|
| `log.enabled` | MLflow gunicorn 로깅 활성화 | `true` |
|
||||||
|
| `log.level` | 로그 레벨 | `info` |
|
||||||
|
|
||||||
# 신규 설치
|
> `extraArgs.uvicornOpts` 사용 시 `log.enabled: false` 필수 (gunicorn/uvicorn 충돌 방지)
|
||||||
helm install mlflow manifests/helm/mlflow/1.9.0/ \
|
|
||||||
-f manifests/helm/mlflow/1.9.0/custom-values.yaml \
|
|
||||||
-n mlflow --create-namespace
|
|
||||||
|
|
||||||
# 업그레이드
|
|
||||||
helm upgrade mlflow manifests/helm/mlflow/1.9.0/ \
|
|
||||||
-f manifests/helm/mlflow/1.9.0/custom-values.yaml \
|
|
||||||
-n mlflow
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. custom-values.yaml 설명
|
### 네트워크
|
||||||
|
|
||||||
### 3.1 이미지 설정
|
| 파라미터 | 설명 | 기본값 |
|
||||||
|
|---------|------|--------|
|
||||||
OIDC 플러그인이 포함된 커스텀 이미지를 사용한다.
|
| `service.type` | 서비스 타입 | `ClusterIP` |
|
||||||
|
| `service.port` | 서비스 포트 | `80` |
|
||||||
```yaml
|
| `ingress.enabled` | Ingress 활성화 여부 | `false` |
|
||||||
image:
|
| `ingress.className` | Ingress class 이름 | `""` |
|
||||||
repository: paasup/mlflow
|
| `ingress.hosts` | Ingress 호스트 목록 | `[]` |
|
||||||
tag: "v3.11.1-oidc"
|
| `ingress.tls` | TLS 설정 목록 | `[]` |
|
||||||
|
|
||||||
initImages:
|
|
||||||
mlflowDbMigration:
|
|
||||||
repository: paasup/mlflow
|
|
||||||
tag: "v3.11.1-oidc"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 3.2 OIDC 환경변수
|
### 리소스
|
||||||
|
|
||||||
```yaml
|
| 파라미터 | 설명 | 기본값 |
|
||||||
extraEnvVars:
|
|---------|------|--------|
|
||||||
SSL_CERT_FILE: "/etc/ssl/certs/custom-ca.crt"
|
| `resources.limits.cpu` | CPU 상한 | `""` |
|
||||||
OIDC_CLIENT_ID: "mlflow"
|
| `resources.limits.memory` | 메모리 상한 | `""` |
|
||||||
OIDC_DISCOVERY_URL: "https://keycloak.example.org/realms/paasup/.well-known/openid-configuration"
|
| `resources.requests.cpu` | CPU 요청량 | `""` |
|
||||||
OIDC_REDIRECT_URI: "https://mlflow.example.org/callback"
|
| `resources.requests.memory` | 메모리 요청량 | `""` |
|
||||||
OIDC_SCOPE: "openid email profile"
|
|
||||||
OIDC_GROUPS_ATTRIBUTE: "groups"
|
|
||||||
OIDC_GROUP_NAME: "mlflow"
|
|
||||||
OIDC_ADMIN_GROUP_NAME: "mlflow-admin"
|
|
||||||
OIDC_USERS_DB_URI: "postgresql://mlflow:mlflow1234@mlflow-postgresql:5432/mlflow"
|
|
||||||
DEFAULT_MLFLOW_PERMISSION: "READ"
|
|
||||||
AUTOMATIC_LOGIN_REDIRECT: "true"
|
|
||||||
OIDC_ALEMBIC_VERSION_TABLE: "mlflow_oidc_alembic_version"
|
|
||||||
```
|
|
||||||
|
|
||||||
**주의사항**
|
|
||||||
|
|
||||||
| 항목 | 올바른 값 | 잘못된 값 | 이유 |
|
|
||||||
|------|-----------|-----------|------|
|
|
||||||
| `SSL_CERT_FILE` | `SSL_CERT_FILE` | `REQUESTS_CA_BUNDLE` | mlflow-oidc-auth는 httpx를 사용하며 httpx는 `REQUESTS_CA_BUNDLE`을 인식하지 않음 |
|
|
||||||
| `OIDC_SCOPE` | `"openid email profile"` | `"openid,email,profile"` | OAuth2 RFC 6749 표준: 스코프는 공백으로 구분 |
|
|
||||||
| `OIDC_REDIRECT_URI` | `.../callback` | `.../oidc/callback` | auth_router에 prefix가 없어 실제 경로는 `/callback` |
|
|
||||||
| `OIDC_ALEMBIC_VERSION_TABLE` | `"mlflow_oidc_alembic_version"` | 기본값(`alembic_version`) | MLflow와 mlflow-oidc-auth가 동일한 테이블 사용 시 마이그레이션 충돌 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 3.3 Multi-Tenant Workspace 환경변수
|
### 확장
|
||||||
|
|
||||||
```yaml
|
| 파라미터 | 설명 | 기본값 |
|
||||||
extraEnvVars:
|
|---------|------|--------|
|
||||||
MLFLOW_ENABLE_WORKSPACES: "true"
|
| `extraEnvVars` | 추가 환경변수 맵 (`KEY: VALUE` 형식) | `{}` |
|
||||||
OIDC_WORKSPACE_DEFAULT_PERMISSION: "EDIT"
|
| `extraSecretNamesForEnvFrom` | 환경변수로 주입할 Secret 이름 목록 | `[]` |
|
||||||
OIDC_WORKSPACE_DETECTION_PLUGIN: "mlflow_workspace_detector"
|
| `extraVolumes` | 추가 볼륨 목록 | `[]` |
|
||||||
PYTHONPATH: "/opt/mlflow-plugins"
|
| `extraVolumeMounts` | 추가 볼륨 마운트 목록 | `[]` |
|
||||||
WORKSPACE_CACHE_MAX_SIZE: "1024"
|
| `extraArgs` | MLflow 서버 추가 인수 맵 | `{}` |
|
||||||
WORKSPACE_CACHE_TTL_SECONDS: "300"
|
|
||||||
PERMISSION_SOURCE_ORDER: "user,group,regex,group-regex"
|
|
||||||
```
|
|
||||||
|
|
||||||
| 항목 | 설명 |
|
|
||||||
|------|------|
|
|
||||||
| `MLFLOW_ENABLE_WORKSPACES` | workspace 기능 활성화 |
|
|
||||||
| `OIDC_WORKSPACE_DEFAULT_PERMISSION` | workspace 첫 접근 시 자동 부여 권한. `EDIT` 설정 시 별도 권한 설정 없이 팀원 전원 편집 가능 |
|
|
||||||
| `OIDC_WORKSPACE_DETECTION_PLUGIN` | workspace 감지 플러그인 모듈명 (함수명 미포함, 모듈명만 지정) |
|
|
||||||
| `PYTHONPATH` | ConfigMap으로 마운트된 플러그인 파일 경로를 Python import 경로에 추가 |
|
|
||||||
| `WORKSPACE_CACHE_MAX_SIZE` | workspace 목록 캐시 최대 항목 수 |
|
|
||||||
| `WORKSPACE_CACHE_TTL_SECONDS` | workspace 목록 캐시 TTL (초) |
|
|
||||||
| `PERMISSION_SOURCE_ORDER` | 권한 판단 순서: 개인 → 그룹 → 정규식 → 그룹-정규식 |
|
|
||||||
|
|
||||||
#### Workspace 동작 방식
|
|
||||||
|
|
||||||
1. 사용자가 로그인하면 `OIDC_WORKSPACE_DETECTION_PLUGIN`으로 지정된 모듈의 `get_user_workspaces(access_token)` 함수 호출
|
|
||||||
2. 함수가 반환한 workspace 이름 목록으로 workspace 자동 생성 (최초 접근 시)
|
|
||||||
3. `OIDC_WORKSPACE_DEFAULT_PERMISSION` 권한을 해당 사용자에게 자동 부여
|
|
||||||
4. 이후 사용자는 자신의 workspace 내에서만 실험·모델 접근 가능
|
|
||||||
|
|
||||||
#### 플러그인 주의사항
|
|
||||||
|
|
||||||
- `OIDC_WORKSPACE_DETECTION_PLUGIN`에는 **모듈명만** 지정한다. (`mlflow_workspace_detector.get_user_workspaces` 형태 불가)
|
|
||||||
- 호출되는 함수 이름은 `get_user_workspaces`로 고정되어 있다.
|
|
||||||
- 함수 인자로 전달되는 `access_token`은 **JWT 문자열**이며, 플러그인 내부에서 base64 디코딩이 필요하다.
|
|
||||||
- 플러그인 오류 발생 시 exception이 조용히 처리되어 workspace 목록이 빈 배열로 반환된다. 로그에서 `WARNING` 레벨로 확인 가능하다.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.4 OIDC App 활성화
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
extraArgs:
|
|
||||||
appName: "oidc-auth"
|
|
||||||
uvicornOpts: "--timeout-keep-alive 600"
|
|
||||||
allowedHosts: "mlflow.example.org"
|
|
||||||
|
|
||||||
log:
|
|
||||||
enabled: false # uvicornOpts 사용 시 반드시 false (gunicorn/uvicorn 충돌 방지)
|
|
||||||
|
|
||||||
auth:
|
|
||||||
enabled: false # mlflow-oidc-auth가 자체 인증 처리
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.5 Secret 참조
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
extraSecretNamesForEnvFrom:
|
|
||||||
- mlflow-oidc-secret # OIDC_CLIENT_SECRET, SECRET_KEY, MLFLOW_WEBHOOK_SECRET_ENCRYPTION_KEY 포함
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.6 볼륨 마운트
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
extraVolumes:
|
|
||||||
- name: keycloak-ca-cert
|
|
||||||
configMap:
|
|
||||||
name: keycloak-ca-cert
|
|
||||||
- name: workspace-plugin
|
|
||||||
configMap:
|
|
||||||
name: mlflow-workspace-plugin
|
|
||||||
|
|
||||||
extraVolumeMounts:
|
|
||||||
- name: keycloak-ca-cert
|
|
||||||
mountPath: /etc/ssl/certs/custom-ca.crt
|
|
||||||
subPath: ca.crt
|
|
||||||
readOnly: true
|
|
||||||
- name: workspace-plugin
|
|
||||||
mountPath: /opt/mlflow-plugins
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.7 OIDC Auth Middleware 패치
|
|
||||||
|
|
||||||
`mlflow-oidc-auth` 플러그인의 `auth_middleware.py`를 차트에 포함된 버전으로 교체한다.
|
|
||||||
워크스페이스 지원(`x-mlflow-workspace` 헤더 처리) 등 업스트림 수정 사항을 반영한다.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
oidcAuthPatch:
|
|
||||||
enabled: true
|
|
||||||
mountPath: "/usr/local/lib/python3.11/site-packages/mlflow_oidc_auth/middleware/auth_middleware.py"
|
|
||||||
```
|
|
||||||
|
|
||||||
파일 소스: `files/auth_middleware.py`
|
|
||||||
|
|
||||||
> **Python 버전 확인**: 컨테이너 이미지의 Python 버전이 다를 경우 `mountPath`를 수정한다.
|
|
||||||
>
|
|
||||||
> ```sh
|
|
||||||
> kubectl exec -n mlflow <pod> -- python -c \
|
|
||||||
> "import mlflow_oidc_auth.middleware.auth_middleware as m; print(m.__file__)"
|
|
||||||
> ```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.8 Ingress 설정
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: "kong"
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "selfsigned-issuer"
|
|
||||||
cert-manager.io/duration: 8760h
|
|
||||||
cert-manager.io/renew-before: 720h
|
|
||||||
hosts:
|
|
||||||
- host: mlflow.example.org
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: ImplementationSpecific
|
|
||||||
tls:
|
|
||||||
- secretName: mlflow-tls-secret
|
|
||||||
hosts:
|
|
||||||
- mlflow.example.org
|
|
||||||
```
|
|
||||||
|
|
||||||
`mlflow.example.org`를 실제 도메인으로 변경한다.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.9 PostgreSQL 설정
|
|
||||||
|
|
||||||
내장 PostgreSQL을 사용한다.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
postgresql:
|
|
||||||
enabled: true
|
|
||||||
auth:
|
|
||||||
username: mlflow
|
|
||||||
password: mlflow1234 # 변경 권장
|
|
||||||
database: mlflow
|
|
||||||
image:
|
|
||||||
repository: bitnamilegacy/postgresql
|
|
||||||
primary:
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
```
|
|
||||||
|
|
||||||
`OIDC_USERS_DB_URI`도 동일한 접속 정보를 사용한다.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
extraEnvVars:
|
|
||||||
OIDC_USERS_DB_URI: "postgresql://mlflow:mlflow1234@mlflow-postgresql:5432/mlflow"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.10 S3 (MinIO) 설정
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
artifactRoot:
|
|
||||||
proxiedArtifactStorage: true
|
|
||||||
defaultArtifactsDestination: "s3://mlflow/artifacts"
|
|
||||||
s3:
|
|
||||||
enabled: true
|
|
||||||
bucket: mlflow
|
|
||||||
path: artifacts
|
|
||||||
awsAccessKeyId: "adminuser" # MinIO access key
|
|
||||||
awsSecretAccessKey: "adminuser" # MinIO secret key
|
|
||||||
|
|
||||||
extraEnvVars:
|
|
||||||
MLFLOW_S3_ENDPOINT_URL: "http://minio.minio.svc.cluster.local:9000"
|
|
||||||
MLFLOW_S3_IGNORE_TLS: "true"
|
|
||||||
```
|
|
||||||
|
|
||||||
외부 MinIO 사용 시 `MLFLOW_S3_ENDPOINT_URL`을 해당 엔드포인트로 변경한다.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. 배포 검증
|
|
||||||
|
|
||||||
### 4.1 OIDC 인증 검증
|
|
||||||
|
|
||||||
1. `https://mlflow.example.org` 접속 → Keycloak 로그인 페이지로 자동 리다이렉트 확인
|
|
||||||
2. `mlflow` 그룹 사용자로 로그인 → MLflow UI 정상 진입 확인
|
|
||||||
3. `mlflow-admin` 그룹 사용자로 로그인 → 관리자 메뉴 접근 확인
|
|
||||||
4. `mlflow` 그룹 미소속 사용자 로그인 → 접근 거부 확인
|
|
||||||
|
|
||||||
### 4.2 Workspace 격리 검증
|
|
||||||
|
|
||||||
| 사용자 | 소속 그룹 | 기대 workspace |
|
|
||||||
|--------|-----------|----------------|
|
|
||||||
| `mlflow-test` | `mlflow`, `mlflow-team-ds` | `team-ds` |
|
|
||||||
| `mlflow-ops-test` | `mlflow`, `mlflow-team-mlops` | `team-mlops` |
|
|
||||||
|
|
||||||
1. `mlflow-test`로 로그인 → `team-ds` workspace 자동 생성 확인
|
|
||||||
2. `mlflow-ops-test`로 로그인 → `team-mlops` workspace 자동 생성 확인
|
|
||||||
3. 각 사용자가 상대방 workspace의 실험·모델에 접근 불가 확인
|
|
||||||
|
|||||||
Reference in New Issue
Block a user