diff --git a/manifests/helm/mlflow/1.9.0/BUILD-README.md b/manifests/helm/mlflow/1.9.0/BUILD-README.md new file mode 100644 index 0000000..8c06477 --- /dev/null +++ b/manifests/helm/mlflow/1.9.0/BUILD-README.md @@ -0,0 +1,84 @@ +# mlflow 버전 갱신 가이드 + +> **카탈로그 업데이트 방식**: 기존 버전 디렉토리는 유지하고, 신규 버전 디렉토리를 새로 생성한다. +> `manifests/helm/mlflow//` 디렉토리를 직접 추가하는 방식으로 관리한다. + +## 1. git 작업 환경 구성 + +- dip-catalog git 다운로드 +```sh +git clone https://github.com/paasup/dip-catalog.git +``` + +- 작업 브랜치로 체크아웃 +```sh +git checkout -b update-mlflow/ +``` + +## 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 +``` + +### 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="" +tar xzvf mlflow-.tgz -C --strip-components=1 + +# 불필요한 파일 삭제 +rm mlflow-.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 diff --git a/manifests/helm/mlflow/1.9.0/CUSTOM-README.md b/manifests/helm/mlflow/1.9.0/CUSTOM-README.md new file mode 100644 index 0000000..1835f34 --- /dev/null +++ b/manifests/helm/mlflow/1.9.0/CUSTOM-README.md @@ -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="" \ + --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 -- 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의 실험·모델에 접근 불가 확인 diff --git a/manifests/helm/mlflow/1.9.0/README.md b/manifests/helm/mlflow/1.9.0/README.md index dd78b1c..4af7133 100644 --- a/manifests/helm/mlflow/1.9.0/README.md +++ b/manifests/helm/mlflow/1.9.0/README.md @@ -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로 멀티테넌시를 구성하는 가이드이다. -커스텀 이미지 `paasup/mlflow:v3.11.1-oidc`에 OIDC 플러그인이 포함되어 있다. +MLflow는 머신러닝 실험 추적, 모델 패키징, 배포를 위한 오픈소스 플랫폼이다. + +- **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` -- 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 -``` +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `image.repository` | MLflow 이미지 저장소 | `paasup/mlflow` | +| `image.tag` | 이미지 태그 | `v3.11.1-oidc` | +| `image.pullPolicy` | 이미지 pull 정책 | `IfNotPresent` | +| `initImages.dbchecker.tag` | DB 연결 확인 init 컨테이너 태그 | `1.37` | +| `initImages.mlflowDbMigration.repository` | DB 마이그레이션 init 컨테이너 이미지 | `paasup/mlflow` | +| `initImages.iniFileInitializer.tag` | ini 파일 초기화 init 컨테이너 태그 | `1.37` | +| `replicaCount` | Pod 복제 수 | `1` | --- -### 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 -``` +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `backendStore.databaseMigration` | 시작 시 DB 마이그레이션 실행 여부 | `false` | +| `backendStore.databaseConnectionCheck` | DB 연결 확인 init 컨테이너 활성화 | `false` | +| `backendStore.postgres.enabled` | 외부 PostgreSQL 사용 여부 | `false` | +| `backendStore.mysql.enabled` | 외부 MySQL 사용 여부 | `false` | +| `postgresql.enabled` | 내장 Bitnami PostgreSQL 배포 여부 | `false` | +| `postgresql.auth.database` | PostgreSQL 데이터베이스 이름 | `mlflow` | --- -### 1.3 OIDC Secret 생성 +### Artifact 스토리지 -`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="" \ - --from-literal=MLFLOW_WEBHOOK_SECRET_ENCRYPTION_KEY="$FERNET_KEY" \ - --from-literal=SECRET_KEY="$SECRET_KEY" -``` - -Keycloak Client Secret은 Keycloak Admin Console → Client → Credentials 탭에서 확인한다. +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `artifactRoot.proxiedArtifactStorage` | MLflow 서버를 통한 프록시 아티팩트 접근 활성화 | `false` | +| `artifactRoot.defaultArtifactsDestination` | 기본 아티팩트 저장 경로 | `./mlartifacts` | +| `artifactRoot.s3.enabled` | S3 아티팩트 스토리지 사용 여부 | `false` | +| `artifactRoot.s3.bucket` | S3 버킷 이름 | `""` | +| `artifactRoot.s3.awsAccessKeyId` | S3 액세스 키 | `""` | +| `artifactRoot.s3.awsSecretAccessKey` | S3 시크릿 키 | `""` | +| `artifactRoot.azureBlob.enabled` | Azure Blob Storage 사용 여부 | `false` | +| `artifactRoot.gcs.enabled` | Google Cloud Storage 사용 여부 | `false` | --- -### 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 -``` +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `auth.enabled` | MLflow 기본 인증 활성화 | `false` | +| `auth.adminUsername` | 기본 관리자 계정명 | `admin` | +| `auth.adminPassword` | 기본 관리자 비밀번호 | `password` | +| `extraArgs.appName` | MLflow 앱 플러그인 이름 (`oidc-auth` 설정 시 OIDC 활성화) | `""` | +| `extraArgs.uvicornOpts` | uvicorn 추가 옵션 | `""` | +| `extraArgs.allowedHosts` | 허용할 호스트명 | `""` | --- -## 2. 배포 방법 +### 로깅 -```sh -git clone https://github.com/paasup/dip-catalog.git -cd dip-catalog +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `log.enabled` | MLflow gunicorn 로깅 활성화 | `true` | +| `log.level` | 로그 레벨 | `info` | -# 신규 설치 -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 -``` +> `extraArgs.uvicornOpts` 사용 시 `log.enabled: false` 필수 (gunicorn/uvicorn 충돌 방지) --- -## 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" -``` +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `service.type` | 서비스 타입 | `ClusterIP` | +| `service.port` | 서비스 포트 | `80` | +| `ingress.enabled` | Ingress 활성화 여부 | `false` | +| `ingress.className` | Ingress class 이름 | `""` | +| `ingress.hosts` | Ingress 호스트 목록 | `[]` | +| `ingress.tls` | TLS 설정 목록 | `[]` | --- -### 3.2 OIDC 환경변수 +### 리소스 -```yaml -extraEnvVars: - SSL_CERT_FILE: "/etc/ssl/certs/custom-ca.crt" - 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가 동일한 테이블 사용 시 마이그레이션 충돌 | +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `resources.limits.cpu` | CPU 상한 | `""` | +| `resources.limits.memory` | 메모리 상한 | `""` | +| `resources.requests.cpu` | CPU 요청량 | `""` | +| `resources.requests.memory` | 메모리 요청량 | `""` | --- -### 3.3 Multi-Tenant Workspace 환경변수 +### 확장 -```yaml -extraEnvVars: - 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` | 권한 판단 순서: 개인 → 그룹 → 정규식 → 그룹-정규식 | - -#### 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 -- 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의 실험·모델에 접근 불가 확인 +| 파라미터 | 설명 | 기본값 | +|---------|------|--------| +| `extraEnvVars` | 추가 환경변수 맵 (`KEY: VALUE` 형식) | `{}` | +| `extraSecretNamesForEnvFrom` | 환경변수로 주입할 Secret 이름 목록 | `[]` | +| `extraVolumes` | 추가 볼륨 목록 | `[]` | +| `extraVolumeMounts` | 추가 볼륨 마운트 목록 | `[]` | +| `extraArgs` | MLflow 서버 추가 인수 맵 | `{}` |